24

I know from experience that reading from /dev/random blocks when the Linux kernel entropy pool runs out of entropy. Also, I've seen many articles and blog entries stating that when running on Linux, java.security.SecureRandom uses /dev/random as its entropy source and thus blocks when the kernel entropy pool runs out of entropy.

However, I'm unable to produce an experiment which causes SecureRandom to block. Conversely, it seems easy to get a simple bash one-liner which reads from /dev/random to block.

Here's the java code I'm using for these experiments:

import java.security.SecureRandom;

public class A {
    public static void main(String[] args) {
        SecureRandom sr = new SecureRandom();
        int out = 0;
        for (int i = 0; i < 1<<20 ; i++) {
            out ^= sr.nextInt();
        }
        System.out.println(out);
    }
}

It generates just over 1,000,000 random 32-bit integers. That should be 2^(20 + log2(32)) = 2^25 bits or 2^22 (a little over 4 million) bytes of entropy, right? However, it never blocks. It always finishes in about 1.2 seconds no matter whether I wiggle the mouse or not.

The bash one-liner I used is:

head -c 100 /dev/random | xxd

This blocks easily. As long as I keep my hand off of the mouse and keyboard, it'll sit there doing nothing for several minutes. And I'm only asking for 100 bytes of entropy.

Surely I'm missing something here. Could someone explain what's going on?

Thanks!

user1483512
  • 417
  • 1
  • 4
  • 8
  • 2
    There is no reason for a CSPRNG to block again after it's seeded once. Blocking is useful after reboot, hibernation etc. because you need sufficient initial entropy, but "entropy drain" is not a threat in practice. – CodesInChaos Aug 14 '13 at 19:35

5 Answers5

41

Both OpenJDK and Sun read from /dev/urandom, not /dev/random, at least on the machine where I tested (OpenJDK JRE 6b27 and Sun JRE 6.26 on Debian squeeze amd64). For some reason, they both open /dev/random as well but never read from it. So the blog articles you read either were mistaken or applied to a different version from mine (and, apparently, yours).

You can check whether yours reads from /dev/random or /dev/urandom by tracing it:

strace -o a.strace -f -e file java A

and look for the relevant part of the trace:

21165 open("/dev/random", O_RDONLY)     = 6
…
21165 open("/dev/urandom", O_RDONLY)    = 7
…
21165 read(7, "\322\223\211\262Zs\300\345\3264l\254\354[\6wS\326q@", 20) = 20
…

Don't worry, /dev/urandom is perfectly fine for cryptography.

Gilles 'SO- stop being evil'
  • 50,912
  • 13
  • 120
  • 179
  • 1
    Simple, answers my question, backs up his claims. Thanks and +1. (That is +1 if I had enough reputation to upvote...) – user1483512 Aug 15 '13 at 13:09
  • 1
    Apparently on Android, SecureRandom has a different implementation which has caused repeated values: http://www.theregister.co.uk/2013/08/12/android_bug_batters_bitcoin_wallets/ – Will Sargent Apr 13 '14 at 20:07
  • 5
    @WillSargent Not true. Android's SecureRandom also reads from `/dev/urandom` so it is conceptually the same deal. There was however, a flaw in the way Android initialized `/dev/urandom`, causing the issue. – DCKing Jul 22 '14 at 11:21
  • @DCKing No, true. The implementation is different, and it caused repeated values. I never said it read from /dev/random. See source at http://android-developers.blogspot.com.au/2013/08/some-securerandom-thoughts.html – Will Sargent Jul 22 '14 at 15:21
  • 4
    Yes, so to clarify: the differences `SecureRandom`'s implementation between Java and Android were not the cause; it was the difference in the implementation of `/dev/urandom` on Android and other *nixes that was the cause. – DCKing Jul 22 '14 at 15:32
12

Java's SecureRandom does use /dev/random, but only briefly.

Specifically it only uses it when generating seed information, either by explicitly calling SecureRandom.generateSeed() or by the first call to nextInt()

So therefore, to repeat your bash example you can do the following, and it should block.

import java.security.SecureRandom;

public class A {
    public static void main(String[] args) {
        SecureRandom sr;
        int out = 0;
        for (int i = 0; i < 1<<20 ; i++) {
            sr = new SecureRandom();
            out ^= sr.nextInt();
        }
        System.out.println(out);
    }
}
diedthreetimes
  • 221
  • 2
  • 4
  • Does this override `securerandom.source` in `$JAVA_HOME/lib/security/java.security`? AFAIK, if `securerandom.source` is `/dev/urandom` then it will never use `/dev/random` – Alastair McCormack Nov 14 '14 at 16:34
  • The way I understand it this depends greatly on your version (and implementation) of Java. From my own experiments with Oracle JVM 7 the only way to completely ignore /dev/random is to set securerandom.source to /dev/./urandom. Otherwise it will still use /dev/random for seed generation. – diedthreetimes Nov 15 '14 at 04:21
  • You say "should block", did you test it? – Neil Smithline Oct 01 '15 at 00:51
  • I don't think that I did. – diedthreetimes Oct 12 '15 at 23:31
4

On recent vintage Linux (last 6 years, say) the computational difference between /dev/random (blocking) and /dev/urandom (non-blocking) is very marginal; the raw entropy is laundered through a random number generator and both devices use the same bits. The only difference is that /dev/random has a rate limiter that prevents it from running too long without reseeding. The rate estimate itself is quite debatable.

This is explained in http://www.2uo.de/myths-about-urandom/ which drives home the point that /dev/urandom is sufficient for seeding your cryptographic algorithms for session keys. If you don't call it too frequently, then both should be of identical or nearly identical quality.

I have seen blocking via /dev/random when running a web crawler that needed to make 1000+ simultaneous SSL connections (session keys), and yes, we switched to /dev/urandom; for bulk generation of long-held public/private key pairs (a rare situation), a custom entropy device is best.

4

Not just to keep an old thread alive but some people might have missed a important part of the long story behind this... It's been about an well known infamous and persistent bug when using /dev/urandom from Java versions 1.4 to versions 1.7. See the links below :

http://bugs.java.com/view_bug.do?bug_id=6202721

http://bugs.java.com/view_bug.do?bug_id=4705093

For that I know, this has finally been addressed in Java 8 as stated by Oracle : https://docs.oracle.com/javase/8/docs/technotes/guides/security/enhancements-8.html

SHA1PRNG and NativePRNG were fixed to properly respect the SecureRandom seed source properties in the java.security file. (The obscure workaround using file:///dev/urandom and file:/dev/./urandom is no longer required.)

Ohnana
  • 4,737
  • 2
  • 23
  • 39
ozys
  • 41
  • 1
4

$JAVA_HOME/lib/security/java.security: securerandom.source property dictates which device to use when using SecureRandom. The default value is /dev/urandom, which means Java will never block.

For new comers to the topic, /dev/urandom is special version of /dev/random, which will use true entropy from /dev/random when available and then pseudo random data seeded true entropy if it was available.

In headless, VM and Cloud environments, I recommend seeding urandom from an external source, such as http://random.org