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 0bf241c commit 2f79c58
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 66 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
59 changes: 59 additions & 0 deletions src/library_wasi.js
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,65 @@ 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__deps: ['$randomFill'],
random_get: (buffer, size) => {
randomFill(HEAPU8.subarray(buffer, buffer + size));
return 0;
},
};

for (var x in WasiLibrary) {
Expand Down
4 changes: 4 additions & 0 deletions system/lib/libc/musl/src/linux/getrandom.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@

ssize_t getrandom(void *buf, size_t buflen, unsigned flags)
{
#if __EMSCRIPTEN__
return __wasi_syscall_ret(__wasi_random_get(buffer, length));
#else
return syscall_cp(SYS_getrandom, buf, buflen, flags);
#endif
}
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
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 2f79c58

Please sign in to comment.