diff --git a/src/WAClient/Messages.ts b/src/WAClient/Messages.ts index 0d02eb8..9c98db7 100644 --- a/src/WAClient/Messages.ts +++ b/src/WAClient/Messages.ts @@ -17,7 +17,7 @@ import { } from './Constants' import { generateMessageID, sha256, hmacSign, aesEncrypWithIV, randomBytes } from '../WAConnection/Utils' import { WAMessageContent, WAMetric, WAFlag, WANode, WAMessage, WAMessageProto } from '../WAConnection/Constants' -import { validateJIDForSending, generateThumbnail, getMediaKeys } from './Utils' +import { validateJIDForSending, generateThumbnail, getMediaKeys, decodeMediaMessageBuffer } from './Utils' import { proto } from '../../WAMessage/WAMessage' export default class WhatsAppWebMessages extends WhatsAppWebGroups { @@ -335,4 +335,19 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups { message: messageJSON as WAMessage } as WASendMessageResponse } + /** + * Securely downloads the media from the message. + * Renews the download url automatically, if necessary. + */ + async downloadMediaMessage (message: WAMessage) { + try { + return decodeMediaMessageBuffer (message.message) + } catch (error) { + if (error.toString().includes('Empty buffer returned')) { + await this.updateMediaMessage (message) + return decodeMediaMessageBuffer (message.message) + } + throw error + } + } } diff --git a/src/WAClient/Tests.ts b/src/WAClient/Tests.ts index 511c1c9..2ef9276 100644 --- a/src/WAClient/Tests.ts +++ b/src/WAClient/Tests.ts @@ -52,7 +52,8 @@ WAClientTest('Messages', (client) => { it('should send a gif', async () => { const content = fs.readFileSync('./Media/ma_gif.mp4') const message = await sendAndRetreiveMessage(client, content, MessageType.video, { mimetype: Mimetype.gif }) - const file = await decodeMediaMessage(message.message, './Media/received_vid') + const buffer = await client.downloadMediaMessage(message) + fs.writeFileSync ('./Media/received_vid.mp4', buffer) }) it('should send an image', async () => { const content = fs.readFileSync('./Media/meme.jpeg') diff --git a/src/WAClient/Utils.ts b/src/WAClient/Utils.ts index c67176b..bd6702e 100644 --- a/src/WAClient/Utils.ts +++ b/src/WAClient/Utils.ts @@ -133,7 +133,7 @@ export async function decodeMediaMessageBuffer(message: WAMessageContent) { const fetched = await fetch(messageContent.url, { headers: { Origin: 'https://web.whatsapp.com' } }) const buffer = await fetched.buffer() - if (buffer.length === 0) { + if (buffer.length <= 10) { throw new Error ('Empty buffer returned. File has possibly been deleted from WA servers. Run `client.updateMediaMessage()` to refresh the url') } @@ -148,11 +148,9 @@ export async function decodeMediaMessageBuffer(message: WAMessageContent) { const testBuff = Buffer.concat([mediaKeys.iv, file]) const sign = hmacSign(testBuff, mediaKeys.macKey).slice(0, 10) // our sign should equal the mac - if (sign.equals(mac)) { - return aesDecryptWithIV(file, mediaKeys.cipherKey, mediaKeys.iv) // decrypt media - } else { - throw new Error() - } + if (!sign.equals(mac)) throw new Error() + + return aesDecryptWithIV(file, mediaKeys.cipherKey, mediaKeys.iv) // decrypt media } const allTypes = [type, ...Object.keys(HKDFInfoKeys)] for (let i = 0; i < allTypes.length;i++) { @@ -165,7 +163,7 @@ export async function decodeMediaMessageBuffer(message: WAMessageContent) { if (i === 0) { console.log (`decryption of ${type} media with original HKDF key failed`) } } } - throw new Error('HMAC sign does not match for ' + buffer.length) + throw new Error('Decryption failed, HMAC sign does not match') } /** * Decode a media message (video, image, document, audio) & save it to the given file