I’ve written about several SecureRandom implementations in the past. While analyzing the default SecureRandom implementation in IBM JCE (v1.7) on *nix, I came across several weaknesses. IBM recently released a patch to fix the issues. Let’s take a look at how this SecureRandom implementation works as well as the issues that were recently patched.
Note that the description below is for the unpatched version of IBMSecureRandom. I have not analyzed the latest version that fixes the issues.
On *nix installations of the IBM JRE, IBMJCE is the only default crypto provider that includes SecureRandom implementations. The default SecureRandom implementation in the IBMJCE is IBMSecureRandom. If it happens to not be the default, the implementation can be instantiated using:
This implementation of SecureRandom can be seeded using two different mechanisms as discussed below:
- If a seed is not specified when instantiating a SecureRandom object, and the caller attempts to get output from the PRNG using nextBytes(), it seeds itself automatically. It seeds itself using a static SecureRandom instance that is a member variable in the class. The static instance is seeded using operations performed on the output of java.util.Random (which in turn is seeded automatically using the system time). There are two security issues here. First, the static instance is seeded using java.util.Random that is in turn seeded using the system time; this means that the outputs of the static instance are potentially predictable – and therefore, all IBMSecureRandom instances outputs are potentially predictable. Second, the static instance is seeded using only 64 bits – and 64-bit values are brute forceable.
- If a seed is specified using setSeed() before any output is obtained from the PRNG, only the specified seed will be used for the PRNG, and the self-seeding mechanism using the static SecureRandom instance will be bypassed.
The output of the IBMSecureRandom PRNG is generated using applications of the MD5 hash function. Although MD5 is not considered a secure hash function anymore since it is not collision resistant, use of MD5 here is probably okay because pre-image resistance has not yet been broken in MD5.
However, the way in which MD5 is used here is insecure. The PRNG essentially outputs 8 bytes of its internal state and then hashes the entire internal state using MD5. Therefore, an attacker who can see the PRNG’s output sees half of the PRNG’s internal state. For example, I observed the following output generated by the given state:
An attacker that sees the PRNG’s output would only need to brute force 64 bits of the internal state to predict all future outputs of the PRNG.
Another potential problem with using MD5 here is that the internal state of the PRNG is only 128 bits, which is not acceptable for many scenarios. For example, if you were to shuffle a deck of cards using this PRNG, you would only get 2128 possible card shuffles. However, there are actually 52! – or approximately 2226 possible card shuffles. Many PRNGs aren’t acceptable for shuffling decks of cards for this reason; however, a 128-bit PRNG state is not acceptable for a lot of applications.
Other Seed Generation
If generateSeed() is called, the resulting seed is generated using operations performed on the output of java.util.Random seeded with the system time. Therefore, these seeds are potentially predictable.
Here’s a summary of the main security issues that I discovered:
- If a seed is provided before any output is obtained from the PRNG, the internal seeding mechanism will be completely bypassed.
- The internal seeding mechanism (and the mechanism used by generateSeed) utilizes only java.util.Random and generates predictable seeds.
- The internal seeding mechanism only generates 64-bit seeds that are brute-forceable
- The PRNG outputs half of its internal state, which makes the remaining internal state brute forceable.
If your application relies on the unpredictability of outputs from an unpatched version of this implementation (e.g. if you use it to generate session identifiers, passwords, cryptographic keys, etc.), then you have a problem.
Note that some other JCE providers also use IBMSecureRandom for various purposes. So, the issues in the unpatched IBMSecureRandom may impact other JCE providers as well. If you have the IBMJCE provider enabled in your application (even if you’re not using IBMSecureRandom explicitly), make sure that you apply the latest patch from IBM as soon as possible.
What are the lessons learned here? Secure pseudo-random number generation is hard. Even widely used pseudo-random number generators from reputable vendors can contain security issues. If you rolled your own pseudo-random number generator without help from cryptography experts, assume that it’s insecure. If you are using a pseudo-random number generator provided by an OS vendor or a crypto library, don’t just assume that it’s secure. Ask the vendor if the implementation has been reviewed by a third party. Ensure that any third-party reviews didn’t just involve performing statistical analysis on the outputs of the PRNG. The actual implementation details need to be analyzed.
+Amit Sethi is a Technical Manager at Cigital. His software security expertise includes architectural risk analysis, threat modeling, secure code reviews, penetration testing, and more. He has helped many multi-national corporations solve complex security problems and is a contributor to The Justice League Blog.