| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * Copyright (c) 1996, 2011, 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; |
| |
| /** |
| * <P> This class generates seeds for the cryptographically strong random |
| * number generator. |
| * <P> The seed is produced using one of two techniques, via a computation |
| * of current system activity or from an entropy gathering device. |
| * <p> In the default technique the seed is produced by counting the |
| * number of times the VM manages to loop in a given period. This number |
| * roughly reflects the machine load at that point in time. |
| * The samples are translated using a permutation (s-box) |
| * and then XORed together. This process is non linear and |
| * should prevent the samples from "averaging out". The s-box |
| * was designed to have even statistical distribution; it's specific |
| * values are not crucial for the security of the seed. |
| * We also create a number of sleeper threads which add entropy |
| * to the system by keeping the scheduler busy. |
| * Twenty such samples should give us roughly 160 bits of randomness. |
| * <P> These values are gathered in the background by a daemon thread |
| * thus allowing the system to continue performing it's different |
| * activites, which in turn add entropy to the random seed. |
| * <p> The class also gathers miscellaneous system information, some |
| * machine dependent, some not. This information is then hashed together |
| * with the 20 seed bytes. |
| * <P> The alternative to the above approach is to acquire seed material |
| * from an entropy gathering device, such as /dev/random. This can be |
| * accomplished by setting the value of the "securerandom.source" |
| * security property (in the Java security properties file) to a URL |
| * specifying the location of the entropy gathering device. |
| * In the event the specified URL cannot be accessed the default |
| * mechanism is used. |
| * The Java security properties file is located in the file named |
| * <JAVA_HOME>/lib/security/java.security. |
| * <JAVA_HOME> refers to the value of the java.home system property, |
| * and specifies the directory where the JRE is installed. |
| * |
| * @author Joshua Bloch |
| * @author Gadi Guy |
| */ |
| |
| import java.security.*; |
| import java.io.*; |
| import java.util.Properties; |
| import java.util.Enumeration; |
| import java.net.*; |
| /* ----- BEGIN android ----- |
| import java.nio.file.DirectoryStream; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| ----- END android ----- */ |
| import java.util.Random; |
| import sun.security.util.Debug; |
| |
| abstract class SeedGenerator { |
| |
| // Static instance is created at link time |
| private static SeedGenerator instance; |
| |
| private static final Debug debug = Debug.getInstance("provider"); |
| |
| final static String URL_DEV_RANDOM = SunEntries.URL_DEV_RANDOM; |
| final static String URL_DEV_URANDOM = SunEntries.URL_DEV_URANDOM; |
| |
| // Static initializer to hook in selected or best performing generator |
| static { |
| String egdSource = SunEntries.getSeedSource(); |
| |
| // Try the URL specifying the source |
| // e.g. file:/dev/random |
| // |
| // The URL file:/dev/random or file:/dev/urandom is used to indicate |
| // the SeedGenerator using OS support, if available. |
| // On Windows, the causes MS CryptoAPI to be used. |
| // On Solaris and Linux, this is the identical to using |
| // URLSeedGenerator to read from /dev/random |
| |
| if (egdSource.equals(URL_DEV_RANDOM) || egdSource.equals(URL_DEV_URANDOM)) { |
| try { |
| instance = new NativeSeedGenerator(); |
| if (debug != null) { |
| debug.println("Using operating system seed generator"); |
| } |
| } catch (IOException e) { |
| if (debug != null) { |
| debug.println("Failed to use operating system seed " |
| + "generator: " + e.toString()); |
| } |
| } |
| } else if (egdSource.length() != 0) { |
| try { |
| instance = new URLSeedGenerator(egdSource); |
| if (debug != null) { |
| debug.println("Using URL seed generator reading from " |
| + egdSource); |
| } |
| } catch (IOException e) { |
| if (debug != null) |
| debug.println("Failed to create seed generator with " |
| + egdSource + ": " + e.toString()); |
| } |
| } |
| |
| // Fall back to ThreadedSeedGenerator |
| if (instance == null) { |
| if (debug != null) { |
| debug.println("Using default threaded seed generator"); |
| } |
| instance = new ThreadedSeedGenerator(); |
| } |
| } |
| |
| /** |
| * Fill result with bytes from the queue. Wait for it if it isn't ready. |
| */ |
| static public void generateSeed(byte[] result) { |
| instance.getSeedBytes(result); |
| } |
| |
| abstract void getSeedBytes(byte[] result); |
| |
| /** |
| * Retrieve some system information, hashed. |
| */ |
| static byte[] getSystemEntropy() { |
| byte[] ba; |
| final MessageDigest md; |
| |
| try { |
| md = MessageDigest.getInstance("SHA"); |
| } catch (NoSuchAlgorithmException nsae) { |
| throw new InternalError("internal error: SHA-1 not available."); |
| } |
| |
| // The current time in millis |
| byte b =(byte)System.currentTimeMillis(); |
| md.update(b); |
| |
| java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedAction<Void>() { |
| public Void run() { |
| |
| try { |
| // System properties can change from machine to machine |
| String s; |
| Properties p = System.getProperties(); |
| Enumeration<?> e = p.propertyNames(); |
| while (e.hasMoreElements()) { |
| s =(String)e.nextElement(); |
| md.update(s.getBytes()); |
| md.update(p.getProperty(s).getBytes()); |
| } |
| |
| md.update |
| (InetAddress.getLocalHost().toString().getBytes()); |
| |
| // The temporary dir |
| File f = new File(p.getProperty("java.io.tmpdir")); |
| int count = 0; |
| /* ----- BEGIN android ----- |
| try (DirectoryStream<Path> stream = Files.newDirectoryStream(f.toPath())) { |
| // We use a Random object to choose what file names |
| // should be used. Otherwise on a machine with too |
| // many files, the same first 1024 files always get |
| // used. Any, We make sure the first 512 files are |
| // always used. |
| Random r = new Random(); |
| for (Path entry: stream) { |
| if (count < 512 || r.nextBoolean()) { |
| md.update(entry.getFileName().toString().getBytes()); |
| } |
| if (count++ > 1024) { |
| break; |
| } |
| } |
| } |
| ----- END android ----- */ |
| String[] sa = f.list(); |
| Random r = new Random(); |
| for(int i = 0; i < sa.length; i++) { |
| if (count < 512 || r.nextBoolean()) { |
| md.update(sa[i].getBytes()); |
| } |
| if (count++ > 1024) { |
| break; |
| } |
| } |
| } catch (Exception ex) { |
| md.update((byte)ex.hashCode()); |
| } |
| |
| // get Runtime memory stats |
| Runtime rt = Runtime.getRuntime(); |
| byte[] memBytes = longToByteArray(rt.totalMemory()); |
| md.update(memBytes, 0, memBytes.length); |
| memBytes = longToByteArray(rt.freeMemory()); |
| md.update(memBytes, 0, memBytes.length); |
| |
| return null; |
| } |
| }); |
| return md.digest(); |
| } |
| |
| /** |
| * Helper function to convert a long into a byte array (least significant |
| * byte first). |
| */ |
| private static byte[] longToByteArray(long l) { |
| byte[] retVal = new byte[8]; |
| |
| for (int i=0; i<8; i++) { |
| retVal[i] = (byte) l; |
| l >>= 8; |
| } |
| |
| return retVal; |
| } |
| |
| /* |
| // This method helps the test utility receive unprocessed seed bytes. |
| public static int genTestSeed() { |
| return myself.getByte(); |
| } |
| */ |
| |
| |
| private static class ThreadedSeedGenerator extends SeedGenerator implements Runnable { |
| // Queue is used to collect seed bytes |
| private byte[] pool; |
| private int start, end, count; |
| |
| // Thread group for our threads |
| ThreadGroup seedGroup; |
| |
| /** |
| * The constructor is only called once to construct the one |
| * instance we actually use. It instantiates the message digest |
| * and starts the thread going. |
| */ |
| |
| ThreadedSeedGenerator() { |
| pool = new byte[20]; |
| start = end = 0; |
| |
| MessageDigest digest; |
| |
| try { |
| digest = MessageDigest.getInstance("SHA"); |
| } catch (NoSuchAlgorithmException e) { |
| throw new InternalError("internal error: SHA-1 not available."); |
| } |
| |
| final ThreadGroup[] finalsg = new ThreadGroup[1]; |
| Thread t = java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedAction<Thread>() { |
| public Thread run() { |
| ThreadGroup parent, group = |
| Thread.currentThread().getThreadGroup(); |
| while ((parent = group.getParent()) != null) |
| group = parent; |
| finalsg[0] = new ThreadGroup |
| (group, "SeedGenerator ThreadGroup"); |
| Thread newT = new Thread(finalsg[0], |
| ThreadedSeedGenerator.this, |
| "SeedGenerator Thread"); |
| newT.setPriority(Thread.MIN_PRIORITY); |
| newT.setDaemon(true); |
| return newT; |
| } |
| }); |
| seedGroup = finalsg[0]; |
| t.start(); |
| } |
| |
| /** |
| * This method does the actual work. It collects random bytes and |
| * pushes them into the queue. |
| */ |
| final public void run() { |
| try { |
| while (true) { |
| // Queue full? Wait till there's room. |
| synchronized(this) { |
| while (count >= pool.length) |
| wait(); |
| } |
| |
| int counter, quanta; |
| byte v = 0; |
| |
| // Spin count must not be under 64000 |
| for (counter = quanta = 0; (counter < 64000) && (quanta < 6); |
| quanta++) { |
| |
| // Start some noisy threads |
| try { |
| BogusThread bt = new BogusThread(); |
| Thread t = new Thread |
| (seedGroup, bt, "SeedGenerator Thread"); |
| t.start(); |
| } catch (Exception e) { |
| throw new InternalError("internal error: " + |
| "SeedGenerator thread creation error."); |
| } |
| |
| // We wait 250milli quanta, so the minimum wait time |
| // cannot be under 250milli. |
| int latch = 0; |
| latch = 0; |
| long l = System.currentTimeMillis() + 250; |
| while (System.currentTimeMillis() < l) { |
| synchronized(this){}; |
| latch++; |
| } |
| |
| // Translate the value using the permutation, and xor |
| // it with previous values gathered. |
| v ^= rndTab[latch % 255]; |
| counter += latch; |
| } |
| |
| // Push it into the queue and notify anybody who might |
| // be waiting for it. |
| synchronized(this) { |
| pool[end] = v; |
| end++; |
| count++; |
| if (end >= pool.length) |
| end = 0; |
| |
| notifyAll(); |
| } |
| } |
| } catch (Exception e) { |
| throw new InternalError("internal error: " + |
| "SeedGenerator thread generated an exception."); |
| } |
| } |
| |
| @Override |
| void getSeedBytes(byte[] result) { |
| for (int i = 0; i < result.length; i++) { |
| result[i] = getSeedByte(); |
| } |
| } |
| |
| byte getSeedByte() { |
| byte b = 0; |
| |
| try { |
| // Wait for it... |
| synchronized(this) { |
| while (count <= 0) |
| wait(); |
| } |
| } catch (Exception e) { |
| if (count <= 0) |
| throw new InternalError("internal error: " + |
| "SeedGenerator thread generated an exception."); |
| } |
| |
| synchronized(this) { |
| // Get it from the queue |
| b = pool[start]; |
| pool[start] = 0; |
| start++; |
| count--; |
| if (start == pool.length) |
| start = 0; |
| |
| // Notify the daemon thread, just in case it is |
| // waiting for us to make room in the queue. |
| notifyAll(); |
| } |
| |
| return b; |
| } |
| |
| // The permutation was calculated by generating 64k of random |
| // data and using it to mix the trivial permutation. |
| // It should be evenly distributed. The specific values |
| // are not crucial to the security of this class. |
| private static byte[] rndTab = { |
| 56, 30, -107, -6, -86, 25, -83, 75, -12, -64, |
| 5, -128, 78, 21, 16, 32, 70, -81, 37, -51, |
| -43, -46, -108, 87, 29, 17, -55, 22, -11, -111, |
| -115, 84, -100, 108, -45, -15, -98, 72, -33, -28, |
| 31, -52, -37, -117, -97, -27, 93, -123, 47, 126, |
| -80, -62, -93, -79, 61, -96, -65, -5, -47, -119, |
| 14, 89, 81, -118, -88, 20, 67, -126, -113, 60, |
| -102, 55, 110, 28, 85, 121, 122, -58, 2, 45, |
| 43, 24, -9, 103, -13, 102, -68, -54, -101, -104, |
| 19, 13, -39, -26, -103, 62, 77, 51, 44, 111, |
| 73, 18, -127, -82, 4, -30, 11, -99, -74, 40, |
| -89, 42, -76, -77, -94, -35, -69, 35, 120, 76, |
| 33, -73, -7, 82, -25, -10, 88, 125, -112, 58, |
| 83, 95, 6, 10, 98, -34, 80, 15, -91, 86, |
| -19, 52, -17, 117, 49, -63, 118, -90, 36, -116, |
| -40, -71, 97, -53, -109, -85, 109, -16, -3, 104, |
| -95, 68, 54, 34, 26, 114, -1, 106, -121, 3, |
| 66, 0, 100, -84, 57, 107, 119, -42, 112, -61, |
| 1, 48, 38, 12, -56, -57, 39, -106, -72, 41, |
| 7, 71, -29, -59, -8, -38, 79, -31, 124, -124, |
| 8, 91, 116, 99, -4, 9, -36, -78, 63, -49, |
| -67, -87, 59, 101, -32, 92, 94, 53, -41, 115, |
| -66, -70, -122, 50, -50, -22, -20, -18, -21, 23, |
| -2, -48, 96, 65, -105, 123, -14, -110, 69, -24, |
| -120, -75, 74, 127, -60, 113, 90, -114, 105, 46, |
| 27, -125, -23, -44, 64 |
| }; |
| |
| /** |
| * This inner thread causes the thread scheduler to become 'noisy', |
| * thus adding entropy to the system load. |
| * At least one instance of this class is generated for every seed byte. |
| */ |
| |
| private static class BogusThread implements Runnable { |
| final public void run() { |
| try { |
| for(int i = 0; i < 5; i++) |
| Thread.sleep(50); |
| // System.gc(); |
| } catch (Exception e) { |
| } |
| } |
| } |
| } |
| |
| static class URLSeedGenerator extends SeedGenerator { |
| |
| private String deviceName; |
| private InputStream devRandom; |
| |
| /** |
| * The constructor is only called once to construct the one |
| * instance we actually use. It opens the entropy gathering device |
| * which will supply the randomness. |
| */ |
| |
| URLSeedGenerator(String egdurl) throws IOException { |
| if (egdurl == null) { |
| throw new IOException("No random source specified"); |
| } |
| deviceName = egdurl; |
| init(); |
| } |
| |
| URLSeedGenerator() throws IOException { |
| this(SeedGenerator.URL_DEV_RANDOM); |
| } |
| |
| private void init() throws IOException { |
| final URL device = new URL(deviceName); |
| try { |
| devRandom = java.security.AccessController.doPrivileged |
| (new java.security.PrivilegedExceptionAction<InputStream>() { |
| public InputStream run() throws IOException { |
| /* |
| * return a FileInputStream for file URLs and |
| * avoid buffering. The openStream() call wraps |
| * InputStream in a BufferedInputStream which |
| * can buffer up to 8K bytes. This read is a |
| * performance issue for entropy sources which |
| * can be slow to replenish. |
| */ |
| if (device.getProtocol().equalsIgnoreCase("file")) { |
| File deviceFile = getDeviceFile(device); |
| return new FileInputStream(deviceFile); |
| } else { |
| return device.openStream(); |
| } |
| } |
| }); |
| } catch (Exception e) { |
| throw new IOException("Failed to open " + deviceName, e.getCause()); |
| } |
| } |
| |
| /* |
| * Use a URI to access this File. Previous code used a URL |
| * which is less strict on syntax. If we encounter a |
| * URISyntaxException we make best efforts for backwards |
| * compatibility. e.g. space character in deviceName string. |
| * |
| * Method called within PrivilegedExceptionAction block. |
| */ |
| private File getDeviceFile(URL device) throws IOException { |
| try { |
| URI deviceURI = device.toURI(); |
| if(deviceURI.isOpaque()) { |
| // File constructor does not accept opaque URI |
| URI localDir = new File(System.getProperty("user.dir")).toURI(); |
| String uriPath = localDir.toString() + |
| deviceURI.toString().substring(5); |
| return new File(URI.create(uriPath)); |
| } else { |
| return new File(deviceURI); |
| } |
| } catch (URISyntaxException use) { |
| /* |
| * Make best effort to access this File. |
| * We can try using the URL path. |
| */ |
| return new File(device.getPath()); |
| } |
| } |
| |
| @Override |
| void getSeedBytes(byte[] result) { |
| int len = result.length; |
| int read = 0; |
| try { |
| while (read < len) { |
| int count = devRandom.read(result, read, len - read); |
| // /dev/random blocks - should never have EOF |
| if (count < 0) |
| throw new InternalError("URLSeedGenerator " + deviceName + |
| " reached end of file"); |
| read += count; |
| } |
| } catch (IOException ioe) { |
| throw new InternalError("URLSeedGenerator " + deviceName + |
| " generated exception: " + |
| ioe.getMessage()); |
| } |
| } |
| |
| } |
| } |