Amarula.Protocol.Messages.PollCrypto (amarula v0.2.4)
View SourceDecrypt poll votes, ported from Baileys decryptPollVote
(src/Utils/process-message.ts).
A vote's encPayload/encIv are AES-256-GCM encrypted under a key derived from
the poll's message_secret and the ids of the poll, its creator, and the voter:
sign = poll_msg_id ++ poll_creator_jid ++ voter_jid ++ "Poll Vote" ++ <<1>>
key0 = HMAC-SHA256(key=message_secret, data=<<0::256>>) # 32 zero bytes
dec_key= HMAC-SHA256(key=key0, data=sign)
aad = "<poll_msg_id>\0<voter_jid>"
plain = AES-256-GCM-decrypt(encPayload, key=dec_key, iv=encIv, aad)
→ Proto.Message.PollVoteMessage (selectedOptions = SHA-256 option hashes)GCM tag is the trailing 16 bytes of encPayload.
Summary
Functions
Decrypt a poll vote (enc = %Proto.Message.PollEncValue{} or a map with
:encPayload/:encIv). Returns {:ok, %PollVoteMessage{}} (its
selectedOptions are SHA-256 hashes of the chosen option names — match against
Poll.tally/3), or {:error, reason}.
Encrypt a poll vote — the inverse of decrypt_vote/2. selected_option_hashes
is the list of sha256(option_name) for the options being voted for. Returns
%Proto.Message.PollEncValue{encPayload: ct<>tag, encIv: iv} ready to wrap in a
pollUpdateMessage. Uses the same key derivation + AAD as the receive side, so
a vote we send round-trips through decrypt_vote/2.
Types
Functions
Decrypt a poll vote (enc = %Proto.Message.PollEncValue{} or a map with
:encPayload/:encIv). Returns {:ok, %PollVoteMessage{}} (its
selectedOptions are SHA-256 hashes of the chosen option names — match against
Poll.tally/3), or {:error, reason}.
Jid forms must match the sender's
The decryption key is derived from poll_creator_jid and voter_jid — so
they must be the exact jid strings the voter used when encrypting. In a
LID group the voter keys on their LID (the participant on the vote
message), not their PN; passing the PN (or vice-versa) derives the wrong key
and decryption fails ({:error, :decrypt_failed}) — the root of Baileys
#2158. Use the author jid as it appears on the vote's key.participant
(resolve LID↔PN with Amarula.Contacts.pn_for_lid/2 only if you need to
compare identities — not to build this context).
Encrypt a poll vote — the inverse of decrypt_vote/2. selected_option_hashes
is the list of sha256(option_name) for the options being voted for. Returns
%Proto.Message.PollEncValue{encPayload: ct<>tag, encIv: iv} ready to wrap in a
pollUpdateMessage. Uses the same key derivation + AAD as the receive side, so
a vote we send round-trips through decrypt_vote/2.