1 /*
   2  * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.provider;
  27 
  28 /**
  29  * This class generates seeds for the SHA1PRNG cryptographically strong
  30  * random number generator.
  31  * <p>
  32  * The seed is produced using one of two techniques, via a computation
  33  * of current system activity or from an entropy gathering device.
  34  * <p>
  35  * In the default technique the seed is produced by counting the
  36  * number of times the VM manages to loop in a given period. This number
  37  * roughly reflects the machine load at that point in time.
  38  * The samples are translated using a permutation (s-box)
  39  * and then XORed together. This process is non linear and
  40  * should prevent the samples from "averaging out". The s-box
  41  * was designed to have even statistical distribution; it's specific
  42  * values are not crucial for the security of the seed.
  43  * We also create a number of sleeper threads which add entropy
  44  * to the system by keeping the scheduler busy.
  45  * Twenty such samples should give us roughly 160 bits of randomness.
  46  * <p>
  47  * These values are gathered in the background by a daemon thread
  48  * thus allowing the system to continue performing it's different
  49  * activites, which in turn add entropy to the random seed.
  50  * <p>
  51  * The class also gathers miscellaneous system information, some
  52  * machine dependent, some not. This information is then hashed together
  53  * with the 20 seed bytes.
  54  * <p>
  55  * The alternative to the above approach is to acquire seed material
  56  * from an entropy gathering device, such as /dev/random. This can be
  57  * accomplished by setting the value of the {@code securerandom.source}
  58  * Security property to a URL specifying the location of the entropy
  59  * gathering device, or by setting the {@code java.security.egd} System
  60  * property.
  61  * <p>
  62  * In the event the specified URL cannot be accessed the default
  63  * threading mechanism is used.
  64  *
  65  * @author Joshua Bloch
  66  * @author Gadi Guy
  67  */
  68 
  69 import java.security.*;
  70 import java.io.*;
  71 import java.util.Properties;
  72 import java.util.Enumeration;
  73 import java.net.*;
  74 import java.nio.file.DirectoryStream;
  75 import java.nio.file.Files;
  76 import java.nio.file.Path;
  77 import java.util.Random;
  78 import sun.security.util.Debug;
  79 
  80 abstract class SeedGenerator {
  81 
  82     // Static instance is created at link time
  83     private static SeedGenerator instance;
  84 
  85     private static final Debug debug = Debug.getInstance("provider");
  86 
  87     // Static initializer to hook in selected or best performing generator
  88     static {
  89         String egdSource = SunEntries.getSeedSource();
  90 
  91         /*
  92          * Try the URL specifying the source (e.g. file:/dev/random)
  93          *
  94          * The URLs "file:/dev/random" or "file:/dev/urandom" are used to
  95          * indicate the SeedGenerator should use OS support, if available.
  96          *
  97          * On Windows, this causes the MS CryptoAPI seeder to be used.
  98          *
  99          * On Solaris/Linux/MacOS, this is identical to using
 100          * URLSeedGenerator to read from /dev/[u]random
 101          */
 102         if (egdSource.equals(SunEntries.URL_DEV_RANDOM) ||
 103                 egdSource.equals(SunEntries.URL_DEV_URANDOM)) {
 104             try {
 105                 instance = new NativeSeedGenerator(egdSource);
 106                 if (debug != null) {
 107                     debug.println(
 108                         "Using operating system seed generator" + egdSource);
 109                 }
 110             } catch (IOException e) {
 111                 if (debug != null) {
 112                     debug.println("Failed to use operating system seed "
 113                                   + "generator: " + e.toString());
 114                 }
 115             }
 116         } else if (!egdSource.isEmpty()) {
 117             try {
 118                 instance = new URLSeedGenerator(egdSource);
 119                 if (debug != null) {
 120                     debug.println("Using URL seed generator reading from "
 121                                   + egdSource);
 122                 }
 123             } catch (IOException e) {
 124                 if (debug != null) {
 125                     debug.println("Failed to create seed generator with "
 126                                   + egdSource + ": " + e.toString());
 127                 }
 128             }
 129         }
 130 
 131         // Fall back to ThreadedSeedGenerator
 132         if (instance == null) {
 133             if (debug != null) {
 134                 debug.println("Using default threaded seed generator");
 135             }
 136             instance = new ThreadedSeedGenerator();
 137         }
 138     }
 139 
 140     /**
 141      * Fill result with bytes from the queue. Wait for it if it isn't ready.
 142      */
 143     public static void generateSeed(byte[] result) {
 144         instance.getSeedBytes(result);
 145     }
 146 
 147     abstract void getSeedBytes(byte[] result);
 148 
 149     /**
 150      * Retrieve some system information, hashed.
 151      */
 152     static byte[] getSystemEntropy() {
 153         final MessageDigest md;
 154 
 155         try {
 156             md = MessageDigest.getInstance("SHA");
 157         } catch (NoSuchAlgorithmException nsae) {
 158             throw new InternalError("internal error: SHA-1 not available.",
 159                     nsae);
 160         }
 161 
 162         // The current time in millis
 163         byte b =(byte)System.currentTimeMillis();
 164         md.update(b);
 165 
 166         java.security.AccessController.doPrivileged
 167             (new java.security.PrivilegedAction<>() {
 168                 @Override
 169                 public Void run() {
 170                     try {
 171                         // System properties can change from machine to machine
 172                         Properties p = System.getProperties();
 173                         for (String s: p.stringPropertyNames()) {
 174                             md.update(s.getBytes());
 175                             md.update(p.getProperty(s).getBytes());
 176                         }
 177 
 178                         // Include network adapter names (and a Mac address)
 179                         addNetworkAdapterInfo(md);
 180 
 181                         // The temporary dir
 182                         File f = new File(p.getProperty("java.io.tmpdir"));
 183                         int count = 0;
 184                         try (
 185                             DirectoryStream<Path> stream =
 186                                 Files.newDirectoryStream(f.toPath())) {
 187                             // We use a Random object to choose what file names
 188                             // should be used. Otherwise on a machine with too
 189                             // many files, the same first 1024 files always get
 190                             // used. Any, We make sure the first 512 files are
 191                             // always used.
 192                             Random r = new Random();
 193                             for (Path entry: stream) {
 194                                 if (count < 512 || r.nextBoolean()) {
 195                                     md.update(entry.getFileName()
 196                                         .toString().getBytes());
 197                                 }
 198                                 if (count++ > 1024) {
 199                                     break;
 200                                 }
 201                             }
 202                         }
 203                     } catch (Exception ex) {
 204                         md.update((byte)ex.hashCode());
 205                     }
 206 
 207                     // get Runtime memory stats
 208                     Runtime rt = Runtime.getRuntime();
 209                     byte[] memBytes = longToByteArray(rt.totalMemory());
 210                     md.update(memBytes, 0, memBytes.length);
 211                     memBytes = longToByteArray(rt.freeMemory());
 212                     md.update(memBytes, 0, memBytes.length);
 213 
 214                     return null;
 215                 }
 216             });
 217         return md.digest();
 218     }
 219 
 220     /*
 221      * Include network adapter names and, if available, a Mac address
 222      *
 223      * See also java.util.concurrent.ThreadLocalRandom.initialSeed()
 224      */
 225     private static void addNetworkAdapterInfo(MessageDigest md) {
 226 
 227         try {
 228             Enumeration<NetworkInterface> ifcs =
 229                 NetworkInterface.getNetworkInterfaces();
 230             while (ifcs.hasMoreElements()) {
 231                 NetworkInterface ifc = ifcs.nextElement();
 232                 md.update(ifc.toString().getBytes());
 233                 if (!ifc.isVirtual()) { // skip fake addresses
 234                     byte[] bs = ifc.getHardwareAddress();
 235                     if (bs != null) {
 236                         md.update(bs);
 237                         break;
 238                     }
 239                 }
 240             }
 241         } catch (Exception ignore) {
 242         }
 243     }
 244 
 245     /**
 246      * Helper function to convert a long into a byte array (least significant
 247      * byte first).
 248      */
 249     private static byte[] longToByteArray(long l) {
 250         byte[] retVal = new byte[8];
 251 
 252         for (int i=0; i<8; i++) {
 253             retVal[i] = (byte) l;
 254             l >>= 8;
 255         }
 256 
 257         return retVal;
 258     }
 259 
 260     /*
 261     // This method helps the test utility receive unprocessed seed bytes.
 262     public static int genTestSeed() {
 263         return myself.getByte();
 264     }
 265     */
 266 
 267 
 268     private static class ThreadedSeedGenerator extends SeedGenerator
 269             implements Runnable {
 270         // Queue is used to collect seed bytes
 271         private byte[] pool;
 272         private int start, end, count;
 273 
 274         // Thread group for our threads
 275         ThreadGroup seedGroup;
 276 
 277         /**
 278          * The constructor is only called once to construct the one
 279          * instance we actually use. It instantiates the message digest
 280          * and starts the thread going.
 281          */
 282         ThreadedSeedGenerator() {
 283             pool = new byte[20];
 284             start = end = 0;
 285 
 286             MessageDigest digest;
 287 
 288             try {
 289                 digest = MessageDigest.getInstance("SHA");
 290             } catch (NoSuchAlgorithmException e) {
 291                 throw new InternalError("internal error: SHA-1 not available."
 292                         , e);
 293             }
 294 
 295             final ThreadGroup[] finalsg = new ThreadGroup[1];
 296             Thread t = java.security.AccessController.doPrivileged
 297                 (new java.security.PrivilegedAction<>() {
 298                         @Override
 299                         public Thread run() {
 300                             ThreadGroup parent, group =
 301                                 Thread.currentThread().getThreadGroup();
 302                             while ((parent = group.getParent()) != null) {
 303                                 group = parent;
 304                             }
 305                             finalsg[0] = new ThreadGroup
 306                                 (group, "SeedGenerator ThreadGroup");
 307                             Thread newT = new Thread(finalsg[0],
 308                                 ThreadedSeedGenerator.this,
 309                                 "SeedGenerator Thread",
 310                                 0,
 311                                 false);
 312                             newT.setPriority(Thread.MIN_PRIORITY);
 313                             newT.setDaemon(true);
 314                             return newT;
 315                         }
 316                     });
 317             seedGroup = finalsg[0];
 318             t.start();
 319         }
 320 
 321         /**
 322          * This method does the actual work. It collects random bytes and
 323          * pushes them into the queue.
 324          */
 325         @Override
 326         public final void run() {
 327             try {
 328                 while (true) {
 329                     // Queue full? Wait till there's room.
 330                     synchronized(this) {
 331                         while (count >= pool.length) {
 332                             wait();
 333                         }
 334                     }
 335 
 336                     int counter, quanta;
 337                     byte v = 0;
 338 
 339                     // Spin count must not be under 64000
 340                     for (counter = quanta = 0;
 341                             (counter < 64000) && (quanta < 6); quanta++) {
 342 
 343                         // Start some noisy threads
 344                         try {
 345                             BogusThread bt = new BogusThread();
 346                             Thread t = new Thread
 347                                 (seedGroup, bt, "SeedGenerator Thread", 0,
 348                                         false);
 349                             t.start();
 350                         } catch (Exception e) {
 351                             throw new InternalError("internal error: " +
 352                                 "SeedGenerator thread creation error.", e);
 353                         }
 354 
 355                         // We wait 250milli quanta, so the minimum wait time
 356                         // cannot be under 250milli.
 357                         int latch = 0;
 358                         long startTime = System.nanoTime();
 359                         while (System.nanoTime() - startTime < 250000000) {
 360                             synchronized(this){};
 361                             // Mask the sign bit and keep latch non-negative
 362                             latch = (latch + 1) & 0x1FFFFFFF;
 363                         }
 364 
 365                         // Translate the value using the permutation, and xor
 366                         // it with previous values gathered.
 367                         v ^= rndTab[latch % 255];
 368                         counter += latch;
 369                     }
 370 
 371                     // Push it into the queue and notify anybody who might
 372                     // be waiting for it.
 373                     synchronized(this) {
 374                         pool[end] = v;
 375                         end++;
 376                         count++;
 377                         if (end >= pool.length) {
 378                             end = 0;
 379                         }
 380 
 381                         notifyAll();
 382                     }
 383                 }
 384             } catch (Exception e) {
 385                 throw new InternalError("internal error: " +
 386                     "SeedGenerator thread generated an exception.", e);
 387             }
 388         }
 389 
 390         @Override
 391         void getSeedBytes(byte[] result) {
 392             for (int i = 0; i < result.length; i++) {
 393                 result[i] = getSeedByte();
 394             }
 395         }
 396 
 397         byte getSeedByte() {
 398             byte b;
 399 
 400             try {
 401                 // Wait for it...
 402                 synchronized(this) {
 403                     while (count <= 0) {
 404                         wait();
 405                     }
 406                 }
 407             } catch (Exception e) {
 408                 if (count <= 0) {
 409                     throw new InternalError("internal error: " +
 410                         "SeedGenerator thread generated an exception.", e);
 411                 }
 412             }
 413 
 414             synchronized(this) {
 415                 // Get it from the queue
 416                 b = pool[start];
 417                 pool[start] = 0;
 418                 start++;
 419                 count--;
 420                 if (start == pool.length) {
 421                     start = 0;
 422                 }
 423 
 424                 // Notify the daemon thread, just in case it is
 425                 // waiting for us to make room in the queue.
 426                 notifyAll();
 427             }
 428 
 429             return b;
 430         }
 431 
 432         // The permutation was calculated by generating 64k of random
 433         // data and using it to mix the trivial permutation.
 434         // It should be evenly distributed. The specific values
 435         // are not crucial to the security of this class.
 436         private static final byte[] rndTab = {
 437             56, 30, -107, -6, -86, 25, -83, 75, -12, -64,
 438             5, -128, 78, 21, 16, 32, 70, -81, 37, -51,
 439             -43, -46, -108, 87, 29, 17, -55, 22, -11, -111,
 440             -115, 84, -100, 108, -45, -15, -98, 72, -33, -28,
 441             31, -52, -37, -117, -97, -27, 93, -123, 47, 126,
 442             -80, -62, -93, -79, 61, -96, -65, -5, -47, -119,
 443             14, 89, 81, -118, -88, 20, 67, -126, -113, 60,
 444             -102, 55, 110, 28, 85, 121, 122, -58, 2, 45,
 445             43, 24, -9, 103, -13, 102, -68, -54, -101, -104,
 446             19, 13, -39, -26, -103, 62, 77, 51, 44, 111,
 447             73, 18, -127, -82, 4, -30, 11, -99, -74, 40,
 448             -89, 42, -76, -77, -94, -35, -69, 35, 120, 76,
 449             33, -73, -7, 82, -25, -10, 88, 125, -112, 58,
 450             83, 95, 6, 10, 98, -34, 80, 15, -91, 86,
 451             -19, 52, -17, 117, 49, -63, 118, -90, 36, -116,
 452             -40, -71, 97, -53, -109, -85, 109, -16, -3, 104,
 453             -95, 68, 54, 34, 26, 114, -1, 106, -121, 3,
 454             66, 0, 100, -84, 57, 107, 119, -42, 112, -61,
 455             1, 48, 38, 12, -56, -57, 39, -106, -72, 41,
 456             7, 71, -29, -59, -8, -38, 79, -31, 124, -124,
 457             8, 91, 116, 99, -4, 9, -36, -78, 63, -49,
 458             -67, -87, 59, 101, -32, 92, 94, 53, -41, 115,
 459             -66, -70, -122, 50, -50, -22, -20, -18, -21, 23,
 460             -2, -48, 96, 65, -105, 123, -14, -110, 69, -24,
 461             -120, -75, 74, 127, -60, 113, 90, -114, 105, 46,
 462             27, -125, -23, -44, 64
 463         };
 464 
 465         /**
 466          * This inner thread causes the thread scheduler to become 'noisy',
 467          * thus adding entropy to the system load.
 468          * At least one instance of this class is generated for every seed byte.
 469          */
 470         private static class BogusThread implements Runnable {
 471             @Override
 472             public final void run() {
 473                 try {
 474                     for (int i = 0; i < 5; i++) {
 475                         Thread.sleep(50);
 476                     }
 477                     // System.gc();
 478                 } catch (Exception e) {
 479                 }
 480             }
 481         }
 482     }
 483 
 484     static class URLSeedGenerator extends SeedGenerator {
 485 
 486         private String deviceName;
 487         private InputStream seedStream;
 488 
 489         /**
 490          * The constructor is only called once to construct the one
 491          * instance we actually use. It opens the entropy gathering device
 492          * which will supply the randomness.
 493          */
 494 
 495         URLSeedGenerator(String egdurl) throws IOException {
 496         if (egdurl == null) {
 497                 throw new IOException("No random source specified");
 498             }
 499             deviceName = egdurl;
 500             init();
 501         }
 502 
 503         private void init() throws IOException {
 504             final URL device = new URL(deviceName);
 505             try {
 506                 seedStream = java.security.AccessController.doPrivileged
 507                     (new java.security.PrivilegedExceptionAction<>() {
 508                         @Override
 509                         public InputStream run() throws IOException {
 510                             /*
 511                              * return a shared InputStream for file URLs and
 512                              * avoid buffering.
 513                              * The URL.openStream() call wraps InputStream in a
 514                              * BufferedInputStream which
 515                              * can buffer up to 8K bytes. This read is a
 516                              * performance issue for entropy sources which
 517                              * can be slow to replenish.
 518                              */
 519                             if (device.getProtocol().equalsIgnoreCase("file")) {
 520                                 File deviceFile =
 521                                     SunEntries.getDeviceFile(device);
 522                                 return FileInputStreamPool
 523                                     .getInputStream(deviceFile);
 524                             } else {
 525                                 return device.openStream();
 526                             }
 527                         }
 528                     });
 529             } catch (Exception e) {
 530                 throw new IOException(
 531                     "Failed to open " + deviceName, e.getCause());
 532             }
 533         }
 534 
 535         @Override
 536         void getSeedBytes(byte[] result) {
 537             int len = result.length;
 538             int read = 0;
 539             try {
 540                 while (read < len) {
 541                     int count = seedStream.read(result, read, len - read);
 542                     // /dev/random blocks - should never have EOF
 543                     if (count < 0) {
 544                         throw new InternalError(
 545                             "URLSeedGenerator " + deviceName +
 546                             " reached end of file");
 547                     }
 548                     read += count;
 549                 }
 550             } catch (IOException ioe) {
 551                 throw new InternalError("URLSeedGenerator " + deviceName +
 552                     " generated exception: " + ioe.getMessage(), ioe);
 553             }
 554         }
 555     }
 556 }