Skip to content

Commit 462e526

Browse files
tniessentargos
authored andcommitted
doc,test: clarify timingSafeEqual semantics
PR-URL: #43228 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent c8aafe2 commit 462e526

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
@@ -5437,8 +5437,11 @@ changes:
54375437
* `b` {ArrayBuffer|Buffer|TypedArray|DataView}
54385438
* Returns: {boolean}
54395439

5440-
This function is based on a constant-time algorithm.
5441-
Returns true if `a` is equal to `b`, without leaking timing information that
5440+
This function compares the underlying bytes that represent the given
5441+
`ArrayBuffer`, `TypedArray`, or `DataView` instances using a constant-time
5442+
algorithm.
5443+
5444+
This function does not leak timing information that
54425445
would allow an attacker to guess one of the values. This is suitable for
54435446
comparing HMAC digests or secret values like authentication cookies or
54445447
[capability urls](https://www.w3.org/TR/capability-urls/).
@@ -5451,6 +5454,12 @@ If at least one of `a` and `b` is a `TypedArray` with more than one byte per
54515454
entry, such as `Uint16Array`, the result will be computed using the platform
54525455
byte order.
54535456

5457+
<strong class="critical">When both of the inputs are `Float32Array`s or
5458+
`Float64Array`s, this function might return unexpected results due to IEEE 754
5459+
encoding of floating-point numbers. In particular, neither `x === y` nor
5460+
`Object.is(x, y)` implies that the byte representations of two floating-point
5461+
numbers `x` and `y` are equal.</strong>
5462+
54545463
Use of `crypto.timingSafeEqual` does not guarantee that the _surrounding_ code
54555464
is timing-safe. Care should be taken to ensure that the surrounding code does
54565465
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)