From 10e2e4fb65e11a6de07c9c53dddc86afe2c222ed Mon Sep 17 00:00:00 2001 From: larabr Date: Thu, 12 Mar 2026 17:27:08 +0100 Subject: [PATCH] Use constant-time select in modExp (#1984) modExp is used in some RSA, DSA and ElGamal operations. With native bigints, the constant-time select no longer introduces a considerable slowdown, so we switch to that to avoid secret-dependent branching, which in principle enables e.g. cache timing attacks (NB: these are still not relevant for our threat model). Thanks to Yayu Wang, Kjell Dankert and Aastha Mehta from the University of British Columbia for reporting the potential security issue and validating the fix. --- src/crypto/biginteger.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/crypto/biginteger.ts b/src/crypto/biginteger.ts index 59227f31..2eaaf1f5 100644 --- a/src/crypto/biginteger.ts +++ b/src/crypto/biginteger.ts @@ -22,6 +22,18 @@ export function mod(a: bigint, m: bigint) { return reduced < _0n ? reduced + m : reduced; } +/** + * Return either `a` or `b` based on `cond`, in algorithmic constant time. + * @param {BigInt} cond + * @param {BigInt} a + * @param {BigInt} b + * @returns `a` if `cond` is `1n`, `b` otherwise + */ +function selectBigInt(cond: 0n | 1n, a: bigint, b: bigint) { + const mask = -cond; // ~0n === -1n + return (a & mask) | (b & ~mask); +} + /** * Compute modular exponentiation using square and multiply * @param {BigInt} a - Base @@ -40,12 +52,12 @@ export function modExp(b: bigint, e: bigint, n: bigint) { x %= n; let r = BigInt(1); while (exp > _0n) { - const lsb = exp & _1n; + const lsb = (exp & _1n) as 1n | 0n; exp >>= _1n; // e / 2 // Always compute multiplication step, to reduce timing leakage const rx = (r * x) % n; // Update r only if lsb is 1 (odd exponent) - r = lsb ? rx : r; + r = selectBigInt(lsb, rx, r); x = (x * x) % n; // Square } return r;