Skip to content

Commit 2f79c58

Browse files
committed
Implement getentropy via __wasi_random_get
This allows it to work in standalone mode. Fixes: #22782
1 parent 0bf241c commit 2f79c58

File tree

6 files changed

+71
-66
lines changed

6 files changed

+71
-66
lines changed

src/library.js

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,65 +1292,6 @@ addToLibrary({
12921292

12931293
#endif // PROXY_POSIX_SOCKETS == 0
12941294

1295-
// random.h
1296-
1297-
$initRandomFill: () => {
1298-
if (typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function') {
1299-
// for modern web browsers
1300-
#if SHARED_MEMORY
1301-
// like with most Web APIs, we can't use Web Crypto API directly on shared memory,
1302-
// so we need to create an intermediate buffer and copy it to the destination
1303-
return (view) => (
1304-
view.set(crypto.getRandomValues(new Uint8Array(view.byteLength))),
1305-
// Return the original view to match modern native implementations.
1306-
view
1307-
);
1308-
#else
1309-
return (view) => crypto.getRandomValues(view);
1310-
#endif
1311-
} else
1312-
#if ENVIRONMENT_MAY_BE_NODE
1313-
if (ENVIRONMENT_IS_NODE) {
1314-
// for nodejs with or without crypto support included
1315-
try {
1316-
var crypto_module = require('crypto');
1317-
var randomFillSync = crypto_module['randomFillSync'];
1318-
if (randomFillSync) {
1319-
// nodejs with LTS crypto support
1320-
return (view) => crypto_module['randomFillSync'](view);
1321-
}
1322-
// very old nodejs with the original crypto API
1323-
var randomBytes = crypto_module['randomBytes'];
1324-
return (view) => (
1325-
view.set(randomBytes(view.byteLength)),
1326-
// Return the original view to match modern native implementations.
1327-
view
1328-
);
1329-
} catch (e) {
1330-
// nodejs doesn't have crypto support
1331-
}
1332-
}
1333-
#endif // ENVIRONMENT_MAY_BE_NODE
1334-
// we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096
1335-
#if ASSERTIONS
1336-
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: (array) => { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };');
1337-
#else
1338-
abort('initRandomDevice');
1339-
#endif
1340-
},
1341-
1342-
$randomFill__deps: ['$initRandomFill'],
1343-
$randomFill: (view) => {
1344-
// Lazily init on the first invocation.
1345-
return (randomFill = initRandomFill())(view);
1346-
},
1347-
1348-
getentropy__deps: ['$randomFill'],
1349-
getentropy: (buffer, size) => {
1350-
randomFill(HEAPU8.subarray(buffer, buffer + size));
1351-
return 0;
1352-
},
1353-
13541295
$timers: {},
13551296

13561297
// Helper function for setitimer that registers timers with the eventloop.

src/library_wasi.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,65 @@ var WasiLibrary = {
568568
#endif // SYSCALLS_REQUIRE_FILESYSTEM
569569
},
570570
fd_sync__async: true,
571+
572+
// random.h
573+
574+
$initRandomFill: () => {
575+
if (typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function') {
576+
// for modern web browsers
577+
#if SHARED_MEMORY
578+
// like with most Web APIs, we can't use Web Crypto API directly on shared memory,
579+
// so we need to create an intermediate buffer and copy it to the destination
580+
return (view) => (
581+
view.set(crypto.getRandomValues(new Uint8Array(view.byteLength))),
582+
// Return the original view to match modern native implementations.
583+
view
584+
);
585+
#else
586+
return (view) => crypto.getRandomValues(view);
587+
#endif
588+
} else
589+
#if ENVIRONMENT_MAY_BE_NODE
590+
if (ENVIRONMENT_IS_NODE) {
591+
// for nodejs with or without crypto support included
592+
try {
593+
var crypto_module = require('crypto');
594+
var randomFillSync = crypto_module['randomFillSync'];
595+
if (randomFillSync) {
596+
// nodejs with LTS crypto support
597+
return (view) => crypto_module['randomFillSync'](view);
598+
}
599+
// very old nodejs with the original crypto API
600+
var randomBytes = crypto_module['randomBytes'];
601+
return (view) => (
602+
view.set(randomBytes(view.byteLength)),
603+
// Return the original view to match modern native implementations.
604+
view
605+
);
606+
} catch (e) {
607+
// nodejs doesn't have crypto support
608+
}
609+
}
610+
#endif // ENVIRONMENT_MAY_BE_NODE
611+
// we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096
612+
#if ASSERTIONS
613+
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: (array) => { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };');
614+
#else
615+
abort('initRandomDevice');
616+
#endif
617+
},
618+
619+
$randomFill__deps: ['$initRandomFill'],
620+
$randomFill: (view) => {
621+
// Lazily init on the first invocation.
622+
return (randomFill = initRandomFill())(view);
623+
},
624+
625+
random_get__deps: ['$randomFill'],
626+
random_get: (buffer, size) => {
627+
randomFill(HEAPU8.subarray(buffer, buffer + size));
628+
return 0;
629+
},
571630
};
572631

573632
for (var x in WasiLibrary) {

system/lib/libc/musl/src/linux/getrandom.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@
33

44
ssize_t getrandom(void *buf, size_t buflen, unsigned flags)
55
{
6+
#if __EMSCRIPTEN__
7+
return __wasi_syscall_ret(__wasi_random_get(buffer, length));
8+
#else
69
return syscall_cp(SYS_getrandom, buf, buflen, flags);
10+
#endif
711
}

system/lib/libc/musl/src/misc/getentropy.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
#include <pthread.h>
55
#include <errno.h>
66

7+
#ifdef __EMSCRIPTEN__
8+
#include <wasi/wasi-helpers.h>
9+
#endif
10+
711
int getentropy(void *buffer, size_t len)
812
{
913
int cs, ret = 0;
@@ -16,6 +20,9 @@ int getentropy(void *buffer, size_t len)
1620

1721
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
1822

23+
#ifdef __EMSCRIPTEN__
24+
ret = __wasi_syscall_ret(__wasi_random_get(buffer, len));
25+
#else
1926
while (len) {
2027
ret = getrandom(pos, len, 0);
2128
if (ret < 0) {
@@ -26,6 +33,7 @@ int getentropy(void *buffer, size_t len)
2633
len -= ret;
2734
ret = 0;
2835
}
36+
#endif
2937

3038
pthread_setcancelstate(cs, 0);
3139

system/lib/standalone/standalone.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,6 @@ weak int __syscall_lstat64(intptr_t path, intptr_t buf) {
136136
return -ENOSYS;
137137
}
138138

139-
// There is no good source of entropy without an import. Make this weak so that
140-
// it can be replaced with a pRNG or a proper import.
141-
weak int getentropy(void* buffer, size_t length) {
142-
abort();
143-
}
144-
145139
// Emscripten additions
146140

147141
size_t emscripten_get_heap_max() {

tools/system_libs.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,6 @@ def get_files(self):
10981098
'ppoll.c',
10991099
'syscall.c', 'popen.c', 'pclose.c',
11001100
'getgrouplist.c', 'initgroups.c', 'wordexp.c', 'timer_create.c',
1101-
'getentropy.c',
11021101
'getauxval.c',
11031102
'lookup_name.c',
11041103
# 'process' exclusion

0 commit comments

Comments
 (0)