0N/A/*
2362N/A * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.provider;
0N/A
0N/Aimport java.io.*;
0N/A
0N/Aimport java.security.*;
0N/Aimport java.security.SecureRandom;
0N/A
0N/A/**
0N/A * Native PRNG implementation for Solaris/Linux. It interacts with
0N/A * /dev/random and /dev/urandom, so it is only available if those
0N/A * files are present. Otherwise, SHA1PRNG is used instead of this class.
0N/A *
0N/A * getSeed() and setSeed() directly read/write /dev/random. However,
0N/A * /dev/random is only writable by root in many configurations. Because
0N/A * we cannot just ignore bytes specified via setSeed(), we keep a
0N/A * SHA1PRNG around in parallel.
0N/A *
0N/A * nextBytes() reads the bytes directly from /dev/urandom (and then
0N/A * mixes them with bytes from the SHA1PRNG for the reasons explained
0N/A * above). Reading bytes from /dev/urandom means that constantly get
0N/A * new entropy the operating system has collected. This is a notable
0N/A * advantage over the SHA1PRNG model, which acquires entropy only
0N/A * initially during startup although the VM may be running for months.
0N/A *
0N/A * Also note that we do not need any initial pure random seed from
0N/A * /dev/random. This is an advantage because on some versions of Linux
0N/A * it can be exhausted very quickly and could thus impact startup time.
0N/A *
0N/A * Finally, note that we use a singleton for the actual work (RandomIO)
0N/A * to avoid having to open and close /dev/[u]random constantly. However,
0N/A * there may me many NativePRNG instances created by the JCA framework.
0N/A *
0N/A * @since 1.5
0N/A * @author Andreas Sterbenz
0N/A */
0N/Apublic final class NativePRNG extends SecureRandomSpi {
0N/A
0N/A private static final long serialVersionUID = -6599091113397072932L;
0N/A
0N/A // name of the pure random file (also used for setSeed())
0N/A private static final String NAME_RANDOM = "/dev/random";
0N/A // name of the pseudo random file
0N/A private static final String NAME_URANDOM = "/dev/urandom";
0N/A
0N/A // singleton instance or null if not available
0N/A private static final RandomIO INSTANCE = initIO();
0N/A
0N/A private static RandomIO initIO() {
28N/A return AccessController.doPrivileged(
28N/A new PrivilegedAction<RandomIO>() {
28N/A public RandomIO run() {
0N/A File randomFile = new File(NAME_RANDOM);
0N/A if (randomFile.exists() == false) {
0N/A return null;
0N/A }
0N/A File urandomFile = new File(NAME_URANDOM);
0N/A if (urandomFile.exists() == false) {
0N/A return null;
0N/A }
0N/A try {
0N/A return new RandomIO(randomFile, urandomFile);
0N/A } catch (Exception e) {
0N/A return null;
0N/A }
0N/A }
0N/A });
0N/A }
0N/A
0N/A // return whether the NativePRNG is available
0N/A static boolean isAvailable() {
0N/A return INSTANCE != null;
0N/A }
0N/A
0N/A // constructor, called by the JCA framework
0N/A public NativePRNG() {
0N/A super();
0N/A if (INSTANCE == null) {
0N/A throw new AssertionError("NativePRNG not available");
0N/A }
0N/A }
0N/A
0N/A // set the seed
0N/A protected void engineSetSeed(byte[] seed) {
0N/A INSTANCE.implSetSeed(seed);
0N/A }
0N/A
0N/A // get pseudo random bytes
0N/A protected void engineNextBytes(byte[] bytes) {
0N/A INSTANCE.implNextBytes(bytes);
0N/A }
0N/A
0N/A // get true random bytes
0N/A protected byte[] engineGenerateSeed(int numBytes) {
0N/A return INSTANCE.implGenerateSeed(numBytes);
0N/A }
0N/A
0N/A /**
0N/A * Nested class doing the actual work. Singleton, see INSTANCE above.
0N/A */
0N/A private static class RandomIO {
0N/A
0N/A // we buffer data we read from /dev/urandom for efficiency,
0N/A // but we limit the lifetime to avoid using stale bits
0N/A // lifetime in ms, currently 100 ms (0.1 s)
0N/A private final static long MAX_BUFFER_TIME = 100;
0N/A
0N/A // size of the /dev/urandom buffer
0N/A private final static int BUFFER_SIZE = 32;
0N/A
0N/A // In/OutputStream for /dev/random and /dev/urandom
0N/A private final InputStream randomIn, urandomIn;
0N/A private OutputStream randomOut;
0N/A
0N/A // flag indicating if we have tried to open randomOut yet
0N/A private boolean randomOutInitialized;
0N/A
0N/A // SHA1PRNG instance for mixing
0N/A // initialized lazily on demand to avoid problems during startup
0N/A private volatile sun.security.provider.SecureRandom mixRandom;
0N/A
0N/A // buffer for /dev/urandom bits
0N/A private final byte[] urandomBuffer;
0N/A
0N/A // number of bytes left in urandomBuffer
0N/A private int buffered;
0N/A
0N/A // time we read the data into the urandomBuffer
0N/A private long lastRead;
0N/A
0N/A // mutex lock for nextBytes()
0N/A private final Object LOCK_GET_BYTES = new Object();
0N/A
0N/A // mutex lock for getSeed()
0N/A private final Object LOCK_GET_SEED = new Object();
0N/A
0N/A // mutex lock for setSeed()
0N/A private final Object LOCK_SET_SEED = new Object();
0N/A
0N/A // constructor, called only once from initIO()
0N/A private RandomIO(File randomFile, File urandomFile) throws IOException {
0N/A randomIn = new FileInputStream(randomFile);
0N/A urandomIn = new FileInputStream(urandomFile);
0N/A urandomBuffer = new byte[BUFFER_SIZE];
0N/A }
0N/A
0N/A // get the SHA1PRNG for mixing
0N/A // initialize if not yet created
0N/A private sun.security.provider.SecureRandom getMixRandom() {
0N/A sun.security.provider.SecureRandom r = mixRandom;
0N/A if (r == null) {
0N/A synchronized (LOCK_GET_BYTES) {
0N/A r = mixRandom;
0N/A if (r == null) {
0N/A r = new sun.security.provider.SecureRandom();
0N/A try {
0N/A byte[] b = new byte[20];
0N/A readFully(urandomIn, b);
0N/A r.engineSetSeed(b);
0N/A } catch (IOException e) {
0N/A throw new ProviderException("init failed", e);
0N/A }
0N/A mixRandom = r;
0N/A }
0N/A }
0N/A }
0N/A return r;
0N/A }
0N/A
0N/A // read data.length bytes from in
0N/A // /dev/[u]random are not normal files, so we need to loop the read.
0N/A // just keep trying as long as we are making progress
0N/A private static void readFully(InputStream in, byte[] data)
0N/A throws IOException {
0N/A int len = data.length;
0N/A int ofs = 0;
0N/A while (len > 0) {
0N/A int k = in.read(data, ofs, len);
0N/A if (k <= 0) {
0N/A throw new EOFException("/dev/[u]random closed?");
0N/A }
0N/A ofs += k;
0N/A len -= k;
0N/A }
0N/A if (len > 0) {
0N/A throw new IOException("Could not read from /dev/[u]random");
0N/A }
0N/A }
0N/A
0N/A // get true random bytes, just read from /dev/random
0N/A private byte[] implGenerateSeed(int numBytes) {
0N/A synchronized (LOCK_GET_SEED) {
0N/A try {
0N/A byte[] b = new byte[numBytes];
0N/A readFully(randomIn, b);
0N/A return b;
0N/A } catch (IOException e) {
0N/A throw new ProviderException("generateSeed() failed", e);
0N/A }
0N/A }
0N/A }
0N/A
0N/A // supply random bytes to the OS
0N/A // write to /dev/random if possible
0N/A // always add the seed to our mixing random
0N/A private void implSetSeed(byte[] seed) {
0N/A synchronized (LOCK_SET_SEED) {
0N/A if (randomOutInitialized == false) {
0N/A randomOutInitialized = true;
0N/A randomOut = AccessController.doPrivileged(
0N/A new PrivilegedAction<OutputStream>() {
0N/A public OutputStream run() {
0N/A try {
0N/A return new FileOutputStream(NAME_RANDOM, true);
0N/A } catch (Exception e) {
0N/A return null;
0N/A }
0N/A }
0N/A });
0N/A }
0N/A if (randomOut != null) {
0N/A try {
0N/A randomOut.write(seed);
0N/A } catch (IOException e) {
0N/A throw new ProviderException("setSeed() failed", e);
0N/A }
0N/A }
0N/A getMixRandom().engineSetSeed(seed);
0N/A }
0N/A }
0N/A
0N/A // ensure that there is at least one valid byte in the buffer
0N/A // if not, read new bytes
0N/A private void ensureBufferValid() throws IOException {
0N/A long time = System.currentTimeMillis();
0N/A if ((buffered > 0) && (time - lastRead < MAX_BUFFER_TIME)) {
0N/A return;
0N/A }
0N/A lastRead = time;
0N/A readFully(urandomIn, urandomBuffer);
0N/A buffered = urandomBuffer.length;
0N/A }
0N/A
0N/A // get pseudo random bytes
0N/A // read from /dev/urandom and XOR with bytes generated by the
0N/A // mixing SHA1PRNG
0N/A private void implNextBytes(byte[] data) {
0N/A synchronized (LOCK_GET_BYTES) {
0N/A try {
0N/A getMixRandom().engineNextBytes(data);
0N/A int len = data.length;
0N/A int ofs = 0;
0N/A while (len > 0) {
0N/A ensureBufferValid();
0N/A int bufferOfs = urandomBuffer.length - buffered;
0N/A while ((len > 0) && (buffered > 0)) {
0N/A data[ofs++] ^= urandomBuffer[bufferOfs++];
0N/A len--;
0N/A buffered--;
0N/A }
0N/A }
0N/A } catch (IOException e) {
0N/A throw new ProviderException("nextBytes() failed", e);
0N/A }
0N/A }
0N/A }
0N/A
0N/A }
0N/A
0N/A}