mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
Merge pull request #1530 from adiwajshing/invalid-qr-patch
Invalid QR Patch
This commit is contained in:
@@ -108,7 +108,6 @@ export const initAuthCreds = (): AuthenticationCreds => {
|
||||
|
||||
nextPreKeyId: 1,
|
||||
firstUnuploadedPreKeyId: 1,
|
||||
serverHasPreKeys: false,
|
||||
accountSettings: {
|
||||
unarchiveChats: false
|
||||
}
|
||||
|
||||
@@ -583,8 +583,10 @@ export const processSyncActions = (
|
||||
name: action.contactAction!.fullName
|
||||
}
|
||||
} else if(action?.pushNameSetting) {
|
||||
map['creds.update'] = map['creds.update'] || { }
|
||||
map['creds.update'].me = { ...me, name: action?.pushNameSetting?.name! }
|
||||
if(me?.name !== action?.pushNameSetting) {
|
||||
map['creds.update'] = map['creds.update'] || { }
|
||||
map['creds.update'].me = { ...me, name: action?.pushNameSetting?.name! }
|
||||
}
|
||||
} else if(action?.pinAction) {
|
||||
update.pin = action.pinAction?.pinned ? toNumber(action.timestamp) : undefined
|
||||
} else if(action?.unarchiveChatsSetting) {
|
||||
|
||||
@@ -1,36 +1,49 @@
|
||||
import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes } from 'crypto'
|
||||
import * as curveJs from 'curve25519-js'
|
||||
import HKDF from 'futoin-hkdf'
|
||||
import * as libsignal from 'libsignal'
|
||||
import { KEY_BUNDLE_TYPE } from '../Defaults'
|
||||
import { KeyPair } from '../Types'
|
||||
|
||||
/** prefix version byte to the pub keys, required for some curve crypto functions */
|
||||
export const generateSignalPubKey = (pubKey: Uint8Array | Buffer) => (
|
||||
pubKey.length === 33
|
||||
? pubKey
|
||||
: Buffer.concat([ KEY_BUNDLE_TYPE, pubKey ])
|
||||
)
|
||||
|
||||
export const Curve = {
|
||||
generateKeyPair: (): KeyPair => {
|
||||
const { public: pubKey, private: privKey } = curveJs.generateKeyPair(randomBytes(32))
|
||||
const { pubKey, privKey } = libsignal.curve.generateKeyPair()
|
||||
return {
|
||||
private: Buffer.from(privKey),
|
||||
public: Buffer.from(pubKey)
|
||||
// remove version byte
|
||||
public: Buffer.from((pubKey as Uint8Array).slice(1))
|
||||
}
|
||||
},
|
||||
sharedKey: (privateKey: Uint8Array, publicKey: Uint8Array) => {
|
||||
const shared = curveJs.sharedKey(privateKey, publicKey)
|
||||
const shared = libsignal.curve.calculateAgreement(generateSignalPubKey(publicKey), privateKey)
|
||||
return Buffer.from(shared)
|
||||
},
|
||||
sign: (privateKey: Uint8Array, buf: Uint8Array) => (
|
||||
Buffer.from(curveJs.sign(privateKey, buf, null))
|
||||
libsignal.curve.calculateSignature(privateKey, buf)
|
||||
),
|
||||
verify: (pubKey: Uint8Array, message: Uint8Array, signature: Uint8Array) => {
|
||||
return curveJs.verify(pubKey, message, signature)
|
||||
try {
|
||||
libsignal.curve.verifySignature(generateSignalPubKey(pubKey), message, signature)
|
||||
return true
|
||||
} catch(error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const signedKeyPair = (keyPair: KeyPair, keyId: number) => {
|
||||
const signKeys = Curve.generateKeyPair()
|
||||
const pubKey = new Uint8Array(33)
|
||||
pubKey.set([5], 0)
|
||||
pubKey.set(signKeys.public, 1)
|
||||
export const signedKeyPair = (identityKeyPair: KeyPair, keyId: number) => {
|
||||
const preKey = Curve.generateKeyPair()
|
||||
const pubKey = generateSignalPubKey(preKey.public)
|
||||
|
||||
const signature = Curve.sign(keyPair.private, pubKey)
|
||||
const signature = Curve.sign(identityKeyPair.private, pubKey)
|
||||
|
||||
return { keyPair: signKeys, signature, keyId }
|
||||
return { keyPair: preKey, signature, keyId }
|
||||
}
|
||||
|
||||
/** decrypt AES 256 CBC; where the IV is prefixed to the buffer */
|
||||
@@ -67,33 +80,6 @@ export function sha256(buffer: Buffer) {
|
||||
}
|
||||
|
||||
// HKDF key expansion
|
||||
// from: https://github.com/benadida/node-hkdf
|
||||
export function hkdf(buffer: Uint8Array, expandedLength: number, { info, salt }: { salt?: Buffer, info?: string }) {
|
||||
const hashAlg = 'sha256'
|
||||
const hashLength = 32
|
||||
salt = salt || Buffer.alloc(hashLength)
|
||||
// now we compute the PRK
|
||||
const prk = createHmac(hashAlg, salt).update(buffer).digest()
|
||||
|
||||
let prev = Buffer.from([])
|
||||
const buffers = []
|
||||
const num_blocks = Math.ceil(expandedLength / hashLength)
|
||||
|
||||
const infoBuff = Buffer.from(info || [])
|
||||
|
||||
for(var i = 0; i < num_blocks; i++) {
|
||||
const hmac = createHmac(hashAlg, prk)
|
||||
// XXX is there a more optimal way to build up buffers?
|
||||
const input = Buffer.concat([
|
||||
prev,
|
||||
infoBuff,
|
||||
Buffer.from(String.fromCharCode(i + 1))
|
||||
])
|
||||
hmac.update(input)
|
||||
|
||||
prev = hmac.digest()
|
||||
buffers.push(prev)
|
||||
}
|
||||
|
||||
return Buffer.concat(buffers, expandedLength)
|
||||
export function hkdf(buffer: Uint8Array | Buffer, expandedLength: number, info: { salt?: Buffer, info?: string }) {
|
||||
return HKDF(!Buffer.isBuffer(buffer) ? Buffer.from(buffer) : buffer, expandedLength, info)
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import { Logger } from 'pino'
|
||||
import { proto } from '../../WAProto'
|
||||
import { version as baileysVersion } from '../Defaults/baileys-version.json'
|
||||
import { CommonBaileysEventEmitter, ConnectionState, DisconnectReason, WAVersion } from '../Types'
|
||||
import { Binary } from '../WABinary'
|
||||
|
||||
const PLATFORM_MAP = {
|
||||
'aix': 'AIX',
|
||||
@@ -41,16 +40,14 @@ export const BufferJSON = {
|
||||
}
|
||||
}
|
||||
|
||||
export const writeRandomPadMax16 = (e: Binary) => {
|
||||
function r(e: Binary, t: number) {
|
||||
for(var r = 0; r < t; r++) {
|
||||
e.writeUint8(t)
|
||||
}
|
||||
export const writeRandomPadMax16 = (msg: Uint8Array) => {
|
||||
const pad = randomBytes(1)
|
||||
pad[0] &= 0xf
|
||||
if(!pad[0]) {
|
||||
pad[0] = 0xf
|
||||
}
|
||||
|
||||
var t = randomBytes(1)
|
||||
r(e, 1 + (15 & t[0]))
|
||||
return e
|
||||
return Buffer.concat([msg, Buffer.alloc(pad[0], pad[0])])
|
||||
}
|
||||
|
||||
export const unpadRandomMax16 = (e: Uint8Array | Buffer) => {
|
||||
@@ -68,24 +65,13 @@ export const unpadRandomMax16 = (e: Uint8Array | Buffer) => {
|
||||
}
|
||||
|
||||
export const encodeWAMessage = (message: proto.IMessage) => (
|
||||
Buffer.from(
|
||||
writeRandomPadMax16(
|
||||
new Binary(proto.Message.encode(message).finish())
|
||||
).readByteArray()
|
||||
writeRandomPadMax16(
|
||||
proto.Message.encode(message).finish()
|
||||
)
|
||||
)
|
||||
|
||||
export const generateRegistrationId = () => (
|
||||
Uint16Array.from(randomBytes(2))[0] & 0x3fff
|
||||
)
|
||||
|
||||
export const encodeInt = (e: number, t: number) => {
|
||||
for(var r = t, a = new Uint8Array(e), i = e - 1; i >= 0; i--) {
|
||||
a[i] = 255 & r
|
||||
r >>>= 8
|
||||
}
|
||||
|
||||
return a
|
||||
export const generateRegistrationId = (): number => {
|
||||
return Uint16Array.from(randomBytes(2))[0] & 16383
|
||||
}
|
||||
|
||||
export const encodeBigEndian = (e: number, t = 4) => {
|
||||
@@ -255,6 +241,12 @@ export const fetchLatestBaileysVersion = async() => {
|
||||
}
|
||||
}
|
||||
|
||||
/** unique message tag prefix for MD clients */
|
||||
export const generateMdTagPrefix = () => {
|
||||
const bytes = randomBytes(4)
|
||||
return `${bytes.readUInt16BE()}.${bytes.readUInt16BE(2)}-`
|
||||
}
|
||||
|
||||
const STATUS_MAP: { [_: string]: proto.WebMessageInfo.WebMessageInfoStatus } = {
|
||||
'played': proto.WebMessageInfo.WebMessageInfoStatus.PLAYED,
|
||||
'read': proto.WebMessageInfo.WebMessageInfoStatus.READ,
|
||||
|
||||
@@ -327,28 +327,30 @@ export const generateWAMessageContent = async(
|
||||
|
||||
m = { buttonsMessage }
|
||||
} else if('templateButtons' in message && !!message.templateButtons) {
|
||||
const templateMessage: proto.ITemplateMessage = {
|
||||
hydratedTemplate: {
|
||||
hydratedButtons: message.templateButtons
|
||||
}
|
||||
const msg: proto.IHydratedFourRowTemplate = {
|
||||
hydratedButtons: message.templateButtons
|
||||
}
|
||||
|
||||
if('text' in message) {
|
||||
templateMessage.hydratedTemplate.hydratedContentText = message.text
|
||||
msg.hydratedContentText = message.text
|
||||
} else {
|
||||
|
||||
if('caption' in message) {
|
||||
templateMessage.hydratedTemplate.hydratedContentText = message.caption
|
||||
msg.hydratedContentText = message.caption
|
||||
}
|
||||
|
||||
Object.assign(templateMessage.hydratedTemplate, m)
|
||||
Object.assign(msg, m)
|
||||
}
|
||||
|
||||
if('footer' in message && !!message.footer) {
|
||||
templateMessage.hydratedTemplate.hydratedFooterText = message.footer
|
||||
msg.hydratedFooterText = message.footer
|
||||
}
|
||||
|
||||
m = { templateMessage }
|
||||
m = {
|
||||
templateMessage: {
|
||||
hydratedTemplate: msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if('sections' in message && !!message.sections) {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Boom } from '@hapi/boom'
|
||||
import { createCipheriv, createDecipheriv } from 'crypto'
|
||||
import { Logger } from 'pino'
|
||||
import { proto } from '../../WAProto'
|
||||
import { NOISE_MODE, NOISE_WA_HEADER } from '../Defaults'
|
||||
import { KeyPair } from '../Types'
|
||||
import { Binary } from '../WABinary'
|
||||
import { BinaryNode, decodeBinaryNode } from '../WABinary'
|
||||
import { Curve, hkdf, sha256 } from './crypto'
|
||||
|
||||
const TAG_LENGTH = 128 >> 3
|
||||
|
||||
const generateIV = (counter: number) => {
|
||||
const iv = new ArrayBuffer(12)
|
||||
new DataView(iv).setUint32(8, counter)
|
||||
@@ -14,7 +16,11 @@ const generateIV = (counter: number) => {
|
||||
return new Uint8Array(iv)
|
||||
}
|
||||
|
||||
export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: KeyPair) => {
|
||||
export const makeNoiseHandler = (
|
||||
{ public: publicKey, private: privateKey }: KeyPair,
|
||||
logger: Logger
|
||||
) => {
|
||||
logger = logger.child({ class: 'ns' })
|
||||
|
||||
const authenticate = (data: Uint8Array) => {
|
||||
if(!isFinished) {
|
||||
@@ -23,8 +29,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
}
|
||||
|
||||
const encrypt = (plaintext: Uint8Array) => {
|
||||
const authTagLength = 128 >> 3
|
||||
const cipher = createCipheriv('aes-256-gcm', encKey, generateIV(writeCounter), { authTagLength })
|
||||
const cipher = createCipheriv('aes-256-gcm', encKey, generateIV(writeCounter), { authTagLength: TAG_LENGTH })
|
||||
cipher.setAAD(hash)
|
||||
|
||||
const result = Buffer.concat([cipher.update(plaintext), cipher.final(), cipher.getAuthTag()])
|
||||
@@ -41,9 +46,8 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
const iv = generateIV(isFinished ? readCounter : writeCounter)
|
||||
const cipher = createDecipheriv('aes-256-gcm', decKey, iv)
|
||||
// decrypt additional adata
|
||||
const tagLength = 128 >> 3
|
||||
const enc = ciphertext.slice(0, ciphertext.length - tagLength)
|
||||
const tag = ciphertext.slice(ciphertext.length - tagLength)
|
||||
const enc = ciphertext.slice(0, ciphertext.length - TAG_LENGTH)
|
||||
const tag = ciphertext.slice(ciphertext.length - TAG_LENGTH)
|
||||
// set additional data
|
||||
cipher.setAAD(hash)
|
||||
cipher.setAuthTag(tag)
|
||||
@@ -94,8 +98,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
let isFinished = false
|
||||
let sentIntro = false
|
||||
|
||||
const outBinary = new Binary()
|
||||
const inBinary = new Binary()
|
||||
let inBytes = Buffer.alloc(0)
|
||||
|
||||
authenticate(NOISE_WA_HEADER)
|
||||
authenticate(publicKey)
|
||||
@@ -114,7 +117,7 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
mixIntoKey(Curve.sharedKey(privateKey, decStaticContent))
|
||||
|
||||
const certDecoded = decrypt(serverHello!.payload!)
|
||||
const { details: certDetails, signature: certSignature } = proto.NoiseCertificate.decode(certDecoded)
|
||||
const { details: certDetails } = proto.NoiseCertificate.decode(certDecoded)
|
||||
|
||||
const { key: certKey } = proto.NoiseCertificateDetails.decode(certDetails)
|
||||
|
||||
@@ -133,47 +136,48 @@ export const makeNoiseHandler = ({ public: publicKey, private: privateKey }: Key
|
||||
}
|
||||
|
||||
const introSize = sentIntro ? 0 : NOISE_WA_HEADER.length
|
||||
|
||||
outBinary.ensureAdditionalCapacity(introSize + 3 + data.byteLength)
|
||||
const frame = Buffer.alloc(introSize + 3 + data.byteLength)
|
||||
|
||||
if(!sentIntro) {
|
||||
outBinary.writeByteArray(NOISE_WA_HEADER)
|
||||
frame.set(NOISE_WA_HEADER)
|
||||
sentIntro = true
|
||||
}
|
||||
|
||||
outBinary.writeUint8(data.byteLength >> 16)
|
||||
outBinary.writeUint16(65535 & data.byteLength)
|
||||
outBinary.write(data)
|
||||
frame.writeUInt8(data.byteLength >> 16, introSize)
|
||||
frame.writeUInt16BE(65535 & data.byteLength, introSize + 1)
|
||||
frame.set(data, introSize + 3)
|
||||
|
||||
const bytes = outBinary.readByteArray()
|
||||
return bytes as Uint8Array
|
||||
return frame
|
||||
},
|
||||
decodeFrame: (newData: Buffer | Uint8Array, onFrame: (buff: Uint8Array | BinaryNode) => void) => {
|
||||
// the binary protocol uses its own framing mechanism
|
||||
// on top of the WS frames
|
||||
// so we get this data and separate out the frames
|
||||
const getBytesSize = () => {
|
||||
return (inBinary.readUint8() << 16) | inBinary.readUint16()
|
||||
if(inBytes.length >= 3) {
|
||||
return (inBytes.readUInt8() << 16) | inBytes.readUInt16BE(1)
|
||||
}
|
||||
}
|
||||
|
||||
const peekSize = () => {
|
||||
return !(inBinary.size() < 3) && getBytesSize() <= inBinary.size()
|
||||
}
|
||||
inBytes = Buffer.concat([ inBytes, newData ])
|
||||
|
||||
logger.trace(`recv ${newData.length} bytes, total recv ${inBytes.length} bytes`)
|
||||
|
||||
let size = getBytesSize()
|
||||
while(size && inBytes.length >= size + 3) {
|
||||
let frame: Uint8Array | BinaryNode = inBytes.slice(3, size + 3)
|
||||
inBytes = inBytes.slice(size + 3)
|
||||
|
||||
inBinary.writeByteArray(newData)
|
||||
while(inBinary.peek(peekSize)) {
|
||||
const bytes = getBytesSize()
|
||||
let frame: Uint8Array | BinaryNode = inBinary.readByteArray(bytes)
|
||||
if(isFinished) {
|
||||
const result = decrypt(frame as Uint8Array)
|
||||
const unpacked = new Binary(result).decompressed()
|
||||
frame = decodeBinaryNode(unpacked)
|
||||
frame = decodeBinaryNode(result)
|
||||
}
|
||||
|
||||
onFrame(frame)
|
||||
}
|
||||
logger.trace({ msg: (frame as any)?.attrs?.id }, 'recv frame')
|
||||
|
||||
inBinary.peek(peekSize)
|
||||
onFrame(frame)
|
||||
size = getBytesSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,12 @@
|
||||
import * as libsignal from 'libsignal'
|
||||
import { proto } from '../../WAProto'
|
||||
import { GroupCipher, GroupSessionBuilder, SenderKeyDistributionMessage, SenderKeyName, SenderKeyRecord } from '../../WASignalGroup'
|
||||
import { AuthenticationCreds, KeyPair, SignalAuthState, SignalIdentity, SignalKeyStore, SignedKeyPair } from '../Types/Auth'
|
||||
import { assertNodeErrorFree, BinaryNode, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildUInt, jidDecode, JidWithDevice } from '../WABinary'
|
||||
import { Curve } from './crypto'
|
||||
import { KEY_BUNDLE_TYPE } from '../Defaults'
|
||||
import { AuthenticationCreds, AuthenticationState, KeyPair, SignalAuthState, SignalIdentity, SignalKeyStore, SignedKeyPair } from '../Types/Auth'
|
||||
import { assertNodeErrorFree, BinaryNode, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildUInt, jidDecode, JidWithDevice, S_WHATSAPP_NET } from '../WABinary'
|
||||
import { Curve, generateSignalPubKey } from './crypto'
|
||||
import { encodeBigEndian } from './generics'
|
||||
|
||||
export const generateSignalPubKey = (pubKey: Uint8Array | Buffer) => {
|
||||
const newPub = Buffer.alloc(33)
|
||||
newPub.set([5], 0)
|
||||
newPub.set(pubKey, 1)
|
||||
return newPub
|
||||
}
|
||||
|
||||
const jidToSignalAddress = (jid: string) => jid.split('@')[0]
|
||||
|
||||
export const jidToSignalProtocolAddress = (jid: string) => {
|
||||
@@ -60,7 +54,6 @@ export const generateOrGetPreKeys = (creds: AuthenticationCreds, range: number)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const xmppSignedPreKey = (key: SignedKeyPair): BinaryNode => (
|
||||
{
|
||||
tag: 'skey',
|
||||
@@ -273,4 +266,46 @@ export const extractDeviceJids = (result: BinaryNode, myJid: string, excludeZero
|
||||
}
|
||||
|
||||
return extracted
|
||||
}
|
||||
|
||||
/**
|
||||
* get the next N keys for upload or processing
|
||||
* @param count number of pre-keys to get or generate
|
||||
*/
|
||||
export const getNextPreKeys = async({ creds, keys }: AuthenticationState, count: number) => {
|
||||
const { newPreKeys, lastPreKeyId, preKeysRange } = generateOrGetPreKeys(creds, count)
|
||||
|
||||
const update: Partial<AuthenticationCreds> = {
|
||||
nextPreKeyId: Math.max(lastPreKeyId + 1, creds.nextPreKeyId),
|
||||
firstUnuploadedPreKeyId: Math.max(creds.firstUnuploadedPreKeyId, lastPreKeyId + 1)
|
||||
}
|
||||
|
||||
await keys.set({ 'pre-key': newPreKeys })
|
||||
|
||||
const preKeys = await getPreKeys(keys, preKeysRange[0], preKeysRange[0] + preKeysRange[1])
|
||||
|
||||
return { update, preKeys }
|
||||
}
|
||||
|
||||
export const getNextPreKeysNode = async(state: AuthenticationState, count: number) => {
|
||||
const { creds } = state
|
||||
const { update, preKeys } = await getNextPreKeys(state, count)
|
||||
|
||||
const node: BinaryNode = {
|
||||
tag: 'iq',
|
||||
attrs: {
|
||||
xmlns: 'encrypt',
|
||||
type: 'set',
|
||||
to: S_WHATSAPP_NET,
|
||||
},
|
||||
content: [
|
||||
{ tag: 'registration', attrs: { }, content: encodeBigEndian(creds.registrationId) },
|
||||
{ tag: 'type', attrs: { }, content: KEY_BUNDLE_TYPE },
|
||||
{ tag: 'identity', attrs: { }, content: creds.signedIdentityKey.public },
|
||||
{ tag: 'list', attrs: { }, content: Object.keys(preKeys).map(k => xmppPreKey(preKeys[+k], +k)) },
|
||||
xmppSignedPreKey(creds.signedPreKey)
|
||||
]
|
||||
}
|
||||
|
||||
return { update, node }
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Boom } from '@hapi/boom'
|
||||
import { createHash } from 'crypto'
|
||||
import { proto } from '../../WAProto'
|
||||
import { KEY_BUNDLE_TYPE } from '../Defaults'
|
||||
import type { AuthenticationCreds, SignalCreds, SocketConfig } from '../Types'
|
||||
import { BinaryNode, getAllBinaryNodeChildren, jidDecode, S_WHATSAPP_NET } from '../WABinary'
|
||||
import { BinaryNode, getBinaryNodeChild, jidDecode, S_WHATSAPP_NET } from '../WABinary'
|
||||
import { Curve, hmacSign } from './crypto'
|
||||
import { encodeInt } from './generics'
|
||||
import { encodeBigEndian } from './generics'
|
||||
import { createSignalIdentity } from './signal'
|
||||
|
||||
type ClientPayloadConfig = Pick<SocketConfig, 'version' | 'browser'>
|
||||
|
||||
const getUserAgent = ({ version }: Pick<SocketConfig, 'version'>): proto.IUserAgent => ({
|
||||
const getUserAgent = ({ version, browser }: ClientPayloadConfig): proto.IUserAgent => ({
|
||||
appVersion: {
|
||||
primary: version[0],
|
||||
secondary: version[1],
|
||||
@@ -19,12 +20,12 @@ const getUserAgent = ({ version }: Pick<SocketConfig, 'version'>): proto.IUserAg
|
||||
releaseChannel: proto.UserAgent.UserAgentReleaseChannel.RELEASE,
|
||||
mcc: '000',
|
||||
mnc: '000',
|
||||
osVersion: '0.1',
|
||||
osVersion: browser[2],
|
||||
manufacturer: '',
|
||||
device: 'Desktop',
|
||||
osBuildNumber: '0.1',
|
||||
osBuildNumber: browser[2],
|
||||
localeLanguageIso6391: 'en',
|
||||
localeCountryIso31661Alpha2: 'US',
|
||||
localeCountryIso31661Alpha2: 'en',
|
||||
})
|
||||
|
||||
const getWebInfo = (): proto.IWebInfo => ({
|
||||
@@ -33,7 +34,6 @@ const getWebInfo = (): proto.IWebInfo => ({
|
||||
|
||||
const getClientPayload = (config: ClientPayloadConfig): proto.IClientPayload => {
|
||||
return {
|
||||
passive: true,
|
||||
connectType: proto.ClientPayload.ClientPayloadConnectType.WIFI_UNKNOWN,
|
||||
connectReason: proto.ClientPayload.ClientPayloadConnectReason.USER_ACTIVATED,
|
||||
userAgent: getUserAgent(config),
|
||||
@@ -45,6 +45,7 @@ export const generateLoginNode = (userJid: string, config: ClientPayloadConfig):
|
||||
const { user, device } = jidDecode(userJid)
|
||||
const payload: proto.IClientPayload = {
|
||||
...getClientPayload(config),
|
||||
passive: true,
|
||||
username: +user,
|
||||
device: device,
|
||||
}
|
||||
@@ -65,11 +66,11 @@ export const generateRegistrationNode = (
|
||||
const companion: proto.ICompanionProps = {
|
||||
os: config.browser[0],
|
||||
version: {
|
||||
primary: +(browserVersion[0] || 10),
|
||||
secondary: +(browserVersion[1] || 0),
|
||||
primary: +(browserVersion[0] || 0),
|
||||
secondary: +(browserVersion[1] || 1),
|
||||
tertiary: +(browserVersion[2] || 0),
|
||||
},
|
||||
platformType: proto.CompanionProps.CompanionPropsPlatformType[config.browser[1].toUpperCase()] || proto.CompanionProps.CompanionPropsPlatformType.CHROME,
|
||||
platformType: proto.CompanionProps.CompanionPropsPlatformType[config.browser[1].toUpperCase()] || proto.CompanionProps.CompanionPropsPlatformType.UNKNOWN,
|
||||
requireFullSync: false,
|
||||
}
|
||||
|
||||
@@ -77,13 +78,14 @@ export const generateRegistrationNode = (
|
||||
|
||||
const registerPayload: proto.IClientPayload = {
|
||||
...getClientPayload(config),
|
||||
passive: false,
|
||||
regData: {
|
||||
buildHash: appVersionBuf,
|
||||
companionProps: companionProto,
|
||||
eRegid: encodeInt(4, registrationId),
|
||||
eKeytype: encodeInt(1, 5),
|
||||
eRegid: encodeBigEndian(registrationId),
|
||||
eKeytype: KEY_BUNDLE_TYPE,
|
||||
eIdent: signedIdentityKey.public,
|
||||
eSkeyId: encodeInt(3, signedPreKey.keyId),
|
||||
eSkeyId: encodeBigEndian(signedPreKey.keyId, 3),
|
||||
eSkeyVal: signedPreKey.keyPair.public,
|
||||
eSkeySig: signedPreKey.signature,
|
||||
},
|
||||
@@ -96,51 +98,47 @@ export const configureSuccessfulPairing = (
|
||||
stanza: BinaryNode,
|
||||
{ advSecretKey, signedIdentityKey, signalIdentities }: Pick<AuthenticationCreds, 'advSecretKey' | 'signedIdentityKey' | 'signalIdentities'>
|
||||
) => {
|
||||
const [pair] = getAllBinaryNodeChildren(stanza)
|
||||
const pairContent = Array.isArray(pair.content) ? pair.content : []
|
||||
|
||||
const msgId = stanza.attrs.id
|
||||
const deviceIdentity = pairContent.find(m => m.tag === 'device-identity')?.content
|
||||
const businessName = pairContent.find(m => m.tag === 'biz')?.attrs?.name
|
||||
const verifiedName = businessName || ''
|
||||
const jid = pairContent.find(m => m.tag === 'device')?.attrs?.jid
|
||||
|
||||
const { details, hmac } = proto.ADVSignedDeviceIdentityHMAC.decode(deviceIdentity as Buffer)
|
||||
const pairSuccessNode = getBinaryNodeChild(stanza, 'pair-success')
|
||||
|
||||
const deviceIdentityNode = getBinaryNodeChild(pairSuccessNode, 'device-identity')
|
||||
const platformNode = getBinaryNodeChild(pairSuccessNode, 'platform')
|
||||
const deviceNode = getBinaryNodeChild(pairSuccessNode, 'device')
|
||||
const businessNode = getBinaryNodeChild(pairSuccessNode, 'biz')
|
||||
|
||||
if(!deviceIdentityNode || !deviceNode) {
|
||||
throw new Boom('Missing device-identity or device in pair success node', { data: stanza })
|
||||
}
|
||||
|
||||
const bizName = businessNode?.attrs.name
|
||||
const jid = deviceNode.attrs.jid
|
||||
|
||||
const { details, hmac } = proto.ADVSignedDeviceIdentityHMAC.decode(deviceIdentityNode.content as Buffer)
|
||||
// check HMAC matches
|
||||
const advSign = hmacSign(details, Buffer.from(advSecretKey, 'base64'))
|
||||
|
||||
if(Buffer.compare(hmac, advSign) !== 0) {
|
||||
throw new Boom('Invalid pairing')
|
||||
throw new Boom('Invalid account signature')
|
||||
}
|
||||
|
||||
const account = proto.ADVSignedDeviceIdentity.decode(details)
|
||||
const { accountSignatureKey, accountSignature } = account
|
||||
|
||||
const accountMsg = Buffer.concat([
|
||||
Buffer.from([6, 0]),
|
||||
account.details,
|
||||
signedIdentityKey.public
|
||||
])
|
||||
const { accountSignatureKey, accountSignature, details: deviceDetails } = account
|
||||
// verify the device signature matches
|
||||
const accountMsg = Buffer.concat([ Buffer.from([6, 0]), deviceDetails, signedIdentityKey.public ])
|
||||
if(!Curve.verify(accountSignatureKey, accountMsg, accountSignature)) {
|
||||
throw new Boom('Failed to verify account signature')
|
||||
}
|
||||
|
||||
const deviceMsg = Buffer.concat([
|
||||
new Uint8Array([6, 1]),
|
||||
account.details,
|
||||
signedIdentityKey.public,
|
||||
account.accountSignatureKey
|
||||
])
|
||||
// sign the details with our identity key
|
||||
const deviceMsg = Buffer.concat([ Buffer.from([6, 1]), deviceDetails, signedIdentityKey.public, accountSignatureKey ])
|
||||
account.deviceSignature = Curve.sign(signedIdentityKey.private, deviceMsg)
|
||||
// do not provide the "accountSignatureKey" back
|
||||
account.accountSignatureKey = null
|
||||
|
||||
const identity = createSignalIdentity(jid, accountSignatureKey)
|
||||
const accountEnc = proto.ADVSignedDeviceIdentity.encode(account).finish()
|
||||
|
||||
const keyIndex = proto.ADVDeviceIdentity.decode(account.details).keyIndex
|
||||
|
||||
const accountEnc = proto.ADVSignedDeviceIdentity.encode({
|
||||
...account.toJSON(),
|
||||
accountSignatureKey: undefined
|
||||
}).finish()
|
||||
const deviceIdentity = proto.ADVDeviceIdentity.decode(account.details)
|
||||
|
||||
const reply: BinaryNode = {
|
||||
tag: 'iq',
|
||||
@@ -156,7 +154,7 @@ export const configureSuccessfulPairing = (
|
||||
content: [
|
||||
{
|
||||
tag: 'device-identity',
|
||||
attrs: { 'key-index': `${keyIndex}` },
|
||||
attrs: { 'key-index': deviceIdentity.keyIndex.toString() },
|
||||
content: accountEnc
|
||||
}
|
||||
]
|
||||
@@ -166,9 +164,14 @@ export const configureSuccessfulPairing = (
|
||||
|
||||
const authUpdate: Partial<AuthenticationCreds> = {
|
||||
account,
|
||||
me: { id: jid, verifiedName },
|
||||
signalIdentities: [...(signalIdentities || []), identity]
|
||||
me: { id: jid, name: bizName },
|
||||
signalIdentities: [
|
||||
...(signalIdentities || []),
|
||||
identity
|
||||
],
|
||||
platform: platformNode?.attrs.name
|
||||
}
|
||||
|
||||
return {
|
||||
creds: authUpdate,
|
||||
reply
|
||||
|
||||
Reference in New Issue
Block a user