Hi Martin,

On 06/22/2014 07:12 PM, Martin Buchholz wrote:
We know that loading the networking machinery is problematic.  On Linux we
would be content to hard-code a read from /dev/urandom, which is safer and
strictly more random than the existing network hardware determination, but
y'all will reject that as too system-dependent (insufficient machinery!).
Hmmmm .... maybe not .... as long as we code up a good fallback ...

I learned that SecureRandom by default on Unix uses /dev/random for "seed
bytes" and /dev/urandom for nextBytes.

Here's my proposal, that in the default case on Unix doesn't load any
machinery, and as a fallback loads the SecureRandom machinery instead of
the network machinery, while maintaining the ultra-secure behavior of the
java.util.secureRandomSeed system property:


     private static long initialSeed() {
         byte[] seedBytes = initialSeedBytes();
         long s = (long)(seedBytes[0]) & 0xffL;
         for (int i = 1; i < seedBytes.length; ++i)
             s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);
         return s ^ mix64(System.currentTimeMillis()) ^
mix64(System.nanoTime());
     }

     private static byte[] initialSeedBytes() {
         String pp = java.security.AccessController.doPrivileged(
                 new sun.security.action.GetPropertyAction(
                         "java.util.secureRandomSeed"));
         boolean secureRandomSeed = (pp != null &&
pp.equalsIgnoreCase("true"));
         if (secureRandomSeed)
             return java.security.SecureRandom.getSeed(8);
         final byte[] seedBytes = new byte[8];
         File seedSource = new File("/dev/urandom");
         if (seedSource.exists()) {
             try (FileInputStream stream = new FileInputStream(seedSource)) {
                 if (stream.read(seedBytes) == 8)
                     return seedBytes;
             } catch (IOException ignore) { }
         }
         new java.security.SecureRandom().nextBytes(seedBytes);
         return seedBytes;
     }

So on platforms lacking "/dev/urandom" special file (Windows only?), the fall-back would be to use SecureRandom.nextBytes() whatever default SecureRandom PRNG is configured to be on such platform. This might be a user-supplied SecureRandom PRNG. The user might want it's own default SecureRandom but not use it for TLR's seed computation (unless requested by "java.util.secureRandomSeed" system property).

I would rather use SecureRandom.generateSeed() instance method instead of SecureRandom.nextBytes(). Why? Because every SecureRandom instance has to initialize it's seed 1st before getBytes() can provide the next random bytes from the PRNG. Since we only need the 1st 8 bytes from the SecureRandom instance to initialize TLR's seeder, we might as well directly call the SecureRandom.generateSeed() method. That's one reason. The other is the peculiar initialization of default SecureRandom algorithm on Windows (see below)...

Even if user does not provide it's own default SecureRandom PRNG, there are basically two algorithms that are used by default on OpenJDK:

On Solaris/Linux/Mac/AIX:

the "NativePRNG" algorithm from "SUN" provider (implemented by sun.security.provider.NativePRNG) which uses /dev/random for getBytes() and /dev/urandom (or whatever is configured with "java.security.egd" or "securerandom.source" system properties) for generateSeed(), and

On Windows:

the "SHA1PRNG" algorithm from "SUN" provider (implemented by sun.security.provider.SecureRandom) which uses SHA1 message digest for generating random numbers with seed computed by gathering system entropy...


The most problematic one is the default on Windows platform (the platform that does not have the "/dev/urandom" special file and would be used as a fall-back by your proposal) - sun.security.provider.SecureRandom. This one seeds itself by constructing an instance of itself with the result returned from SeedGenerator.getSystemEntropy() method. This method, among other things, uses networking code to gather system entropy:

                        ...
                        md.update
(InetAddress.getLocalHost().toString().getBytes());
                        ...

This is problematic since it not only initializes NameService providers but also uses them to resolve local host name. This can block for several seconds on unusual configurations and was the main motivation to replace similar code in TLR with code that uses NetworkInterface.getHardwareAddress() instead. Using SecureRandom.generateSeed() instead of SecureRandom.getBytes() does not invoke SeedGenerator.getSystemEntropy(), but uses just SeedGenerator.generateSeed(), which by default on Windows uses ThreadedSeedGenerator.getSeedBytes()...

I showed how we could suppress NameService providers initialization while still using NetworkInterface.getHardwareAddress() for TLR's seeder initialization. If that's not enough and we would like to get-away without using networking code at all, then I propose the following:


    private static byte[] initialSeedBytes() {

String secureRandomSeed = java.security.AccessController.doPrivileged(
            new sun.security.action.GetPropertyAction(
                "java.util.secureRandomSeed", ""))
            .toLowerCase(Locale.ROOT);

        String osName = java.security.AccessController.doPrivileged(
            new sun.security.action.GetPropertyAction(
                "os.name"))
            .toLowerCase(Locale.ROOT);

if (!secureRandomSeed.equals("true") && !secureRandomSeed.equals("blocking")) {
            secureRandomSeed = "nonblocking"; // default
        }

        SecureRandom srnd = null;

        if (secureRandomSeed.equals("nonblocking")) { // the default
            try {
                if (osName.startsWith("windows")) {
                    // native implementation using MSCAPI implemented by
                    // sun.security.mscapi.PRNG
srnd = SecureRandom.getInstance("Windows-PRNG", "SunMSCAPI");
                } else { // Solaris/Linux/Mac/AIX
// a non-blocking native implementation using /dev/urandom for both
                    // generateSeed() and nextBytes() implemented by
                    // sun.security.provider.NativePRNG$NonBlocking
srnd = SecureRandom.getInstance("NativePRNGNonBlocking", "SUN");
                }
} catch (NoSuchProviderException | NoSuchAlgorithmException ignore) {}
        } else if (secureRandomSeed.equals("blocking")) {
            try {
                if (osName.startsWith("windows")) {
                    // native implementation using MSCAPI implemented by
                    // sun.security.mscapi.PRNG
srnd = SecureRandom.getInstance("Windows-PRNG", "SunMSCAPI");
                } else { // Solaris/Linux/Mac/AIX
// a blocking native implementation using /dev/random for both
                    // generateSeed() and nextBytes() implemented by
                    // sun.security.provider.NativePRNG$Blocking
srnd = SecureRandom.getInstance("NativePRNGBlocking", "SUN");
                }
} catch (NoSuchProviderException | NoSuchAlgorithmException ignore) {}
        } else {
            assert secureRandomSeed.equals("true");
        }

if (srnd == null) { // fall back to default SecureRandom algorithm / provider
            srnd = new SecureRandom();
        }

        return srnd.generateSeed(8);
    }


By default (or when "java.util.secureRandomSeed" is set to "nonblocking" or unrecognized value) this would use /dev/urandom on UNIX-es and MSCAPI on Windows. More entropy for TLR's initial seeder could be provided on UNIX-es by risking some blocking with "java.util.secureRandomSeed" set to "blocking". This would still be independend of user's choice of default SecureRandom provider. The backward-compatible ("java.util.secureRandomSeed" set to "true") or fall-back would be to use default SecureRandom algorithm / provider.

Regards, Peter




On Sat, Jun 21, 2014 at 9:05 PM, Martin Buchholz <marti...@google.com>
wrote:

While looking at NativePRNG, I filed

https://bugs.openjdk.java.net/browse/JDK-8047769

SecureRandom should be more frugal with file descriptors

If I run this java program on Linux

public class SecureRandoms {
     public static void main(String[] args) throws Throwable {
         new java.security.SecureRandom();
     }
}

it creates 6 file descriptors for /dev/random and /dev/urandom, as shown
by:

strace -q -ff -e open java SecureRandoms |& grep /dev/
[pid 20769] open("/dev/random", O_RDONLY) = 5
[pid 20769] open("/dev/urandom", O_RDONLY) = 6
[pid 20769] open("/dev/random", O_RDONLY) = 7
[pid 20769] open("/dev/random", O_RDONLY) = 8
[pid 20769] open("/dev/urandom", O_RDONLY) = 9
[pid 20769] open("/dev/urandom", O_RDONLY) = 10

Looking at jdk/src/solaris/classes/sun/security/provider/NativePRNG.java
it looks like 2 file descriptors are created for every variant of
NativePRNG, whether or not they are ever used. Which is wasteful. In fact,
you only ever need at most two file descriptors, one for /dev/random and
one for /dev/urandom.

Further, it would be nice if the file descriptors were closed when idle
and lazily re-created. Especially /dev/random should typically be used at
startup and never thereafter.


On Fri, Jun 20, 2014 at 7:59 AM, Alan Bateman <alan.bate...@oracle.com>
wrote:

On 20/06/2014 15:02, Peter Levart wrote:

And, as Martin pointed out, it seems to be used for tests that exercise
particular responses from NameService API to test the behaviour of JDK
classes. It would be a shame for those tests to go away.

We've been talking about removing it for many years because it has been
so troublesome. If we really need to having something for testing then I
don't think it needs to be general purpose, we can get right of the lookup
at least.

-Alan.



Reply via email to