Skip to content

Commit bc5ab7a

Browse files
committed
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>
1 parent 3641c36 commit bc5ab7a

4 files changed

Lines changed: 57 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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,44 @@ 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+
ClearErrorOnReturn clear_error_on_return;
232+
if (ncrypto::CSPRNG(in.data() + byte_offset, size)) return;
233+
234+
CryptoErrorStore errors;
235+
errors.Capture();
236+
if (errors.Empty()) {
237+
errors.Insert(NodeCryptoError::DERIVING_BITS_FAILED);
238+
errors.SetNodeErrorCode("ERR_CRYPTO_OPERATION_FAILED");
239+
}
240+
Local<Value> exception;
241+
if (errors.ToException(env).ToLocal(&exception)) {
242+
env->isolate()->ThrowException(exception);
243+
}
244+
}
245+
217246
void Initialize(Environment* env, Local<Object> target) {
247+
SetMethod(env->context(), target, "randomFillSync", RandomFillSync);
218248
RandomBytesJob::Initialize(env, target);
219249
RandomPrimeJob::Initialize(env, target);
220250
CheckPrimeJob::Initialize(env, target);
221251
}
222252

223253
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
254+
registry->Register(RandomFillSync);
224255
RandomBytesJob::RegisterExternalReferences(registry);
225256
RandomPrimeJob::RegisterExternalReferences(registry);
226257
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)