From 289bf9e3839b818841fa3723869d7cc2ed91262f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 28 May 2022 15:05:03 +0000 Subject: [PATCH] fixup! doc: warn about using timingSafeEqual with floats --- doc/api/crypto.md | 11 +++++++--- .../test-crypto-timing-safe-equal.js | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 378e210824141f..b1d5010a195dd7 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -5443,8 +5443,11 @@ changes: * `b` {ArrayBuffer|Buffer|TypedArray|DataView} * Returns: {boolean} -This function is based on a constant-time algorithm. -Returns true if `a` is equal to `b`, without leaking timing information that +This function compares the underlying bytes that represent the given +`ArrayBuffer`, `TypedArray`, or `DataView` instances using a constant-time +algorithm. + +It returns true if `a` is equal to `b`, without leaking timing information that would allow an attacker to guess one of the values. This is suitable for comparing HMAC digests or secret values like authentication cookies or [capability urls](https://www.w3.org/TR/capability-urls/). @@ -5454,7 +5457,9 @@ must have the same byte length. An error is thrown if `a` and `b` have different byte lengths. This function does not compare the elements of `a` and `b` directly. Instead, it -compares the bitwise representations of `a` and `b`. +compares the bitwise representations of `a` and `b`. If `a` and `b` are +instances of the same `TypedArray` class, this is equivalent to an element-wise +[SameValue comparison][]. In particular, this function does not follow the usual definition of equality for floating-point numbers when `a` or `b` is a diff --git a/test/sequential/test-crypto-timing-safe-equal.js b/test/sequential/test-crypto-timing-safe-equal.js index a8bd3abf4cf1ae..b37c7d42dcc4ab 100644 --- a/test/sequential/test-crypto-timing-safe-equal.js +++ b/test/sequential/test-crypto-timing-safe-equal.js @@ -32,6 +32,28 @@ assert.strictEqual( } } +{ + // timingSafeEqual has SameValue semantics, not equality semantics, when the + // inputs are floating-point numbers. + + const cmp = (fn) => (a, b) => a.every((x, i) => fn(x, b[i])); + const eq = cmp((a, b) => a === b); + const is = cmp(Object.is); + + // NaN !== NaN, but Object.is(NaN, NaN) === true. + const a = new Float32Array(10).fill(NaN); + assert.strictEqual(eq(a, a), false); + assert.strictEqual(is(a, a), true); + assert.strictEqual(crypto.timingSafeEqual(a, a), true); + + // 0 === -0, but Object.is(0, -0) === false. + const pos0 = new Float64Array(10).fill(0); + const neg0 = new Float64Array(10).fill(-0); + assert.strictEqual(eq(pos0, neg0), true); + assert.strictEqual(is(pos0, neg0), false); + assert.strictEqual(crypto.timingSafeEqual(pos0, neg0), false); +} + assert.throws( () => crypto.timingSafeEqual(Buffer.from([1, 2, 3]), Buffer.from([1, 2])), {