1

This is more of a fundamental question, but the context is specifically in terms of JavaScript. Given that Math.random is not cryptographically secure, can the results still be considered secure when it has been called a certain number of times that cannot be predicted?

So if I was to generate a 32bit number using window.crypto.getRandomValues for example and select one of the digits as an iteration count – calling Math.random that number of times and using the last result, is the result still predictable?

The purpose of this is to generate a set of secure random numbers between 0 and 1 (exclusive) without having the ability to manually seed Math.random.

My initial thoughts are that the result shouldn't be predictable – but I want to make sure I'm not overlooking something crucial.

Ken White
  • 123,280
  • 14
  • 225
  • 444
Speedy
  • 467
  • 1
  • 6
  • 16
  • 2
    no, it's easy to run random() billions of times until a known pattern is observed, then each number it will make can be predicted ahead of time from that point; adding 0-9 extra checks won't matter. you know about `window.crypto.getRandomValues`, so use it if you need a CSPRNG, don't roll your own crypto. you can use fast+slow timers, like the time methods at https://github.com/rndme/rndme – dandavis Jan 11 '17 at 04:13
  • If you're going to use `window.crypto.getRandomValues`, why not just use its output directly instead of bringing `Math.random` into the picture? – user2357112 Jan 11 '17 at 04:34
  • Well as mentioned in the question, my purpose is to generate a set of secure random numbers between 0 and 1 (exclusive). Is there a way to achieve this with only `window.crypto.getRandomValues`, leaving `Math.random` out of the equation and get a good distribution of values? – Speedy Jan 11 '17 at 04:54
  • 1
    ^ Found one possible solution: http://stackoverflow.com/a/18230432/2905686 – Speedy Jan 11 '17 at 05:02

2 Answers2

3

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.

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!

e-sushi
  • 13,786
  • 10
  • 38
  • 57
  • what do you think of `performance.now().toString().split(".")[1][0];` for a random single digit? (the current tenth of a millisecond) – dandavis Jan 11 '17 at 05:34
  • 1
    @dandavis Personally? Don't do it. // Cryptographically? Don't do it. // Logically? Perf counters (here: clocking ms value) are a tiny part of what the system feeds to its internal secure random pool. Seems not logic to limit randomness sources to a performance counter or anything else when you can have a whole pool full of different (let's just call it) noise sources. Besides, it's hard to outperform a system native randomness pool with a Javascript line like that (especially when it splits stuff et al.) – e-sushi Jan 11 '17 at 05:47
  • thanks. some folks don't trust OS-provided randomness, so i ask around. The slow-clock timer methods is unpredictable, probably the most important but certainly not the only part of a CSPRNG... – dandavis Jan 11 '17 at 06:02
3

Here is a simple Math.random()-style CSPRNG drop-in:

Math.randomer=function(){
  return crypto.getRandomValues(new Uint32Array(1))[0] / Math.pow(2,32);
};

// usage demo:
alert(Math.randomer());

Unlike the unsafe random(), this code will still rate-limit because of the use of crypto.getRandomValues, but that's probably a good thing, and you can get dozens of KBs a second with this.

dandavis
  • 16,370
  • 5
  • 40
  • 36