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