diff --git a/ChangeLog.md b/ChangeLog.md index 455dc79c20223..6af38160f9ce1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -17,6 +17,8 @@ See docs/process.md for how version tagging works. Current Trunk ------------- +- Add `getentropy` in `sys/random.h`, and use that from libc++'s + `random_device`. This is more efficient, see #12240. 2.0.4: 09/16/2020 ----------------- diff --git a/src/library.js b/src/library.js index e2b42e443d387..1651d36d129cb 100644 --- a/src/library.js +++ b/src/library.js @@ -2845,6 +2845,47 @@ LibraryManager.library = { endgrent: function() { throw 'endgrent: TODO' }, setgrent: function() { throw 'setgrent: TODO' }, + // random.h + + // TODO: consider allowing the API to get a parameter for the number of + // bytes. + $getRandomDevice: function() { + if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') { + // for modern web browsers + var randomBuffer = new Uint8Array(1); + return function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; + } else +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE) { + // for nodejs with or without crypto support included + try { + var crypto_module = require('crypto'); + // nodejs has crypto support + return function() { return crypto_module['randomBytes'](1)[0]; }; + } 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 + return function() { 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 } };"); }; +#else + return function() { abort("randomDevice"); }; +#endif + }, + + getentropy__deps: ['$getRandomDevice'], + getentropy: function(buffer, size) { + if (!_getentropy.randomDevice) { + _getentropy.randomDevice = getRandomDevice(); + } + for (var i = 0; i < size; i++) { + {{{ makeSetValue('buffer', 'i', '_getentropy.randomDevice()', 'i8') }}} + } + return 0; + }, + // ========================================================================== // emscripten.h // ========================================================================== diff --git a/src/library_fs.js b/src/library_fs.js index 528bbb3a2c1d9..ee4d55d4d591f 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -5,7 +5,7 @@ */ mergeInto(LibraryManager.library, { - $FS__deps: ['$setErrNo', '$PATH', '$PATH_FS', '$TTY', '$MEMFS', + $FS__deps: ['$setErrNo', '$getRandomDevice', '$PATH', '$PATH_FS', '$TTY', '$MEMFS', #if LibraryManager.has('library_idbfs.js') '$IDBFS', #endif @@ -1344,33 +1344,7 @@ FS.staticInit();` + FS.mkdev('/dev/tty', FS.makedev(5, 0)); FS.mkdev('/dev/tty1', FS.makedev(6, 0)); // setup /dev/[u]random - var random_device; - if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') { - // for modern web browsers - var randomBuffer = new Uint8Array(1); - random_device = function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; - } else -#if ENVIRONMENT_MAY_BE_NODE - if (ENVIRONMENT_IS_NODE) { - // for nodejs with or without crypto support included - try { - var crypto_module = require('crypto'); - // nodejs has crypto support - random_device = function() { return crypto_module['randomBytes'](1)[0]; }; - } catch (e) { - // nodejs doesn't have crypto support - } - } else -#endif // ENVIRONMENT_MAY_BE_NODE - {} - if (!random_device) { - // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096 -#if ASSERTIONS - random_device = function() { abort("no cryptographic support found for random_device. 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 } };"); }; -#else - random_device = function() { abort("random_device"); }; -#endif - } + var random_device = getRandomDevice(); FS.createDevice('/dev', 'random', random_device); FS.createDevice('/dev', 'urandom', random_device); // we're not going to emulate the actual shm device, diff --git a/system/include/compat/sys/random.h b/system/include/compat/sys/random.h new file mode 100644 index 0000000000000..72f3bf7a26bd6 --- /dev/null +++ b/system/include/compat/sys/random.h @@ -0,0 +1,14 @@ +#ifdef __cplusplus +extern "C" { +#endif + +// This is used by libc++ as an efficient way to get high-quality random data +// (more efficiently than via the filesystem using /dev/urandom). +// Upstream musl added support for this, so we can switch to that, but it isn't +// where libc++ looks for it (which is here and not unistd.h), and it uses a +// syscall which is unnecessary indirection for us. +int getentropy(void *buffer, size_t length); + +#ifdef __cplusplus +} +#endif diff --git a/system/include/libcxx/__config b/system/include/libcxx/__config index 088213618b1e7..34f4d7731fdba 100644 --- a/system/include/libcxx/__config +++ b/system/include/libcxx/__config @@ -308,7 +308,7 @@ // random data even when using sandboxing mechanisms such as chroots, // Capsicum, etc. # define _LIBCPP_USING_ARC4_RANDOM -#elif defined(__Fuchsia__) || defined(__wasi__) +#elif defined(__Fuchsia__) || defined(__wasi__) || defined(__EMSCRIPTEN__) # define _LIBCPP_USING_GETENTROPY #elif defined(__native_client__) // NaCl's sandbox (which PNaCl also runs in) doesn't allow filesystem access, diff --git a/system/lib/libcxx/readme.txt b/system/lib/libcxx/readme.txt index 969e0884e5ae3..acd017aff5b98 100755 --- a/system/lib/libcxx/readme.txt +++ b/system/lib/libcxx/readme.txt @@ -20,3 +20,5 @@ Local modifications are marked with the comment: 'XXX EMSCRIPTEN' 3. Define _LIBCPP_ELAST in libcxx/include/config_elast.h 4. Set init_priority of __start_std_streams in libcxx/iostream.cpp + +5. Use _LIBCPP_USING_GETENTROPY (like wasi) diff --git a/tests/core/test_random_device.cpp b/tests/core/test_random_device.cpp index 9ab6d3c7f650a..4c8de8efe83c0 100644 --- a/tests/core/test_random_device.cpp +++ b/tests/core/test_random_device.cpp @@ -12,7 +12,7 @@ auto main() try { std::random_device rd; - std::cout << "random was read" << "\n"; + std::cout << rd() << ", a random was read\n"; } catch( const std::exception& e ) {