neroxbailx
Version:
baileys whatsapp-api
150 lines • 4.49 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true })
const boom_1 = require("@hapi/boom")
const WAProto_1 = require("../../WAProto")
const Defaults_1 = require("../Defaults")
const WABinary_1 = require("../WABinary")
const crypto_1 = require("./crypto")
const generateIV = (counter) => {
const iv = new ArrayBuffer(12)
new DataView(iv).setUint32(8, counter)
return new Uint8Array(iv)
}
const makeNoiseHandler = ({ keyPair: { private: privateKey, public: publicKey }, NOISE_HEADER, logger, routingInfo }) => {
logger = logger.child({ class: 'ns' })
const authenticate = (data) => {
if (!isFinished) {
hash = crypto_1.sha256(Buffer.concat([hash, data]))
}
}
const encrypt = (plaintext) => {
const result = crypto_1.aesEncryptGCM(plaintext, encKey, generateIV(writeCounter), hash)
writeCounter += 1
authenticate(result)
return result
}
const decrypt = (ciphertext) => {
// before the handshake is finished, we use the same counter
// after handshake, the counters are different
const iv = generateIV(isFinished ? readCounter : writeCounter)
const result = crypto_1.aesDecryptGCM(ciphertext, decKey, iv, hash)
if (isFinished) {
readCounter += 1
}
else {
writeCounter += 1
}
authenticate(ciphertext)
return result
}
const localHKDF = async (data) => {
const key = await crypto_1.hkdf(Buffer.from(data), 64, { salt, info: '' })
return [key.slice(0, 32), key.slice(32)]
}
const mixIntoKey = async (data) => {
const [write, read] = await localHKDF(data)
salt = write
encKey = read
decKey = read
readCounter = 0
writeCounter = 0
}
const finishInit = async () => {
const [write, read] = await localHKDF(new Uint8Array(0))
encKey = write
decKey = read
hash = Buffer.from([])
readCounter = 0
writeCounter = 0
isFinished = true
}
const data = Buffer.from(Defaults_1.NOISE_MODE)
let hash = data.byteLength === 32 ? data : crypto_1.sha256(data)
let salt = hash
let encKey = hash
let decKey = hash
let readCounter = 0
let writeCounter = 0
let isFinished = false
let sentIntro = false
let inBytes = Buffer.alloc(0)
authenticate(NOISE_HEADER)
authenticate(publicKey)
return {
encrypt,
decrypt,
authenticate,
mixIntoKey,
finishInit,
processHandshake: async ({ serverHello }, noiseKey) => {
authenticate(serverHello.ephemeral)
await mixIntoKey(crypto_1.Curve.sharedKey(privateKey, serverHello.ephemeral))
const decStaticContent = decrypt(serverHello.static)
await mixIntoKey(crypto_1.Curve.sharedKey(privateKey, decStaticContent))
const certDecoded = decrypt(serverHello.payload)
const { intermediate: certIntermediate } = WAProto_1.proto.CertChain.decode(certDecoded)
const { issuerSerial } = WAProto_1.proto.CertChain.NoiseCertificate.Details.decode(certIntermediate.details)
if (issuerSerial !== Defaults_1.WA_CERT_DETAILS.SERIAL) {
throw new boom_1.Boom('certification match failed', { statusCode: 400 })
}
const keyEnc = encrypt(noiseKey.public)
await mixIntoKey(crypto_1.Curve.sharedKey(noiseKey.private, serverHello.ephemeral))
return keyEnc
},
encodeFrame: (data) => {
if (isFinished) {
data = encrypt(data)
}
let header
if (routingInfo) {
header = Buffer.alloc(7)
header.write('ED', 0, 'utf8')
header.writeUint8(0, 2)
header.writeUint8(1, 3)
header.writeUint8(routingInfo.byteLength >> 16, 4)
header.writeUint16BE(routingInfo.byteLength & 65535, 5)
header = Buffer.concat([header, routingInfo, NOISE_HEADER])
}
else {
header = Buffer.from(NOISE_HEADER)
}
const introSize = sentIntro ? 0 : header.length
const frame = Buffer.alloc(introSize + 3 + data.byteLength)
if (!sentIntro) {
frame.set(header)
sentIntro = true
}
frame.writeUInt8(data.byteLength >> 16, introSize)
frame.writeUInt16BE(65535 & data.byteLength, introSize + 1)
frame.set(data, introSize + 3)
return frame
},
decodeFrame: async (newData, onFrame) => {
// 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 = () => {
if (inBytes.length >= 3) {
return (inBytes.readUInt8() << 16) | inBytes.readUInt16BE(1)
}
}
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 = inBytes.slice(3, size + 3)
inBytes = inBytes.slice(size + 3)
if (isFinished) {
const result = decrypt(frame)
frame = await WABinary_1.decodeBinaryNode(result)
}
logger.trace({ msg: frame?.attrs?.id }, 'recv frame')
onFrame(frame)
size = getBytesSize()
}
}
}
}
module.exports = {
makeNoiseHandler
}