94a56eab9d
Added // eslint-disable-next-line eqeqeq comments for intentional null/undefined checks: - sender-key-message.ts: checking for null OR undefined parameters - WAM/encode.ts: checking for null OR undefined id - baileys-event-stream.test.ts: renamed unused event parameter to _event These changes are COSMETIC ONLY - no logic changed: - Build still passes ✅ - All tests still work ✅ - API behavior unchanged ✅ The == null pattern is intentionally used to check for BOTH null AND undefined, which is the correct behavior for these optional parameters. https://claude.ai/code/session_015R3U3kiprQiNTTNNt31Sg6
102 lines
3.1 KiB
TypeScript
102 lines
3.1 KiB
TypeScript
import { calculateSignature, verifySignature } from 'libsignal/src/curve'
|
|
import { proto } from '../../../WAProto/index.js'
|
|
import { CiphertextMessage } from './ciphertext-message'
|
|
|
|
interface SenderKeyMessageStructure {
|
|
id: number
|
|
iteration: number
|
|
ciphertext: string | Buffer
|
|
}
|
|
|
|
export class SenderKeyMessage extends CiphertextMessage {
|
|
private readonly SIGNATURE_LENGTH = 64
|
|
private readonly messageVersion: number
|
|
private readonly keyId: number
|
|
private readonly iteration: number
|
|
private readonly ciphertext: Uint8Array
|
|
private readonly signature: Uint8Array
|
|
private readonly serialized: Uint8Array
|
|
|
|
constructor(
|
|
keyId?: number | null,
|
|
iteration?: number | null,
|
|
ciphertext?: Uint8Array | null,
|
|
signatureKey?: Uint8Array | null,
|
|
serialized?: Uint8Array | null
|
|
) {
|
|
super()
|
|
|
|
if (serialized) {
|
|
const version = serialized[0] ?? 0
|
|
const message = serialized.slice(1, serialized.length - this.SIGNATURE_LENGTH)
|
|
const signature = serialized.slice(-1 * this.SIGNATURE_LENGTH)
|
|
const senderKeyMessage = proto.SenderKeyMessage.decode(message).toJSON() as SenderKeyMessageStructure
|
|
|
|
this.serialized = serialized
|
|
this.messageVersion = (version & 0xff) >> 4
|
|
this.keyId = senderKeyMessage.id
|
|
this.iteration = senderKeyMessage.iteration
|
|
this.ciphertext =
|
|
typeof senderKeyMessage.ciphertext === 'string'
|
|
? Buffer.from(senderKeyMessage.ciphertext, 'base64')
|
|
: senderKeyMessage.ciphertext
|
|
this.signature = signature
|
|
} else {
|
|
// eslint-disable-next-line eqeqeq
|
|
if (ciphertext == null || keyId == null || iteration == null || signatureKey == null) {
|
|
throw new Error('Missing required parameters for SenderKeyMessage construction')
|
|
}
|
|
|
|
const version = (((this.CURRENT_VERSION << 4) | this.CURRENT_VERSION) & 0xff) % 256
|
|
const ciphertextBuffer = Buffer.from(ciphertext)
|
|
const message = proto.SenderKeyMessage.encode(
|
|
proto.SenderKeyMessage.create({
|
|
id: keyId,
|
|
iteration: iteration,
|
|
ciphertext: ciphertextBuffer
|
|
})
|
|
).finish()
|
|
|
|
const signature = this.getSignature(signatureKey, Buffer.concat([Buffer.from([version]), message]))
|
|
|
|
this.serialized = Buffer.concat([Buffer.from([version]), message, Buffer.from(signature)])
|
|
this.messageVersion = this.CURRENT_VERSION
|
|
this.keyId = keyId
|
|
this.iteration = iteration
|
|
this.ciphertext = ciphertextBuffer
|
|
this.signature = signature
|
|
}
|
|
}
|
|
|
|
public getKeyId(): number {
|
|
return this.keyId
|
|
}
|
|
|
|
public getIteration(): number {
|
|
return this.iteration
|
|
}
|
|
|
|
public getCipherText(): Uint8Array {
|
|
return this.ciphertext
|
|
}
|
|
|
|
public verifySignature(signatureKey: Uint8Array): void {
|
|
const part1 = this.serialized.slice(0, this.serialized.length - this.SIGNATURE_LENGTH)
|
|
const part2 = this.serialized.slice(-1 * this.SIGNATURE_LENGTH)
|
|
const res = verifySignature(signatureKey, part1, part2)
|
|
if (!res) throw new Error('Invalid signature!')
|
|
}
|
|
|
|
private getSignature(signatureKey: Uint8Array, serialized: Uint8Array): Uint8Array {
|
|
return Buffer.from(calculateSignature(signatureKey, serialized))
|
|
}
|
|
|
|
public serialize(): Uint8Array {
|
|
return this.serialized
|
|
}
|
|
|
|
public getType(): number {
|
|
return 4
|
|
}
|
|
}
|