mercredi 22 juin 2016

What is the maximum number of bytes I can extract from Java’s SecureRandom CSPRNG before needing to reseed

I am implementing an application in Java and I am using SecureRandom for generating randomness. I need to be able to encrypt 1 billion files. I looked on StackOverflow for the answer of my question but I found no exact answers. Everyone is pretty vague:

  • You don’t need to reseed SecureRandom. It has a “large” period. But what is **large**?
  • You don’t need to reseed SecureRandom because it is a “well designed" CSPRNG. But what is a **well designed** CSPRNG’s period?

So I decided to do the math myself and see if anyone can check it for me. What facts do we know about SecureRandom’s current implementation in Java 8? Actually there is some controversy from the sources I found.


What I know about Java's SecureRandom implementation

  • Internally it uses SHA1PRNG when generating randomness via the calls to nextBytes(), nextInt(), nextLong() etc. [Terasystems](http://ift.tt/1UKwCZM), [Cigital](http://ift.tt/28NkXco). The Cigital explanation of what SecureRandom does under the hood is contradicting the official explanation from the [Java docs:](http://ift.tt/12REWAM). The Official documentation from Oracle states that NativePRNG, NativePRNGBlocking, NativePRNGNonBlocking and Windows-PRNG always use the native source of randomness instead of Java’s own SHA1PRNG algorithm (also mentioned by this [answer](http://ift.tt/28NkXcp) and [this one](http://ift.tt/28OkB8V)). Cigital’s link says that Java always uses SHA1PRNG, but the type of SecureRandom dictates where it is seeded from (/dev/random, /dev/urandom etc.). Is SecureRandom always using SHA1PRNG under the hood? This is what I assume in my math calculations so if this is not the case please correct me.
  • The official [Oracle documents](http://ift.tt/12REWAM) state that SHA1PRNG truncates its output to 64 bits, from the full 160 bit hash output? Looking at the [OpenJDK’s implementation](http://ift.tt/28NkTJJ) of SecureRandom I see nowhere a truncation of the SHA1 output. Actually it does the opposite: it saves any unused output from the SHA1 hash for future calls to engineNextBytes(). If the official Oracle documents are the main authority on the subject then why OpenJDK’s implementation does something different? This is strange.
  • When nextBytes() is called immediately on the SecureRandom instance, It is seeded automatically by the operating system’s CSPRNG (/dev/random in Linux/Unix and CryptGenRandom in Windows) or when those are not available by custom made entropy generators like [ThreadLocalRandom](http://ift.tt/1K0C4Xs). But ThreadLocalRandom provides very low and slow entropy so it should be avoided when possible.
  • Nowhere did I see evidence of automatic reseeding functionality in SecureRandom. The Fortuna CSPRNG, as explained in Cryptography Engineering, has an elaborate mechanism of reseeding and I would expect that any modern CSPRNG would abstract that logic from the client and handle it automatically. I am not sure why there is such a lack of information and understanding about CSPRNG reseeding. It needs to happen if you exhaust the period of the CSPRNG. There is no doubt about it. The question is what is the period and is it a real concern in Java.

Doing the math

Due to the birthday attack we know that we should generally get a collision when we produce half the output size of the internal hash number of outputs. We know that the internal hash is truncated to 64 bits. This means that we can at most generate 2^32-1 rounds of 64 bit randomness before we have a 50% chance of collision. This will be the time we want to reseed.

(2^32-1) * 64 = 274,877,906,880 bits of randomness per seed of SecureRandom

Converted to bytes we get

(2^32 - 1) * 64 / 8 / (1024 * 1000 * 1000 ) = ~33.55 GBytes of randomness per seed of SecureRandom.

This means that we can generally expect around 33.55GB of high quality randomness generated by SecureRandom’s SHA1PRNG before it needs to be reseeded with a strong seed.

Is my math correct?

Why this matters? Because my use case is encrypting 1 billion files. Each one needs an IV (128 bits) and a Key (256 bits) or 384 bits. This comes out to 46.875GB of randomness needed in the bast case. Because of this I will exhaust the period of SecureRandom if that period is only 33.55GB. A reseeding strategy will be needed in this case, which I’ll post as a separate question.




Aucun commentaire:

Enregistrer un commentaire