Hi Peter,
A few questions and comments:
- Can/should this function be fit into one of the existing classes?
- Is more than one instance needed?
Seed material seems to be needed only as a one-shot so a simpler
implementation that opens,
uses and closes would leave fewer leftovers (and not introduce a new
finalizer use).
- The Windows native code could be simpler if the hCryptProv
was returned from the init function and passed as an argument where
needed in getBytes and close .
- The static checking tool we use will complain about JNI functions that may
throw exceptions if those exceptions are not checked for.
For example, SystemRandomImpl_md.c:80.
The macros in jni_util.h like CHECK_NULL, CHECK_NULL_RETURN can be used.
Thanks, Roger
On 12/2/2014 11:42 AM, Peter Levart wrote:
On 12/02/2014 11:02 AM, Paul Sandoz wrote:
Hi,
Please find below a patch to remove the networking code computing a
seed in ThreadLocal/SplittableRandom.
We thought it a good idea at the time :-) but subsequently on certain
platforms this results in very high initalization costs that can
propagate to other classes such as ConcurrentSkipList*.
The short-term solution is to remove this code and fallback just
using current system time. This needs to be back-ported to 8u40.
A longer term solution is to provide a simple public API to get
access to some seed bytes that is optimal for the underlying
platform, for example, based on Peter's investigations. For linux
/dev/urandom is sufficient as a source of bytes. The main problem
seems to be Windows. It would also be nice to back-port to say 8u60
using a private API and update TLR/SR.
Hi,
Here's a proof of concept for an API that just delegates to
system-provided "cryptographically secure" (as declared by the
system(s)) pseudo random number generator:
http://cr.openjdk.java.net/~plevart/jdk9-dev/SystemRandom/webrev.01/
On UNIX-es this uses /dev/urandom (which is non-blocking and uses
system entropy at least for it's seed):
http://en.wikipedia.org/?title=/dev/random
On Windows it uses MS Crypto API's function CryptGenRandom (the JNI
code is ripped from the sun.security.provider.NativeSeedGenerator),
which also seeds from various system sources of entropy:
http://en.wikipedia.org/wiki/CryptGenRandom
http://msdn.microsoft.com/en-us/library/windows/desktop/aa379942%28v=vs.85%29.aspx
The initialization overhead is low on UNIX (the enclosed test run on
64 bit Fedora 20, i7 PC):
SystemRandomTest... (8 bytes / invocation)
1st invocation: 112315 ns, result: [25, 61, -12, -106, 75, -7, -73, -55]
Following 1000000 invocations: 0.636644474 s, (636 ns/invocation)
The same test run on 32 bit Windows 7 (as VirtualBox guest on the same
machine):
SystemRandomTest... (8 bytes / invocation)
1st invocation: 4880788 ns, result: [-32, 53, -31, 62, 51, 83, 9, -5]
Following 1000000 invocations: 1.761087512 s, (1761 ns/invocation)
I think the initialization on Windows has an initial latency of approx
5ms because it has to initialize the whole MS Crypto API with it's
providers. But CryptGenRandom, which is part of this API, actually
delegates it's work to RtlGenRandom function:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694%28v=vs.85%29.aspx
...which might have lower initialization costs. Unfortunately, the
wording in the Microsoft document states that it might be removed in
the future. Perhaps we could try to use it and fallback to
CryptGenRandom if it is not available...
Regards, Peter
Paul.
[1] https://bugs.openjdk.java.net/browse/JDK-8060435
diff -r 1b599b4755bd
src/java.base/share/classes/java/util/SplittableRandom.java
--- a/src/java.base/share/classes/java/util/SplittableRandom.java Mon
Dec 01 17:59:39 2014 -0800
+++ b/src/java.base/share/classes/java/util/SplittableRandom.java Tue
Dec 02 10:53:47 2014 +0100
@@ -237,34 +237,7 @@
s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);
return s;
}
- long h = 0L;
- try {
- Enumeration<NetworkInterface> ifcs =
- NetworkInterface.getNetworkInterfaces();
- boolean retry = false; // retry once if
getHardwareAddress is null
- while (ifcs.hasMoreElements()) {
- NetworkInterface ifc = ifcs.nextElement();
- if (!ifc.isVirtual()) { // skip fake addresses
- byte[] bs = ifc.getHardwareAddress();
- if (bs != null) {
- int n = bs.length;
- int m = Math.min(n >>> 1, 4);
- for (int i = 0; i < m; ++i)
- h = (h << 16) ^ (bs[i] << 8) ^ bs[n-1-i];
- if (m < 4)
- h = (h << 8) ^ bs[n-1-m];
- h = mix64(h);
- break;
- }
- else if (!retry)
- retry = true;
- else
- break;
- }
- }
- } catch (Exception ignore) {
- }
- return (h ^ mix64(System.currentTimeMillis()) ^
+ return (mix64(System.currentTimeMillis()) ^
mix64(System.nanoTime()));
}
diff -r 1b599b4755bd
src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java
---
a/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java
Mon Dec 01 17:59:39 2014 -0800
+++
b/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java
Tue Dec 02 10:53:47 2014 +0100
@@ -147,34 +147,7 @@
s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);
return s;
}
- long h = 0L;
- try {
- Enumeration<NetworkInterface> ifcs =
- NetworkInterface.getNetworkInterfaces();
- boolean retry = false; // retry once if
getHardwareAddress is null
- while (ifcs.hasMoreElements()) {
- NetworkInterface ifc = ifcs.nextElement();
- if (!ifc.isVirtual()) { // skip fake addresses
- byte[] bs = ifc.getHardwareAddress();
- if (bs != null) {
- int n = bs.length;
- int m = Math.min(n >>> 1, 4);
- for (int i = 0; i < m; ++i)
- h = (h << 16) ^ (bs[i] << 8) ^ bs[n-1-i];
- if (m < 4)
- h = (h << 8) ^ bs[n-1-m];
- h = mix64(h);
- break;
- }
- else if (!retry)
- retry = true;
- else
- break;
- }
- }
- } catch (Exception ignore) {
- }
- return (h ^ mix64(System.currentTimeMillis()) ^
+ return (mix64(System.currentTimeMillis()) ^
mix64(System.nanoTime()));
}