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: https://github.com/nodejs/node/pull/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>
This commit is contained in:
Tobias Nießen 2024-04-08 13:36:53 +02:00 committed by GitHub
parent 9ef724bc81
commit 08609b5222
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 52 additions and 2 deletions

View File

@ -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));
}

View File

@ -9,6 +9,8 @@
namespace node {
using v8::FastApiCallbackOptions;
using v8::FastApiTypedArray;
using v8::FunctionCallbackInfo;
using v8::Local;
using v8::Object;
@ -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

View File

@ -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>,
@ -51,6 +56,7 @@ class ExternalReferenceRegistry {
V(CFunctionCallbackWithBool) \
V(CFunctionCallbackWithString) \
V(CFunctionCallbackWithStrings) \
V(CFunctionCallbackWithTwoUint8ArraysFallback) \
V(CFunctionWithUint32) \
V(CFunctionWithDoubleReturnDouble) \
V(CFunctionWithInt64Fallback) \