001: /*
002: * @(#)SeedGenerator.java 1.32 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.provider;
029:
030: /**
031: * <P> This class generates seeds for the cryptographically strong random
032: * number generator.
033: * <P> The seed is produced using one of two techniques, via a computation
034: * of current system activity or from an entropy gathering device.
035: * <p> In the default technique the seed is produced by counting the
036: * number of times the VM manages to loop in a given period. This number
037: * roughly reflects the machine load at that point in time.
038: * The samples are translated using a permutation (s-box)
039: * and then XORed together. This process is non linear and
040: * should prevent the samples from "averaging out". The s-box
041: * was designed to have even statistical distribution; it's specific
042: * values are not crucial for the security of the seed.
043: * We also create a number of sleeper threads which add entropy
044: * to the system by keeping the scheduler busy.
045: * Twenty such samples should give us roughly 160 bits of randomness.
046: * <P> These values are gathered in the background by a daemon thread
047: * thus allowing the system to continue performing it's different
048: * activites, which in turn add entropy to the random seed.
049: * <p> The class also gathers miscellaneous system information, some
050: * machine dependent, some not. This information is then hashed together
051: * with the 20 seed bytes.
052: * <P> The alternative to the above approach is to acquire seed material
053: * from an entropy gathering device, such as /dev/random. This can be
054: * accomplished by setting the value of the "securerandom.source"
055: * security property (in the Java security properties file) to a URL
056: * specifying the location of the entropy gathering device.
057: * In the event the specified URL cannot be accessed the default
058: * mechanism is used.
059: * The Java security properties file is located in the file named
060: * <JAVA_HOME>/lib/security/java.security, where <JAVA_HOME>
061: * refers to the directory where the SDK was installed.
062: *
063: * @version 1.22, 05/17/00
064: * @author Joshua Bloch
065: * @author Gadi Guy
066: */
067:
068: import java.security.*;
069: import java.io.*;
070: import java.util.Properties;
071: import java.util.Enumeration;
072: import java.net.*;
073: import sun.security.util.Debug;
074:
075: abstract class SeedGenerator {
076:
077: // Static instance is created at link time
078: private static SeedGenerator instance;
079:
080: private static final Debug debug = Debug.getInstance("provider");
081:
082: private final static String PROP_EGD = "java.security.egd";
083: private final static String PROP_RNDSOURCE = "securerandom.source";
084:
085: final static String URL_DEV_RANDOM = "file:/dev/random";
086:
087: // Static initializer to hook in selected or best performing generator
088: static {
089: String egdSource = (String) java.security.AccessController
090: .doPrivileged(new java.security.PrivilegedAction() {
091: public Object run() {
092: String egdSource = System.getProperty(PROP_EGD,
093: "");
094: if (egdSource.length() != 0) {
095: return egdSource;
096: }
097: egdSource = Security
098: .getProperty(PROP_RNDSOURCE);
099: if (egdSource == null) {
100: return "";
101: }
102: return egdSource;
103: }
104: });
105:
106: // Try the URL specifying the source
107: // e.g. file:/dev/random
108: //
109: // The URL file:/dev/random is used to indicate the SeedGenerator
110: // using OS support, if available.
111: // On Windows, the causes MS CryptoAPI to be used.
112: // On Solaris, this is the identical to using
113: // URLSeedGenerator to read from /dev/random. For Linux,
114: // CDC/FP has implemented a fix to handle reads on /dev/random
115: // hanging on some devices, and we use NativeURLSeedGenerator
116: // (CR 6260366).
117:
118: if (egdSource.equals(URL_DEV_RANDOM)) {
119: try {
120: instance = new NativeSeedGenerator();
121: if (debug != null) {
122: debug
123: .println("Using operating system seed generator");
124: }
125: } catch (IOException e) {
126: if (debug != null) {
127: debug
128: .println("Failed to use operating system seed "
129: + "generator: " + e.toString());
130: }
131: }
132: } else if (egdSource.length() != 0) {
133: try {
134: instance = new URLSeedGenerator(egdSource);
135: if (debug != null) {
136: debug
137: .println("Using URL seed generator reading from "
138: + egdSource);
139: }
140: } catch (IOException e) {
141: if (debug != null)
142: debug
143: .println("Failed to create seed generator with "
144: + egdSource + ": " + e.toString());
145: }
146: }
147:
148: // Fall back to ThreadedSeedGenerator
149: if (instance == null) {
150: if (debug != null) {
151: debug.println("Using default threaded seed generator");
152: }
153: instance = new ThreadedSeedGenerator();
154: }
155: }
156:
157: /**
158: * Fill result with bytes from the queue. Wait for it if it isn't ready.
159: */
160: static public void generateSeed(byte[] result) {
161: instance.getSeedBytes(result);
162: }
163:
164: void getSeedBytes(byte[] result) {
165: for (int i = 0; i < result.length; i++) {
166: result[i] = getSeedByte();
167: }
168: }
169:
170: abstract byte getSeedByte();
171:
172: /**
173: * Retrieve some system information, hashed.
174: */
175: static byte[] getSystemEntropy() {
176: byte[] ba;
177: final MessageDigest md;
178:
179: try {
180: md = MessageDigest.getInstance("SHA");
181: } catch (NoSuchAlgorithmException nsae) {
182: throw new InternalError(
183: "internal error: SHA-1 not available.");
184: }
185:
186: // The current time in millis
187: byte b = (byte) System.currentTimeMillis();
188: md.update(b);
189:
190: java.security.AccessController
191: .doPrivileged(new java.security.PrivilegedAction() {
192: public Object run() {
193:
194: try {
195: // System properties can change from machine to machine
196: String s;
197: Properties p = System.getProperties();
198: Enumeration e = p.propertyNames();
199: while (e.hasMoreElements()) {
200: s = (String) e.nextElement();
201: md.update(s.getBytes());
202: md.update(p.getProperty(s).getBytes());
203: }
204:
205: md.update(InetAddress.getLocalHost()
206: .toString().getBytes());
207:
208: // The temporary dir
209: File f = new File(p
210: .getProperty("java.io.tmpdir"));
211: String[] sa = f.list();
212: for (int i = 0; i < sa.length; i++)
213: md.update(sa[i].getBytes());
214:
215: } catch (Exception ex) {
216: md.update((byte) ex.hashCode());
217: }
218:
219: // get Runtime memory stats
220: Runtime rt = Runtime.getRuntime();
221: byte[] memBytes = longToByteArray(rt
222: .totalMemory());
223: md.update(memBytes, 0, memBytes.length);
224: memBytes = longToByteArray(rt.freeMemory());
225: md.update(memBytes, 0, memBytes.length);
226:
227: return null;
228: }
229: });
230: return md.digest();
231: }
232:
233: /**
234: * Helper function to convert a long into a byte array (least significant
235: * byte first).
236: */
237: private static byte[] longToByteArray(long l) {
238: byte[] retVal = new byte[8];
239:
240: for (int i = 0; i < 8; i++) {
241: retVal[i] = (byte) l;
242: l >>= 8;
243: }
244:
245: return retVal;
246: }
247:
248: /*
249: // This method helps the test utility receive unprocessed seed bytes.
250: public static int genTestSeed() {
251: return myself.getByte();
252: }
253: */
254:
255: private static class ThreadedSeedGenerator extends SeedGenerator
256: implements Runnable {
257: // Queue is used to collect seed bytes
258: private byte[] pool;
259: private int start, end, count;
260:
261: // Thread group for our threads
262: ThreadGroup seedGroup;
263:
264: /**
265: * The constructor is only called once to construct the one
266: * instance we actually use. It instantiates the message digest
267: * and starts the thread going.
268: */
269:
270: ThreadedSeedGenerator() {
271: pool = new byte[20];
272: start = end = 0;
273:
274: MessageDigest digest;
275:
276: try {
277: digest = MessageDigest.getInstance("SHA");
278: } catch (NoSuchAlgorithmException e) {
279: throw new InternalError(
280: "internal error: SHA-1 not available.");
281: }
282:
283: final ThreadGroup[] finalsg = new ThreadGroup[1];
284: Thread t = (Thread) java.security.AccessController
285: .doPrivileged(new java.security.PrivilegedAction() {
286: public Object run() {
287: ThreadGroup parent, group = Thread
288: .currentThread().getThreadGroup();
289: while ((parent = group.getParent()) != null)
290: group = parent;
291: finalsg[0] = new ThreadGroup(group,
292: "SeedGenerator ThreadGroup");
293: Thread newT = new Thread(finalsg[0],
294: ThreadedSeedGenerator.this ,
295: "SeedGenerator Thread");
296: newT.setPriority(Thread.MIN_PRIORITY);
297: newT.setDaemon(true);
298: return newT;
299: }
300: });
301: seedGroup = finalsg[0];
302: t.start();
303: }
304:
305: /**
306: * This method does the actual work. It collects random bytes and
307: * pushes them into the queue.
308: */
309: final public void run() {
310: try {
311: while (true) {
312: // Queue full? Wait till there's room.
313: synchronized (this ) {
314: while (count >= pool.length)
315: wait();
316: }
317:
318: int counter, quanta;
319: byte v = 0;
320:
321: // Spin count must not be under 64000
322: for (counter = quanta = 0; (counter < 64000)
323: && (quanta < 6); quanta++) {
324:
325: // Start some noisy threads
326: try {
327: BogusThread bt = new BogusThread();
328: Thread t = new Thread(seedGroup, bt,
329: "SeedGenerator Thread");
330: t.start();
331: } catch (Exception e) {
332: throw new InternalError(
333: "internal error: "
334: + "SeedGenerator thread creation error.");
335: }
336:
337: // We wait 250milli quanta, so the minimum wait time
338: // cannot be under 250milli.
339: int latch = 0;
340: latch = 0;
341: long l = System.currentTimeMillis() + 250;
342: while (System.currentTimeMillis() < l) {
343: synchronized (this ) {
344: }
345: ;
346: latch++;
347: }
348:
349: // Translate the value using the permutation, and xor
350: // it with previous values gathered.
351: v ^= rndTab[latch % 255];
352: counter += latch;
353: }
354:
355: // Push it into the queue and notify anybody who might
356: // be waiting for it.
357: synchronized (this ) {
358: pool[end] = v;
359: end++;
360: count++;
361: if (end >= pool.length)
362: end = 0;
363:
364: notifyAll();
365: }
366: }
367: } catch (Exception e) {
368: throw new InternalError(
369: "internal error: "
370: + "SeedGenerator thread generated an exception.");
371: }
372: }
373:
374: byte getSeedByte() {
375: byte b = 0;
376:
377: try {
378: // Wait for it...
379: synchronized (this ) {
380: while (count <= 0)
381: wait();
382: }
383: } catch (Exception e) {
384: if (count <= 0)
385: throw new InternalError(
386: "internal error: "
387: + "SeedGenerator thread generated an exception.");
388: }
389:
390: synchronized (this ) {
391: // Get it from the queue
392: b = pool[start];
393: pool[start] = 0;
394: start++;
395: count--;
396: if (start == pool.length)
397: start = 0;
398:
399: // Notify the daemon thread, just in case it is
400: // waiting for us to make room in the queue.
401: notifyAll();
402: }
403:
404: return b;
405: }
406:
407: // The permutation was calculated by generating 64k of random
408: // data and using it to mix the trivial permutation.
409: // It should be evenly distributed. The specific values
410: // are not crucial to the security of this class.
411: private static byte[] rndTab = { 56, 30, -107, -6, -86, 25,
412: -83, 75, -12, -64, 5, -128, 78, 21, 16, 32, 70, -81,
413: 37, -51, -43, -46, -108, 87, 29, 17, -55, 22, -11,
414: -111, -115, 84, -100, 108, -45, -15, -98, 72, -33, -28,
415: 31, -52, -37, -117, -97, -27, 93, -123, 47, 126, -80,
416: -62, -93, -79, 61, -96, -65, -5, -47, -119, 14, 89, 81,
417: -118, -88, 20, 67, -126, -113, 60, -102, 55, 110, 28,
418: 85, 121, 122, -58, 2, 45, 43, 24, -9, 103, -13, 102,
419: -68, -54, -101, -104, 19, 13, -39, -26, -103, 62, 77,
420: 51, 44, 111, 73, 18, -127, -82, 4, -30, 11, -99, -74,
421: 40, -89, 42, -76, -77, -94, -35, -69, 35, 120, 76, 33,
422: -73, -7, 82, -25, -10, 88, 125, -112, 58, 83, 95, 6,
423: 10, 98, -34, 80, 15, -91, 86, -19, 52, -17, 117, 49,
424: -63, 118, -90, 36, -116, -40, -71, 97, -53, -109, -85,
425: 109, -16, -3, 104, -95, 68, 54, 34, 26, 114, -1, 106,
426: -121, 3, 66, 0, 100, -84, 57, 107, 119, -42, 112, -61,
427: 1, 48, 38, 12, -56, -57, 39, -106, -72, 41, 7, 71, -29,
428: -59, -8, -38, 79, -31, 124, -124, 8, 91, 116, 99, -4,
429: 9, -36, -78, 63, -49, -67, -87, 59, 101, -32, 92, 94,
430: 53, -41, 115, -66, -70, -122, 50, -50, -22, -20, -18,
431: -21, 23, -2, -48, 96, 65, -105, 123, -14, -110, 69,
432: -24, -120, -75, 74, 127, -60, 113, 90, -114, 105, 46,
433: 27, -125, -23, -44, 64 };
434:
435: /**
436: * This inner thread causes the thread scheduler to become 'noisy',
437: * thus adding entropy to the system load.
438: * At least one instance of this class is generated for every seed byte.
439: */
440:
441: private class BogusThread implements Runnable {
442: final public void run() {
443: try {
444: for (int i = 0; i < 5; i++)
445: Thread.sleep(50);
446: // System.gc();
447: } catch (Exception e) {
448: }
449: }
450: }
451: }
452:
453: static class URLSeedGenerator extends SeedGenerator {
454:
455: private String deviceName;
456: private BufferedInputStream devRandom;
457:
458: /**
459: * The constructor is only called once to construct the one
460: * instance we actually use. It opens the entropy gathering device
461: * which will supply the randomness.
462: */
463:
464: URLSeedGenerator(String egdurl) throws IOException {
465: if (egdurl == null) {
466: throw new IOException("No random source specified");
467: }
468: deviceName = egdurl;
469: init();
470: }
471:
472: URLSeedGenerator() throws IOException {
473: this (SeedGenerator.URL_DEV_RANDOM);
474: }
475:
476: private void init() throws IOException {
477: final URL device = new URL(deviceName);
478: devRandom = (BufferedInputStream) java.security.AccessController
479: .doPrivileged(new java.security.PrivilegedAction() {
480: public Object run() {
481: try {
482: return new BufferedInputStream(device
483: .openStream());
484: } catch (IOException ioe) {
485: return null;
486: }
487: }
488: });
489:
490: if (devRandom == null) {
491: throw new IOException("failed to open " + device);
492: }
493: }
494:
495: byte getSeedByte() {
496: byte b[] = new byte[1];
497: int stat;
498: try {
499: stat = devRandom.read(b, 0, b.length);
500: } catch (IOException ioe) {
501: throw new InternalError("URLSeedGenerator "
502: + deviceName + " generated exception: "
503: + ioe.getMessage());
504: }
505: if (stat == b.length) {
506: return b[0];
507: } else if (stat == -1) {
508: throw new InternalError("URLSeedGenerator "
509: + deviceName + " reached end of file");
510: } else {
511: throw new InternalError("URLSeedGenerator "
512: + deviceName + " failed read");
513: }
514: }
515:
516: }
517:
518: }
|