Skip to content

Commit 289bf9e

Browse files
committed
fixup! doc: warn about using timingSafeEqual with floats
1 parent 554be50 commit 289bf9e

File tree

2 files changed

+30
-3
lines changed

2 files changed

+30
-3
lines changed

doc/api/crypto.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -5443,8 +5443,11 @@ changes:
54435443
* `b` {ArrayBuffer|Buffer|TypedArray|DataView}
54445444
* Returns: {boolean}
54455445

5446-
This function is based on a constant-time algorithm.
5447-
Returns true if `a` is equal to `b`, without leaking timing information that
5446+
This function compares the underlying bytes that represent the given
5447+
`ArrayBuffer`, `TypedArray`, or `DataView` instances using a constant-time
5448+
algorithm.
5449+
5450+
It returns true if `a` is equal to `b`, without leaking timing information that
54485451
would allow an attacker to guess one of the values. This is suitable for
54495452
comparing HMAC digests or secret values like authentication cookies or
54505453
[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
54545457
different byte lengths.
54555458

54565459
This function does not compare the elements of `a` and `b` directly. Instead, it
5457-
compares the bitwise representations of `a` and `b`.
5460+
compares the bitwise representations of `a` and `b`. If `a` and `b` are
5461+
instances of the same `TypedArray` class, this is equivalent to an element-wise
5462+
[SameValue comparison][].
54585463

54595464
<strong class="critical">In particular, this function does not follow the usual
54605465
definition of equality for floating-point numbers when `a` or `b` is a

test/sequential/test-crypto-timing-safe-equal.js

+22
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,28 @@ assert.strictEqual(
3232
}
3333
}
3434

35+
{
36+
// timingSafeEqual has SameValue semantics, not equality semantics, when the
37+
// inputs are floating-point numbers.
38+
39+
const cmp = (fn) => (a, b) => a.every((x, i) => fn(x, b[i]));
40+
const eq = cmp((a, b) => a === b);
41+
const is = cmp(Object.is);
42+
43+
// NaN !== NaN, but Object.is(NaN, NaN) === true.
44+
const a = new Float32Array(10).fill(NaN);
45+
assert.strictEqual(eq(a, a), false);
46+
assert.strictEqual(is(a, a), true);
47+
assert.strictEqual(crypto.timingSafeEqual(a, a), true);
48+
49+
// 0 === -0, but Object.is(0, -0) === false.
50+
const pos0 = new Float64Array(10).fill(0);
51+
const neg0 = new Float64Array(10).fill(-0);
52+
assert.strictEqual(eq(pos0, neg0), true);
53+
assert.strictEqual(is(pos0, neg0), false);
54+
assert.strictEqual(crypto.timingSafeEqual(pos0, neg0), false);
55+
}
56+
3557
assert.throws(
3658
() => crypto.timingSafeEqual(Buffer.from([1, 2, 3]), Buffer.from([1, 2])),
3759
{

0 commit comments

Comments
 (0)