Skip to content

Commit

Permalink
crypto: make timingSafeEqual faster for Uint8Array
Browse files Browse the repository at this point in the history
Add a fast API that V8 can use if the user supplies Uint8Arrays
(including Buffers) to timingSafeEqual.

PR-URL: #52341
Reviewed-By: Yagiz Nizipli <[email protected]>
Reviewed-By: Vinícius Lourenço Claro Cardoso <[email protected]>
Reviewed-By: Daniel Lemire <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
  • Loading branch information
tniessen authored Apr 8, 2024
1 parent 9ef724b commit 08609b5
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 2 deletions.
22 changes: 22 additions & 0 deletions benchmark/crypto/timingSafeEqual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

const common = require('../common.js');
const assert = require('node:assert');
const { randomBytes, timingSafeEqual } = require('node:crypto');

const bench = common.createBenchmark(main, {
n: [1e5],
bufferSize: [10, 100, 200, 2_100, 22_023],
});

function main({ n, bufferSize }) {
const bufs = [randomBytes(bufferSize), randomBytes(bufferSize)];
bench.start();
let count = 0;
for (let i = 0; i < n; i++) {
const ret = timingSafeEqual(bufs[i % 2], bufs[1]);
if (ret) count++;
}
bench.end(n);
assert.strictEqual(count, Math.floor(n / 2));
}
26 changes: 24 additions & 2 deletions src/crypto/crypto_timing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

namespace node {

using v8::FastApiCallbackOptions;
using v8::FastApiTypedArray;
using v8::FunctionCallbackInfo;
using v8::Local;
using v8::Object;
Expand Down Expand Up @@ -46,12 +48,32 @@ void TimingSafeEqual(const FunctionCallbackInfo<Value>& args) {
CRYPTO_memcmp(buf1.data(), buf2.data(), buf1.size()) == 0);
}

bool FastTimingSafeEqual(Local<Value> receiver,
const FastApiTypedArray<uint8_t>& a,
const FastApiTypedArray<uint8_t>& b,
// NOLINTNEXTLINE(runtime/references)
FastApiCallbackOptions& options) {
uint8_t* data_a;
uint8_t* data_b;
if (a.length() != b.length() || !a.getStorageIfAligned(&data_a) ||
!b.getStorageIfAligned(&data_b)) {
options.fallback = true;
return false;
}

return CRYPTO_memcmp(data_a, data_b, a.length()) == 0;
}

static v8::CFunction fast_equal(v8::CFunction::Make(FastTimingSafeEqual));

void Initialize(Environment* env, Local<Object> target) {
SetMethodNoSideEffect(
env->context(), target, "timingSafeEqual", TimingSafeEqual);
SetFastMethodNoSideEffect(
env->context(), target, "timingSafeEqual", TimingSafeEqual, &fast_equal);
}
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(TimingSafeEqual);
registry->Register(FastTimingSafeEqual);
registry->Register(fast_equal.GetTypeInfo());
}
} // namespace Timing

Expand Down
6 changes: 6 additions & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ using CFunctionCallbackWithStrings =
bool (*)(v8::Local<v8::Value>,
const v8::FastOneByteString& input,
const v8::FastOneByteString& base);
using CFunctionCallbackWithTwoUint8ArraysFallback =
bool (*)(v8::Local<v8::Value>,
const v8::FastApiTypedArray<uint8_t>&,
const v8::FastApiTypedArray<uint8_t>&,
v8::FastApiCallbackOptions&);
using CFunctionWithUint32 = uint32_t (*)(v8::Local<v8::Value>,
const uint32_t input);
using CFunctionWithDoubleReturnDouble = double (*)(v8::Local<v8::Value>,
Expand All @@ -51,6 +56,7 @@ class ExternalReferenceRegistry {
V(CFunctionCallbackWithBool) \
V(CFunctionCallbackWithString) \
V(CFunctionCallbackWithStrings) \
V(CFunctionCallbackWithTwoUint8ArraysFallback) \
V(CFunctionWithUint32) \
V(CFunctionWithDoubleReturnDouble) \
V(CFunctionWithInt64Fallback) \
Expand Down

0 comments on commit 08609b5

Please sign in to comment.