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 }