@violetix/baileys 0.5.6 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1530 -2009
- package/WAProto/index.d.ts +14030 -14586
- package/WAProto/index.js +58426 -88874
- package/lib/Defaults/index.d.ts +2 -7
- package/lib/Defaults/index.js +5 -20
- package/lib/Signal/Group/sender-key-message.js +19 -1
- package/lib/Signal/libsignal.js +82 -29
- package/lib/Signal/xeddsa-fast.js +114 -0
- package/lib/Socket/Client/types.js +6 -8
- package/lib/Socket/Client/websocket.js +3 -38
- package/lib/Socket/business.d.ts +0 -3
- package/lib/Socket/chats.d.ts +0 -3
- package/lib/Socket/chats.js +18 -45
- package/lib/Socket/communities.d.ts +0 -3
- package/lib/Socket/graphql.js +3 -7
- package/lib/Socket/groups.d.ts +0 -3
- package/lib/Socket/groups.js +3 -8
- package/lib/Socket/index.d.ts +15 -17
- package/lib/Socket/messages-recv.d.ts +0 -3
- package/lib/Socket/messages-recv.js +43 -127
- package/lib/Socket/messages-send.d.ts +0 -4
- package/lib/Socket/messages-send.js +63 -191
- package/lib/Socket/newsletter.d.ts +0 -3
- package/lib/Socket/newsletter.js +1 -2
- package/lib/Socket/newsletter.js.map +1 -1
- package/lib/Socket/socket.d.ts +0 -9
- package/lib/Socket/socket.js +53 -346
- package/lib/Store/make-in-memory-store.js +30 -41
- package/lib/Types/Auth.d.ts +2 -60
- package/lib/Types/Bussines.d.ts +2 -9
- package/lib/Types/Call.d.ts +2 -22
- package/lib/Types/Chat.d.ts +1 -2
- package/lib/Types/Contact.d.ts +2 -11
- package/lib/Types/Events.d.ts +2 -41
- package/lib/Types/GroupMetadata.d.ts +2 -36
- package/lib/Types/Message.d.ts +1 -11
- package/lib/Types/Product.d.ts +2 -5
- package/lib/Types/Signal.d.ts +2 -10
- package/lib/Types/Socket.d.ts +2 -33
- package/lib/Types/USync.d.ts +2 -9
- package/lib/Types/index.d.ts +1 -3
- package/lib/Utils/auth-utils.d.ts +2 -3
- package/lib/Utils/auth-utils.js +53 -37
- package/lib/Utils/business.js +11 -25
- package/lib/Utils/chat-utils.js +4 -6
- package/lib/Utils/decode-wa-message.js +2 -2
- package/lib/Utils/event-buffer.js +12 -21
- package/lib/Utils/generics.d.ts +5 -5
- package/lib/Utils/generics.js +7 -25
- package/lib/Utils/history.d.ts +1 -2
- package/lib/Utils/history.js +83 -137
- package/lib/Utils/index.d.ts +0 -4
- package/lib/Utils/index.js +0 -4
- package/lib/Utils/listener-guard.js +0 -2
- package/lib/Utils/message-retry-manager.js +0 -20
- package/lib/Utils/messages-media.d.ts +8 -39
- package/lib/Utils/messages-media.js +89 -218
- package/lib/Utils/messages.d.ts +0 -1
- package/lib/Utils/messages.js +36 -122
- package/lib/Utils/process-message.d.ts +0 -18
- package/lib/Utils/process-message.js +47 -99
- package/lib/Utils/rich-message-utils.js +4 -3
- package/lib/Utils/use-multi-file-auth-state.d.ts +3 -5
- package/lib/Utils/use-multi-file-auth-state.js +6 -27
- package/lib/Utils/use-single-file-auth-state.d.ts +1 -1
- package/lib/Utils/use-single-file-auth-state.js +3 -12
- package/lib/WABinary/types.d.ts +2 -6
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +3 -6
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -3
- package/lib/WAUSync/USyncQuery.d.ts +1 -1
- package/lib/WAUSync/USyncQuery.js +4 -7
- package/package.json +107 -123
- package/lib/Utils/a2ui.d.ts +0 -50
- package/lib/Utils/a2ui.js +0 -168
- package/lib/Utils/anchor-guard.d.ts +0 -71
- package/lib/Utils/anchor-guard.js +0 -404
- package/lib/Utils/connection-manager.d.ts +0 -44
- package/lib/Utils/connection-manager.js +0 -258
- package/lib/Utils/ignore-key.d.ts +0 -51
- package/lib/Utils/ignore-key.js +0 -176
|
@@ -6,7 +6,6 @@ import { createReadStream, createWriteStream, promises as fs, WriteStream } from
|
|
|
6
6
|
import { tmpdir } from 'os';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { Readable, Transform } from 'stream';
|
|
9
|
-
import { pipeline } from 'stream/promises';
|
|
10
9
|
import { URL } from 'url';
|
|
11
10
|
import { proto } from '../../WAProto/index.js';
|
|
12
11
|
import { DEFAULT_ORIGIN, MEDIA_HKDF_KEY_MAPPING, MEDIA_PATH_MAP, NEWSLETTER_MEDIA_PATH_MAP } from '../Defaults/index.js';
|
|
@@ -303,31 +302,9 @@ export const getStream = async (item, opts) => {
|
|
|
303
302
|
if (Buffer.isBuffer(item)) {
|
|
304
303
|
return { stream: toReadable(item), type: 'buffer' };
|
|
305
304
|
}
|
|
306
|
-
// A bare path or URL string is accepted, and the reason is the error it used to produce. The
|
|
307
|
-
// contract below is `Buffer | { stream } | { url }`, so `'stream' in item` throws on a string —
|
|
308
|
-
// and it throws with the path interpolated:
|
|
309
|
-
//
|
|
310
|
-
// TypeError: Cannot use 'in' operator to search for 'stream' in /tmp/8cktb7o7x.webp
|
|
311
|
-
//
|
|
312
|
-
// Measured on a production fleet, 2026-10-10: five of those under [SEND_STICKER]. The message
|
|
313
|
-
// names a file, so it reads as a missing or unreadable one, while the real fault is the SHAPE of
|
|
314
|
-
// the argument — and the file was perfectly readable: `createReadStream(item.url)` at the end of
|
|
315
|
-
// this function handles exactly that string. Widening here costs nothing and removes the whole
|
|
316
|
-
// class, for every caller rather than the one that was caught.
|
|
317
|
-
if (typeof item === 'string') {
|
|
318
|
-
item = { url: item };
|
|
319
|
-
}
|
|
320
|
-
if (!item || typeof item !== 'object') {
|
|
321
|
-
throw new Boom(`media source must be a Buffer, a string, { stream } or { url }, got ${item === null ? 'null' : typeof item}`);
|
|
322
|
-
}
|
|
323
305
|
if ('stream' in item) {
|
|
324
306
|
return { stream: item.stream, type: 'readable' };
|
|
325
307
|
}
|
|
326
|
-
// Named rather than left to throw "Cannot read properties of undefined (reading 'toString')",
|
|
327
|
-
// which says nothing about which field is missing.
|
|
328
|
-
if (item.url === undefined || item.url === null) {
|
|
329
|
-
throw new Boom('media source object has neither `stream` nor `url`');
|
|
330
|
-
}
|
|
331
308
|
const urlStr = item.url.toString();
|
|
332
309
|
if (urlStr.startsWith('data:')) {
|
|
333
310
|
const buffer = Buffer.from(urlStr.split(',')[1], 'base64');
|
|
@@ -370,122 +347,18 @@ export const getHttpStream = async (url, options = {}) => {
|
|
|
370
347
|
const response = await fetch(url.toString(), {
|
|
371
348
|
dispatcher: options.dispatcher,
|
|
372
349
|
method: 'GET',
|
|
373
|
-
headers: options.headers
|
|
374
|
-
signal: options.signal
|
|
350
|
+
headers: options.headers
|
|
375
351
|
});
|
|
376
352
|
if (!response.ok) {
|
|
377
|
-
if (response.body instanceof Readable) {
|
|
378
|
-
response.body.destroy();
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
await response.body?.cancel().catch(() => { });
|
|
382
|
-
}
|
|
383
353
|
throw new Boom(`Failed to fetch stream from ${url}`, { statusCode: response.status, data: { url } });
|
|
384
354
|
}
|
|
385
355
|
// @ts-ignore Node18+ Readable.fromWeb exists
|
|
386
356
|
return response.body instanceof Readable ? response.body : Readable.fromWeb(response.body);
|
|
387
357
|
};
|
|
388
|
-
const
|
|
389
|
-
const SIDECAR_HMAC_SIZE = 10;
|
|
390
|
-
const SIDECAR_IV_SIZE = 16;
|
|
391
|
-
const CONTAINER_SCAN_MAX_BYTES = 1024;
|
|
392
|
-
const SIDECAR_MEDIA_TYPES = new Set(['video', 'audio', 'ptt', 'gif']);
|
|
393
|
-
const readU32 = (bytes, offset) => ((bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]) >>> 0;
|
|
394
|
-
/**
|
|
395
|
-
* Walks the top-level ISO-BMFF boxes (mp4/mov/3gp/m4a) in the leading bytes. A file is
|
|
396
|
-
* streamable only when `moov` precedes `mdat`; advertising a non-faststart mp4 as seekable
|
|
397
|
-
* (by attaching a sidecar) makes the recipient's player fail instead of falling back to a
|
|
398
|
-
* plain download. Non-ISO-BMFF payloads (ogg/opus voice notes) are streamable.
|
|
399
|
-
* Returns true / false, or null while `head` is too short to decide.
|
|
400
|
-
*/
|
|
401
|
-
export const scanStreamable = (head) => {
|
|
402
|
-
if (head.length < 8) {
|
|
403
|
-
return null;
|
|
404
|
-
}
|
|
405
|
-
if (readU32(head, 4) !== 0x66747970 /* ftyp */) {
|
|
406
|
-
return true;
|
|
407
|
-
}
|
|
408
|
-
let offset = 0;
|
|
409
|
-
while (offset + 8 <= head.length) {
|
|
410
|
-
const type = readU32(head, offset + 4);
|
|
411
|
-
if (type === 0x6d6f6f76 /* moov */) {
|
|
412
|
-
return true;
|
|
413
|
-
}
|
|
414
|
-
if (type === 0x6d646174 /* mdat */) {
|
|
415
|
-
return false;
|
|
416
|
-
}
|
|
417
|
-
let size = readU32(head, offset);
|
|
418
|
-
let headerSize = 8;
|
|
419
|
-
if (size === 1) {
|
|
420
|
-
if (offset + 16 > head.length) {
|
|
421
|
-
return null;
|
|
422
|
-
}
|
|
423
|
-
if (readU32(head, offset + 8) !== 0) {
|
|
424
|
-
return true;
|
|
425
|
-
}
|
|
426
|
-
size = readU32(head, offset + 12);
|
|
427
|
-
headerSize = 16;
|
|
428
|
-
}
|
|
429
|
-
else if (size === 0) {
|
|
430
|
-
return true;
|
|
431
|
-
}
|
|
432
|
-
if (size < headerSize) {
|
|
433
|
-
return true;
|
|
434
|
-
}
|
|
435
|
-
offset += size;
|
|
436
|
-
if (offset > CONTAINER_SCAN_MAX_BYTES) {
|
|
437
|
-
return true;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return offset > CONTAINER_SCAN_MAX_BYTES ? true : null;
|
|
441
|
-
};
|
|
442
|
-
/**
|
|
443
|
-
* Streaming sidecar as WhatsApp Web builds it: HMAC-SHA256(macKey) truncated to 10 bytes over
|
|
444
|
-
* each 64 KiB window of iv+ciphertext+mac, with the last 16 bytes of the previous window
|
|
445
|
-
* prepended, so the recipient can verify and play a range without the whole file.
|
|
446
|
-
*/
|
|
447
|
-
class SidecarAccumulator {
|
|
448
|
-
constructor(macKey) {
|
|
449
|
-
this.macKey = macKey;
|
|
450
|
-
this.window = Buffer.alloc(SIDECAR_IV_SIZE + SIDECAR_CHUNK_SIZE);
|
|
451
|
-
this.windowOffset = 0;
|
|
452
|
-
this.totalPushed = 0;
|
|
453
|
-
this.nextChunkStart = 0;
|
|
454
|
-
this.digests = [];
|
|
455
|
-
}
|
|
456
|
-
push(data) {
|
|
457
|
-
let srcOffset = 0;
|
|
458
|
-
while (srcOffset < data.length) {
|
|
459
|
-
const windowEnd = this.nextChunkStart + SIDECAR_IV_SIZE + SIDECAR_CHUNK_SIZE;
|
|
460
|
-
const toCopy = Math.min(windowEnd - this.totalPushed, data.length - srcOffset);
|
|
461
|
-
this.window.set(data.subarray(srcOffset, srcOffset + toCopy), this.windowOffset);
|
|
462
|
-
this.windowOffset += toCopy;
|
|
463
|
-
this.totalPushed += toCopy;
|
|
464
|
-
srcOffset += toCopy;
|
|
465
|
-
if (this.totalPushed === windowEnd) {
|
|
466
|
-
this.flushChunk();
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
finish() {
|
|
471
|
-
if (this.windowOffset > 0) {
|
|
472
|
-
this.flushChunk();
|
|
473
|
-
}
|
|
474
|
-
return Buffer.concat(this.digests);
|
|
475
|
-
}
|
|
476
|
-
flushChunk() {
|
|
477
|
-
const digest = Crypto.createHmac('sha256', this.macKey).update(this.window.subarray(0, this.windowOffset)).digest();
|
|
478
|
-
this.digests.push(digest.subarray(0, SIDECAR_HMAC_SIZE));
|
|
479
|
-
this.nextChunkStart += SIDECAR_CHUNK_SIZE;
|
|
480
|
-
this.window.copyWithin(0, this.windowOffset - SIDECAR_IV_SIZE, this.windowOffset);
|
|
481
|
-
this.windowOffset = SIDECAR_IV_SIZE;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
export const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, mediaKey: givenMediaKey } = {}) => {
|
|
358
|
+
export const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
|
|
485
359
|
const { stream, type } = await getStream(media, opts);
|
|
486
360
|
logger?.debug('fetched media stream');
|
|
487
|
-
|
|
488
|
-
const mediaKey = givenMediaKey ?? Crypto.randomBytes(32);
|
|
361
|
+
const mediaKey = Crypto.randomBytes(32);
|
|
489
362
|
const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
|
|
490
363
|
const encFilePath = join(getTmpFilesDirectory(), mediaType + generateMessageIDV2() + '-enc');
|
|
491
364
|
const encFileWriteStream = createWriteStream(encFilePath);
|
|
@@ -500,14 +373,9 @@ export const encryptedStream = async (media, mediaType, { logger, saveOriginalFi
|
|
|
500
373
|
const hmac = Crypto.createHmac('sha256', macKey).update(iv);
|
|
501
374
|
const sha256Plain = Crypto.createHash('sha256');
|
|
502
375
|
const sha256Enc = Crypto.createHash('sha256');
|
|
503
|
-
const sidecar = SIDECAR_MEDIA_TYPES.has(mediaType) ? new SidecarAccumulator(macKey) : undefined;
|
|
504
|
-
sidecar?.push(iv);
|
|
505
|
-
let streamableVerdict = sidecar ? null : false;
|
|
506
|
-
let head = Buffer.alloc(0);
|
|
507
376
|
const onChunk = async (buff) => {
|
|
508
377
|
sha256Enc.update(buff);
|
|
509
378
|
hmac.update(buff);
|
|
510
|
-
sidecar?.push(buff);
|
|
511
379
|
// Handle backpressure: if write returns false, wait for drain
|
|
512
380
|
if (!encFileWriteStream.write(buff)) {
|
|
513
381
|
await once(encFileWriteStream, 'drain');
|
|
@@ -518,7 +386,7 @@ export const encryptedStream = async (media, mediaType, { logger, saveOriginalFi
|
|
|
518
386
|
fileLength += data.length;
|
|
519
387
|
if (type === 'remote' &&
|
|
520
388
|
opts?.maxContentLength &&
|
|
521
|
-
fileLength > opts.maxContentLength) {
|
|
389
|
+
fileLength + data.length > opts.maxContentLength) {
|
|
522
390
|
throw new Boom(`content length exceeded when encrypting "${type}"`, {
|
|
523
391
|
data: { media, type }
|
|
524
392
|
});
|
|
@@ -529,21 +397,11 @@ export const encryptedStream = async (media, mediaType, { logger, saveOriginalFi
|
|
|
529
397
|
}
|
|
530
398
|
}
|
|
531
399
|
sha256Plain.update(data);
|
|
532
|
-
if (streamableVerdict === null) {
|
|
533
|
-
head = Buffer.concat([head, data]);
|
|
534
|
-
streamableVerdict = scanStreamable(head);
|
|
535
|
-
if (streamableVerdict === null && head.length > CONTAINER_SCAN_MAX_BYTES) {
|
|
536
|
-
streamableVerdict = true;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
400
|
await onChunk(aes.update(data));
|
|
540
401
|
}
|
|
541
402
|
await onChunk(aes.final());
|
|
542
403
|
const mac = hmac.digest().slice(0, 10);
|
|
543
404
|
sha256Enc.update(mac);
|
|
544
|
-
sidecar?.push(mac);
|
|
545
|
-
// a non-faststart mp4 (mdat before moov) must not advertise seekability
|
|
546
|
-
const streamingSidecar = sidecar && streamableVerdict !== false ? sidecar.finish() : undefined;
|
|
547
405
|
const fileSha256 = sha256Plain.digest();
|
|
548
406
|
const fileEncSha256 = sha256Enc.digest();
|
|
549
407
|
encFileWriteStream.write(mac);
|
|
@@ -564,8 +422,7 @@ export const encryptedStream = async (media, mediaType, { logger, saveOriginalFi
|
|
|
564
422
|
mac,
|
|
565
423
|
fileEncSha256,
|
|
566
424
|
fileSha256,
|
|
567
|
-
fileLength
|
|
568
|
-
streamingSidecar
|
|
425
|
+
fileLength
|
|
569
426
|
};
|
|
570
427
|
}
|
|
571
428
|
catch (error) {
|
|
@@ -591,6 +448,9 @@ export const encryptedStream = async (media, mediaType, { logger, saveOriginalFi
|
|
|
591
448
|
};
|
|
592
449
|
export const DEF_MEDIA_HOST = 'mmg.whatsapp.net';
|
|
593
450
|
const AES_CHUNK_SIZE = 16;
|
|
451
|
+
const toSmallestChunkSize = (num) => {
|
|
452
|
+
return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE;
|
|
453
|
+
};
|
|
594
454
|
export const getUrlFromDirectPath = (directPath, host = DEF_MEDIA_HOST) => `https://${host}${directPath}`;
|
|
595
455
|
const extractHost = (url) => {
|
|
596
456
|
if (!url)
|
|
@@ -614,83 +474,94 @@ export const downloadContentFromMessage = async ({ mediaKey, directPath, url },
|
|
|
614
474
|
return downloadEncryptedContent(downloadUrl, keys, opts);
|
|
615
475
|
};
|
|
616
476
|
/**
|
|
617
|
-
*
|
|
618
|
-
*
|
|
619
|
-
* memory bounded. Ranges use an inclusive startByte and exclusive endByte and are
|
|
620
|
-
* sliced after full authentication; they still download the complete media.
|
|
621
|
-
* Consume or destroy the returned stream to remove its temporary file.
|
|
477
|
+
* Decrypts and downloads an AES256-CBC encrypted file given the keys.
|
|
478
|
+
* Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
|
|
622
479
|
* */
|
|
623
|
-
export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
if
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
480
|
+
export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
|
|
481
|
+
let bytesFetched = 0;
|
|
482
|
+
let startChunk = 0;
|
|
483
|
+
let firstBlockIsIV = false;
|
|
484
|
+
// if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
|
|
485
|
+
if (startByte) {
|
|
486
|
+
const chunk = toSmallestChunkSize(startByte || 0);
|
|
487
|
+
if (chunk) {
|
|
488
|
+
startChunk = chunk - AES_CHUNK_SIZE;
|
|
489
|
+
bytesFetched = chunk;
|
|
490
|
+
firstBlockIsIV = true;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined;
|
|
494
|
+
const headersInit = options?.headers ? options.headers : undefined;
|
|
495
|
+
const headers = {
|
|
496
|
+
...(headersInit
|
|
497
|
+
? Array.isArray(headersInit)
|
|
498
|
+
? Object.fromEntries(headersInit)
|
|
499
|
+
: headersInit
|
|
500
|
+
: {}),
|
|
501
|
+
Origin: DEFAULT_ORIGIN
|
|
502
|
+
};
|
|
503
|
+
if (startChunk || endChunk) {
|
|
504
|
+
headers.Range = `bytes=${startChunk}-`;
|
|
505
|
+
if (endChunk) {
|
|
506
|
+
headers.Range += endChunk;
|
|
641
507
|
}
|
|
642
|
-
|
|
643
|
-
|
|
508
|
+
}
|
|
509
|
+
// download the message
|
|
510
|
+
const fetched = await getHttpStream(downloadUrl, {
|
|
511
|
+
...(options || {}),
|
|
512
|
+
headers
|
|
513
|
+
});
|
|
514
|
+
let remainingBytes = Buffer.from([]);
|
|
515
|
+
let aes;
|
|
516
|
+
const pushBytes = (bytes, push) => {
|
|
517
|
+
if (startByte || endByte) {
|
|
518
|
+
const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0);
|
|
519
|
+
const end = bytesFetched + bytes.length < endByte ? undefined : Math.max(endByte - bytesFetched, 0);
|
|
520
|
+
push(bytes.slice(start, end));
|
|
521
|
+
bytesFetched += bytes.length;
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
push(bytes);
|
|
644
525
|
}
|
|
645
526
|
};
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
trailingMac = data.subarray(length);
|
|
658
|
-
const ciphertext = data.subarray(0, length);
|
|
659
|
-
ciphertextLength += length;
|
|
660
|
-
hmac.update(ciphertext);
|
|
661
|
-
callback(null, ciphertext);
|
|
662
|
-
},
|
|
663
|
-
flush(callback) {
|
|
664
|
-
if (trailingMac.length !== 10 || ciphertextLength === 0 || ciphertextLength % AES_CHUNK_SIZE !== 0) {
|
|
665
|
-
callback(new Boom('Invalid or truncated media payload', { statusCode: 400 }));
|
|
666
|
-
}
|
|
667
|
-
else if (!Crypto.timingSafeEqual(hmac.digest().subarray(0, 10), trailingMac)) {
|
|
668
|
-
callback(new Boom('Media MAC verification failed', { statusCode: 400 }));
|
|
527
|
+
const output = new Transform({
|
|
528
|
+
transform(chunk, _, callback) {
|
|
529
|
+
let data = remainingBytes.length ? Buffer.concat([remainingBytes, chunk]) : chunk;
|
|
530
|
+
const decryptLength = toSmallestChunkSize(data.length);
|
|
531
|
+
remainingBytes = data.slice(decryptLength);
|
|
532
|
+
data = data.slice(0, decryptLength);
|
|
533
|
+
if (!aes) {
|
|
534
|
+
let ivValue = iv;
|
|
535
|
+
if (firstBlockIsIV) {
|
|
536
|
+
ivValue = data.slice(0, AES_CHUNK_SIZE);
|
|
537
|
+
data = data.slice(AES_CHUNK_SIZE);
|
|
669
538
|
}
|
|
670
|
-
|
|
671
|
-
|
|
539
|
+
aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue);
|
|
540
|
+
// if an end byte that is not EOF is specified
|
|
541
|
+
// stop auto padding (PKCS7) -- otherwise throws an error for decryption
|
|
542
|
+
if (endByte) {
|
|
543
|
+
aes.setAutoPadding(false);
|
|
672
544
|
}
|
|
673
545
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
546
|
+
try {
|
|
547
|
+
pushBytes(aes.update(data), b => this.push(b));
|
|
548
|
+
callback();
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
callback(error);
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
final(callback) {
|
|
555
|
+
try {
|
|
556
|
+
pushBytes(aes.final(), b => this.push(b));
|
|
557
|
+
callback();
|
|
558
|
+
}
|
|
559
|
+
catch (error) {
|
|
560
|
+
callback(error);
|
|
561
|
+
}
|
|
682
562
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
void cleanup().catch(() => { });
|
|
686
|
-
});
|
|
687
|
-
return output;
|
|
688
|
-
}
|
|
689
|
-
catch (error) {
|
|
690
|
-
fetched?.destroy();
|
|
691
|
-
await cleanup().catch(() => { });
|
|
692
|
-
throw error;
|
|
693
|
-
}
|
|
563
|
+
});
|
|
564
|
+
return fetched.pipe(output, { end: true });
|
|
694
565
|
};
|
|
695
566
|
export function extensionForMediaMessage(message) {
|
|
696
567
|
const getExtension = (mimetype) => mimetype.split(';')[0]?.split('/')[1];
|
|
@@ -977,4 +848,4 @@ const MEDIA_RETRY_STATUS_MAP = {
|
|
|
977
848
|
[proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
978
849
|
[proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
|
|
979
850
|
};
|
|
980
|
-
//# sourceMappingURL=messages-media.js.map
|
|
851
|
+
//# sourceMappingURL=messages-media.js.map
|
package/lib/Utils/messages.d.ts
CHANGED
|
@@ -19,7 +19,6 @@ export function getAggregateResponsesInEventMessage({ eventResponses }: {
|
|
|
19
19
|
}, meId: any): any[];
|
|
20
20
|
export function extractUrlFromText(text: any): any;
|
|
21
21
|
export function generateLinkPreviewIfRequired(text: any, getUrlInfo: any, logger: any): Promise<any>;
|
|
22
|
-
export function hashImagePollOption(optionName: any, fileSha256?: Uint8Array | null): string;
|
|
23
22
|
export function prepareWAMessageMedia(message: any, options: any): Promise<proto.Message>;
|
|
24
23
|
export function prepareDisappearingMessageSettingContent(ephemeralExpiration: any): proto.Message;
|
|
25
24
|
export function generateForwardMessageContent(message: any, forceForward: any): any;
|