Source code: gnu/javax/crypto/prng/CSPRNG.java
1 /* CSPRNG.java -- continuously-seeded pseudo-random number generator.
2 Copyright (C) 2004, 2006 Free Software Foundation, Inc.
3
4 This file is a part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 USA
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package gnu.javax.crypto.prng;
40
41 import gnu.java.security.Properties;
42 import gnu.java.security.Registry;
43 import gnu.java.security.hash.HashFactory;
44 import gnu.java.security.hash.IMessageDigest;
45 import gnu.java.security.prng.BasePRNG;
46 import gnu.java.security.prng.EntropySource;
47 import gnu.java.security.prng.IRandom;
48 import gnu.java.security.prng.LimitReachedException;
49 import gnu.java.security.util.SimpleList;
50 import gnu.java.security.util.Util;
51
52 import gnu.javax.crypto.cipher.CipherFactory;
53 import gnu.javax.crypto.cipher.IBlockCipher;
54
55 import java.io.ByteArrayOutputStream;
56 import java.io.FileInputStream;
57 import java.io.InputStream;
58 import java.io.PrintStream;
59
60 import java.net.MalformedURLException;
61 import java.net.URL;
62
63 import java.security.AccessController;
64 import java.security.InvalidKeyException;
65 import java.security.PrivilegedAction;
66
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.Collections;
70 import java.util.HashMap;
71 import java.util.Iterator;
72 import java.util.LinkedList;
73 import java.util.List;
74 import java.util.Map;
75 import java.util.StringTokenizer;
76
77 /**
78 * <p>An entropy pool-based pseudo-random number generator based on the PRNG
79 * in Peter Gutmann's cryptlib (<a
80 * href="http://www.cs.auckland.ac.nz/~pgut001/cryptlib/">http://www.cs.auckland.ac.nz/~pgut001/cryptlib/</a>).</p>
81 *
82 * <p>The basic properties of this generator are:</p>
83 *
84 * <ol>
85 * <li>The internal state cannot be determined by knowledge of the input.</li>
86 * <li>It is resistant to bias introduced by specific inputs.</li>
87 * <li>The output does not reveal the state of the generator.</li>
88 * </ol>
89 */
90 public class CSPRNG extends BasePRNG
91 {
92
93 // Constants and fields.
94 // -------------------------------------------------------------------------
95
96 private static final boolean DEBUG = false;
97
98 private static void debug(String msg)
99 {
100 System.err.print(">>> CSPRNG: ");
101 System.err.println(msg);
102 }
103
104 /**
105 * Property name for the list of files to read for random values. The
106 * mapped value is a list with the following values:
107 *
108 * <ol>
109 * <li>A {@link Double}, indicating the suggested <i>quality</i> of this
110 * source. This value must be between 0 and 100.</li>
111 * <li>An {@link Integer}, indicating the number of bytes to skip in the file
112 * before reading bytes. This can be any nonnegative value.</li>
113 * <li>An {@link Integer}, indicating the number of bytes to read.</li>
114 * <li>A {@link String}, indicating the path to the file.</li>
115 * </ol>
116 *
117 * @see gnu.crypto.util.SimpleList
118 */
119 public static final String FILE_SOURCES = "gnu.crypto.prng.pool.files";
120
121 /**
122 * Property name for the list of URLs to poll for random values. The
123 * mapped value is a list formatted similarly as in {@link #FILE_SOURCES},
124 * but the fourth member is a {@link URL}.
125 */
126 public static final String URL_SOURCES = "gnu.crypto.prng.pool.urls";
127
128 /**
129 * Property name for the list of programs to execute, and use the output
130 * as new random bytes. The mapped property is formatted similarly an in
131 * {@link #FILE_SOURCES} and {@link #URL_SOURCES}, except the fourth
132 * member is a {@link String} of the program to execute.
133 */
134 public static final String PROGRAM_SOURCES = "gnu.crypto.prng.pool.programs";
135
136 /**
137 * Property name for a list of other sources of entropy. The mapped
138 * value must be a list of {@link EntropySource} objects.
139 */
140 public static final String OTHER_SOURCES = "gnu.crypto.prng.pool.other";
141
142 /**
143 * Property name for whether or not to wait for the slow poll to
144 * complete, passed as a {@link Boolean}. The default value is true.
145 */
146 public static final String BLOCKING = "gnu.crypto.prng.pool.blocking";
147
148 private static final String FILES = "gnu.crypto.csprng.file.";
149
150 private static final String URLS = "gnu.crypto.csprng.url.";
151
152 private static final String PROGS = "gnu.crypto.csprng.program.";
153
154 private static final String OTHER = "gnu.crypto.csprng.other.";
155
156 private static final String BLOCK = "gnu.crypto.csprng.blocking";
157
158 private static final int POOL_SIZE = 256;
159
160 private static final int ALLOC_SIZE = 260;
161
162 private static final int OUTPUT_SIZE = POOL_SIZE / 2;
163
164 private static final int X917_POOL_SIZE = 16;
165
166 private static final String HASH_FUNCTION = Registry.SHA160_HASH;
167
168 private static final String CIPHER = Registry.AES_CIPHER;
169
170 private static final int MIX_COUNT = 10;
171
172 private static final int X917_LIFETIME = 8192;
173
174 // FIXME this should be configurable.
175 private static final int SPINNER_COUNT = 8;
176
177 /**
178 * The spinner group singleton. We use this to add a small amount of
179 * randomness (in addition to the current time and the amount of
180 * free memory) based on the randomness (if any) present due to
181 * system load and thread scheduling.
182 */
183 private static final Spinner[] SPINNERS = new Spinner[SPINNER_COUNT];
184
185 private static final Thread[] SPINNER_THREADS = new Thread[SPINNER_COUNT];
186 static
187 {
188 for (int i = 0; i < SPINNER_COUNT; i++)
189 {
190 SPINNER_THREADS[i] = new Thread(SPINNERS[i] = new Spinner(),
191 "spinner-" + i);
192 SPINNER_THREADS[i].setDaemon(true);
193 SPINNER_THREADS[i].setPriority(Thread.MIN_PRIORITY);
194 SPINNER_THREADS[i].start();
195 }
196 }
197
198 /**
199 * The message digest (SHA-1) used in the mixing function.
200 */
201 private final IMessageDigest hash;
202
203 /**
204 * The cipher (AES) used in the output masking function.
205 */
206 private final IBlockCipher cipher;
207
208 /**
209 * The number of times the pool has been mixed.
210 */
211 private int mixCount;
212
213 /**
214 * The entropy pool.
215 */
216 private final byte[] pool;
217
218 /**
219 * The quality of the random pool (percentage).
220 */
221 private double quality;
222
223 /**
224 * The index of the next byte in the entropy pool.
225 */
226 private int index;
227
228 /**
229 * The pool for the X9.17-like generator.
230 */
231 private byte[] x917pool;
232
233 /**
234 * The number of iterations of the X9.17-like generators.
235 */
236 private int x917count;
237
238 /**
239 * Whether or not the X9.17-like generator is initialized.
240 */
241 private boolean x917init;
242
243 /**
244 * The list of file soures.
245 */
246 private final List files;
247
248 /**
249 * The list of URL sources.
250 */
251 private final List urls;
252
253 /**
254 * The list of program sources.
255 */
256 private final List progs;
257
258 /**
259 * The list of other sources.
260 */
261 private final List other;
262
263 /**
264 * Whether or not to wait for the slow poll to complete.
265 */
266 private boolean blocking;
267
268 /**
269 * The thread that polls for random data.
270 */
271 private Poller poller;
272
273 private Thread pollerThread;
274
275 // Constructor.
276 // -------------------------------------------------------------------------
277
278 public CSPRNG()
279 {
280 super("CSPRNG");
281 pool = new byte[ALLOC_SIZE];
282 x917pool = new byte[X917_POOL_SIZE];
283 x917count = 0;
284 x917init = false;
285 quality = 0.0;
286 hash = HashFactory.getInstance(HASH_FUNCTION);
287 cipher = CipherFactory.getInstance(CIPHER);
288 buffer = new byte[OUTPUT_SIZE];
289 ndx = 0;
290 initialised = false;
291 files = new LinkedList();
292 urls = new LinkedList();
293 progs = new LinkedList();
294 other = new LinkedList();
295 }
296
297 // Class methods.
298 // -------------------------------------------------------------------------
299
300 /**
301 * <p>Create and initialize a CSPRNG instance with the "system" parameters;
302 * the files, URLs, programs, and {@link EntropySource} sources used by
303 * the instance are derived from properties set in the system {@link
304 * Properties}.</p>
305 *
306 * <p>All properties are of the from <i>name</i>.</i>N</i>, where <i>name</i>
307 * is the name of the source, and <i>N</i> is an integer (staring at 1) that
308 * indicates the preference number for that source.</p>
309 *
310 * <p>The following vales for <i>name</i> are used here:</p>
311 *
312 * <dl>
313 * <dt>gnu.crypto.csprng.file</dt>
314 * <dd><p>These properties are file sources, passed as the {@link #FILE_SOURCES}
315 * parameter of the instance. The property value is a 4-tuple formatted as:</p>
316 *
317 * <blockquote><i>quality</i> ; <i>offset</i> ; <i>count</i> ; <i>path</i></blockquote>
318 *
319 * <p>The parameters are mapped to the parameters defined for {@link
320 * #FILE_SOURCES}. Leading or trailing spaces on any item are trimmed
321 * off.</p></dd>
322 *
323 * <dt>gnu.crypto.csprng.url</dt>
324 * <dd><p>These properties are URL sources, passed as the {@link #URL_SOURCES}
325 * parameter of the instance. The property is formatted the same way as file
326 * sources, but the <i>path</i> argument must be a valid URL.</p></dd>
327 *
328 * <dt>gnu.crypto.csprng.program</dt>
329 * <dd><p>These properties are program sources, passed as the {@link
330 * #PROGRAM_SOURCES} parameter of the instance. This property is formatted
331 * the same way as file and URL sources, but the last argument is a program
332 * and its arguments.</p></dd>
333 *
334 * <dt>gnu.crypto.cspring.other</dt>
335 * <dd><p>These properties are other sources, passed as the {@link OTHER_SOURCES}
336 * parameter of the instance. The property value must be the full name
337 * of a class that implements the {@link EntropySource} interface and has a
338 * public no-argument constructor.</p></dd>
339 * </dl>
340 *
341 * <p>Finally, a boolean property "gnu.crypto.csprng.blocking" can be set to
342 * the desired value of {@link #BLOCKING}.</p>
343 *
344 * <p>An example of valid properties would be:</p>
345 *
346 * <pre>
347 * gnu.crypto.csprng.blocking=true
348 *
349 * gnu.crypto.csprng.file.1=75.0;0;256;/dev/random
350 * gnu.crypto.csprng.file.2=10.0;0;100;/home/user/file
351 *
352 * gnu.crypto.csprng.url.1=5.0;0;256;http://www.random.org/cgi-bin/randbyte?nbytes=256
353 * gnu.crypto.csprng.url.2=0;256;256;http://slashdot.org/
354 *
355 * gnu.crypto.csprng.program.1=0.5;0;10;last -n 50
356 * gnu.crypto.csprng.program.2=0.5;0;10;tcpdump -c 5
357 *
358 * gnu.crypto.csprng.other.1=foo.bar.MyEntropySource
359 * gnu.crypto.csprng.other.2=com.company.OtherEntropySource
360 * </pre>
361 */
362 public static IRandom getSystemInstance() throws ClassNotFoundException,
363 MalformedURLException, NumberFormatException
364 {
365 CSPRNG instance = new CSPRNG();
366 HashMap attrib = new HashMap();
367 attrib.put(BLOCKING, Boolean.valueOf(getProperty(BLOCK)));
368 String s = null;
369
370 // Get each file source "gnu.crypto.csprng.file.N".
371 List l = new LinkedList();
372 for (int i = 0; (s = getProperty(FILES + i)) != null; i++)
373 {
374 try
375 {
376 l.add(parseString(s.trim()));
377 }
378 catch (NumberFormatException nfe)
379 {
380 }
381 }
382 attrib.put(FILE_SOURCES, l);
383
384 l = new LinkedList();
385 for (int i = 0; (s = getProperty(URLS + i)) != null; i++)
386 {
387 try
388 {
389 l.add(parseURL(s.trim()));
390 }
391 catch (NumberFormatException nfe)
392 {
393 }
394 catch (MalformedURLException mue)
395 {
396 }
397 }
398 attrib.put(URL_SOURCES, l);
399
400 l = new LinkedList();
401 for (int i = 0; (s = getProperty(PROGS + i)) != null; i++)
402 {
403 try
404 {
405 l.add(parseString(s.trim()));
406 }
407 catch (NumberFormatException nfe)
408 {
409 }
410 }
411 attrib.put(PROGRAM_SOURCES, l);
412
413 l = new LinkedList();
414 for (int i = 0; (s = getProperty(OTHER + i)) != null; i++)
415 {
416 try
417 {
418 Class c = Class.forName(s.trim());
419 l.add(c.newInstance());
420 }
421 catch (ClassNotFoundException cnfe)
422 {
423 }
424 catch (InstantiationException ie)
425 {
426 }
427 catch (IllegalAccessException iae)
428 {
429 }
430 }
431 attrib.put(OTHER_SOURCES, l);
432
433 instance.init(attrib);
434 return instance;
435 }
436
437 private static String getProperty(final String name)
438 {
439 return (String) AccessController.doPrivileged(new PrivilegedAction()
440 {
441 public Object run()
442 {
443 return Properties.getProperty(name);
444 }
445 });
446 }
447
448 private static List parseString(String s) throws NumberFormatException
449 {
450 StringTokenizer tok = new StringTokenizer(s, ";");
451 if (tok.countTokens() != 4)
452 {
453 throw new IllegalArgumentException("malformed property");
454 }
455 Double quality = new Double(tok.nextToken());
456 Integer offset = new Integer(tok.nextToken());
457 Integer length = new Integer(tok.nextToken());
458 String str = tok.nextToken();
459 return new SimpleList(quality, offset, length, str);
460 }
461
462 private static List parseURL(String s) throws MalformedURLException,
463 NumberFormatException
464 {
465 StringTokenizer tok = new StringTokenizer(s, ";");
466 if (tok.countTokens() != 4)
467 {
468 throw new IllegalArgumentException("malformed property");
469 }
470 Double quality = new Double(tok.nextToken());
471 Integer offset = new Integer(tok.nextToken());
472 Integer length = new Integer(tok.nextToken());
473 URL url = new URL(tok.nextToken());
474 return new SimpleList(quality, offset, length, url);
475 }
476
477 // Instance methods.
478 // -------------------------------------------------------------------------
479
480 public Object clone()
481 {
482 return new CSPRNG();
483 }
484
485 public void setup(Map attrib)
486 {
487 List list = null;
488
489 if (DEBUG)
490 {
491 debug(String.valueOf(attrib));
492 }
493 try
494 {
495 list = (List) attrib.get(FILE_SOURCES);
496 if (DEBUG)
497 {
498 debug(String.valueOf(list));
499 }
500 if (list != null)
501 {
502 files.clear();
503 for (Iterator it = list.iterator(); it.hasNext();)
504 {
505 List l = (List) it.next();
506 if (DEBUG)
507 {
508 debug("l=" + l);
509 }
510 if (l.size() != 4)
511 {
512 if (DEBUG)
513 {
514 debug("file list too small: " + l.size());
515 }
516 throw new IllegalArgumentException("invalid file list");
517 }
518 Double quality = (Double) l.get(0);
519 Integer offset = (Integer) l.get(1);
520 Integer length = (Integer) l.get(2);
521 String source = (String) l.get(3);
522 files.add(new SimpleList(quality, offset, length, source));
523 }
524 }
525 }
526 catch (ClassCastException cce)
527 {
528 if (DEBUG)
529 {
530 debug("bad file list: " + cce.getMessage());
531 cce.printStackTrace();
532 }
533 throw new IllegalArgumentException("invalid file list");
534 }
535
536 try
537 {
538 list = (List) attrib.get(URL_SOURCES);
539 if (DEBUG)
540 {
541 debug(String.valueOf(list));
542 }
543 if (list != null)
544 {
545 urls.clear();
546 for (Iterator it = list.iterator(); it.hasNext();)
547 {
548 List l = (List) it.next();
549 if (DEBUG)
550 {
551 debug("l=" + l);
552 }
553 if (l.size() != 4)
554 {
555 if (DEBUG)
556 {
557 debug("URL list too small: " + l.size());
558 }
559 throw new IllegalArgumentException("invalid URL list");
560 }
561 Double quality = (Double) l.get(0);
562 Integer offset = (Integer) l.get(1);
563 Integer length = (Integer) l.get(2);
564 URL source = (URL) l.get(3);
565 urls.add(new SimpleList(quality, offset, length, source));
566 }
567 }
568 }
569 catch (ClassCastException cce)
570 {
571 if (DEBUG)
572 {
573 debug("bad URL list: " + cce.getMessage());
574 cce.printStackTrace();
575 }
576 throw new IllegalArgumentException("invalid URL list");
577 }
578
579 try
580 {
581 list = (List) attrib.get(PROGRAM_SOURCES);
582 if (DEBUG)
583 {
584 debug(String.valueOf(list));
585 }
586 if (list != null)
587 {
588 progs.clear();
589 for (Iterator it = list.iterator(); it.hasNext();)
590 {
591 List l = (List) it.next();
592 if (DEBUG)
593 {
594 debug("l=" + l);
595 }
596 if (l.size() != 4)
597 {
598 if (DEBUG)
599 {
600 debug("program list too small: " + l.size());
601 }
602 throw new IllegalArgumentException("invalid program list");
603 }
604 Double quality = (Double) l.get(0);
605 Integer offset = (Integer) l.get(1);
606 Integer length = (Integer) l.get(2);
607 String source = (String) l.get(3);
608 progs.add(new SimpleList(quality, offset, length, source));
609 }
610 }
611 }
612 catch (ClassCastException cce)
613 {
614 if (DEBUG)
615 {
616 debug("bad program list: " + cce.getMessage());
617 cce.printStackTrace();
618 }
619 throw new IllegalArgumentException("invalid program list");
620 }
621
622 try
623 {
624 list = (List) attrib.get(OTHER_SOURCES);
625 if (DEBUG)
626 {
627 debug(String.valueOf(list));
628 }
629 if (list != null)
630 {
631 other.clear();
632 for (Iterator it = list.iterator(); it.hasNext();)
633 {
634 EntropySource src = (EntropySource) it.next();
635 if (DEBUG)
636 {
637 debug("src=" + src);
638 }
639 if (src == null)
640 {
641 throw new NullPointerException("null source in source list");
642 }
643 other.add(src);
644 }
645 }
646 }
647 catch (ClassCastException cce)
648 {
649 throw new IllegalArgumentException("invalid source list");
650 }
651
652 try
653 {
654 Boolean block = (Boolean) attrib.get(BLOCKING);
655 if (block != null)
656 {
657 blocking = block.booleanValue();
658 }
659 else
660 {
661 blocking = true;
662 }
663 }
664 catch (ClassCastException cce)
665 {
666 throw new IllegalArgumentException("invalid blocking parameter");
667 }
668
669 poller = new Poller(files, urls, progs, other, this);
670 try
671 {
672 fillBlock();
673 }
674 catch (LimitReachedException lre)
675 {
676 throw new RuntimeException("bootstrapping CSPRNG failed");
677 }
678 }
679
680 public void fillBlock() throws LimitReachedException
681 {
682 if (DEBUG)
683 {
684 debug("fillBlock");
685 }
686 if (getQuality() < 100.0)
687 {
688 if (DEBUG)
689 {
690 debug("doing slow poll");
691 }
692 slowPoll();
693 }
694
695 do
696 {
697 fastPoll();
698 mixRandomPool();
699 }
700 while (mixCount < MIX_COUNT);
701
702 if (!x917init || x917count >= X917_LIFETIME)
703 {
704 mixRandomPool(pool);
705 Map attr = new HashMap();
706 byte[] key = new byte[32];
707 System.arraycopy(pool, 0, key, 0, 32);
708 cipher.reset();
709 attr.put(IBlockCipher.KEY_MATERIAL, key);
710 try
711 {
712 cipher.init(attr);
713 }
714 catch (InvalidKeyException ike)
715 {
716 throw new Error(ike.toString());
717 }
718
719 mixRandomPool(pool);
720 generateX917(pool);
721 mixRandomPool(pool);
722 generateX917(pool);
723
724 if (x917init)
725 {
726 quality = 0.0;
727 }
728 x917init = true;
729 x917count = 0;
730 }
731
732 byte[] export = new byte[ALLOC_SIZE];
733 for (int i = 0; i < ALLOC_SIZE; i++)
734 {
735 export[i] = (byte) (pool[i] ^ 0xFF);
736 }
737
738 mixRandomPool();
739 mixRandomPool(export);
740
741 generateX917(export);
742
743 for (int i = 0; i < OUTPUT_SIZE; i++)
744 {
745 buffer[i] = (byte) (export[i] ^ export[i + OUTPUT_SIZE]);
746 }
747 Arrays.fill(export, (byte) 0);
748 }
749
750 /**
751 * Add an array of bytes into the randomness pool. Note that this method
752 * will <i>not</i> increment the pool's quality counter (this can only be
753 * done via a source provided to the setup method).
754 *
755 * @param buf The byte array.
756 * @param off The offset from whence to start reading bytes.
757 * @param len The number of bytes to add.
758 * @throws ArrayIndexOutOfBoundsException If <i>off</i> or <i>len</i> are
759 * out of the range of <i>buf</i>.
760 */
761 public synchronized void addRandomBytes(byte[] buf, int off, int len)
762 {
763 if (off < 0 || len < 0 || off + len > buf.length)
764 {
765 throw new ArrayIndexOutOfBoundsException();
766 }
767 if (DEBUG)
768 {
769 debug("adding random bytes:");
770 debug(Util.toString(buf, off, len));
771 }
772 final int count = off + len;
773 for (int i = off; i < count; i++)
774 {
775 pool[index++] ^= buf[i];
776 if (index == pool.length)
777 {
778 mixRandomPool();
779 index = 0;
780 }
781 }
782 }
783
784 /**
785 * Add a single random byte to the randomness pool. Note that this method
786 * will <i>not</i> increment the pool's quality counter (this can only be
787 * done via a source provided to the setup method).
788 *
789 * @param b The byte to add.
790 */
791 public synchronized void addRandomByte(byte b)
792 {
793 if (DEBUG)
794 {
795 debug("adding byte " + Integer.toHexString(b));
796 }
797 pool[index++] ^= b;
798 if (index >= pool.length)
799 {
800 mixRandomPool();
801 index = 0;
802 }
803 }
804
805 // Package methods.
806 // -------------------------------------------------------------------------
807
808 synchronized void addQuality(double quality)
809 {
810 if (DEBUG)
811 {
812 debug("adding quality " + quality);
813 }
814 if (this.quality < 100)
815 {
816 this.quality += quality;
817 }
818 if (DEBUG)
819 {
820 debug("quality now " + this.quality);
821 }
822 }
823
824 synchronized double getQuality()
825 {
826 return quality;
827 }
828
829 // Own methods.
830 // -------------------------------------------------------------------------
831
832 /**
833 * The mix operation. This method will, for every 20-byte block in the
834 * random pool, hash that block, the previous 20 bytes, and the next
835 * 44 bytes with SHA-1, writing the result back into that block.
836 */
837 private void mixRandomPool(byte[] buf)
838 {
839 int hashSize = hash.hashSize();
840 for (int i = 0; i < buf.length; i += hashSize)
841 {
842 // First update the bytes [p-19..p-1].
843 if (i == 0)
844 {
845 hash.update(buf, buf.length - hashSize, hashSize);
846 }
847 else
848 {
849 hash.update(buf, i - hashSize, hashSize);
850 }
851
852 // Now the next 64 bytes.
853 if (i + 64 < buf.length)
854 {
855 hash.update(buf, i, 64);
856 }
857 else
858 {
859 hash.update(buf, i, buf.length - i);
860 hash.update(buf, 0, 64 - (buf.length - i));
861 }
862
863 byte[] digest = hash.digest();
864 System.arraycopy(digest, 0, buf, i, hashSize);
865 }
866 }
867
868 private void mixRandomPool()
869 {
870 mixRandomPool(pool);
871 mixCount++;
872 }
873
874 private void generateX917(byte[] buf)
875 {
876 int off = 0;
877 for (int i = 0; i < buf.length; i += X917_POOL_SIZE)
878 {
879 int copy = Math.min(buf.length - i, X917_POOL_SIZE);
880 for (int j = 0; j < copy; j++)
881 {
882 x917pool[j] ^= pool[off + j];
883 }
884
885 cipher.encryptBlock(x917pool, 0, x917pool, 0);
886 System.arraycopy(x917pool, 0, buf, off, copy);
887 cipher.encryptBlock(x917pool, 0, x917pool, 0);
888
889 off += copy;
890 x917count++;
891 }
892 }
893
894 /**
895 * Add random data always immediately available into the random pool, such
896 * as the values of the eight asynchronous counters, the current time, the
897 * current memory usage, the calling thread name, and the current stack
898 * trace.
899 *
900 * <p>This method does not alter the quality counter, and is provided more
901 * to maintain randomness, not to seriously improve the current random
902 * state.
903 */
904 private void fastPoll()
905 {
906 byte b = 0;
907 for (int i = 0; i < SPINNER_COUNT; i++)
908 b ^= SPINNERS[i].counter;
909 addRandomByte(b);
910 addRandomByte((byte) System.currentTimeMillis());
911 addRandomByte((byte) Runtime.getRuntime().freeMemory());
912
913 String s = Thread.currentThread().getName();
914 if (s != null)
915 {
916 byte[] buf = s.getBytes();
917 addRandomBytes(buf, 0, buf.length);
918 }
919
920 ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
921 PrintStream pout = new PrintStream(bout);
922 Throwable t = new Throwable();
923 t.printStackTrace(pout);
924 pout.flush();
925 byte[] buf = bout.toByteArray();
926 addRandomBytes(buf, 0, buf.length);
927 }
928
929 private void slowPoll() throws LimitReachedException
930 {
931 if (DEBUG)
932 {
933 debug("poller is alive? "
934 + (pollerThread == null ? false : pollerThread.isAlive()));
935 }
936 if (pollerThread == null || !pollerThread.isAlive())
937 {
938 boolean interrupted = false;
939 pollerThread = new Thread(poller);
940 pollerThread.setDaemon(true);
941 pollerThread.setPriority(Thread.NORM_PRIORITY - 1);
942 pollerThread.start();
943 if (blocking)
944 {
945 try
946 {
947 pollerThread.join();
948 }
949 catch (InterruptedException ie)
950 {
951 interrupted = true;
952 }
953 }
954
955 // If the full slow poll has completed after we waited for it,
956 // and there in insufficient randomness, throw an exception.
957 if (!interrupted && blocking && quality < 100.0)
958 {
959 if (DEBUG)
960 {
961 debug("insufficient quality: " + quality);
962 }
963 throw new LimitReachedException(
964 "insufficient randomness was polled");
965 }
966 }
967 }
968
969 protected void finalize() throws Throwable
970 {
971 if (poller != null && pollerThread != null && pollerThread.isAlive())
972 {
973 pollerThread.interrupt();
974 poller.stopUpdating();
975 pollerThread.interrupt();
976 }
977 Arrays.fill(pool, (byte) 0);
978 Arrays.fill(x917pool, (byte) 0);
979 Arrays.fill(buffer, (byte) 0);
980 }
981
982 // Inner classes.
983 // -------------------------------------------------------------------------
984
985 /**
986 * A simple thread that constantly updates a byte counter. This class is
987 * used in a group of lowest-priority threads and the values of their
988 * counters (updated in competition with all other threads) is used as a
989 * source of entropy bits.
990 */
991 private static class Spinner implements Runnable
992 {
993
994 // Field.
995 // -----------------------------------------------------------------------
996
997 private byte counter;
998
999 // Constructor.
1000 // -----------------------------------------------------------------------
1001
1002 private Spinner()
1003 {
1004 }
1005
1006 // Instance methods.
1007 // -----------------------------------------------------------------------
1008
1009 public void run()
1010 {
1011 while (true)
1012 {
1013 counter++;
1014 try
1015 {
1016 Thread.sleep(100);
1017 }
1018 catch (InterruptedException ie)
1019 {
1020 }
1021 }
1022 }
1023 }
1024
1025 private final class Poller implements Runnable
1026 {
1027
1028 // Fields.
1029 // -----------------------------------------------------------------------
1030
1031 private final List files;
1032
1033 private final List urls;
1034
1035 private final List progs;
1036
1037 private final List other;
1038
1039 private final CSPRNG pool;
1040
1041 private boolean running;
1042
1043 // Constructor.
1044 // -----------------------------------------------------------------------
1045
1046 Poller(List files, List urls, List progs, List other, CSPRNG pool)
1047 {
1048 super();
1049 this.files = Collections.unmodifiableList(files);
1050 this.urls = Collections.unmodifiableList(urls);
1051 this.progs = Collections.unmodifiableList(progs);
1052 this.other = Collections.unmodifiableList(other);
1053 this.pool = pool;
1054 }
1055
1056 // Instance methods.
1057 // -----------------------------------------------------------------------
1058
1059 public void run()
1060 {
1061 running = true;
1062 if (DEBUG)
1063 {
1064 debug("files: " + files);
1065 debug("URLs: " + urls);
1066 debug("progs: " + progs);
1067 }
1068 Iterator files_it = files.iterator();
1069 Iterator urls_it = urls.iterator();
1070 Iterator prog_it = progs.iterator();
1071 Iterator other_it = other.iterator();
1072
1073 while (files_it.hasNext() || urls_it.hasNext() || prog_it.hasNext()
1074 || other_it.hasNext())
1075 {
1076
1077 // There is enough random data. Go away.
1078 if (pool.getQuality() >= 100.0 || !running)
1079 {
1080 return;
1081 }
1082
1083 if (files_it.hasNext())
1084 {
1085 try
1086 {
1087 List l = (List) files_it.next();
1088 if (DEBUG)
1089 {
1090 debug(l.toString());
1091 }
1092 double qual = ((Double) l.get(0)).doubleValue();
1093 int offset = ((Integer) l.get(1)).intValue();
1094 int count = ((Integer) l.get(2)).intValue();
1095 String src = (String) l.get(3);
1096 InputStream in = new FileInputStream(src);
1097 byte[] buf = new byte[count];
1098 if (offset > 0)
1099 {
1100 in.skip(offset);
1101 }
1102 int len = in.read(buf);
1103 if (len >= 0)
1104 {
1105 pool.addRandomBytes(buf, 0, len);
1106 pool.addQuality(qual * ((double) len / (double) count));
1107 }
1108 if (DEBUG)
1109 {
1110 debug("got " + len + " bytes from " + src);
1111 }
1112 }
1113 catch (Exception x)
1114 {
1115 if (DEBUG)
1116 {
1117 debug(x.toString());
1118 x.printStackTrace();
1119 }
1120 }
1121 }
1122
1123 if (pool.getQuality() >= 100.0 || !running)
1124 {
1125 return;
1126 }
1127
1128 if (urls_it.hasNext())
1129 {
1130 try
1131 {
1132 List l = (List) urls_it.next();
1133 if (DEBUG)
1134 {
1135 debug(l.toString());
1136 }
1137 double qual = ((Double) l.get(0)).doubleValue();
1138 int offset = ((Integer) l.get(1)).intValue();
1139 int count = ((Integer) l.get(2)).intValue();
1140 URL src = (URL) l.get(3);
1141 InputStream in = src.openStream();
1142 byte[] buf = new byte[count];
1143 if (offset > 0)
1144 {
1145 in.skip(offset);
1146 }
1147 int len = in.read(buf);
1148 if (len >= 0)
1149 {
1150 pool.addRandomBytes(buf, 0, len);
1151 pool.addQuality(qual * ((double) len / (double) count));
1152 }
1153 if (DEBUG)
1154 {
1155 debug("got " + len + " bytes from " + src);
1156 }
1157 }
1158 catch (Exception x)
1159 {
1160 if (DEBUG)
1161 {
1162 debug(x.toString());
1163 x.printStackTrace();
1164 }
1165 }
1166 }
1167
1168 if (pool.getQuality() >= 100.0 || !running)
1169 {
1170 return;
1171 }
1172
1173 Process proc = null;
1174 if (prog_it.hasNext())
1175 {
1176 try
1177 {
1178 List l = (List) prog_it.next();
1179 if (DEBUG)
1180 {
1181 debug(l.toString());
1182 }
1183 double qual = ((Double) l.get(0)).doubleValue();
1184 int offset = ((Integer) l.get(1)).intValue();
1185 int count = ((Integer) l.get(2)).intValue();
1186 String src = (String) l.get(3);
1187 proc = null;
1188 proc = Runtime.getRuntime().exec(src);
1189 InputStream in = proc.getInputStream();
1190 byte[] buf = new byte[count];
1191 if (offset > 0)
1192 {
1193 in.skip(offset);
1194 }
1195 int len = in.read(buf);
1196 if (len >= 0)
1197 {
1198 pool.addRandomBytes(buf, 0, len);
1199 pool.addQuality(qual * ((double) len / (double) count));
1200 }
1201 proc.destroy();
1202 proc.waitFor();
1203 if (DEBUG)
1204 {
1205 debug("got " + len + " bytes from " + src);
1206 }
1207 }
1208 catch (Exception x)
1209 {
1210 if (DEBUG)
1211 {
1212 debug(x.toString());
1213 x.printStackTrace();
1214 }
1215 try
1216 {
1217 if (proc != null)
1218 {
1219 proc.destroy();
1220 proc.waitFor();
1221 }
1222 }
1223 catch (Exception ignored)
1224 {
1225 }
1226 }
1227 }
1228
1229 if (pool.getQuality() >= 100.0 || !running)
1230 {
1231 return;
1232 }
1233
1234 if (other_it.hasNext())
1235 {
1236 try
1237 {
1238 EntropySource src = (EntropySource) other_it.next();
1239 byte[] buf = src.nextBytes();
1240 if (pool == null)
1241 {
1242 return;
1243 }
1244 pool.addRandomBytes(buf, 0, buf.length);
1245 pool.addQuality(src.quality());
1246 if (DEBUG)
1247 {
1248 debug("got " + buf.length + " bytes from " + src);
1249 }
1250 }
1251 catch (Exception x)
1252 {
1253 if (DEBUG)
1254 {
1255 debug(x.toString());
1256 x.printStackTrace();
1257 }
1258 }
1259 }
1260 }
1261 }
1262
1263 public void stopUpdating()
1264 {
1265 running = false;
1266 }
1267 }
1268}