Skip to content

Commit 800cff1

Browse files
authored
doc,test: clarify timingSafeEqual semantics
PR-URL: #43228 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 08d6a82 commit 800cff1

File tree

2 files changed

+46
-2
lines changed

2 files changed

+46
-2
lines changed

doc/api/crypto.md

Lines changed: 11 additions & 2 deletions
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+
This function does not leak 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/).
@@ -5457,6 +5460,12 @@ If at least one of `a` and `b` is a `TypedArray` with more than one byte per
54575460
entry, such as `Uint16Array`, the result will be computed using the platform
54585461
byte order.
54595462

5463+
<strong class="critical">When both of the inputs are `Float32Array`s or
5464+
`Float64Array`s, this function might return unexpected results due to IEEE 754
5465+
encoding of floating-point numbers. In particular, neither `x === y` nor
5466+
`Object.is(x, y)` implies that the byte representations of two floating-point
5467+
numbers `x` and `y` are equal.</strong>
5468+
54605469
Use of `crypto.timingSafeEqual` does not guarantee that the _surrounding_ code
54615470
is timing-safe. Care should be taken to ensure that the surrounding code does
54625471
not introduce timing vulnerabilities.

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,41 @@ assert.strictEqual(
3232
}
3333
}
3434

35+
{
36+
// When the inputs are floating-point numbers, timingSafeEqual neither has
37+
// equality nor SameValue semantics. It just compares the underlying bytes,
38+
// ignoring the TypedArray type completely.
39+
40+
const cmp = (fn) => (a, b) => a.every((x, i) => fn(x, b[i]));
41+
const eq = cmp((a, b) => a === b);
42+
const is = cmp(Object.is);
43+
44+
function test(a, b, { equal, sameValue, timingSafeEqual }) {
45+
assert.strictEqual(eq(a, b), equal);
46+
assert.strictEqual(is(a, b), sameValue);
47+
assert.strictEqual(crypto.timingSafeEqual(a, b), timingSafeEqual);
48+
}
49+
50+
test(new Float32Array([NaN]), new Float32Array([NaN]), {
51+
equal: false,
52+
sameValue: true,
53+
timingSafeEqual: true
54+
});
55+
56+
test(new Float64Array([0]), new Float64Array([-0]), {
57+
equal: true,
58+
sameValue: false,
59+
timingSafeEqual: false
60+
});
61+
62+
const x = new BigInt64Array([0x7ff0000000000001n, 0xfff0000000000001n]);
63+
test(new Float64Array(x.buffer), new Float64Array([NaN, NaN]), {
64+
equal: false,
65+
sameValue: true,
66+
timingSafeEqual: false
67+
});
68+
}
69+
3570
assert.throws(
3671
() => crypto.timingSafeEqual(Buffer.from([1, 2, 3]), Buffer.from([1, 2])),
3772
{

0 commit comments

Comments
 (0)