Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion msgsecret.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,19 @@ func (cli *Client) decryptMsgSecret(ctx context.Context, msg *events.Message, us
if err != nil {
return nil, err
}
baseEncKey, origSender, err := cli.Store.MsgSecrets.GetMessageSecret(ctx, msg.Info.Chat, origSender, origMsgKey.GetID())
// GetMessageSecret transparently resolves @lid↔@s.whatsapp.net via
// whatsmeow_lid_map and returns whichever sender form the secret was
// originally stored under (realSender). We intentionally *do not*
// override the caller's origSender with realSender here: the HKDF
// context used to derive the symmetric key includes the sender JID
// string, and the peer that encrypted the message used the JID form
// present on the echoed origMsgKey (i.e. the same form we have in
// origSender above). Overriding with a differently-represented
// realSender — which happens after chat migration when the secret
// was stored pre-migration as @s.whatsapp.net but the vote key
// arrives as @lid (or vice-versa) — produces a mismatched HKDF
// context and GCM auth fails.
baseEncKey, realSender, err := cli.Store.MsgSecrets.GetMessageSecret(ctx, msg.Info.Chat, origSender, origMsgKey.GetID())
if err != nil {
return nil, fmt.Errorf("failed to get original message secret key: %w", err)
}
Expand All @@ -108,6 +120,16 @@ func (cli *Client) decryptMsgSecret(ctx context.Context, msg *events.Message, us
secretKey, additionalData := generateMsgSecretKey(useCase, msg.Info.Sender, origMsgKey.GetID(), origSender, baseEncKey)
plaintext, err := gcmutil.Decrypt(secretKey, encrypted.GetEncIV(), encrypted.GetEncPayload(), additionalData)
if err != nil {
// Fall back to realSender (legacy behaviour) in case the stored
// form actually is the one the peer used — e.g. secrets persisted
// by an older whatsmeow version that only ever stored the phone
// representation.
if !realSender.IsEmpty() && realSender != origSender {
secretKey2, additionalData2 := generateMsgSecretKey(useCase, msg.Info.Sender, origMsgKey.GetID(), realSender, baseEncKey)
if plaintext2, err2 := gcmutil.Decrypt(secretKey2, encrypted.GetEncIV(), encrypted.GetEncPayload(), additionalData2); err2 == nil {
return plaintext2, nil
}
}
return nil, fmt.Errorf("failed to decrypt secret message: %w", err)
}
return plaintext, nil
Expand Down