From fb978fedd0e03f91601829eb2ee72589be56667c Mon Sep 17 00:00:00 2001 From: Mert Can Altin Date: Sun, 27 Sep 2026 15:07:14 +0300 Subject: [PATCH] crypto: improve random synchronous number generation performance Instead of creating a RandomBytesJob object, it calls CSPRNG() directly now. Assisted-by: Claude Code Signed-off-by: Mert Can Altin --- lib/internal/crypto/random.js | 12 ++----- src/crypto/crypto_random.cc | 34 +++++++++++++++++++ ...est-crypto-randomfillsync-trace-sync-io.js | 23 +++++++++++++ typings/internalBinding/crypto.d.ts | 1 + 4 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 test/parallel/test-crypto-randomfillsync-trace-sync-io.js diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js index 10bd2627b0bb..c53494aed848 100644 --- a/lib/internal/crypto/random.js +++ b/lib/internal/crypto/random.js @@ -34,6 +34,7 @@ const { CheckPrimeJob, kCryptoJobAsync, kCryptoJobSync, + randomFillSync: randomFillSyncImpl, secureBuffer, } = internalBinding('crypto'); @@ -175,16 +176,7 @@ function randomFillSync(buf, offset = 0, size) { if (size === 0) return buf; - const job = new RandomBytesJob( - kCryptoJobSync, - buf, - offset, - size); - - const err = job.run()[0]; - if (err) - throw err; - + randomFillSyncImpl(buf, offset, size); return buf; } diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc index 0318d9157b94..c80eb2c800ac 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -214,13 +214,47 @@ MaybeLocal CheckPrimeTraits::EncodeOutput(Environment* env, } namespace Random { +static void RandomFillSync(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(IsAnyBufferSource(args[0])); // Buffer to fill + CHECK(args[1]->IsUint32()); // Offset + CHECK(args[2]->IsUint32()); // Size + + ArrayBufferOrViewContents in(args[0]); + + const uint32_t byte_offset = args[1].As()->Value(); + const uint32_t size = args[2].As()->Value(); + CHECK_GE(byte_offset + size, byte_offset); // Overflow check. + CHECK_LE(byte_offset + size, in.size()); // Bounds check. + + env->PrintSyncTrace(); + if (ERR_peek_error() != 0) ERR_clear_error(); + if (ncrypto::CSPRNG(in.data() + byte_offset, size)) { + if (ERR_peek_error() != 0) ERR_clear_error(); + return; + } + + CryptoErrorStore errors; + errors.Capture(); + if (errors.Empty()) { + errors.Insert(NodeCryptoError::DERIVING_BITS_FAILED); + errors.SetNodeErrorCode("ERR_CRYPTO_OPERATION_FAILED"); + } + Local exception; + if (errors.ToException(env).ToLocal(&exception)) { + env->isolate()->ThrowException(exception); + } +} + void Initialize(Environment* env, Local target) { + SetMethod(env->context(), target, "randomFillSync", RandomFillSync); RandomBytesJob::Initialize(env, target); RandomPrimeJob::Initialize(env, target); CheckPrimeJob::Initialize(env, target); } void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(RandomFillSync); RandomBytesJob::RegisterExternalReferences(registry); RandomPrimeJob::RegisterExternalReferences(registry); CheckPrimeJob::RegisterExternalReferences(registry); diff --git a/test/parallel/test-crypto-randomfillsync-trace-sync-io.js b/test/parallel/test-crypto-randomfillsync-trace-sync-io.js new file mode 100644 index 000000000000..8f9461f19f38 --- /dev/null +++ b/test/parallel/test-crypto-randomfillsync-trace-sync-io.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +// randomFillSync() should be reported by --trace-sync-io when it runs after +// the first event loop turn. + +if (process.argv[2] === 'child') { + setImmediate(() => { + require('crypto').randomFillSync(Buffer.alloc(16)); + }); + return; +} + +const { stderr, status } = spawnSync(process.execPath, + ['--trace-sync-io', __filename, 'child'], + { encoding: 'utf8' }); +assert.strictEqual(status, 0); +assert.match(stderr, /WARNING: Detected use of sync API[\s\S]*randomFillSync/); diff --git a/typings/internalBinding/crypto.d.ts b/typings/internalBinding/crypto.d.ts index e12016ec9161..22ee303f5991 100644 --- a/typings/internalBinding/crypto.d.ts +++ b/typings/internalBinding/crypto.d.ts @@ -1002,6 +1002,7 @@ export interface CryptoBinding { privateEncrypt: InternalCryptoBinding.PublicKeyCipher; publicDecrypt: InternalCryptoBinding.PublicKeyCipher; publicEncrypt: InternalCryptoBinding.PublicKeyCipher; + randomFillSync(buf: ArrayBufferLike | ArrayBufferView, offset: number, size: number): void; resetRootCertStore(): void; secureBuffer(length: number): Uint8Array | undefined; secureHeapUsed(): bigint | undefined;