From 4e681d32194b5f11e9f9b55a7059d98e3df99694 Mon Sep 17 00:00:00 2001 From: Skid <112231743+Skidy89@users.noreply.github.com> Date: Thu, 8 Jan 2026 13:49:37 -0600 Subject: [PATCH] feat: Verify leaf signature (#2208) * Update WA_CERT_DETAILS with issuer and public key * certificate validation * fix:lint * padding * lint: fix tab --------- Co-authored-by: skidy89 --- src/Defaults/index.ts | 5 +++-- src/Utils/noise-handler.ts | 32 +++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/Defaults/index.ts b/src/Defaults/index.ts index 4efe8ed9..20d254be 100644 --- a/src/Defaults/index.ts +++ b/src/Defaults/index.ts @@ -29,9 +29,10 @@ export const NOISE_WA_HEADER = Buffer.from([87, 65, 6, DICT_VERSION]) // last is /** from: https://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url */ export const URL_REGEX = /https:\/\/(?![^:@\/\s]+:[^:@\/\s]+@)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(:\d+)?(\/[^\s]*)?/g -// TODO: Add WA root CA export const WA_CERT_DETAILS = { - SERIAL: 0 + SERIAL: 0, + ISSUER: 'WhatsAppLongTerm1', + PUBLIC_KEY: Buffer.from('142375574d0a587166aae71ebe516437c4a28b73e3695c6ce1f7f9545da8ee6b', 'hex') } export const PROCESSABLE_HISTORY_TYPES = [ diff --git a/src/Utils/noise-handler.ts b/src/Utils/noise-handler.ts index 3ff5f4e5..779a4bab 100644 --- a/src/Utils/noise-handler.ts +++ b/src/Utils/noise-handler.ts @@ -112,9 +112,35 @@ export const makeNoiseHandler = ({ const certDecoded = decrypt(serverHello!.payload!) - const { intermediate: certIntermediate /*leaf*/ } = proto.CertChain.decode(certDecoded) - // TODO: handle this leaf stuff - const { issuerSerial } = proto.CertChain.NoiseCertificate.Details.decode(certIntermediate!.details!) + const { intermediate: certIntermediate, leaf } = proto.CertChain.decode(certDecoded) + // leaf + if (!leaf?.details || !leaf?.signature) { + throw new Boom('invalid noise leaf certificate', { statusCode: 400 }) + } + + if (!certIntermediate?.details || !certIntermediate?.signature) { + throw new Boom('invalid noise intermediate certificate', { statusCode: 400 }) + } + + const details = proto.CertChain.NoiseCertificate.Details.decode(certIntermediate.details) + + const { issuerSerial } = details + + const verify = Curve.verify(details.key!, leaf.details, leaf.signature) + + const verifyIntermediate = Curve.verify( + WA_CERT_DETAILS.PUBLIC_KEY, + certIntermediate.details, + certIntermediate.signature + ) + + if (!verify) { + throw new Boom('noise certificate signature invalid', { statusCode: 400 }) + } + + if (!verifyIntermediate) { + throw new Boom('noise intermediate certificate signature invalid', { statusCode: 400 }) + } if (issuerSerial !== WA_CERT_DETAILS.SERIAL) { throw new Boom('certification match failed', { statusCode: 400 })