UNPKG

@whiskeysockets/baileys

Version:

A WebSockets library for interacting with WhatsApp Web

258 lines 8.7 kB
import { createHmac } from 'crypto'; import { proto } from '../../WAProto/index.js'; import { hkdf } from './crypto.js'; const reportingFields = [ { f: 1 }, { f: 3, s: [{ f: 2 }, { f: 3 }, { f: 8 }, { f: 11 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }, { f: 25 }] }, { f: 4, s: [{ f: 1 }, { f: 16 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }] }, { f: 5, s: [{ f: 3 }, { f: 4 }, { f: 5 }, { f: 16 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }] }, { f: 6, s: [{ f: 1 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }, { f: 30 }] }, { f: 7, s: [{ f: 2 }, { f: 7 }, { f: 10 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }, { f: 20 }] }, { f: 8, s: [{ f: 2 }, { f: 7 }, { f: 9 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }, { f: 21 }] }, { f: 9, s: [{ f: 2 }, { f: 6 }, { f: 7 }, { f: 13 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }, { f: 20 }] }, { f: 12, s: [{ f: 1 }, { f: 2 }, { f: 14, m: true }, { f: 15 }] }, { f: 18, s: [{ f: 6 }, { f: 16 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }] }, { f: 26, s: [{ f: 4 }, { f: 5 }, { f: 8 }, { f: 13 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }] }, { f: 28, s: [{ f: 1 }, { f: 2 }, { f: 4 }, { f: 5 }, { f: 6 }, { f: 7, s: [{ f: 21 }, { f: 22 }] }] }, { f: 37, s: [{ f: 1, m: true }] }, { f: 49, s: [ { f: 2 }, { f: 3, s: [{ f: 1 }, { f: 2 }] }, { f: 5, s: [{ f: 21 }, { f: 22 }] }, { f: 8, s: [{ f: 1 }, { f: 2 }] } ] }, { f: 53, s: [{ f: 1, m: true }] }, { f: 55, s: [{ f: 1, m: true }] }, { f: 58, s: [{ f: 1, m: true }] }, { f: 59, s: [{ f: 1, m: true }] }, { f: 60, s: [ { f: 2 }, { f: 3, s: [{ f: 1 }, { f: 2 }] }, { f: 5, s: [{ f: 21 }, { f: 22 }] }, { f: 8, s: [{ f: 1 }, { f: 2 }] } ] }, { f: 64, s: [ { f: 2 }, { f: 3, s: [{ f: 1 }, { f: 2 }] }, { f: 5, s: [{ f: 21 }, { f: 22 }] }, { f: 8, s: [{ f: 1 }, { f: 2 }] } ] }, { f: 66, s: [{ f: 2 }, { f: 6 }, { f: 7 }, { f: 13 }, { f: 17, s: [{ f: 21 }, { f: 22 }] }, { f: 20 }] }, { f: 74, s: [{ f: 1, m: true }] }, { f: 87, s: [{ f: 1, m: true }] }, { f: 88, s: [{ f: 1 }, { f: 2, s: [{ f: 1 }] }, { f: 3, s: [{ f: 21 }, { f: 22 }] }] }, { f: 92, s: [{ f: 1, m: true }] }, { f: 93, s: [{ f: 1, m: true }] }, { f: 94, s: [{ f: 1, m: true }] } ]; const compileReportingFields = (fields) => { const map = new Map(); for (const f of fields) { map.set(f.f, { m: f.m, children: f.s ? compileReportingFields(f.s) : undefined }); } return map; }; const compiledReportingFields = compileReportingFields(reportingFields); const EMPTY_MAP = new Map(); const ENC_SECRET_REPORT_TOKEN = 'Report Token'; const WIRE = { VARINT: 0, FIXED64: 1, BYTES: 2, FIXED32: 5 }; export const shouldIncludeReportingToken = (message) => !message.reactionMessage && !message.encReactionMessage && !message.encEventResponseMessage && !message.pollUpdateMessage; const generateMsgSecretKey = (modificationType, origMsgId, origMsgSender, modificationSender, origMsgSecret) => { const useCaseSecret = Buffer.concat([ Buffer.from(origMsgId, 'utf8'), Buffer.from(origMsgSender, 'utf8'), Buffer.from(modificationSender, 'utf8'), Buffer.from(modificationType, 'utf8') ]); return hkdf(origMsgSecret, 32, { info: useCaseSecret.toString('latin1') }); }; const extractReportingTokenContent = (data, cfg) => { const out = []; let i = 0; while (i < data.length) { const tag = decodeVarint(data, i); if (!tag.ok) { return null; } const fieldNum = tag.value >> 3; const wireType = tag.value & 0x7; const fieldStart = i; i += tag.bytes; const fieldCfg = cfg.get(fieldNum); const pushSlice = (end) => { if (end > data.length) { return false; } out.push({ num: fieldNum, bytes: data.subarray(fieldStart, end) }); i = end; return true; }; const skip = (end) => { if (end > data.length) { return false; } i = end; return true; }; if (wireType === WIRE.VARINT) { const v = decodeVarint(data, i); if (!v.ok) { return null; } const end = i + v.bytes; if (!fieldCfg) { if (!skip(end)) { return null; } continue; } if (!pushSlice(end)) { return null; } continue; } if (wireType === WIRE.FIXED64) { const end = i + 8; if (!fieldCfg) { if (!skip(end)) { return null; } continue; } if (!pushSlice(end)) { return null; } continue; } if (wireType === WIRE.FIXED32) { const end = i + 4; if (!fieldCfg) { if (!skip(end)) { return null; } continue; } if (!pushSlice(end)) { return null; } continue; } if (wireType === WIRE.BYTES) { const len = decodeVarint(data, i); if (!len.ok) { return null; } const valStart = i + len.bytes; const valEnd = valStart + len.value; if (valEnd > data.length) { return null; } if (!fieldCfg) { i = valEnd; continue; } if (fieldCfg.m || fieldCfg.children) { const sub = extractReportingTokenContent(data.subarray(valStart, valEnd), fieldCfg.children ?? EMPTY_MAP); if (sub === null) { return null; } if (sub.length > 0) { const newTag = encodeVarint(tag.value); const newLen = encodeVarint(sub.length); out.push({ num: fieldNum, bytes: Buffer.concat([newTag, newLen, sub]) }); } i = valEnd; continue; } out.push({ num: fieldNum, bytes: data.subarray(fieldStart, valEnd) }); i = valEnd; continue; } return null; } if (out.length === 0) { return Buffer.alloc(0); } out.sort((a, b) => a.num - b.num); return Buffer.concat(out.map(f => f.bytes)); }; const decodeVarint = (buffer, offset) => { let value = 0; let bytes = 0; let shift = 0; while (offset + bytes < buffer.length) { const current = buffer[offset + bytes]; value |= (current & 0x7f) << shift; bytes++; if ((current & 0x80) === 0) { return { value, bytes, ok: true }; } shift += 7; if (shift > 35) { return { value: 0, bytes: 0, ok: false }; } } return { value: 0, bytes: 0, ok: false }; }; const encodeVarint = (value) => { const parts = []; let remaining = value >>> 0; while (remaining > 0x7f) { parts.push((remaining & 0x7f) | 0x80); remaining >>>= 7; } parts.push(remaining); return Buffer.from(parts); }; export const getMessageReportingToken = async (msgProtobuf, message, key) => { const msgSecret = message.messageContextInfo?.messageSecret; if (!msgSecret || !key.id) { return null; } const from = key.fromMe ? key.remoteJid : key.participant || key.remoteJid; const to = key.fromMe ? key.participant || key.remoteJid : key.remoteJid; const reportingSecret = generateMsgSecretKey(ENC_SECRET_REPORT_TOKEN, key.id, from, to, msgSecret); const content = extractReportingTokenContent(msgProtobuf, compiledReportingFields); if (!content || content.length === 0) { return null; } const reportingToken = createHmac('sha256', reportingSecret).update(content).digest().subarray(0, 16); return { tag: 'reporting', attrs: {}, content: [ { tag: 'reporting_token', attrs: { v: '2' }, content: reportingToken } ] }; }; //# sourceMappingURL=reporting-utils.js.map