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