Skip to content

Instantly share code, notes, and snippets.

@salman0ansari
Created January 13, 2026 14:05
Show Gist options
  • Select an option

  • Save salman0ansari/9fe9826ff70f7a7d322fadd78b7739b7 to your computer and use it in GitHub Desktop.

Select an option

Save salman0ansari/9fe9826ff70f7a7d322fadd78b7739b7 to your computer and use it in GitHub Desktop.
decrypt call offer whatsmeow
// return the decrypted payload bytes, or nil if no <enc> node was found.
func (cli *Client) decryptCallOffer(ctx context.Context, offerNode *waBinary.Node, callCreator types.JID, timestamp time.Time) ([]byte, error) {
// Find the <enc> node inside the offer
encNode, ok := offerNode.GetOptionalChildByTag("enc")
if !ok {
return nil, nil // No encrypted content
}
content, ok := encNode.Content.([]byte)
if !ok {
return nil, fmt.Errorf("enc node content is not bytes")
}
ag := encNode.AttrGetter()
encType := ag.String("type")
encVersion := ag.Int("v")
cli.Log.Debugf("Decrppting call offer enc node: type=%s, v=%d, size=%d bytes", encType, encVersion, len(content))
// Determine the sender JID for decryption
// Call offers may come from LID (Hidden User Server) or regular JID
senderJID := callCreator
if callCreator.Server == types.DefaultUserServer {
if lid, err := cli.Store.LIDs.GetLIDForPN(ctx, callCreator); err != nil {
cli.Log.Warnf("Failed to get LID for call creator %s: %v", callCreator, err)
} else if !lid.IsEmpty() {
senderJID = lid
cli.Log.Debugf("Using LID %s for decryption instead of %s", lid, callCreator)
}
}
var plaintext []byte
var err error
builder := session.NewBuilderFromSignal(cli.Store, senderJID.SignalAddress(), pbSerializer)
cipher := session.NewCipher(builder, senderJID.SignalAddress())
if encType == "pkmsg" {
preKeyMsg, parseErr := protocol.NewPreKeySignalMessageFromBytes(content, pbSerializer.PreKeySignalMessage, pbSerializer.SignalMessage)
if parseErr != nil {
return nil, fmt.Errorf("failed to parse prekey message: %w", parseErr)
}
plaintext, err = cipher.DecryptMessage(ctx, preKeyMsg)
if cli.AutoTrustIdentity && errors.Is(err, signalerror.ErrUntrustedIdentity) {
cli.Log.Warnf("Got %v error while trying to decrypt call offer from %s, clearing stored identity and retrying", err, senderJID)
if clearErr := cli.clearUntrustedIdentity(ctx, senderJID); clearErr != nil {
return nil, fmt.Errorf("failed to clear untrusted identity: %w", clearErr)
}
// Retry decryption after clearing identity
plaintext, err = cipher.DecryptMessage(ctx, preKeyMsg)
}
if err != nil {
return nil, fmt.Errorf("failed to decrypt prekey message: %w", err)
}
} else if encType == "msg" {
msg, parseErr := protocol.NewSignalMessageFromBytes(content, pbSerializer.SignalMessage)
if parseErr != nil {
return nil, fmt.Errorf("failed to parse signal message: %w", parseErr)
}
plaintext, err = cipher.Decrypt(ctx, msg)
if err != nil {
return nil, fmt.Errorf("failed to decrypt signal message: %w", err)
}
} else if encType == "skmsg" {
groupJID, _ := offerNode.Attrs["group-jid"].(types.JID)
if groupJID.IsEmpty() {
return nil, fmt.Errorf("skmsg without group-jid")
}
senderKeyName := protocol.NewSenderKeyName(groupJID.String(), senderJID.SignalAddress())
groupBuilder := groups.NewGroupSessionBuilder(cli.Store, pbSerializer)
groupCipher := groups.NewGroupCipher(groupBuilder, senderKeyName, cli.Store)
skMsg, parseErr := protocol.NewSenderKeyMessageFromBytes(content, pbSerializer.SenderKeyMessage)
if parseErr != nil {
return nil, fmt.Errorf("failed to parse sender key message: %w", parseErr)
}
plaintext, err = groupCipher.Decrypt(ctx, skMsg)
if err != nil {
return nil, fmt.Errorf("failed to decrypt sender key message: %w", err)
}
} else {
return nil, fmt.Errorf("unknown enc type: %s", encType)
}
plaintext, err = unpadMessage(plaintext, encVersion)
if err != nil {
return nil, fmt.Errorf("failed to unpad message: %w", err)
}
return plaintext, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment