feat: replace async crypto with sync Rust WASM (port of Baileys b5c1741)
Surgically applies the changes from WhiskeySockets/Baileys commit b5c1741
("feat: replace async crypto with sync Rust WASM" by jlucaso1) while
preserving all custom modifications (interactive messages, carousels,
albums, sticker packs, native flow buttons, Prometheus metrics, etc).
Changes:
- Replace async hkdf/md5 (Web Crypto API) with sync re-exports from whatsapp-rust-bridge@0.5.2
- Replace LTHash class with LTHashAntiTampering from WASM
- Replace mutationKeys() with expandAppStateKeys() from WASM
- Remove ~25 unnecessary await keywords across crypto call chain
- Update Buffer→Uint8Array types for MediaDecryptionKeyInfo and internal crypto functions
- Make noise handshake, media retry encrypt/decrypt, and reporting token generation synchronous
Performance impact:
- Eliminates Promise overhead on every HKDF/LTHash operation
- Significant improvement during app state sync (hundreds of mutations per reconnection)
- Sync crypto reduces event loop pressure under high session load
Custom code preserved (zero conflicts):
- messages-send.ts: All interactive message, carousel, album, sticker pack logic intact
- Types/Message.ts: All custom types (NativeFlowButton, Carousel, Album, etc.) intact
- All Prometheus metrics, circuit breakers, session TTL logic intact
https://claude.ai/code/session_01Ffc5YrPuqv8N9SwEuSM8mr
This commit is contained in:
@@ -98,10 +98,10 @@ export const getRawMediaUploadData = async (media: WAMediaUpload, mediaType: Med
|
||||
}
|
||||
|
||||
/** generates all the keys required to encrypt/decrypt & sign a media message */
|
||||
export async function getMediaKeys(
|
||||
export function getMediaKeys(
|
||||
buffer: Uint8Array | string | null | undefined,
|
||||
mediaType: MediaType
|
||||
): Promise<MediaDecryptionKeyInfo> {
|
||||
): MediaDecryptionKeyInfo {
|
||||
if (!buffer) {
|
||||
throw new Boom('Cannot derive from empty media key')
|
||||
}
|
||||
@@ -111,7 +111,7 @@ export async function getMediaKeys(
|
||||
}
|
||||
|
||||
// expand using HKDF to 112 bytes, also pass in the relevant app info
|
||||
const expandedMediaKey = await hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) })
|
||||
const expandedMediaKey = hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) })
|
||||
return {
|
||||
iv: expandedMediaKey.slice(0, 16),
|
||||
cipherKey: expandedMediaKey.slice(16, 48),
|
||||
@@ -400,7 +400,7 @@ export const encryptedStream = async (
|
||||
|
||||
// Use provided mediaKey or generate new one
|
||||
const mediaKey = providedMediaKey || Crypto.randomBytes(32)
|
||||
const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType)
|
||||
const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType)
|
||||
|
||||
const encFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-enc')
|
||||
const encFileWriteStream = createWriteStream(encFilePath)
|
||||
@@ -534,7 +534,7 @@ export const downloadContentFromMessage = async (
|
||||
throw new Boom('No valid media URL or directPath present in message', { statusCode: 400 })
|
||||
}
|
||||
|
||||
const keys = await getMediaKeys(mediaKey, type)
|
||||
const keys = getMediaKeys(mediaKey, type)
|
||||
|
||||
return downloadEncryptedContent(downloadUrl, keys, opts)
|
||||
}
|
||||
@@ -896,12 +896,12 @@ const getMediaRetryKey = (mediaKey: Buffer | Uint8Array) => {
|
||||
/**
|
||||
* Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
|
||||
*/
|
||||
export const encryptMediaRetryRequest = async (key: WAMessageKey, mediaKey: Buffer | Uint8Array, meId: string) => {
|
||||
export const encryptMediaRetryRequest = (key: WAMessageKey, mediaKey: Buffer | Uint8Array, meId: string) => {
|
||||
const recp: proto.IServerErrorReceipt = { stanzaId: key.id }
|
||||
const recpBuffer = proto.ServerErrorReceipt.encode(recp).finish()
|
||||
|
||||
const iv = Crypto.randomBytes(12)
|
||||
const retryKey = await getMediaRetryKey(mediaKey)
|
||||
const retryKey = getMediaRetryKey(mediaKey)
|
||||
const ciphertext = aesEncryptGCM(recpBuffer, retryKey, iv, Buffer.from(key.id!))
|
||||
|
||||
const req: BinaryNode = {
|
||||
@@ -971,12 +971,12 @@ export const decodeMediaRetryNode = (node: BinaryNode) => {
|
||||
return event
|
||||
}
|
||||
|
||||
export const decryptMediaRetryData = async (
|
||||
export const decryptMediaRetryData = (
|
||||
{ ciphertext, iv }: { ciphertext: Uint8Array; iv: Uint8Array },
|
||||
mediaKey: Uint8Array,
|
||||
msgId: string
|
||||
) => {
|
||||
const retryKey = await getMediaRetryKey(mediaKey)
|
||||
const retryKey = getMediaRetryKey(mediaKey)
|
||||
const plaintext = aesDecryptGCM(ciphertext, retryKey, iv, Buffer.from(msgId))
|
||||
return proto.MediaRetryNotification.decode(plaintext)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user