Skip to content

Commit 08609b5

Browse files
authored
crypto: make timingSafeEqual faster for Uint8Array
Add a fast API that V8 can use if the user supplies Uint8Arrays (including Buffers) to timingSafeEqual. PR-URL: #52341 Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io> Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br> Reviewed-By: Daniel Lemire <daniel@lemire.me> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 9ef724b commit 08609b5

File tree

3 files changed

+52
-2
lines changed

3 files changed

+52
-2
lines changed

benchmark/crypto/timingSafeEqual.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const assert = require('node:assert');
5+
const { randomBytes, timingSafeEqual } = require('node:crypto');
6+
7+
const bench = common.createBenchmark(main, {
8+
n: [1e5],
9+
bufferSize: [10, 100, 200, 2_100, 22_023],
10+
});
11+
12+
function main({ n, bufferSize }) {
13+
const bufs = [randomBytes(bufferSize), randomBytes(bufferSize)];
14+
bench.start();
15+
let count = 0;
16+
for (let i = 0; i < n; i++) {
17+
const ret = timingSafeEqual(bufs[i % 2], bufs[1]);
18+
if (ret) count++;
19+
}
20+
bench.end(n);
21+
assert.strictEqual(count, Math.floor(n / 2));
22+
}

src/crypto/crypto_timing.cc

+24-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace node {
1111

12+
using v8::FastApiCallbackOptions;
13+
using v8::FastApiTypedArray;
1214
using v8::FunctionCallbackInfo;
1315
using v8::Local;
1416
using v8::Object;
@@ -46,12 +48,32 @@ void TimingSafeEqual(const FunctionCallbackInfo<Value>& args) {
4648
CRYPTO_memcmp(buf1.data(), buf2.data(), buf1.size()) == 0);
4749
}
4850

51+
bool FastTimingSafeEqual(Local<Value> receiver,
52+
const FastApiTypedArray<uint8_t>& a,
53+
const FastApiTypedArray<uint8_t>& b,
54+
// NOLINTNEXTLINE(runtime/references)
55+
FastApiCallbackOptions& options) {
56+
uint8_t* data_a;
57+
uint8_t* data_b;
58+
if (a.length() != b.length() || !a.getStorageIfAligned(&data_a) ||
59+
!b.getStorageIfAligned(&data_b)) {
60+
options.fallback = true;
61+
return false;
62+
}
63+
64+
return CRYPTO_memcmp(data_a, data_b, a.length()) == 0;
65+
}
66+
67+
static v8::CFunction fast_equal(v8::CFunction::Make(FastTimingSafeEqual));
68+
4969
void Initialize(Environment* env, Local<Object> target) {
50-
SetMethodNoSideEffect(
51-
env->context(), target, "timingSafeEqual", TimingSafeEqual);
70+
SetFastMethodNoSideEffect(
71+
env->context(), target, "timingSafeEqual", TimingSafeEqual, &fast_equal);
5272
}
5373
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
5474
registry->Register(TimingSafeEqual);
75+
registry->Register(FastTimingSafeEqual);
76+
registry->Register(fast_equal.GetTypeInfo());
5577
}
5678
} // namespace Timing
5779

src/node_external_reference.h

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ using CFunctionCallbackWithStrings =
2727
bool (*)(v8::Local<v8::Value>,
2828
const v8::FastOneByteString& input,
2929
const v8::FastOneByteString& base);
30+
using CFunctionCallbackWithTwoUint8ArraysFallback =
31+
bool (*)(v8::Local<v8::Value>,
32+
const v8::FastApiTypedArray<uint8_t>&,
33+
const v8::FastApiTypedArray<uint8_t>&,
34+
v8::FastApiCallbackOptions&);
3035
using CFunctionWithUint32 = uint32_t (*)(v8::Local<v8::Value>,
3136
const uint32_t input);
3237
using CFunctionWithDoubleReturnDouble = double (*)(v8::Local<v8::Value>,
@@ -51,6 +56,7 @@ class ExternalReferenceRegistry {
5156
V(CFunctionCallbackWithBool) \
5257
V(CFunctionCallbackWithString) \
5358
V(CFunctionCallbackWithStrings) \
59+
V(CFunctionCallbackWithTwoUint8ArraysFallback) \
5460
V(CFunctionWithUint32) \
5561
V(CFunctionWithDoubleReturnDouble) \
5662
V(CFunctionWithInt64Fallback) \

0 commit comments

Comments
 (0)