Skip to content

Commit 3cf83fd

Browse files
authored
Optimise getentropy (#19056)
1 parent b116323 commit 3cf83fd

File tree

2 files changed

+45
-20
lines changed

2 files changed

+45
-20
lines changed

src/library.js

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2223,43 +2223,61 @@ mergeInto(LibraryManager.library, {
22232223

22242224
// random.h
22252225

2226-
// TODO: consider allowing the API to get a parameter for the number of
2227-
// bytes.
2228-
$getRandomDevice: function() {
2226+
$initRandomFill: function() {
22292227
if (typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function') {
22302228
// for modern web browsers
2231-
var randomBuffer = new Uint8Array(1);
2232-
return () => { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; };
2229+
#if SHARED_MEMORY
2230+
// like with most Web APIs, we can't use Web Crypto API directly on shared memory,
2231+
// so we need to create an intermediate buffer and copy it to the destination
2232+
return (view) => (
2233+
view.set(crypto.getRandomValues(new Uint8Array(view.byteLength))),
2234+
// Return the original view to match modern native implementations.
2235+
view
2236+
);
2237+
#else
2238+
return (view) => crypto.getRandomValues(view);
2239+
#endif
22332240
} else
22342241
#if ENVIRONMENT_MAY_BE_NODE
22352242
if (ENVIRONMENT_IS_NODE) {
22362243
// for nodejs with or without crypto support included
22372244
try {
22382245
var crypto_module = require('crypto');
2239-
// nodejs has crypto support
2240-
return () => crypto_module['randomBytes'](1)[0];
2246+
var randomFillSync = crypto_module['randomFillSync'];
2247+
if (randomFillSync) {
2248+
// nodejs with LTS crypto support
2249+
return (view) => crypto_module['randomFillSync'](view);
2250+
}
2251+
// very old nodejs with the original crypto API
2252+
var randomBytes = crypto_module['randomBytes'];
2253+
return (view) => (
2254+
view.set(randomBytes(view.byteLength)),
2255+
// Return the original view to match modern native implementations.
2256+
view
2257+
);
22412258
} catch (e) {
22422259
// nodejs doesn't have crypto support
22432260
}
22442261
}
22452262
#endif // ENVIRONMENT_MAY_BE_NODE
22462263
// we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096
22472264
#if ASSERTIONS
2248-
return () => abort("no cryptographic support found for randomDevice. consider polyfilling it if you want to use something insecure like Math.random(), e.g. put this in a --pre-js: var crypto = { getRandomValues: function(array) { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };");
2265+
abort("no cryptographic support found for randomDevice. consider polyfilling it if you want to use something insecure like Math.random(), e.g. put this in a --pre-js: var crypto = { getRandomValues: function(array) { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };");
22492266
#else
2250-
return () => abort("randomDevice");
2267+
abort("initRandomDevice");
22512268
#endif
22522269
},
22532270

2254-
getentropy__deps: ['$getRandomDevice'],
2271+
$randomFill__deps: ['$initRandomFill'],
2272+
$randomFill: function(view) {
2273+
// Lazily init on the first invocation.
2274+
return (randomFill = initRandomFill())(view);
2275+
},
2276+
2277+
getentropy__deps: ['$randomFill'],
22552278
getentropy__sig: 'ipp',
22562279
getentropy: function(buffer, size) {
2257-
if (!_getentropy.randomDevice) {
2258-
_getentropy.randomDevice = getRandomDevice();
2259-
}
2260-
for (var i = 0; i < size; i++) {
2261-
{{{ makeSetValue('buffer', 'i', '_getentropy.randomDevice()', 'i8') }}};
2262-
}
2280+
randomFill(HEAPU8.subarray(buffer, buffer + size));
22632281
return 0;
22642282
},
22652283

src/library_fs.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
mergeInto(LibraryManager.library, {
8-
$FS__deps: ['$getRandomDevice', '$PATH', '$PATH_FS', '$TTY', '$MEMFS', '$asyncLoad', '$intArrayFromString',
8+
$FS__deps: ['$randomFill', '$PATH', '$PATH_FS', '$TTY', '$MEMFS', '$asyncLoad', '$intArrayFromString',
99
#if LibraryManager.has('library_idbfs.js')
1010
'$IDBFS',
1111
#endif
@@ -1348,9 +1348,16 @@ FS.staticInit();` +
13481348
FS.mkdev('/dev/tty', FS.makedev(5, 0));
13491349
FS.mkdev('/dev/tty1', FS.makedev(6, 0));
13501350
// setup /dev/[u]random
1351-
var random_device = getRandomDevice();
1352-
FS.createDevice('/dev', 'random', random_device);
1353-
FS.createDevice('/dev', 'urandom', random_device);
1351+
// use a buffer to avoid overhead of individual crypto calls per byte
1352+
var randomBuffer = new Uint8Array(1024), randomLeft = 0;
1353+
var randomByte = () => {
1354+
if (randomLeft === 0) {
1355+
randomLeft = randomFill(randomBuffer).byteLength;
1356+
}
1357+
return randomBuffer[--randomLeft];
1358+
};
1359+
FS.createDevice('/dev', 'random', randomByte);
1360+
FS.createDevice('/dev', 'urandom', randomByte);
13541361
// we're not going to emulate the actual shm device,
13551362
// just create the tmp dirs that reside in it commonly
13561363
FS.mkdir('/dev/shm');

0 commit comments

Comments
 (0)