JDK-8060435 : Provide the capability to obtain a system seed to seed PRNGs
  • Type: Enhancement
  • Component: security-libs
  • Sub-Component: java.security
  • Affected Version: 9
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2014-10-14
  • Updated: 2014-12-03
Related Reports
Relates :  
Relates :  
In JDK 8 ThreadLocalRandom (and SplittableRandom) by default computes an initial seed to the PRNG using the mac address obtained via NetworkInterface.getNetworkInterfaces(). This unfortunately can result in high very initialization costs on certain platforms (Windows) which in turn can propagate to other classes such as ConcurrentSkipList*. 

The fix for JDK-8066397 will remove the network code and by default compute a seed using the current system time.

It would be useful to enhance the seed generating functionality in the JDK to compute seed bytes of reasonable cryptographic strength for the underlying platform that:

  1) has a very low initialization cost; and
  2) can be used by ThreadLocalRandom and SplittableRandom as the default.

One possible solution is to add a new public static method SecureRandom.getSystemSeed() (as proposed in the comments) in JDK 9. Ideally this functionality could be back-ported as a private interface in 8u60 (which will unfortunately result in a deviation of ThreadLocalRandom in JDK 8u60 and the version in the 166 repository.)
See Peter Levart's prototype for a SystemSeed class: http://mail.openjdk.java.net/pipermail/core-libs-dev/2014-December/030008.html

Paul's version of Peter's suggestion looks good to me. Although in the mean time for 8u TLR, just basing initial seed on (two forms of) time is worth considering (i.e., disabling network interface probe). This is no worse than jdk7.

I think Peter's patch is the most promising direction, namely exposing a way to obtain plattform-specific seed bytes: http://mail.openjdk.java.net/pipermail/security-dev/2014-June/010700.html http://cr.openjdk.java.net/~plevart/jdk9-dev/TLR_SR_SeedGenerator/webrev.01/jdk.patch Here is a proposal to simplify that. Rather than exposing SeedGenerator define a static method on SecureRandom that uses SeedGenerator e.g. something along the lines of: /** * Returns the given number of seed bytes, computed using a platform-specific, * SHA1PRNG cryptographically strong, seed generation algorithm. This call may * be used to seed other random number generators. * <p> * For control over the seed generation algorithm the caller should use one * of the alternative {@code getInstance} methods to obtain a SecureRandom * object, and then call the {@code generateSeed} method to obtain seed bytes * from that object. * <p> * @apiNote this call may be used is scenarios where the use of {@code getInstance} * has a high initial cost (for example, results in the loading of many additional classes) * and/or introduces unwanted dependencies (for example, perhaps resulting in class * initialization issues). * <p> * @implNote <documented platform-specific algorithms> * */ static public byte[] getSystemSeed(int n) On linux i think we can always use /dev/urandom (http://www.2uo.de/myths-about-urandom/). And we should probably deprecate the static method SecureRandom.getSeed.

Below are links to the most relevant discussion on this issue last June/July: http://mail.openjdk.java.net/pipermail/security-dev/2014-June/010672.html http://mail.openjdk.java.net/pipermail/security-dev/2014-July/010774.html Are we waiting on security review / guidance here?

JDK-8047769 is possibly related, in that it may be a blocker to using alternative APIs for the initial seed.

I refreshed my webrev http://cr.openjdk.java.net/~martin/webrevs/openjdk9/ThreadLocalRandom-system-entropy/ and confirmed that this is code is in use at Google (with jdk8 and on Linux). It's true that at Google we have more control over the execution environment and can assume the presence of /dev/urandom, so our patch is not directly suitable for use in openjdk, but I think something along these lines should be done. Perhaps a new public API that classes like SplittableRandom can call that will cause bytes to be read from /dev/urandom on platforms that have one? I think very few applications will need such high quality randomness that they are willing to possibly block on /dev/random instead of getting whatever /dev/urandom has available, assuming /dev/urandom is reasonably implemented.

I see this has been made a P2 and there is concern that this is a regression in 8u20 or 8u40. As has been noted, the issue seems to be generation of the initial seed in TLR, new code in JDK 8 (no changes in this area in 8u20 or 8u40 to my knowledge). That said, the elapsed time in the bug report seems very excessive. It would interesting to get an ipconfig /all output to get an idea on the number of interfaces in case that gives a clue (assuming that the issue is the enumeration of the network interfaces). Pardeep pasted in his timings which are also surprising. I checked a system running Windows Server 2012 and TLR initializes in ~33ms with JDK 8 GA builds compared to 1-2ms ms with JDK 7 GA. So yes, the initialization is slow on Windows when comparing JDK 7 and JDK 8, much harder to detect this on Linux (at least the systems that I tried). A possible workaround for the submitter is to run with -Djava.util.secureRandomSeed=true to see if that helps.

At least for NetworkInterface, then I think we should should re-examine the need for the service provider mechanism in InetAddress as that is highly problematic and leads to recursive initialization issue in other areas. It's a legacy mechanism, dating back to when NIS was popular and where InetAddress couldn't get the FQDN. It's only use now is testing and could easily be replaced with a mechanism specific for testing that wouldn't require service lookup. If we do this then it would at least remove one of the issues that the current TLR random initialization can potential run into.

I wonder if Windows is the more problematic platform? Martin, is your patch mostly focused on improving the behaviour on Linux platforms? The idea in the current approach to ThreadLocalRandom initialization was to reduce the cost of using SecureRandom, which might have to wait for some entropy to build up (i seem to recall there actually might be some confusion about such waiting on linux platforms and one can just directly use /dev/urandom as you have done but i have forgotten the link). It would be interesting to known what comparatively is the cost of the default behaviour compared to SecureRandom with "Djava.util.secureRandomSeed=true". IIUC both SecureRandom and NetworkInterface.getNetworkInterfaces can result in classes dynamically instantiated via service loader-like functionality. It would be good to find a cross platform solution that by default did not exhibit such behaviour.

If this is in fact due to ThreadLocalRandom initialization, I continue to lobby for a different TLR init process as in my change http://cr.openjdk.java.net/~martin/webrevs/openjdk9/ThreadLocalRandom-system-entropy/ which is in use at Google.

This might due to the initialization of the seed in ThreadLocalRandom which was significantly modified in 8 (there should be no significant difference between 8u20 and 8u40 in this respect). See method ThreadLocalRandom.initialSeed that attempts to obtain the mac address of the machine, and create an initial seed with a hash of that mac address mixed in with a hash of the system millisecond and nano time. The times from the sample code will include warm up and class loading costs which may contribute to some additional cost but very high costs are likely due to the interaction with the network stack to obtain the network interfaces and the MAC address. It would be useful to trace the classes loaded and the system calls

Checked with 7u67, 8u20 and 8u40 (b09) and reported following execution time: 7u67 - 44ms 8u20 - 585ms 8u40 (b09)- 674ms