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