Path: blob/master/node_modules/@adiwajshing/baileys/lib/Utils/legacy-msgs.js
1129 views
"use strict";1Object.defineProperty(exports, "__esModule", { value: true });2exports.getAuthenticationCredsType = exports.useSingleFileLegacyAuthState = exports.computeChallengeResponse = exports.validateNewConnection = exports.decodeWAMessage = exports.newLegacyAuthCreds = void 0;3const boom_1 = require("@hapi/boom");4const crypto_1 = require("crypto");5const Types_1 = require("../Types");6const WABinary_1 = require("../WABinary");7const crypto_2 = require("./crypto");8const generics_1 = require("./generics");9const newLegacyAuthCreds = () => ({10clientID: (0, crypto_1.randomBytes)(16).toString('base64')11});12exports.newLegacyAuthCreds = newLegacyAuthCreds;13const decodeWAMessage = (message, auth, fromMe = false) => {14let commaIndex = message.indexOf(','); // all whatsapp messages have a tag and a comma, followed by the actual message15if (commaIndex < 0) {16throw new boom_1.Boom('invalid message', { data: message });17} // if there was no comma, then this message must be not be valid18if (message[commaIndex + 1] === ',') {19commaIndex += 1;20}21let data = message.slice(commaIndex + 1, message.length);22// get the message tag.23// If a query was done, the server will respond with the same message tag we sent the query with24const messageTag = message.slice(0, commaIndex).toString();25let json;26let tags;27if (data.length) {28const possiblyEnc = (data.length > 32 && data.length % 16 === 0);29if (typeof data === 'string' || !possiblyEnc) {30json = JSON.parse(data.toString()); // parse the JSON31}32else {33try {34json = JSON.parse(data.toString());35}36catch (_a) {37const { macKey, encKey } = auth || {};38if (!macKey || !encKey) {39throw new boom_1.Boom('recieved encrypted buffer when auth creds unavailable', { data: message, statusCode: Types_1.DisconnectReason.badSession });40}41/*42If the data recieved was not a JSON, then it must be an encrypted message.43Such a message can only be decrypted if we're connected successfully to the servers & have encryption keys44*/45if (fromMe) {46tags = [data[0], data[1]];47data = data.slice(2, data.length);48}49const checksum = data.slice(0, 32); // the first 32 bytes of the buffer are the HMAC sign of the message50data = data.slice(32, data.length); // the actual message51const computedChecksum = (0, crypto_2.hmacSign)(data, macKey); // compute the sign of the message we recieved using our macKey52if (checksum.equals(computedChecksum)) {53// the checksum the server sent, must match the one we computed for the message to be valid54const decrypted = (0, crypto_2.aesDecrypt)(data, encKey); // decrypt using AES55json = (0, WABinary_1.decodeBinaryNodeLegacy)(decrypted, { index: 0 }); // decode the binary message into a JSON array56}57else {58throw new boom_1.Boom('Bad checksum', {59data: {60received: checksum.toString('hex'),61computed: computedChecksum.toString('hex'),62data: data.slice(0, 80).toString(),63tag: messageTag,64message: message.slice(0, 80).toString()65},66statusCode: Types_1.DisconnectReason.badSession67});68}69}70}71}72return [messageTag, json, tags];73};74exports.decodeWAMessage = decodeWAMessage;75/**76* Once the QR code is scanned and we can validate our connection, or we resolved the challenge when logging back in77* @private78* @param json79*/80const validateNewConnection = (json, auth, curveKeys) => {81// set metadata: one's WhatsApp ID [cc][number]@s.whatsapp.net, name on WhatsApp, info about the phone82const onValidationSuccess = () => {83const user = {84id: (0, WABinary_1.jidNormalizedUser)(json.wid),85name: json.pushname86};87return { user, auth, phone: json.phone };88};89if (!json.secret) {90// if we didn't get a secret, we don't need it, we're validated91if (json.clientToken && json.clientToken !== auth.clientToken) {92auth = { ...auth, clientToken: json.clientToken };93}94if (json.serverToken && json.serverToken !== auth.serverToken) {95auth = { ...auth, serverToken: json.serverToken };96}97return onValidationSuccess();98}99const secret = Buffer.from(json.secret, 'base64');100if (secret.length !== 144) {101throw new Error('incorrect secret length received: ' + secret.length);102}103// generate shared key from our private key & the secret shared by the server104const sharedKey = crypto_2.Curve.sharedKey(curveKeys.private, secret.slice(0, 32));105// expand the key to 80 bytes using HKDF106const expandedKey = (0, crypto_2.hkdf)(sharedKey, 80, {});107// perform HMAC validation.108const hmacValidationKey = expandedKey.slice(32, 64);109const hmacValidationMessage = Buffer.concat([secret.slice(0, 32), secret.slice(64, secret.length)]);110const hmac = (0, crypto_2.hmacSign)(hmacValidationMessage, hmacValidationKey);111if (!hmac.equals(secret.slice(32, 64))) {112// if the checksums didn't match113throw new boom_1.Boom('HMAC validation failed', { statusCode: 400 });114}115// computed HMAC should equal secret[32:64]116// expandedKey[64:] + secret[64:] are the keys, encrypted using AES, that are used to encrypt/decrypt the messages recieved from WhatsApp117// they are encrypted using key: expandedKey[0:32]118const encryptedAESKeys = Buffer.concat([119expandedKey.slice(64, expandedKey.length),120secret.slice(64, secret.length),121]);122const decryptedKeys = (0, crypto_2.aesDecrypt)(encryptedAESKeys, expandedKey.slice(0, 32));123// set the credentials124auth = {125encKey: decryptedKeys.slice(0, 32),126macKey: decryptedKeys.slice(32, 64),127clientToken: json.clientToken,128serverToken: json.serverToken,129clientID: auth.clientID,130};131return onValidationSuccess();132};133exports.validateNewConnection = validateNewConnection;134const computeChallengeResponse = (challenge, auth) => {135const bytes = Buffer.from(challenge, 'base64'); // decode the base64 encoded challenge string136const signed = (0, crypto_2.hmacSign)(bytes, auth.macKey).toString('base64'); // sign the challenge string with our macKey137return ['admin', 'challenge', signed, auth.serverToken, auth.clientID]; // prepare to send this signed string with the serverToken & clientID138};139exports.computeChallengeResponse = computeChallengeResponse;140const useSingleFileLegacyAuthState = (file) => {141// require fs here so that in case "fs" is not available -- the app does not crash142const { readFileSync, writeFileSync, existsSync } = require('fs');143let state;144if (existsSync(file)) {145state = JSON.parse(readFileSync(file, { encoding: 'utf-8' }), generics_1.BufferJSON.reviver);146if (typeof state.encKey === 'string') {147state.encKey = Buffer.from(state.encKey, 'base64');148}149if (typeof state.macKey === 'string') {150state.macKey = Buffer.from(state.macKey, 'base64');151}152}153else {154state = (0, exports.newLegacyAuthCreds)();155}156return {157state,158saveState: () => {159const str = JSON.stringify(state, generics_1.BufferJSON.replacer, 2);160writeFileSync(file, str);161}162};163};164exports.useSingleFileLegacyAuthState = useSingleFileLegacyAuthState;165const getAuthenticationCredsType = (creds) => {166if ('clientID' in creds && !!creds.clientID) {167return 'legacy';168}169if ('noiseKey' in creds && !!creds.noiseKey) {170return 'md';171}172};173exports.getAuthenticationCredsType = getAuthenticationCredsType;174175176