diff -r 836adbf7a2cd -r 3317bb8137f4 jdk/src/java.base/unix/classes/sun/security/provider/NativePRNG.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/unix/classes/sun/security/provider/NativePRNG.java Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.provider;
+
+import java.io.*;
+import java.net.*;
+import java.security.*;
+import sun.security.util.Debug;
+
+/**
+ * Native PRNG implementation for Solaris/Linux/MacOS.
+ *
+ * It obtains seed and random numbers by reading system files such as
+ * the special device files /dev/random and /dev/urandom. This
+ * implementation respects the {@code securerandom.source} Security
+ * property and {@code java.security.egd} System property for obtaining
+ * seed material. If the file specified by the properties does not
+ * exist, /dev/random is the default seed source. /dev/urandom is
+ * the default source of random numbers.
+ *
+ * On some Unix platforms, /dev/random may block until enough entropy is
+ * available, but that may negatively impact the perceived startup
+ * time. By selecting these sources, this implementation tries to
+ * strike a balance between performance and security.
+ *
+ * generateSeed() and setSeed() attempt to directly read/write to the seed
+ * source. However, this file may only be writable by root in many
+ * configurations. Because we cannot just ignore bytes specified via
+ * setSeed(), we keep a SHA1PRNG around in parallel.
+ *
+ * nextBytes() reads the bytes directly from the source of random
+ * numbers (and then mixes them with bytes from the SHA1PRNG for the
+ * reasons explained above). Reading bytes from the random generator means
+ * that we are generally getting entropy from the operating system. This
+ * is a notable advantage over the SHA1PRNG model, which acquires
+ * entropy only initially during startup although the VM may be running
+ * for months.
+ *
+ * Also note for nextBytes() that we do not need any initial pure random
+ * seed from /dev/random. This is an advantage because on some versions
+ * of Linux entropy can be exhausted very quickly and could thus impact
+ * startup time.
+ *
+ * Finally, note that we use a singleton for the actual work (RandomIO)
+ * to avoid having to open and close /dev/[u]random constantly. However,
+ * there may be many NativePRNG instances created by the JCA framework.
+ *
+ * @since 1.5
+ * @author Andreas Sterbenz
+ */
+public final class NativePRNG extends SecureRandomSpi {
+
+ private static final long serialVersionUID = -6599091113397072932L;
+
+ private static final Debug debug = Debug.getInstance("provider");
+
+ // name of the pure random file (also used for setSeed())
+ private static final String NAME_RANDOM = "/dev/random";
+ // name of the pseudo random file
+ private static final String NAME_URANDOM = "/dev/urandom";
+
+ // which kind of RandomIO object are we creating?
+ private enum Variant {
+ MIXED, BLOCKING, NONBLOCKING
+ }
+
+ // singleton instance or null if not available
+ private static final RandomIO INSTANCE = initIO(Variant.MIXED);
+
+ /**
+ * Get the System egd source (if defined). We only allow "file:"
+ * URLs for now. If there is a egd value, parse it.
+ *
+ * @return the URL or null if not available.
+ */
+ private static URL getEgdUrl() {
+ // This will return "" if nothing was set.
+ String egdSource = SunEntries.getSeedSource();
+ URL egdUrl;
+
+ if (egdSource.length() != 0) {
+ if (debug != null) {
+ debug.println("NativePRNG egdUrl: " + egdSource);
+ }
+ try {
+ egdUrl = new URL(egdSource);
+ if (!egdUrl.getProtocol().equalsIgnoreCase("file")) {
+ return null;
+ }
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ } else {
+ egdUrl = null;
+ }
+
+ return egdUrl;
+ }
+
+ /**
+ * Create a RandomIO object for all I/O of this Variant type.
+ */
+ private static RandomIO initIO(final Variant v) {
+ return AccessController.doPrivileged(
+ new PrivilegedAction() {
+ @Override
+ public RandomIO run() {
+
+ File seedFile;
+ File nextFile;
+
+ switch(v) {
+ case MIXED:
+ URL egdUrl;
+ File egdFile = null;
+
+ if ((egdUrl = getEgdUrl()) != null) {
+ try {
+ egdFile = SunEntries.getDeviceFile(egdUrl);
+ } catch (IOException e) {
+ // Swallow, seedFile is still null
+ }
+ }
+
+ // Try egd first.
+ if ((egdFile != null) && egdFile.canRead()) {
+ seedFile = egdFile;
+ } else {
+ // fall back to /dev/random.
+ seedFile = new File(NAME_RANDOM);
+ }
+ nextFile = new File(NAME_URANDOM);
+ break;
+
+ case BLOCKING:
+ seedFile = new File(NAME_RANDOM);
+ nextFile = new File(NAME_RANDOM);
+ break;
+
+ case NONBLOCKING:
+ seedFile = new File(NAME_URANDOM);
+ nextFile = new File(NAME_URANDOM);
+ break;
+
+ default:
+ // Shouldn't happen!
+ return null;
+ }
+
+ if (debug != null) {
+ debug.println("NativePRNG." + v +
+ " seedFile: " + seedFile +
+ " nextFile: " + nextFile);
+ }
+
+ if (!seedFile.canRead() || !nextFile.canRead()) {
+ if (debug != null) {
+ debug.println("NativePRNG." + v +
+ " Couldn't read Files.");
+ }
+ return null;
+ }
+
+ try {
+ return new RandomIO(seedFile, nextFile);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ });
+ }
+
+ // return whether the NativePRNG is available
+ static boolean isAvailable() {
+ return INSTANCE != null;
+ }
+
+ // constructor, called by the JCA framework
+ public NativePRNG() {
+ super();
+ if (INSTANCE == null) {
+ throw new AssertionError("NativePRNG not available");
+ }
+ }
+
+ // set the seed
+ @Override
+ protected void engineSetSeed(byte[] seed) {
+ INSTANCE.implSetSeed(seed);
+ }
+
+ // get pseudo random bytes
+ @Override
+ protected void engineNextBytes(byte[] bytes) {
+ INSTANCE.implNextBytes(bytes);
+ }
+
+ // get true random bytes
+ @Override
+ protected byte[] engineGenerateSeed(int numBytes) {
+ return INSTANCE.implGenerateSeed(numBytes);
+ }
+
+ /**
+ * A NativePRNG-like class that uses /dev/random for both
+ * seed and random material.
+ *
+ * Note that it does not respect the egd properties, since we have
+ * no way of knowing what those qualities are.
+ *
+ * This is very similar to the outer NativePRNG class, minimizing any
+ * breakage to the serialization of the existing implementation.
+ *
+ * @since 1.8
+ */
+ public static final class Blocking extends SecureRandomSpi {
+ private static final long serialVersionUID = -6396183145759983347L;
+
+ private static final RandomIO INSTANCE = initIO(Variant.BLOCKING);
+
+ // return whether this is available
+ static boolean isAvailable() {
+ return INSTANCE != null;
+ }
+
+ // constructor, called by the JCA framework
+ public Blocking() {
+ super();
+ if (INSTANCE == null) {
+ throw new AssertionError("NativePRNG$Blocking not available");
+ }
+ }
+
+ // set the seed
+ @Override
+ protected void engineSetSeed(byte[] seed) {
+ INSTANCE.implSetSeed(seed);
+ }
+
+ // get pseudo random bytes
+ @Override
+ protected void engineNextBytes(byte[] bytes) {
+ INSTANCE.implNextBytes(bytes);
+ }
+
+ // get true random bytes
+ @Override
+ protected byte[] engineGenerateSeed(int numBytes) {
+ return INSTANCE.implGenerateSeed(numBytes);
+ }
+ }
+
+ /**
+ * A NativePRNG-like class that uses /dev/urandom for both
+ * seed and random material.
+ *
+ * Note that it does not respect the egd properties, since we have
+ * no way of knowing what those qualities are.
+ *
+ * This is very similar to the outer NativePRNG class, minimizing any
+ * breakage to the serialization of the existing implementation.
+ *
+ * @since 1.8
+ */
+ public static final class NonBlocking extends SecureRandomSpi {
+ private static final long serialVersionUID = -1102062982994105487L;
+
+ private static final RandomIO INSTANCE = initIO(Variant.NONBLOCKING);
+
+ // return whether this is available
+ static boolean isAvailable() {
+ return INSTANCE != null;
+ }
+
+ // constructor, called by the JCA framework
+ public NonBlocking() {
+ super();
+ if (INSTANCE == null) {
+ throw new AssertionError(
+ "NativePRNG$NonBlocking not available");
+ }
+ }
+
+ // set the seed
+ @Override
+ protected void engineSetSeed(byte[] seed) {
+ INSTANCE.implSetSeed(seed);
+ }
+
+ // get pseudo random bytes
+ @Override
+ protected void engineNextBytes(byte[] bytes) {
+ INSTANCE.implNextBytes(bytes);
+ }
+
+ // get true random bytes
+ @Override
+ protected byte[] engineGenerateSeed(int numBytes) {
+ return INSTANCE.implGenerateSeed(numBytes);
+ }
+ }
+
+ /**
+ * Nested class doing the actual work. Singleton, see INSTANCE above.
+ */
+ private static class RandomIO {
+
+ // we buffer data we read from the "next" file for efficiency,
+ // but we limit the lifetime to avoid using stale bits
+ // lifetime in ms, currently 100 ms (0.1 s)
+ private final static long MAX_BUFFER_TIME = 100;
+
+ // size of the "next" buffer
+ private final static int BUFFER_SIZE = 32;
+
+ // Holder for the seedFile. Used if we ever add seed material.
+ File seedFile;
+
+ // In/OutputStream for "seed" and "next"
+ private final InputStream seedIn, nextIn;
+ private OutputStream seedOut;
+
+ // flag indicating if we have tried to open seedOut yet
+ private boolean seedOutInitialized;
+
+ // SHA1PRNG instance for mixing
+ // initialized lazily on demand to avoid problems during startup
+ private volatile sun.security.provider.SecureRandom mixRandom;
+
+ // buffer for next bits
+ private final byte[] nextBuffer;
+
+ // number of bytes left in nextBuffer
+ private int buffered;
+
+ // time we read the data into the nextBuffer
+ private long lastRead;
+
+ // mutex lock for nextBytes()
+ private final Object LOCK_GET_BYTES = new Object();
+
+ // mutex lock for generateSeed()
+ private final Object LOCK_GET_SEED = new Object();
+
+ // mutex lock for setSeed()
+ private final Object LOCK_SET_SEED = new Object();
+
+ // constructor, called only once from initIO()
+ private RandomIO(File seedFile, File nextFile) throws IOException {
+ this.seedFile = seedFile;
+ seedIn = new FileInputStream(seedFile);
+ nextIn = new FileInputStream(nextFile);
+ nextBuffer = new byte[BUFFER_SIZE];
+ }
+
+ // get the SHA1PRNG for mixing
+ // initialize if not yet created
+ private sun.security.provider.SecureRandom getMixRandom() {
+ sun.security.provider.SecureRandom r = mixRandom;
+ if (r == null) {
+ synchronized (LOCK_GET_BYTES) {
+ r = mixRandom;
+ if (r == null) {
+ r = new sun.security.provider.SecureRandom();
+ try {
+ byte[] b = new byte[20];
+ readFully(nextIn, b);
+ r.engineSetSeed(b);
+ } catch (IOException e) {
+ throw new ProviderException("init failed", e);
+ }
+ mixRandom = r;
+ }
+ }
+ }
+ return r;
+ }
+
+ // read data.length bytes from in
+ // These are not normal files, so we need to loop the read.
+ // just keep trying as long as we are making progress
+ private static void readFully(InputStream in, byte[] data)
+ throws IOException {
+ int len = data.length;
+ int ofs = 0;
+ while (len > 0) {
+ int k = in.read(data, ofs, len);
+ if (k <= 0) {
+ throw new EOFException("File(s) closed?");
+ }
+ ofs += k;
+ len -= k;
+ }
+ if (len > 0) {
+ throw new IOException("Could not read from file(s)");
+ }
+ }
+
+ // get true random bytes, just read from "seed"
+ private byte[] implGenerateSeed(int numBytes) {
+ synchronized (LOCK_GET_SEED) {
+ try {
+ byte[] b = new byte[numBytes];
+ readFully(seedIn, b);
+ return b;
+ } catch (IOException e) {
+ throw new ProviderException("generateSeed() failed", e);
+ }
+ }
+ }
+
+ // supply random bytes to the OS
+ // write to "seed" if possible
+ // always add the seed to our mixing random
+ private void implSetSeed(byte[] seed) {
+ synchronized (LOCK_SET_SEED) {
+ if (seedOutInitialized == false) {
+ seedOutInitialized = true;
+ seedOut = AccessController.doPrivileged(
+ new PrivilegedAction() {
+ @Override
+ public OutputStream run() {
+ try {
+ return new FileOutputStream(seedFile, true);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ });
+ }
+ if (seedOut != null) {
+ try {
+ seedOut.write(seed);
+ } catch (IOException e) {
+ throw new ProviderException("setSeed() failed", e);
+ }
+ }
+ getMixRandom().engineSetSeed(seed);
+ }
+ }
+
+ // ensure that there is at least one valid byte in the buffer
+ // if not, read new bytes
+ private void ensureBufferValid() throws IOException {
+ long time = System.currentTimeMillis();
+ if ((buffered > 0) && (time - lastRead < MAX_BUFFER_TIME)) {
+ return;
+ }
+ lastRead = time;
+ readFully(nextIn, nextBuffer);
+ buffered = nextBuffer.length;
+ }
+
+ // get pseudo random bytes
+ // read from "next" and XOR with bytes generated by the
+ // mixing SHA1PRNG
+ private void implNextBytes(byte[] data) {
+ synchronized (LOCK_GET_BYTES) {
+ try {
+ getMixRandom().engineNextBytes(data);
+ int len = data.length;
+ int ofs = 0;
+ while (len > 0) {
+ ensureBufferValid();
+ int bufferOfs = nextBuffer.length - buffered;
+ while ((len > 0) && (buffered > 0)) {
+ data[ofs++] ^= nextBuffer[bufferOfs++];
+ len--;
+ buffered--;
+ }
+ }
+ } catch (IOException e) {
+ throw new ProviderException("nextBytes() failed", e);
+ }
+ }
+ }
+ }
+}