Tracing the Source of Randomness for Cryptographically Secure Random Number Generators in Browsers and JavaScript Runtimes

Beguène,javascriptcryptography

Doing cryptography in the browser was viewed as completely insecure a few years ago. This has now changed for the better because there is now a standard (opens in a new tab) Web API Crypto and the source of randomness is better and more consistent across browsers. It's now easier for developers to use cryptographic primitives in a secure way. I was curious to know where exactly the source of entropy was for this Web API getRandomValue and how secure this is.

Why does the source of entropy matter for security ? It’s one of the main types of insecurity in cryptography. Most protocols stay secure as long as the generation of randomness is secure.

There are two kinds of randomness : true randomness and pseudorandomness.

True randomness is the randomness that can be found in Nature: the decay of a radioactive isotope1 or the roll of a fair dice. Here "true" means that the output is unpredictable and cannot be predicted by an attacker. Unpredictability is what we are looking for as unpredictability implies security in cryptography.

Pseudo-randomness, on the other hand, is generated by a computer algorithm. It produces output that is statistically indistinguishable (opens in a new tab) from true randomness, but ultimately deterministic and can be compromised if an attacker knows the seed. The algorithm needs a source of entropy to generate the seed. The source of entropy is the key to the security of the algorithm. This entropy is generally a random number generated by the operating system and can use different sources such as the mouse movements or the keyboard.

In the browser context, we can generate this cryptographically secure pseudo-random number (CSPRNG (opens in a new tab)) via the Web API

https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues (opens in a new tab)

Do browsers always use /dev/urandom if available ? What happens on Windows 😱 ?

To verify, I looked into the source code and I compiled a list based on my research for each main browser and OS. I added Node.js and Deno to have the full picture (at least in the javascript world)

The source of entropy depends on the browser and OS. Below is a list of what I found (updated 29/12/2022)

Browser Library MacOs Windows Linux Android iOS

BrowserLibraryMacOsWindowsLinuxAndroidiOS
Chrome / BraveBoringSSLCommonCrypto (opens in a new tab)CryptGenRandom (opens in a new tab)/dev/urandom/dev/urandomCommonCrypto (opens in a new tab)
FirefoxSubtleCrypto/dev/urandomCryptGenRandom (opens in a new tab)getRandom() or /dev/urandom/dev/urandom/dev/urandom
SafariCommonCrypto (opens in a new tab)CommonCrypto (opens in a new tab)N/AN/AN/ACommonCrypto (opens in a new tab)
Node.js Openssl CommonCryptoCryptGenRandom (opens in a new tab)/dev/urandom/dev/urandomCommonCrypto (opens in a new tab)
DenoRust StdRng (opens in a new tab)getentropy if available, otherwise /dev/urandomBCryptGenRandomgetrandom system call if available, otherwise /dev/urandomgetrandom system call if available, otherwise /dev/urandomCommonCrypto (opens in a new tab) (SecRandomCopyBytes)

Source of randomness Below are some more details about each browser and how the randomness is generated from the browser library down to the OS. I have also provided the source code that I read to get to this information.

Chrome

I see here (opens in a new tab) that Chrome uses BoringSSL (opens in a new tab) a fork of OpenSSL

The code for the source of entropy of BoringSSL is here urandom.c (opens in a new tab)

symmetric_key.cc (opens in a new tab)

RAND_bytes.pod (opens in a new tab)

Webkit (Apple, safari)

WebKit is a cross-platform browser engine that is used in many web browsers, mainly Safari.

The randomness is generated from Crypto::getRandomValues

Crypto.cpp (opens in a new tab) which calls CCRandomGenerateBytes a function in the WebKit open-source web browser engine that is used to generate a sequence of random bytes.

It is part of the CommonCrypto library, which is a library of cryptographic functions that is provided by Apple on macOS and iOS.

The function generates a sequence of random bytes by calling a system-specific function to obtain random data from the operating system. On macOS, the CCRandomGenerateBytes function uses the /dev/random device to obtain random data. On iOS, it uses the arc4random_buf() function to obtain random data.

Those are imported here (opens in a new tab)

SubtleCrypto.cpp (opens in a new tab)

Windows Crypto API primitive

windows provides an abstraction to cryptographic primitives, it's called CryptoAPI (opens in a new tab)

Some of the primitives are BCryptGenRandom and CryptGenRandom

Firefox

I found it way more difficult to trace the function calls for random number generation in the Firefox codebase. I traced where the system call and OS detection are made RandomNum.cpp (opens in a new tab), but I could not trace the entire chain from getRandomValue to the OS system call.

Crypto.h (opens in a new tab)

RandomNum.h (opens in a new tab)

RandomNum.cpp (opens in a new tab)

The Web Crypto API uses the underlying platform's random number generator.

The NSS library provides several functions for generating random numbers, including PK11_GenerateRandom, which generates a random number using the default PKCS #11 security token.

pk11sdr.c (opens in a new tab)

Node.js

NodeJs uses openssl as we can see here (opens in a new tab)

and in crypto_random.h (opens in a new tab) crypto_random.cc (opens in a new tab)

WebAssembly

Webassembly can either be run in node or in a browser environment.

Therefore to generate a cryptographically secure random number, one can use node crypto in node environment and web.crypto in browser which will in turn use their respective library (mostly OpenSSL)

rust-random has a good example of one way to do it.

wasm32_bindgen.rs (opens in a new tab)

But what happens if you call rand from rust directly in wasm ? The wasm_bidgen automatically generates and maps the call to the underlying os random number.

Neat !

OpenSSL

You might have already noticed it, but OpenSSL is at the core of many of those environments. The OpenSSL library uses multiple sources of entropy to seed the CSPRNG, including system-specific random number generators and hardware devices.

The source of entropy is done via the RAND_add() function or the RAND_seed() function.

The RAND_add() and RAND_seed() functions are typically called from within the OpenSSL library, rather than by applications that use the OpenSSL library.

This code checks the OS and picks the OS randomness source accordingly

rand.h (opens in a new tab)

For windows it uses the OS CryptGenRandom (opens in a new tab)

rand_win.c (opens in a new tab)

Hardware based source of entropy

OpenSSL can also use hardware source of entropy if available using the Entropy Gathering Daemon (EGD), which is a daemon that is used to provide a source of entropy (randomness) on some systems.

See rand.h (opens in a new tab)

The EGD is a software program that runs in the background and listens for requests for random data from other programs. It gathers entropy from various sources, such as system events and hardware devices, and uses it to seed a random number generator. The random number generator is then used to generate random data that is returned to the requesting program.

Deno

Deno’s core runtime comes with an implementation ofgetRandomValues function that can generate strong random numbers efficiently. Internally, Deno uses Rust’s StdRng (struct.StdRng.html (opens in a new tab)) to generate the numbers.

00_crypto.js (opens in a new tab)

lib.rs (opens in a new tab)

Which uses getrandom (opens in a new tab) which retrieves random data from the operating system source

The full table of the sources is clearly described in this comment (opens in a new tab)

Resources

Firefox

Chrome

OpenSSL

Footnotes

  1. Radioactive decay is a stochastic (i.e. random) process at the level of single atoms. According to quantum theory, it is impossible to predict when a particular radionuclide will decay, regardless of how long the atom has existed. Although the rate of decay for a specific radionuclide can be calculated from knowledge of the number of radioactive atoms and the half-life, there is no way of knowing which specific radioactive atom will decay at which time interval. That is, it is impossible to predict when a particular radionuclide will decay, regardless of how long the atom has existed. Furthermore, in all practical circumstances, the probability of a given radioactive atom decaying in a particular time interval is extremely small. The most natural approach is to see radioactive decay as a Bernoulli process, as if many coins were flipping randomly, independently and without memory.

2024 © Beguène. - github.com/beguene twitter.com/beguene