feat(events): decrypt event responses (#2056)

This commit is contained in:
vini
2025-11-19 10:17:21 -03:00
committed by GitHub
parent b4863b624e
commit 40f128d88a
4 changed files with 154 additions and 5 deletions
+101 -2
View File
@@ -12,6 +12,7 @@ import type {
RequestJoinMethod,
SignalKeyStoreWithTransaction,
SignalRepositoryWithLIDStore,
SocketConfig,
WAMessage,
WAMessageKey
} from '../Types'
@@ -23,12 +24,13 @@ import {
isHostedPnUser,
isJidBroadcast,
isJidStatusBroadcast,
isLidUser,
jidDecode,
jidEncode,
jidNormalizedUser
} from '../WABinary'
import { aesDecryptGCM, hmacSign } from './crypto'
import { toNumber } from './generics'
import { getKeyAuthor, toNumber } from './generics'
import { downloadAndProcessHistorySyncNotification } from './history'
import type { ILogger } from './logger'
@@ -41,6 +43,7 @@ type ProcessMessageContext = {
logger?: ILogger
options: RequestInit
signalRepository: SignalRepositoryWithLIDStore
getMessage: SocketConfig['getMessage']
}
const REAL_MSG_STUB_TYPES = new Set([
@@ -144,6 +147,17 @@ type PollContext = {
voterJid: string
}
type EventContext = {
/** normalised jid of the person that created the event */
eventCreatorJid: string
/** ID of the event creation message */
eventMsgId: string
/** event creation message enc key */
eventEncKey: Uint8Array
/** jid of the person that responded */
responderJid: string
}
/**
* Decrypt a poll vote
* @param vote encrypted vote
@@ -174,6 +188,36 @@ export function decryptPollVote(
}
}
/**
* Decrypt an event response
* @param response encrypted event response
* @param ctx additional info about the event required for decryption
* @returns event response message
*/
export function decryptEventResponse(
{ encPayload, encIv }: proto.Message.IPollEncValue,
{ eventCreatorJid, eventMsgId, eventEncKey, responderJid }: EventContext
) {
const sign = Buffer.concat([
toBinary(eventMsgId),
toBinary(eventCreatorJid),
toBinary(responderJid),
toBinary('Event Response'),
new Uint8Array([1])
])
const key0 = hmacSign(eventEncKey, new Uint8Array(32), 'sha256')
const decKey = hmacSign(sign, key0, 'sha256')
const aad = toBinary(`${eventMsgId}\u0000${responderJid}`)
const decrypted = aesDecryptGCM(encPayload!, decKey, encIv!, aad)
return proto.Message.EventResponseMessage.decode(decrypted)
function toBinary(txt: string) {
return Buffer.from(txt)
}
}
const processMessage = async (
message: WAMessage,
{
@@ -184,7 +228,8 @@ const processMessage = async (
signalRepository,
keyStore,
logger,
options
options,
getMessage
}: ProcessMessageContext
) => {
const meId = creds.me!.id
@@ -363,6 +408,60 @@ const processMessage = async (
key: content.reactionMessage?.key!
}
])
} else if (content?.encEventResponseMessage) {
const encEventResponse = content.encEventResponseMessage
const creationMsgKey = encEventResponse.eventCreationMessageKey!
// we need to fetch the event creation message to get the event enc key
const eventMsg = await getMessage(creationMsgKey)
if (eventMsg) {
try {
const meIdNormalised = jidNormalizedUser(meId)
// all jids need to be PN
const eventCreatorKey = creationMsgKey.participant || creationMsgKey.remoteJid!
const eventCreatorPn = isLidUser(eventCreatorKey)
? await signalRepository.lidMapping.getPNForLID(eventCreatorKey)
: eventCreatorKey
const eventCreatorJid = getKeyAuthor(
{ remoteJid: jidNormalizedUser(eventCreatorPn!), fromMe: meIdNormalised === eventCreatorPn },
meIdNormalised
)
const responderJid = getKeyAuthor(message.key, meIdNormalised)
const eventEncKey = eventMsg?.messageContextInfo?.messageSecret
if (!eventEncKey) {
logger?.warn({ creationMsgKey }, 'event response: missing messageSecret for decryption')
} else {
const responseMsg = decryptEventResponse(encEventResponse, {
eventEncKey,
eventCreatorJid,
eventMsgId: creationMsgKey.id!,
responderJid
})
const eventResponse = {
eventResponseMessageKey: message.key,
senderTimestampMs: responseMsg.timestampMs!,
response: responseMsg
}
ev.emit('messages.update', [
{
key: creationMsgKey,
update: {
eventResponses: [eventResponse]
}
}
])
}
} catch (err) {
logger?.warn({ err, creationMsgKey }, 'failed to decrypt event response')
}
} else {
logger?.warn({ creationMsgKey }, 'event creation message not found, cannot decrypt response')
}
} else if (message.messageStubType) {
const jid = message.key?.remoteJid!
//let actor = whatsappID (message.participant)