From f0dcc30e0f0fc991fc2a79f2d73c7d28460a68c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B1=D1=95=D1=82=D1=8F=CF=83=CF=8711?= Date: Sat, 13 Sep 2025 09:04:26 +0100 Subject: [PATCH] fix: handle invalid signatureKeyPublic types in sender-key-state AND fix handling chain key objects for skmsg group message decryption (#1760) * fix: handle invalid signatureKeyPublic types in sender-key-state - added extra check to ensure signatureKeyPublic is either a base64 string or a Buffer - fallback to empty buffer when unexpected object type is received - prevents ERR_INVALID_ARG_TYPE error in Buffer.from * fix: lint * adjusted based on @jlucaso1 recommmendation * fix: handle chain key objects for skmsg group message decryption previously, some sender chain keys were stored or retrieved as plain objects (e.g. { '0': 85, '1': 100, ... }) instead of Buffers. this caused "ERR_INVALID_ARG_TYPE" during initial decryption of skmsg group messages. this fixes any chain key object is converted to a proper Buffer before being passed to SenderChainKey. * fix: add more robust checks and fallbacks * hacky fix(sender-key-state): normalize signing pubkey as Buffer with 0x05 prefix a little hacky fix that ensures getSigningKeyPublic always returns a proper Buffer to satisfy libsignal. adds a fallback to prepend 0x05 when only a 32-byte key is present, avoiding "Invalid public key" errors and TS type issues around Uint8Array vs Buffer. --- src/Signal/Group/sender-chain-key.ts | 6 ++- src/Signal/Group/sender-key-state.ts | 66 +++++++++++++++++----------- src/Socket/messages-recv.ts | 4 +- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/Signal/Group/sender-chain-key.ts b/src/Signal/Group/sender-chain-key.ts index 74e390d1..768a131c 100644 --- a/src/Signal/Group/sender-chain-key.ts +++ b/src/Signal/Group/sender-chain-key.ts @@ -10,12 +10,14 @@ export class SenderChainKey { constructor(iteration: number, chainKey: any) { this.iteration = iteration - if (chainKey instanceof Buffer) { + if (Buffer.isBuffer(chainKey)) { this.chainKey = chainKey } else if (chainKey instanceof Uint8Array) { this.chainKey = Buffer.from(chainKey) + } else if (chainKey && typeof chainKey === 'object') { + this.chainKey = Buffer.from(Object.values(chainKey)) } else { - this.chainKey = Buffer.from(chainKey || []) + this.chainKey = Buffer.alloc(0) } } diff --git a/src/Signal/Group/sender-key-state.ts b/src/Signal/Group/sender-key-state.ts index 47e46e3b..c176a698 100644 --- a/src/Signal/Group/sender-key-state.ts +++ b/src/Signal/Group/sender-key-state.ts @@ -30,17 +30,25 @@ export class SenderKeyState { constructor( id?: number | null, iteration?: number | null, - chainKey?: Uint8Array | null, - signatureKeyPair?: { public: Uint8Array; private: Uint8Array } | null, - signatureKeyPublic?: Uint8Array | null, - signatureKeyPrivate?: Uint8Array | null, + chainKey?: Uint8Array | null | string, + signatureKeyPair?: { public: Uint8Array | string; private: Uint8Array | string } | null, + signatureKeyPublic?: Uint8Array | string | null, + signatureKeyPrivate?: Uint8Array | string | null, senderKeyStateStructure?: SenderKeyStateStructure | null ) { + const toBuffer = (val: any) => { + if (!val) return Buffer.alloc(0) + if (typeof val === 'string') return Buffer.from(val, 'base64') + if (val instanceof Uint8Array || Array.isArray(val)) return Buffer.from(val) + return Buffer.alloc(0) + } + if (senderKeyStateStructure) { - if (!Array.isArray(senderKeyStateStructure.senderMessageKeys)) { - this.senderKeyStateStructure = { ...senderKeyStateStructure, senderMessageKeys: [] } - } else { - this.senderKeyStateStructure = senderKeyStateStructure + this.senderKeyStateStructure = { + ...senderKeyStateStructure, + senderMessageKeys: Array.isArray(senderKeyStateStructure.senderMessageKeys) + ? senderKeyStateStructure.senderMessageKeys + : [] } } else { if (signatureKeyPair) { @@ -52,19 +60,12 @@ export class SenderKeyState { const senderChainKeyStructure: SenderChainKeyStructure = { iteration: iteration || 0, - seed: chainKey || Buffer.alloc(0) + seed: chainKey ? toBuffer(chainKey) : Buffer.alloc(0) } const signingKeyStructure: SenderSigningKeyStructure = { - public: - typeof signatureKeyPublic === 'string' - ? Buffer.from(signatureKeyPublic, 'base64') - : signatureKeyPublic || Buffer.alloc(0) - } - - if (signatureKeyPrivate) { - signingKeyStructure.private = - typeof signatureKeyPrivate === 'string' ? Buffer.from(signatureKeyPrivate, 'base64') : signatureKeyPrivate + public: toBuffer(signatureKeyPublic), + private: signatureKeyPrivate ? toBuffer(signatureKeyPrivate) : undefined } this.senderKeyStateStructure = { @@ -95,16 +96,29 @@ export class SenderKeyState { } public getSigningKeyPublic(): Buffer { - const publicKey = this.senderKeyStateStructure.senderSigningKey.public - if (publicKey instanceof Buffer) { - return publicKey - } else if (publicKey instanceof Uint8Array) { - return Buffer.from(publicKey) - } else if (typeof publicKey === 'string') { - return Buffer.from(publicKey, 'base64') + let key = this.senderKeyStateStructure.senderSigningKey.public + + // normalize into Buffer + if (!(key instanceof Buffer)) { + if (key instanceof Uint8Array) { + key = Buffer.from(key) + } else if (typeof key === 'string') { + key = Buffer.from(key, 'base64') + } else { + key = Buffer.from(key || []) + } } - return Buffer.from(publicKey || []) + const publicKey = key as Buffer + + if (publicKey.length === 32) { + const fixed = Buffer.alloc(33) + fixed[0] = 0x05 + publicKey.copy(fixed, 1) + return fixed + } + + return publicKey } public getSigningKeyPrivate(): Buffer | undefined { diff --git a/src/Socket/messages-recv.ts b/src/Socket/messages-recv.ts index 3307d1f7..a7cd37c5 100644 --- a/src/Socket/messages-recv.ts +++ b/src/Socket/messages-recv.ts @@ -1159,11 +1159,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { const altServer = jidDecode(alt)?.server const lidMapping = signalRepository.getLIDMappingStore() if (altServer === 'lid') { - if (typeof await lidMapping.getPNForLID(alt) == "string") { + if (typeof (await lidMapping.getPNForLID(alt)) === 'string') { await lidMapping.storeLIDPNMapping(alt, msg.key.participant || msg.key.remoteJid!) } } else { - if (typeof await lidMapping.getLIDForPN(alt) == "string") { + if (typeof (await lidMapping.getLIDForPN(alt)) === 'string') { await lidMapping.storeLIDPNMapping(msg.key.participant || msg.key.remoteJid!, alt) } }