Skip to content

Commit 95cf67e

Browse files
tniessenguangwong
authored andcommitted
doc,test: clarify timingSafeEqual semantics
PR-URL: nodejs/node#43228 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 2231205 commit 95cf67e

File tree

2 files changed

+46
-2
lines changed

2 files changed

+46
-2
lines changed

doc/api/crypto.md

+11-2
Original file line numberDiff line numberDiff line change
@@ -5340,8 +5340,11 @@ changes:
53405340
* `b` {ArrayBuffer|Buffer|TypedArray|DataView}
53415341
* Returns: {boolean}
53425342

5343-
This function is based on a constant-time algorithm.
5344-
Returns true if `a` is equal to `b`, without leaking timing information that
5343+
This function compares the underlying bytes that represent the given
5344+
`ArrayBuffer`, `TypedArray`, or `DataView` instances using a constant-time
5345+
algorithm.
5346+
5347+
This function does not leak timing information that
53455348
would allow an attacker to guess one of the values. This is suitable for
53465349
comparing HMAC digests or secret values like authentication cookies or
53475350
[capability urls](https://www.w3.org/TR/capability-urls/).
@@ -5354,6 +5357,12 @@ If at least one of `a` and `b` is a `TypedArray` with more than one byte per
53545357
entry, such as `Uint16Array`, the result will be computed using the platform
53555358
byte order.
53565359

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

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

+35
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)