22

Is there any good way to generate cryptographically strong pseudorandom (or true random) numbers in Javascript?

The crucial requirement: if a.com's Javascript generates some random numbers, no one else should be able to predict those random numbers. For instance, Javascript on evil.com should not be able to predict the random numbers that a.com got.

Summary of what I know about this subject. Here's what I've been able to find, in my own research:

  • All browsers provide Math.random() as a library call to generate pseudorandom numbers. Unfortunately, it does not provide crypto-quality random numbers and fails the requirement above. On the browsers I've seen, Math.random() uses a non-cryptographic PRNG, and its internal state is shared across multiple sites. Therefore, evil.com can call Math.random() a bunch of times, recover the internal state of the PRNG, and then infer what random numbers a.com got when it called Math.random(). Also, it uses a non-cryptographic-quality PRNG, so if a.com generates a random key and a random IV using Math.random() and then publishes the IV, it might be possible to infer the PRNG's internal state (from the IV) and then recover the key. So, Math.random() is right out.

  • I found a research paper that looks at doing cryptography in Javascript. Their code, the Stanford Javascript Crypto Library, is publicly available. It does include a crypto-strength pseudorandom number.

    However, it appears to have a hefty limitation: if I'm reading the paper correctly, it takes 10-40 seconds of user interaction with your site before the pseudorandom number gets adequately seeded. Moreover, each site has to start over from scratch: if a.com includes the SJCL library, then it looks like a.com's script has to wait for the user to interact with the a.com site for 10-40 seconds (typically) before a.com can generate crypto-quality random numbers. This is a pretty significant limitation.

    Here's their paper:

  • The classic essay, Javascript Cryptography Considered Harmful, mentions the lack of any good way to get crypto-strength random numbers in Javascript as a major barrier to doing secure cryptography in Javascript. The essay considers several obvious approaches and explains why they are flawed.

The bottom line is that I don't know of any reasonable solution; the options I found all seem pretty problematic. However, it has been several years since those papers and essays were written, and I know that web technology can change rapidly. Does anyone know of any good solution to this problem?

D.W.
  • 98,420
  • 30
  • 267
  • 572

4 Answers4

17

There is an experimental API for this: window.crypto.getRandomValues.

It's supported in

  • Chrome 11.0
  • Firefox 21
  • Internet Explore 11.0
  • Chrome with Opera skin 15.0

Opera 12 does not support this API, but its Math.Random is secure.

Opera has not yet implemented window.crypto.getRandomValues(). However, our Math.Random() is using a cryptographically secure random generator. If used carefully (noting that it only returns 53 bits of entropy for each call) it is possible to use it to implement a javascript version of window.crypto.getRandomValues(). Just make sure to only do this in Opera, along with some good comments in the code.

http://lists.w3.org/Archives/Public/public-webcrypto/2013Jan/0063.html

CodesInChaos
  • 11,854
  • 2
  • 40
  • 50
  • Its not supported by Android as well. At least according to that link at MDN: https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues – Yaroslav Yakovlev Sep 08 '15 at 13:58
5

In a Web context, Javascript is provided by a trusted Web server (i.e. a Web server who already has the power to be very nasty, by sending altered code). You may as well keep on trusting it, and ask for a random seed from that server, with a secondary Ajax call, or simply by having the server include the random seed in the Javascript code directly, which should be a matter of a couple lines of PHP code. Something like that:

<script src="theJavascriptCode.js"></script>
<?php
    $randblob = bin2hex(openssl_random_pseudo_bytes(16));
    echo "<script>init_PRNG(\"$randblob\")</script>"
?>

The Javascript code can then include a good PRNG implementation, seeded through the init_PRNG() function.

(Safety against sloppy integrators might be difficult, though. You cannot easily test, from the Javascript alone, that its seed was indeed provided from a strong PRNG, and not from something weak, or even hardcoded server-side.)

Thomas Pornin
  • 320,799
  • 57
  • 780
  • 949
  • Is there anyway to have signed JavaScripts to make sure they are trusted? – Petah Jun 06 '13 at 00:39
  • It used to be possible to sign Javascript in Netscape Communicator and some of its offspring such as Mozilla and then Firefox; see [this](http://www-archive.mozilla.org/projects/security/components/signed-scripts.html). However, signatures don't guarantee trustworthiness; they just are a convenient proof if you want to sue the script author. If you just want to be sure that the script is as it was on the (trusted) server, use HTTPS. – Thomas Pornin Jun 06 '13 at 01:07
  • I'm more looking for a way to verify that the trusted server has not been hacked and its scripts modified. Any ideas? – Petah Jun 06 '13 at 01:52
  • If the server is hacked, there is no point in signing the scripts because the hacker could also sign them. Use https to guarantee that the script is not modified between server and client. That's all you can do. – Josef Aug 17 '16 at 13:18
2

Unfortunately, browsers just don't provide enough entropy to produce strong random numbers. I think it's always going to be a compromise with usability.

I'd go take a look at seedrandom.js (BSD license). It's based on RC4, and seems to be quite popular. It allows you to plug in entropy from your own sources, with a rather simple API. I've only used it once, and I've never really looked into the security proofs, but it's certainly easier and faster than a lot of other solutions I've seen.

My additional entropy sources were:

  • Hash of mt_rand() and microtime() outputs from PHP
  • Mouse position data taken every 1s
  • Keyboard timing data (in this case the user filled out a form before RNG was required)

You could also time a few Ajax calls over the network and add those timings to the pool.

I don't know how much entropy that really produced, but it was as much as I could possibly hope to grab without inconveniencing the user.

CodesInChaos
  • 11,854
  • 2
  • 40
  • 50
Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • I'd really not advice anyone to start using RC4 nowadays. Should at least come with a little "How to RC4 securely" manual. – Lodewijk Aug 12 '14 at 16:46
2

You may also try the Fortuna implementation included in the Javascript Crypto Library (AGPL 3 license)

(produced by Clipperz, the online password manager, I'm a co-founder)

CodesInChaos
  • 11,854
  • 2
  • 40
  • 50