Random number generation in JavaScript

Generating a random number in JavaScript is simple if you don't care about quality, more complicated if you do. When I say "in JavaScript" I mean both of the JavaScript runtimes - the browsers with their DOM/window, and Node.js without those things - and without all the modern browser APIs.

  1. The default, old-fashioned way to get a random number in JavaScript is by using Math.random(). However, the numbers produced from Math.random() are not secure for crypto applications as they are known to contain predictable patterns. The reason for this non-randomness isn't simply because it's a pseudo-random number generator but due to a fault in its implementation. This implementation is up to browsers. But it is currently not considered secure. If security is a concern then read on.

  2. There is a working draft for a Crypto API for EcmaScript/JavaScript which most modern browsers' JavaScript implementations are using today. Of interest to us is the crypographically secure random number generator implementation that's included in "Crypto". Practically speaking, today you can access "crypto" in the window object: e.g. crypto = window.crypto || window.msCrypto // for IE 11

If that works (just try in a Chrome Developer Javascript console), which it should, then you're ready to use it. The static function window.crypto.getRandomValues will fill an array (its only argument) of typed integers (using classes like Uint8Array, Uint16Array, etc. for 8- or 16-bit unsigned integers). The integers will be random in the range allowed for the specified type. So larger-sized integers give you more granularity, though the randomness should not be affected by size. You can then map that range to the range of values you're interested in, the same way you'd map a random floating point number between 0 and 1 to a range of values.

Unlike the old Math.random function, window.crypto.getRandomValues uses your underlying OS's entropy for seeding randomness. This entropy is something an outside attacker should not be able to have any control over, relative to browser-only entropy.

  1. The problem with depending on window.crypto is that window.crypto.getRandomValues isn't an "isomorphic JavaScript" function. It is not implemented by Node. The window object is not in the Node runtime at all. Thus you need to grab an npm module (npm install get-random-values) if you want to be able to use one isomorphic (i.e. the same code will work in Node or in browser) call to generate random numbers in any environment.

But for pure Node.js, you can use Node's builtin crypto: crypto.randomBytes. This is Node, so the call can optionally/possibly be asynchronous. Why? It's possible for a system to run out of entropy and thus be blocked for a tiny amount of time (should not be more than a second) to gather more entropy from various hardware sources. Generating millions of random numbers could exhaust OS entropy. A single call should return immediately, though. Also note that Node's crypto.randomBytes returns a variable-length byte string whereas window.crypto.getRandomValues modifies in place the integer values in a given array where the integers can be from 8 to 64 bits.

Cryptography is still new to JavaScript. So while there are implementations we can use, they vary among implementations of JavaScript engines. If you absolutely need isomorphic JavaScript code then you'll need to choose an appropriate wrapper. Otherwise, choose one of the two functions mentioned above. Their apis are slightly different, and that may be reason enough to use the wrapper for Node.