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.
This commit is contained in:
αѕтяσχ11
2025-09-13 09:04:26 +01:00
committed by GitHub
parent 88ec0c4db2
commit f0dcc30e0f
3 changed files with 46 additions and 30 deletions
+4 -2
View File
@@ -10,12 +10,14 @@ export class SenderChainKey {
constructor(iteration: number, chainKey: any) { constructor(iteration: number, chainKey: any) {
this.iteration = iteration this.iteration = iteration
if (chainKey instanceof Buffer) { if (Buffer.isBuffer(chainKey)) {
this.chainKey = chainKey this.chainKey = chainKey
} else if (chainKey instanceof Uint8Array) { } else if (chainKey instanceof Uint8Array) {
this.chainKey = Buffer.from(chainKey) this.chainKey = Buffer.from(chainKey)
} else if (chainKey && typeof chainKey === 'object') {
this.chainKey = Buffer.from(Object.values(chainKey))
} else { } else {
this.chainKey = Buffer.from(chainKey || []) this.chainKey = Buffer.alloc(0)
} }
} }
+40 -26
View File
@@ -30,17 +30,25 @@ export class SenderKeyState {
constructor( constructor(
id?: number | null, id?: number | null,
iteration?: number | null, iteration?: number | null,
chainKey?: Uint8Array | null, chainKey?: Uint8Array | null | string,
signatureKeyPair?: { public: Uint8Array; private: Uint8Array } | null, signatureKeyPair?: { public: Uint8Array | string; private: Uint8Array | string } | null,
signatureKeyPublic?: Uint8Array | null, signatureKeyPublic?: Uint8Array | string | null,
signatureKeyPrivate?: Uint8Array | null, signatureKeyPrivate?: Uint8Array | string | null,
senderKeyStateStructure?: SenderKeyStateStructure | 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 (senderKeyStateStructure) {
if (!Array.isArray(senderKeyStateStructure.senderMessageKeys)) { this.senderKeyStateStructure = {
this.senderKeyStateStructure = { ...senderKeyStateStructure, senderMessageKeys: [] } ...senderKeyStateStructure,
} else { senderMessageKeys: Array.isArray(senderKeyStateStructure.senderMessageKeys)
this.senderKeyStateStructure = senderKeyStateStructure ? senderKeyStateStructure.senderMessageKeys
: []
} }
} else { } else {
if (signatureKeyPair) { if (signatureKeyPair) {
@@ -52,19 +60,12 @@ export class SenderKeyState {
const senderChainKeyStructure: SenderChainKeyStructure = { const senderChainKeyStructure: SenderChainKeyStructure = {
iteration: iteration || 0, iteration: iteration || 0,
seed: chainKey || Buffer.alloc(0) seed: chainKey ? toBuffer(chainKey) : Buffer.alloc(0)
} }
const signingKeyStructure: SenderSigningKeyStructure = { const signingKeyStructure: SenderSigningKeyStructure = {
public: public: toBuffer(signatureKeyPublic),
typeof signatureKeyPublic === 'string' private: signatureKeyPrivate ? toBuffer(signatureKeyPrivate) : undefined
? Buffer.from(signatureKeyPublic, 'base64')
: signatureKeyPublic || Buffer.alloc(0)
}
if (signatureKeyPrivate) {
signingKeyStructure.private =
typeof signatureKeyPrivate === 'string' ? Buffer.from(signatureKeyPrivate, 'base64') : signatureKeyPrivate
} }
this.senderKeyStateStructure = { this.senderKeyStateStructure = {
@@ -95,16 +96,29 @@ export class SenderKeyState {
} }
public getSigningKeyPublic(): Buffer { public getSigningKeyPublic(): Buffer {
const publicKey = this.senderKeyStateStructure.senderSigningKey.public let key = this.senderKeyStateStructure.senderSigningKey.public
if (publicKey instanceof Buffer) {
return publicKey // normalize into Buffer
} else if (publicKey instanceof Uint8Array) { if (!(key instanceof Buffer)) {
return Buffer.from(publicKey) if (key instanceof Uint8Array) {
} else if (typeof publicKey === 'string') { key = Buffer.from(key)
return Buffer.from(publicKey, 'base64') } 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 { public getSigningKeyPrivate(): Buffer | undefined {
+2 -2
View File
@@ -1159,11 +1159,11 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
const altServer = jidDecode(alt)?.server const altServer = jidDecode(alt)?.server
const lidMapping = signalRepository.getLIDMappingStore() const lidMapping = signalRepository.getLIDMappingStore()
if (altServer === 'lid') { 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!) await lidMapping.storeLIDPNMapping(alt, msg.key.participant || msg.key.remoteJid!)
} }
} else { } 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) await lidMapping.storeLIDPNMapping(msg.key.participant || msg.key.remoteJid!, alt)
} }
} }