1 /* 2 * Copyright (c) 1996, 2015, 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.length() != 0) { 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, false); 348 t.start(); 349 } catch (Exception e) { 350 throw new InternalError("internal error: " + 351 "SeedGenerator thread creation error.", e); 352 } 353 354 // We wait 250milli quanta, so the minimum wait time 355 // cannot be under 250milli. 356 int latch = 0; 357 long l = System.currentTimeMillis() + 250; 358 while (System.currentTimeMillis() < l) { 359 synchronized(this){}; 360 latch++; 361 } 362 363 // Translate the value using the permutation, and xor 364 // it with previous values gathered. 365 v ^= rndTab[latch % 255]; 366 counter += latch; 367 } 368 369 // Push it into the queue and notify anybody who might 370 // be waiting for it. 371 synchronized(this) { 372 pool[end] = v; 373 end++; 374 count++; 375 if (end >= pool.length) { 376 end = 0; 377 } 378 379 notifyAll(); 380 } 381 } 382 } catch (Exception e) { 383 throw new InternalError("internal error: " + 384 "SeedGenerator thread generated an exception.", e); 385 } 386 } 387 388 @Override 389 void getSeedBytes(byte[] result) { 390 for (int i = 0; i < result.length; i++) { 391 result[i] = getSeedByte(); 392 } 393 } 394 395 byte getSeedByte() { 396 byte b; 397 398 try { 399 // Wait for it... 400 synchronized(this) { 401 while (count <= 0) { 402 wait(); 403 } 404 } 405 } catch (Exception e) { 406 if (count <= 0) { 407 throw new InternalError("internal error: " + 408 "SeedGenerator thread generated an exception.", e); 409 } 410 } 411 412 synchronized(this) { 413 // Get it from the queue 414 b = pool[start]; 415 pool[start] = 0; 416 start++; 417 count--; 418 if (start == pool.length) { 419 start = 0; 420 } 421 422 // Notify the daemon thread, just in case it is 423 // waiting for us to make room in the queue. 424 notifyAll(); 425 } 426 427 return b; 428 } 429 430 // The permutation was calculated by generating 64k of random 431 // data and using it to mix the trivial permutation. 432 // It should be evenly distributed. The specific values 433 // are not crucial to the security of this class. 434 private static byte[] rndTab = { 435 56, 30, -107, -6, -86, 25, -83, 75, -12, -64, 436 5, -128, 78, 21, 16, 32, 70, -81, 37, -51, 437 -43, -46, -108, 87, 29, 17, -55, 22, -11, -111, 438 -115, 84, -100, 108, -45, -15, -98, 72, -33, -28, 439 31, -52, -37, -117, -97, -27, 93, -123, 47, 126, 440 -80, -62, -93, -79, 61, -96, -65, -5, -47, -119, 441 14, 89, 81, -118, -88, 20, 67, -126, -113, 60, 442 -102, 55, 110, 28, 85, 121, 122, -58, 2, 45, 443 43, 24, -9, 103, -13, 102, -68, -54, -101, -104, 444 19, 13, -39, -26, -103, 62, 77, 51, 44, 111, 445 73, 18, -127, -82, 4, -30, 11, -99, -74, 40, 446 -89, 42, -76, -77, -94, -35, -69, 35, 120, 76, 447 33, -73, -7, 82, -25, -10, 88, 125, -112, 58, 448 83, 95, 6, 10, 98, -34, 80, 15, -91, 86, 449 -19, 52, -17, 117, 49, -63, 118, -90, 36, -116, 450 -40, -71, 97, -53, -109, -85, 109, -16, -3, 104, 451 -95, 68, 54, 34, 26, 114, -1, 106, -121, 3, 452 66, 0, 100, -84, 57, 107, 119, -42, 112, -61, 453 1, 48, 38, 12, -56, -57, 39, -106, -72, 41, 454 7, 71, -29, -59, -8, -38, 79, -31, 124, -124, 455 8, 91, 116, 99, -4, 9, -36, -78, 63, -49, 456 -67, -87, 59, 101, -32, 92, 94, 53, -41, 115, 457 -66, -70, -122, 50, -50, -22, -20, -18, -21, 23, 458 -2, -48, 96, 65, -105, 123, -14, -110, 69, -24, 459 -120, -75, 74, 127, -60, 113, 90, -114, 105, 46, 460 27, -125, -23, -44, 64 461 }; 462 463 /** 464 * This inner thread causes the thread scheduler to become 'noisy', 465 * thus adding entropy to the system load. 466 * At least one instance of this class is generated for every seed byte. 467 */ 468 private static class BogusThread implements Runnable { 469 @Override 470 public final void run() { 471 try { 472 for (int i = 0; i < 5; i++) { 473 Thread.sleep(50); 474 } 475 // System.gc(); 476 } catch (Exception e) { 477 } 478 } 479 } 480 } 481 482 static class URLSeedGenerator extends SeedGenerator { 483 484 private String deviceName; 485 private InputStream seedStream; 486 487 /** 488 * The constructor is only called once to construct the one 489 * instance we actually use. It opens the entropy gathering device 490 * which will supply the randomness. 491 */ 492 493 URLSeedGenerator(String egdurl) throws IOException { 494 if (egdurl == null) { 495 throw new IOException("No random source specified"); 496 } 497 deviceName = egdurl; 498 init(); 499 } 500 501 private void init() throws IOException { 502 final URL device = new URL(deviceName); 503 try { 504 seedStream = java.security.AccessController.doPrivileged 505 (new java.security.PrivilegedExceptionAction<>() { 506 @Override 507 public InputStream run() throws IOException { 508 /* 509 * return a shared InputStream for file URLs and 510 * avoid buffering. 511 * The URL.openStream() call wraps InputStream in a 512 * BufferedInputStream which 513 * can buffer up to 8K bytes. This read is a 514 * performance issue for entropy sources which 515 * can be slow to replenish. 516 */ 517 if (device.getProtocol().equalsIgnoreCase("file")) { 518 File deviceFile = 519 SunEntries.getDeviceFile(device); 520 return FileInputStreamPool 521 .getInputStream(deviceFile); 522 } else { 523 return device.openStream(); 524 } 525 } 526 }); 527 } catch (Exception e) { 528 throw new IOException( 529 "Failed to open " + deviceName, e.getCause()); 530 } 531 } 532 533 @Override 534 void getSeedBytes(byte[] result) { 535 int len = result.length; 536 int read = 0; 537 try { 538 while (read < len) { 539 int count = seedStream.read(result, read, len - read); 540 // /dev/random blocks - should never have EOF 541 if (count < 0) { 542 throw new InternalError( 543 "URLSeedGenerator " + deviceName + 544 " reached end of file"); 545 } 546 read += count; 547 } 548 } catch (IOException ioe) { 549 throw new InternalError("URLSeedGenerator " + deviceName + 550 " generated exception: " + ioe.getMessage(), ioe); 551 } 552 } 553 } 554 } --- EOF ---