Secure Random Numbers in JavaScript

Secure random numbers in javascript?

You can for instance use mouse movement as seed for random numbers, read out time and mouse position whenever the onmousemove event happens, feed that data to a whitening function and you will have some first class random at hand. Though do make sure that user has moved the mouse sufficiently before you use the data.

Edit: I have myself played a bit with the concept by making a password generator, I wouldn't guarantee that my whitening function is flawless, but being constantly reseeded I'm pretty sure that it's plenty for the job: ebusiness.hopto.org/generator.htm

Edit2: It now sort of works with smartphones, but only by disabling touch functionality while the entropy is gathered. Android won't work properly any other way.

How can I generate a cryptographically secure pseudo-random number in Javascript?

In the browser, you can look into window.crypto.getRandomValues. See details here.

const array = new Uint32Array(10);
window.crypto.getRandomValues(array);

In node, take a peek at the crypto module.

const crypto = require('crypto');
crypto.randomBytes(20, (err, buffer) => {
const token = buffer.toString('hex');
console.log(token);
});

If you have browser support concerns, consider looking into an npm package like this one. Note: I've never used this one so I can't vouch for it.

Generating secure random numbers for HTML form

getElementsByName() is a document level method and not available at element level

Use querySelectorAll() instead to query elements that only exist in the specific form

const form = document.forms[0]

function secureRandomNums(len) {
let array = new Uint8Array(len);
window.crypto.getRandomValues(array);
return array;
}

function getRandomNumbers() {
let elements = form.querySelectorAll('input[name="number[]"]');
let nums = secureRandomNums(elements.length);
for (let i = 0; i < nums.length; i++) {
elements[i].value = nums[i]
}
}
<form>
<input name="number[]" />
<input name="number[]" />
<input name="number[]" />
<input type="button" value="Generate numbers!" onclick="getRandomNumbers()"><br>
</form>

Is Math.random() cryptographically secure?

Nope; JavaScript's Math.random() function is not a cryptographically-secure random number generator. You are better off using the JavaScript Crypto Library's Fortuna implementation which is a strong pseudo-random number generator (have a look at src/js/Clipperz/Crypto/PRNG.js), or the Web Crypto API for getRandomValues

  • Here is a detailed explanation: How trustworthy is javascript's random implementation in various browsers?
  • Here is how to generate a good crypto grade random number: Secure random numbers in javascript?

Can you make a non-cryptographically secure random number generator secure?

Let's start with a warning; just in case

Honestly, I'm not sure why you would want to use something beyond window.crypto.getRandomValues (or its Linux equivalent /dev/random). If you're planning to "stretch" its output for some reason, chances are you're doing it wrong. Whatever your scenario is, don't hardcode such a seed seed into your script before serving it to clients. Not even if your .js file is created dynamically on the server side. That would be as if you would push encrypted data together with your encryption key… voiding any security gains in its root.

That being said, let's look at your question in your line of thinking…

About your idea

The output of math.random is insecure as it produces predictable outputs. Meaning: having a sequence of outputs, an attacker can successfully recover the state and the following outputs it will produce. Seeding it with a cryptographically secure seed from window.crypto.getRandomValues (or its Linux equivalent /dev/random) will not fix that problem.

As a securer approach you might want to take a look at ChaCha20, which is a cryptographically secure stream cipher. It definitely produces securer outputs than math.random and I've seen several pure vanilla implementation of ChaCha20 at Github et al. So, using something "safer" than math.random shouldn't be all too hard to implement in your script(s). Seed ChaCha20 with window.crypto.getRandomValues (or its Linux equivalent /dev/random) as you were planning to do and you're set.

But…

Please note that I haven't dived into the use of Javascript for crypto purposes itself. Doing so tends to introduce attack vectors. Which is why you'ld (at least) need HTTPS when your project is served online. I'll have to skip mentioning all the other related nitpicks… mainly because you didn't mention such details in your question, but also to prevent this answer from getting too broad/long. A quick search at Security.SE tends to enlighten you about using-Javascript-for-crypto related issues.

Instead - use the Web Cryptographic API

Last but not least, I'ld like to get back to what I said for starters and point you to the fact that you might as well simply use window.crypto.getRandomValues (or its Linux equivalent /dev/random) for all randomness purposes. The speed gains of not doing so are minimal in most scenarios.

Crypto is hard… don't break your neck trying to solve problems on your own. Even for Javascript, an applicable solution already exist:

Web Cryptographic API - Example:

/* assuming that window.crypto.getRandomValues is available */

var array = new Uint32Array(10);
window.crypto.getRandomValues(array);
console.log("Your lucky numbers:");
for (var i = 0; i < array.length; i++) {
console.log(array[i]);
}

See, most modern browsers support a minimum of CryptoAPI which allows your clients to call obj.getRandomValues() from within Javascript - which is practically a call to the system's getRandomValues or /dev/random.

  • The WebCrypto API was enabled by default starting in Chrome 37 (August 26, 2014)
  • Mozilla Firefox supports it
  • Internet Explorer 11 supports it
  • etc.

Some final words regarding polyfills

If you really must support outdated browsers, decent polyfills can close the gap. But when it comes to security, both "using old browsers" as well as "using polyfills" is a nightmare waiting to go wrong. Instead, be professional and educate clients about the fact that its easier to upgrade to a newer browser, than to pick up polyfills and the problems that come with them.

Murphy's Law applies here: When using polyfills for security/cryptography, what can go wrong will go wrong!

In the end, its always better to be safe and not use polyfills just to support some outdated browsers, than to be sorry when stuff hits the fan. A browser update will cost your client a few minutes. A cryptographic polyfill that fails ruins your reputation forever. Remember that!

Generating random whole numbers in JavaScript in a specific range

There are some examples on the Mozilla Developer Network page:

/**
* Returns a random number between min (inclusive) and max (exclusive)
*/
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}

/**
* Returns a random integer between min (inclusive) and max (inclusive).
* The value is no lower than min (or the next integer greater than min
* if min isn't an integer) and no greater than max (or the next integer
* lower than max if max isn't an integer).
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}

Here's the logic behind it. It's a simple rule of three:

Math.random() returns a Number between 0 (inclusive) and 1 (exclusive). So we have an interval like this:

[0 .................................... 1)

Now, we'd like a number between min (inclusive) and max (exclusive):

[0 .................................... 1)
[min .................................. max)

We can use the Math.random to get the correspondent in the [min, max) interval. But, first we should factor a little bit the problem by subtracting min from the second interval:

[0 .................................... 1)
[min - min ............................ max - min)

This gives:

[0 .................................... 1)
[0 .................................... max - min)

We may now apply Math.random and then calculate the correspondent. Let's choose a random number:

                Math.random()
|
[0 .................................... 1)
[0 .................................... max - min)
|
x (what we need)

So, in order to find x, we would do:

x = Math.random() * (max - min);

Don't forget to add min back, so that we get a number in the [min, max) interval:

x = Math.random() * (max - min) + min;

That was the first function from MDN. The second one, returns an integer between min and max, both inclusive.

Now for getting integers, you could use round, ceil or floor.

You could use Math.round(Math.random() * (max - min)) + min, this however gives a non-even distribution. Both, min and max only have approximately half the chance to roll:

min...min+0.5...min+1...min+1.5   ...    max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘ ← Math.round()
min min+1 max

With max excluded from the interval, it has an even less chance to roll than min.

With Math.floor(Math.random() * (max - min +1)) + min you have a perfectly even distribution.

 min...  min+1...    ...      max-1... max....   (max+1 is excluded from interval)
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘ ← Math.floor()
min min+1 max-1 max

You can't use ceil() and -1 in that equation because max now had a slightly less chance to roll, but you can roll the (unwanted) min-1 result too.

Seeding the random number generator in Javascript

No, it is not possible to seed Math.random(), but it's fairly easy to write your own generator, or better yet, use an existing one.

Check out: this related question.

Also, see David Bau's blog for more information on seeding.

SecureRandom in JavaScript?

There is no such helper function in JS. You can generates a fairly random hash using:

function hex(n){
n = n || 16;
var result = '';
while (n--){
result += Math.floor(Math.random()*16).toString(16).toUpperCase();
}
return result;
}

You can modify it to form a guid:

function generateGuid(){
var result = '', n=0;
while (n<32){
result += (~[8,12,16,20].indexOf(n++) ? '-': '') +
Math.floor(Math.random()*16).toString(16).toUpperCase();
}
return result;
}

Generate cryptographically strong pseudorandom integer in a specific range

// NodeJS -> test in -> https://repl.it/languages/nodejs

const crypto = require('crypto');
const buf = crypto.randomBytes(256);
console.log(`${buf.length} bytes of random data: ${buf.toString('hex')}`);

var converter = require('hex2dec');
var dec = converter.hexToDec(buf.toString('hex'));
console.log(`random number: ${dec}`);


Related Topics



Leave a reply



Submit