Skip to content

Commit 52b68e0

Browse files
authored
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 <mertgold60@gmail.com> PR-URL: #66348 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 32caf9c commit 52b68e0

4 files changed

Lines changed: 60 additions & 10 deletions

File tree

‎lib/internal/crypto/random.js‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const {
3434
CheckPrimeJob,
3535
kCryptoJobAsync,
3636
kCryptoJobSync,
37+
randomFillSync: randomFillSyncImpl,
3738
secureBuffer,
3839
} = internalBinding('crypto');
3940

@@ -175,16 +176,7 @@ function randomFillSync(buf, offset = 0, size) {
175176
if (size === 0)
176177
return buf;
177178

178-
const job = new RandomBytesJob(
179-
kCryptoJobSync,
180-
buf,
181-
offset,
182-
size);
183-
184-
const err = job.run()[0];
185-
if (err)
186-
throw err;
187-
179+
randomFillSyncImpl(buf, offset, size);
188180
return buf;
189181
}
190182

‎src/crypto/crypto_random.cc‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,47 @@ MaybeLocal<Value> CheckPrimeTraits::EncodeOutput(Environment* env,
214214
}
215215

216216
namespace Random {
217+
static void RandomFillSync(const FunctionCallbackInfo<Value>& args) {
218+
Environment* env = Environment::GetCurrent(args);
219+
CHECK(IsAnyBufferSource(args[0])); // Buffer to fill
220+
CHECK(args[1]->IsUint32()); // Offset
221+
CHECK(args[2]->IsUint32()); // Size
222+
223+
ArrayBufferOrViewContents<unsigned char> in(args[0]);
224+
225+
const uint32_t byte_offset = args[1].As<Uint32>()->Value();
226+
const uint32_t size = args[2].As<Uint32>()->Value();
227+
CHECK_GE(byte_offset + size, byte_offset); // Overflow check.
228+
CHECK_LE(byte_offset + size, in.size()); // Bounds check.
229+
230+
env->PrintSyncTrace();
231+
if (ERR_peek_error() != 0) ERR_clear_error();
232+
if (ncrypto::CSPRNG(in.data() + byte_offset, size)) {
233+
if (ERR_peek_error() != 0) ERR_clear_error();
234+
return;
235+
}
236+
237+
CryptoErrorStore errors;
238+
errors.Capture();
239+
if (errors.Empty()) {
240+
errors.Insert(NodeCryptoError::DERIVING_BITS_FAILED);
241+
errors.SetNodeErrorCode("ERR_CRYPTO_OPERATION_FAILED");
242+
}
243+
Local<Value> exception;
244+
if (errors.ToException(env).ToLocal(&exception)) {
245+
env->isolate()->ThrowException(exception);
246+
}
247+
}
248+
217249
void Initialize(Environment* env, Local<Object> target) {
250+
SetMethod(env->context(), target, "randomFillSync", RandomFillSync);
218251
RandomBytesJob::Initialize(env, target);
219252
RandomPrimeJob::Initialize(env, target);
220253
CheckPrimeJob::Initialize(env, target);
221254
}
222255

223256
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
257+
registry->Register(RandomFillSync);
224258
RandomBytesJob::RegisterExternalReferences(registry);
225259
RandomPrimeJob::RegisterExternalReferences(registry);
226260
CheckPrimeJob::RegisterExternalReferences(registry);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
6+
const assert = require('assert');
7+
const { spawnSync } = require('child_process');
8+
9+
// randomFillSync() should be reported by --trace-sync-io when it runs after
10+
// the first event loop turn.
11+
12+
if (process.argv[2] === 'child') {
13+
setImmediate(() => {
14+
require('crypto').randomFillSync(Buffer.alloc(16));
15+
});
16+
return;
17+
}
18+
19+
const { stderr, status } = spawnSync(process.execPath,
20+
['--trace-sync-io', __filename, 'child'],
21+
{ encoding: 'utf8' });
22+
assert.strictEqual(status, 0);
23+
assert.match(stderr, /WARNING: Detected use of sync API[\s\S]*randomFillSync/);

‎typings/internalBinding/crypto.d.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,7 @@ export interface CryptoBinding {
10021002
privateEncrypt: InternalCryptoBinding.PublicKeyCipher;
10031003
publicDecrypt: InternalCryptoBinding.PublicKeyCipher;
10041004
publicEncrypt: InternalCryptoBinding.PublicKeyCipher;
1005+
randomFillSync(buf: ArrayBufferLike | ArrayBufferView, offset: number, size: number): void;
10051006
resetRootCertStore(): void;
10061007
secureBuffer(length: number): Uint8Array | undefined;
10071008
secureHeapUsed(): bigint | undefined;

0 commit comments

Comments
 (0)