feat(secret-encrypted): decrypt poll-edit / poll-add-option / event-edit envelopes - #645
Conversation
…dit envelopes secret_encrypted_message previously only decrypted MESSAGE_EDIT; the other SecretEncType envelope variants were dropped. They share the same shape (decrypt to a Message with an empty AAD) and differ only in the HKDF use-case secret, so this generalizes the path to all supported kinds. - wacore: decrypt_secret_encrypted(+_with_fallback) over ModificationType; decrypt_message_edit now delegates. - features: SecretEncKind + extract_secret_encrypted + decrypt_secret_encrypted (+fallback); the MESSAGE_EDIT-only extract_envelope delegates to the general extractor. MESSAGE_SCHEDULE/UNKNOWN are rejected (no use-case secret). Verified against WA Web WAWebAddonEncryption (E() use-case map + empty-AAD function g) and mirrors whatsmeow fcbcaad5. Kept consumer-driven (no auto dispatch) since decryption needs the parent message's messageSecret. Encrypted reactions/comments/event-responses use separate proto structures and are out of scope.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds a generic secret-encrypted envelope extraction and decryption path (feature + wacore), refactors MESSAGE_EDIT to use it, normalizes target sender resolution, broadens exports to expose the new API, and extends tests to cover IV validation, kind filtering, fallback deduping, and a PollEdit roundtrip. ChangesSecret-encrypted envelope handling
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/features/message_edit.rs (1)
75-119: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueConsider consolidating duplicate fallback dedup logic.
Look, I need my code to be efficient and maintainable at scale. The JID normalization and fallback deduplication logic at lines 93-110 is essentially duplicated at lines 355-369.
decrypt_with_fallbackcould delegate todecrypt_secret_encrypted_with_fallbackwithSecretEncKind::MessageEditinstead of reimplementing the same pattern.This isn't blocking — the code works correctly — but when we're connecting billions of people, we can't afford diverging implementations of the same logic.
♻️ Suggested consolidation
pub fn decrypt_with_fallback( enc_payload: &[u8], enc_iv: &[u8], message_secret: &[u8], original_msg_id: &str, original_sender_jid: &Jid, editor_jid: &Jid, fallback_original_sender: Option<&Jid>, fallback_editor: Option<&Jid>, ) -> Result<wa::Message> { - let primary_orig = original_sender_jid.to_non_ad().to_string(); - let primary_editor = editor_jid.to_non_ad().to_string(); - let primary = MessageEditContext { - original_msg_id, - original_sender_jid: &primary_orig, - editor_jid: &primary_editor, - }; - - let fb_orig = fallback_original_sender.map(|j| j.to_non_ad().to_string()); - let fb_editor = fallback_editor.map(|j| j.to_non_ad().to_string()); - let fb_orig_resolved = fb_orig.as_deref().unwrap_or(primary.original_sender_jid); - let fb_editor_resolved = fb_editor.as_deref().unwrap_or(primary.editor_jid); - let fallback_ctx = if fb_orig_resolved == primary.original_sender_jid - && fb_editor_resolved == primary.editor_jid - { - None - } else { - Some(MessageEditContext { - original_msg_id, - original_sender_jid: fb_orig_resolved, - editor_jid: fb_editor_resolved, - }) - }; - - message_edit::decrypt_message_edit_with_fallback( + decrypt_secret_encrypted_with_fallback( enc_payload, enc_iv, message_secret, - &primary, - fallback_ctx.as_ref(), + SecretEncKind::MessageEdit, + original_msg_id, + original_sender_jid, + editor_jid, + fallback_original_sender, + fallback_editor, ) }Also applies to: 333-379
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/features/message_edit.rs` around lines 75 - 119, The duplicate JID normalization and fallback deduplication in decrypt_with_fallback should be consolidated by delegating to the existing decrypt_secret_encrypted_with_fallback path using SecretEncKind::MessageEdit instead of reimplementing the same logic; replace the manual normalization/compare/fallback_ctx construction in decrypt_with_fallback with a call to decrypt_secret_encrypted_with_fallback(enc_payload, enc_iv, message_secret, original_msg_id, original_sender_jid, editor_jid, fallback_original_sender, fallback_editor, SecretEncKind::MessageEdit) (or a thin wrapper that adapts those parameters) so the shared logic lives only in decrypt_secret_encrypted_with_fallback and you avoid duplicated normalization and dedup handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/features/mod.rs`:
- Line 41: The crate root is missing exports for symbols re-exported in
features: add EncryptedEdit, SecretEncKind, and SecretEncrypted to the public
re-export list in lib.rs so they are available from the crate root (i.e.,
include these identifiers alongside any existing pub use ... exports in the
top-level re-export section).
---
Outside diff comments:
In `@src/features/message_edit.rs`:
- Around line 75-119: The duplicate JID normalization and fallback deduplication
in decrypt_with_fallback should be consolidated by delegating to the existing
decrypt_secret_encrypted_with_fallback path using SecretEncKind::MessageEdit
instead of reimplementing the same logic; replace the manual
normalization/compare/fallback_ctx construction in decrypt_with_fallback with a
call to decrypt_secret_encrypted_with_fallback(enc_payload, enc_iv,
message_secret, original_msg_id, original_sender_jid, editor_jid,
fallback_original_sender, fallback_editor, SecretEncKind::MessageEdit) (or a
thin wrapper that adapts those parameters) so the shared logic lives only in
decrypt_secret_encrypted_with_fallback and you avoid duplicated normalization
and dedup handling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 51dff6bf-6284-4956-a205-5fae8d0e7c31
📒 Files selected for processing (3)
src/features/message_edit.rssrc/features/mod.rswacore/src/message_edit.rs
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 14bbd4da0b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| pub use media_reupload::{MediaRetryResult, MediaReupload, MediaReuploadRequest}; | ||
|
|
||
| pub use message_edit::EncryptedEdit; | ||
| pub use message_edit::{EncryptedEdit, SecretEncKind, SecretEncrypted}; |
There was a problem hiding this comment.
Re-export the new secret-encrypted types at crate root
This adds SecretEncKind/SecretEncrypted to features, but src/lib.rs still only re-exports EncryptedEdit, so consumers following the existing crate-level API cannot import whatsapp_rust::SecretEncKind or whatsapp_rust::SecretEncrypted. This also violates the repo instruction in AGENTS.md that new features be exposed via src/features/mod.rs and re-exported in src/lib.rs; please add these new public types to the root pub use features::{...} list as well.
Useful? React with 👍 / 👎.
- lib.rs: re-export SecretEncKind/SecretEncrypted at the crate root next to the already-exported EncryptedEdit. - decrypt_with_fallback now delegates to decrypt_secret_encrypted_with_fallback (kind = MessageEdit) instead of duplicating the JID normalization + fallback dedup logic.
Summary
secret_encrypted_messagepreviously only decryptedMESSAGE_EDIT; incomingEVENT_EDIT,POLL_EDITandPOLL_ADD_OPTIONenvelopes were silently dropped as "unsupported". All fourSecretEncTypevariants share the same shape — they decrypt to a fullMessageproto with an empty AAD — and differ only in the use-case secret fed into the HKDF. This generalizes the decrypt path to all of them.Verified against the real WhatsApp Web module
WAWebAddonEncryption(docs/captured-js/WAWeb/Addon/Encryption.js): itsE()maps each kind to a use-case secret (Event Edit/Message Edit/Poll Edit/Poll Add Option), and its functionggives these an empty AAD (onlyPollVote/EventResponsebind stanza+sender). Mirrors whatsmeow'sDecryptSecretEncryptedMessagedispatch (commitfcbcaad5).Design
messageContextInfo.messageSecret, which only the consumer's store has — so the library exposes extract + decrypt primitives, exactly like the existing edit/poll-vote paths.MESSAGE_SCHEDULE/UNKNOWNare rejected — neither WA Web nor whatsmeow assigns them a use-case secret.secret_encrypted_message) and are out of scope here.Changes
wacore/src/message_edit.rs: generaldecrypt_secret_encrypted(+_with_fallback) overModificationType;decrypt_message_editdelegates.src/features/message_edit.rs:SecretEncKind,extract_secret_encrypted,decrypt_secret_encrypted(+fallback). TheMESSAGE_EDIT-onlyextract_envelopenow delegates to the general extractor (no duplication); shared sender-resolution factored into one helper.src/features/mod.rs: re-exportSecretEncKind,SecretEncrypted.Test plan
cargo fmt --allcargo clippy --all --tests(clean)cargo test --workspace --exclude e2e-tests(0 failures)extract_envelopestill only matches MESSAGE_EDIT; full POLL_EDIT extract→resolve-sender→decrypt roundtrip