Skip to content

Commit

Permalink
Implement getentropy via __wasi_random_get
Browse files Browse the repository at this point in the history
This allows it to work in standalone mode.

Fixes: emscripten-core#22782
  • Loading branch information
sbc100 committed Oct 31, 2024
1 parent 753b3bf commit a9992c0
Show file tree
Hide file tree
Showing 17 changed files with 104 additions and 109 deletions.
59 changes: 0 additions & 59 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -1292,65 +1292,6 @@ addToLibrary({

#endif // PROXY_POSIX_SOCKETS == 0

// random.h

$initRandomFill: () => {
if (typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function') {
// for modern web browsers
#if SHARED_MEMORY
// like with most Web APIs, we can't use Web Crypto API directly on shared memory,
// so we need to create an intermediate buffer and copy it to the destination
return (view) => (
view.set(crypto.getRandomValues(new Uint8Array(view.byteLength))),
// Return the original view to match modern native implementations.
view
);
#else
return (view) => crypto.getRandomValues(view);
#endif
} else
#if ENVIRONMENT_MAY_BE_NODE
if (ENVIRONMENT_IS_NODE) {
// for nodejs with or without crypto support included
try {
var crypto_module = require('crypto');
var randomFillSync = crypto_module['randomFillSync'];
if (randomFillSync) {
// nodejs with LTS crypto support
return (view) => crypto_module['randomFillSync'](view);
}
// very old nodejs with the original crypto API
var randomBytes = crypto_module['randomBytes'];
return (view) => (
view.set(randomBytes(view.byteLength)),
// Return the original view to match modern native implementations.
view
);
} catch (e) {
// nodejs doesn't have crypto support
}
}
#endif // ENVIRONMENT_MAY_BE_NODE
// we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096
#if ASSERTIONS
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 } };');
#else
abort('initRandomDevice');
#endif
},

$randomFill__deps: ['$initRandomFill'],
$randomFill: (view) => {
// Lazily init on the first invocation.
return (randomFill = initRandomFill())(view);
},

getentropy__deps: ['$randomFill'],
getentropy: (buffer, size) => {
randomFill(HEAPU8.subarray(buffer, buffer + size));
return 0;
},

$timers: {},

// Helper function for setitimer that registers timers with the eventloop.
Expand Down
1 change: 0 additions & 1 deletion src/library_sigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,6 @@ sigs = {
filledEllipseColor__sig: 'ipiiiii',
filledEllipseRGBA__sig: 'ipiiiiiiii',
getaddrinfo__sig: 'ipppp',
getentropy__sig: 'ipp',
getnameinfo__sig: 'ipipipii',
getprotobyname__sig: 'pp',
getprotobynumber__sig: 'pi',
Expand Down
66 changes: 60 additions & 6 deletions src/library_wasi.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@ var WasiLibrary = {
sched_yield__nothrow: true,
sched_yield: () => 0,

random_get__deps: ['getentropy'],
random_get: (buf, buf_len) => {
_getentropy(buf, buf_len);
return 0;
},

$getEnvStrings__deps: ['$ENV', '$getExecutableName'],
$getEnvStrings: () => {
if (!getEnvStrings.strings) {
Expand Down Expand Up @@ -568,6 +562,66 @@ var WasiLibrary = {
#endif // SYSCALLS_REQUIRE_FILESYSTEM
},
fd_sync__async: true,

// random.h

$initRandomFill: () => {
if (typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function') {
// for modern web browsers
#if SHARED_MEMORY
// like with most Web APIs, we can't use Web Crypto API directly on shared memory,
// so we need to create an intermediate buffer and copy it to the destination
return (view) => (
view.set(crypto.getRandomValues(new Uint8Array(view.byteLength))),
// Return the original view to match modern native implementations.
view
);
#else
return (view) => crypto.getRandomValues(view);
#endif
} else
#if ENVIRONMENT_MAY_BE_NODE
if (ENVIRONMENT_IS_NODE) {
// for nodejs with or without crypto support included
try {
var crypto_module = require('crypto');
var randomFillSync = crypto_module['randomFillSync'];
if (randomFillSync) {
// nodejs with LTS crypto support
return (view) => crypto_module['randomFillSync'](view);
}
// very old nodejs with the original crypto API
var randomBytes = crypto_module['randomBytes'];
return (view) => (
view.set(randomBytes(view.byteLength)),
// Return the original view to match modern native implementations.
view
);
} catch (e) {
// nodejs doesn't have crypto support
}
}
#endif // ENVIRONMENT_MAY_BE_NODE
// we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096
#if ASSERTIONS
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 } };');
#else
abort('initRandomDevice');
#endif
},

$randomFill__deps: ['$initRandomFill'],
$randomFill: (view) => {
// Lazily init on the first invocation.
return (randomFill = initRandomFill())(view);
},

random_get__proxy: 'none',
random_get__deps: ['$randomFill'],
random_get: (buffer, size) => {
randomFill(HEAPU8.subarray(buffer, buffer + size));
return 0;
},
};

for (var x in WasiLibrary) {
Expand Down
8 changes: 8 additions & 0 deletions system/lib/libc/musl/src/misc/getentropy.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#include <pthread.h>
#include <errno.h>

#ifdef __EMSCRIPTEN__
#include <wasi/wasi-helpers.h>
#endif

int getentropy(void *buffer, size_t len)
{
int cs, ret = 0;
Expand All @@ -16,6 +20,9 @@ int getentropy(void *buffer, size_t len)

pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);

#ifdef __EMSCRIPTEN__
ret = __wasi_syscall_ret(__wasi_random_get(buffer, len));
#else
while (len) {
ret = getrandom(pos, len, 0);
if (ret < 0) {
Expand All @@ -26,6 +33,7 @@ int getentropy(void *buffer, size_t len)
len -= ret;
ret = 0;
}
#endif

pthread_setcancelstate(cs, 0);

Expand Down
6 changes: 0 additions & 6 deletions system/lib/standalone/standalone.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,6 @@ weak int __syscall_lstat64(intptr_t path, intptr_t buf) {
return -ENOSYS;
}

// There is no good source of entropy without an import. Make this weak so that
// it can be replaced with a pRNG or a proper import.
weak int getentropy(void* buffer, size_t length) {
abort();
}

// Emscripten additions

size_t emscripten_get_heap_max() {
Expand Down
2 changes: 1 addition & 1 deletion test/other/codesize/test_codesize_cxx_wasmfs.gzsize
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3899
3895
2 changes: 1 addition & 1 deletion test/other/codesize/test_codesize_cxx_wasmfs.imports
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ env.emscripten_date_now
env.emscripten_err
env.emscripten_out
env.emscripten_resize_heap
env.getentropy
wasi_snapshot_preview1.environ_get
wasi_snapshot_preview1.environ_sizes_get
wasi_snapshot_preview1.random_get
2 changes: 1 addition & 1 deletion test/other/codesize/test_codesize_cxx_wasmfs.sent
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ emscripten_out
emscripten_resize_heap
environ_get
environ_sizes_get
getentropy
random_get
2 changes: 1 addition & 1 deletion test/other/codesize/test_codesize_cxx_wasmfs.size
Original file line number Diff line number Diff line change
@@ -1 +1 @@
169486
169554
2 changes: 1 addition & 1 deletion test/other/codesize/test_codesize_files_wasmfs.gzsize
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2987
2856
28 changes: 14 additions & 14 deletions test/other/codesize/test_codesize_files_wasmfs.imports
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
a (emscripten_date_now)
b (emscripten_err)
c (getentropy)
d (emscripten_resize_heap)
e (emscripten_out)
f (_wasmfs_stdin_get_char)
g (_wasmfs_get_preloaded_path_name)
h (_wasmfs_get_preloaded_parent_path)
i (_wasmfs_get_preloaded_file_size)
j (_wasmfs_get_preloaded_file_mode)
k (_wasmfs_get_preloaded_child_path)
l (_wasmfs_get_num_preloaded_files)
m (_wasmfs_get_num_preloaded_dirs)
n (_wasmfs_copy_preloaded_file_data)
o (_emscripten_memcpy_js)
p (_abort_js)
c (emscripten_resize_heap)
d (emscripten_out)
e (_wasmfs_stdin_get_char)
f (_wasmfs_get_preloaded_path_name)
g (_wasmfs_get_preloaded_parent_path)
h (_wasmfs_get_preloaded_file_size)
i (_wasmfs_get_preloaded_file_mode)
j (_wasmfs_get_preloaded_child_path)
k (_wasmfs_get_num_preloaded_files)
l (_wasmfs_get_num_preloaded_dirs)
m (_wasmfs_copy_preloaded_file_data)
n (_emscripten_memcpy_js)
o (_abort_js)
p (random_get)
2 changes: 1 addition & 1 deletion test/other/codesize/test_codesize_files_wasmfs.jssize
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6329
5982
27 changes: 13 additions & 14 deletions test/other/codesize/test_codesize_files_wasmfs.sent
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
a (emscripten_date_now)
b (emscripten_err)
c (getentropy)
d (emscripten_resize_heap)
e (emscripten_out)
f (_wasmfs_stdin_get_char)
g (_wasmfs_get_preloaded_path_name)
h (_wasmfs_get_preloaded_parent_path)
i (_wasmfs_get_preloaded_file_size)
j (_wasmfs_get_preloaded_file_mode)
k (_wasmfs_get_preloaded_child_path)
l (_wasmfs_get_num_preloaded_files)
m (_wasmfs_get_num_preloaded_dirs)
n (_wasmfs_copy_preloaded_file_data)
o (_emscripten_memcpy_js)
p (_abort_js)
c (emscripten_resize_heap)
d (emscripten_out)
e (_wasmfs_stdin_get_char)
f (_wasmfs_get_preloaded_path_name)
g (_wasmfs_get_preloaded_parent_path)
h (_wasmfs_get_preloaded_file_size)
i (_wasmfs_get_preloaded_file_mode)
j (_wasmfs_get_preloaded_child_path)
k (_wasmfs_get_num_preloaded_files)
l (_wasmfs_get_num_preloaded_dirs)
m (_wasmfs_copy_preloaded_file_data)
n (_emscripten_memcpy_js)
o (_abort_js)
2 changes: 1 addition & 1 deletion test/other/codesize/test_codesize_files_wasmfs.size
Original file line number Diff line number Diff line change
@@ -1 +1 @@
51099
51157
2 changes: 1 addition & 1 deletion test/other/codesize/test_codesize_minimal_O0.gzsize
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6576
6577
1 change: 1 addition & 0 deletions tools/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,7 @@ def metadce(js_file, wasm_file, debug_info, last):
'clock_res_get',
'clock_time_get',
'path_open',
'random_get',
}
for item in graph:
if 'import' in item and item['import'][1] in WASI_IMPORTS:
Expand Down
1 change: 0 additions & 1 deletion tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,6 @@ def get_files(self):
'ppoll.c',
'syscall.c', 'popen.c', 'pclose.c',
'getgrouplist.c', 'initgroups.c', 'wordexp.c', 'timer_create.c',
'getentropy.c',
'getauxval.c',
'lookup_name.c',
# 'process' exclusion
Expand Down

0 comments on commit a9992c0

Please sign in to comment.