diff --git a/.gitignore b/.gitignore index e2aa22d..f61bb1f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ package-lock.json .env lib auth_info_browser.json +yarn.lock \ No newline at end of file diff --git a/src/WAConnection/Base.ts b/src/WAConnection/Base.ts index d29e637..eafc513 100644 --- a/src/WAConnection/Base.ts +++ b/src/WAConnection/Base.ts @@ -4,8 +4,15 @@ import WS from 'ws' import * as Utils from './Utils' import Encoder from '../Binary/Encoder' import Decoder from '../Binary/Decoder' -import { AuthenticationCredentials, UserMetaData, WANode, AuthenticationCredentialsBase64, WATag, MessageLogLevel, AuthenticationCredentialsBrowser } from './Constants' - +import { + AuthenticationCredentials, + UserMetaData, + WANode, + AuthenticationCredentialsBase64, + WATag, + MessageLogLevel, + AuthenticationCredentialsBrowser, +} from './Constants' /** Generate a QR code from the ref & the curve public key. This is scanned by the phone */ const generateQRCode = function ([ref, publicKey, clientID]) { @@ -96,9 +103,9 @@ export default class WAConnectionBase { authInfo = JSON.parse(file) as AuthenticationCredentialsBrowser } this.authInfo = { - clientID: authInfo.WABrowserId.replace (/\"/g, ''), - serverToken: authInfo.WAToken2.replace (/\"/g, ''), - clientToken: authInfo.WAToken1.replace (/\"/g, ''), + clientID: authInfo.WABrowserId.replace(/\"/g, ''), + serverToken: authInfo.WAToken2.replace(/\"/g, ''), + clientToken: authInfo.WAToken1.replace(/\"/g, ''), encKey: Buffer.from(authInfo.WASecretBundle.encKey, 'base64'), // decode from base64 macKey: Buffer.from(authInfo.WASecretBundle.macKey, 'base64'), // decode from base64 } @@ -216,7 +223,7 @@ export default class WAConnectionBase { let buff = Utils.aesEncrypt(binary, this.authInfo.encKey) // encrypt it using AES and our encKey const sign = Utils.hmacSign(buff, this.authInfo.macKey) // sign the message using HMAC and our macKey - tag = tag || Utils.generateMessageTag() + tag = tag || Utils.generateMessageTag(this.msgCount) buff = Buffer.concat([ Buffer.from(tag + ','), // generate & prefix the message tag Buffer.from(tags), // prefix some bytes that tell whatsapp what the message is about @@ -234,7 +241,7 @@ export default class WAConnectionBase { * @return the message tag */ private sendJSON(json: any[] | WANode, tag: string = null) { - tag = tag || Utils.generateMessageTag() + tag = tag || Utils.generateMessageTag(this.msgCount) this.send(tag + ',' + JSON.stringify(json)) return tag } diff --git a/src/WAConnection/Utils.ts b/src/WAConnection/Utils.ts index 6f93ba6..f4e53dc 100644 --- a/src/WAConnection/Utils.ts +++ b/src/WAConnection/Utils.ts @@ -38,7 +38,9 @@ export function randomBytes(length) { return Crypto.randomBytes(length) } export function promiseTimeout(ms: number, promise: Promise) { - if (!ms) { return promise } + if (!ms) { + return promise + } // Create a promise that rejects in milliseconds const timeout = new Promise((_, reject) => { const id = setTimeout(() => { @@ -49,8 +51,10 @@ export function promiseTimeout(ms: number, promise: Promise) { return Promise.race([promise, timeout]) as Promise } // whatsapp requires a message tag for every message, we just use the timestamp as one -export function generateMessageTag() { - return new Date().getTime().toString() +export function generateMessageTag(epoch?: number) { + let tag = new Date().getTime().toString() + if (epoch) tag += '-' + epoch // attach epoch if provided + return tag } // generate a random 16 byte client ID export function generateClientID() { diff --git a/src/WAConnection/Validation.ts b/src/WAConnection/Validation.ts index 48935fb..b81ead5 100644 --- a/src/WAConnection/Validation.ts +++ b/src/WAConnection/Validation.ts @@ -74,6 +74,11 @@ export default class WAConnectionValidator extends WAConnectionBase { return this.userMetaData }) } + /** Refresh QR Code */ + protected refreshQRCode() { + const data = ['admin', 'Conn', 'reref'] + return this.query(data) + } /** * Once the QR code is scanned and we can validate our connection, or we resolved the challenge when logging back in * @private @@ -153,13 +158,27 @@ export default class WAConnectionValidator extends WAConnectionBase { * When starting a new session, generate a QR code by generating a private/public key pair & the keys the server sends * @private */ - protected generateKeysForAuth(ref: string) { + protected async generateKeysForAuth(ref: string) { this.curveKeys = Curve.generateKeyPair(Utils.randomBytes(32)) - this.onReadyForPhoneAuthentication([ - ref, - Buffer.from(this.curveKeys.public).toString('base64'), - this.authInfo.clientID, - ]) - return this.waitForMessage('s1', []) + + let retries = 0 + let _ref = ref + + while (retries < 5) { + retries++ + + this.onReadyForPhoneAuthentication([ + _ref, + Buffer.from(this.curveKeys.public).toString('base64'), + this.authInfo.clientID, + ]) + + try { + return await this.waitForMessage('s1', [], 20 * 1000) + } catch (err) { + const json = await this.refreshQRCode() + _ref = json.ref + } + } } }