Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libjava / classpath / gnu / javax / crypto / prng / CSPRNG.java
1 /* CSPRNG.java -- continuously-seeded pseudo-random number generator.
2    Copyright (C) 2004, 2006, 2010  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.Configuration;
42 import gnu.java.security.Properties;
43 import gnu.java.security.Registry;
44 import gnu.java.security.hash.HashFactory;
45 import gnu.java.security.hash.IMessageDigest;
46 import gnu.java.security.prng.BasePRNG;
47 import gnu.java.security.prng.EntropySource;
48 import gnu.java.security.prng.IRandom;
49 import gnu.java.security.prng.LimitReachedException;
50 import gnu.java.security.util.SimpleList;
51 import gnu.java.security.util.Util;
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 import java.net.MalformedURLException;
60 import java.net.URL;
61 import java.security.AccessController;
62 import java.security.InvalidKeyException;
63 import java.security.PrivilegedAction;
64 import java.util.Arrays;
65 import java.util.Collections;
66 import java.util.HashMap;
67 import java.util.Iterator;
68 import java.util.LinkedList;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.StringTokenizer;
72 import java.util.logging.Level;
73 import java.util.logging.Logger;
74
75 /**
76  * An entropy pool-based pseudo-random number generator based on the PRNG in
77  * Peter Gutmann's cryptlib (<a
78  * href="http://www.cs.auckland.ac.nz/~pgut001/cryptlib/">http://www.cs.auckland.ac.nz/~pgut001/cryptlib/</a>).
79  * <p>
80  * The basic properties of this generator are:
81  * <ol>
82  * <li>The internal state cannot be determined by knowledge of the input.</li>
83  * <li>It is resistant to bias introduced by specific inputs.</li>
84  * <li>The output does not reveal the state of the generator.</li>
85  * </ol>
86  */
87 public class CSPRNG
88     extends BasePRNG
89 {
90   private static final Logger log = Configuration.DEBUG ?
91                          Logger.getLogger(CSPRNG.class.getName()) : null;
92
93   /**
94    * Property name for the list of files to read for random values. The mapped
95    * value is a list with the following values:
96    * <ol>
97    * <li>A {@link Double}, indicating the suggested <i>quality</i> of this
98    * source. This value must be between 0 and 100.</li>
99    * <li>An {@link Integer}, indicating the number of bytes to skip in the
100    * file before reading bytes. This can be any nonnegative value.</li>
101    * <li>An {@link Integer}, indicating the number of bytes to read.</li>
102    * <li>A {@link String}, indicating the path to the file.</li>
103    * </ol>
104    *
105    * @see gnu.java.security.util.SimpleList
106    */
107   public static final String FILE_SOURCES = "gnu.crypto.prng.pool.files";
108   /**
109    * Property name for the list of URLs to poll for random values. The mapped
110    * value is a list formatted similarly as in {@link #FILE_SOURCES}, but the
111    * fourth member is a {@link URL}.
112    */
113   public static final String URL_SOURCES = "gnu.crypto.prng.pool.urls";
114   /**
115    * Property name for the list of programs to execute, and use the output as
116    * new random bytes. The mapped property is formatted similarly an in
117    * {@link #FILE_SOURCES} and {@link #URL_SOURCES}, except the fourth member
118    * is a {@link String} of the program to execute.
119    */
120   public static final String PROGRAM_SOURCES = "gnu.crypto.prng.pool.programs";
121   /**
122    * Property name for a list of other sources of entropy. The mapped value must
123    * be a list of {@link EntropySource} objects.
124    */
125   public static final String OTHER_SOURCES = "gnu.crypto.prng.pool.other";
126   /**
127    * Property name for whether or not to wait for the slow poll to complete,
128    * passed as a {@link Boolean}. The default value is true.
129    */
130   public static final String BLOCKING = "gnu.crypto.prng.pool.blocking";
131   private static final String FILES = "gnu.crypto.csprng.file.";
132   private static final String URLS = "gnu.crypto.csprng.url.";
133   private static final String PROGS = "gnu.crypto.csprng.program.";
134   private static final String OTHER = "gnu.crypto.csprng.other.";
135   private static final String BLOCK = "gnu.crypto.csprng.blocking";
136   private static final int POOL_SIZE = 256;
137   private static final int ALLOC_SIZE = 260;
138   private static final int OUTPUT_SIZE = POOL_SIZE / 2;
139   private static final int X917_POOL_SIZE = 16;
140   private static final String HASH_FUNCTION = Registry.SHA160_HASH;
141   private static final String CIPHER = Registry.AES_CIPHER;
142   private static final int MIX_COUNT = 10;
143   private static final int X917_LIFETIME = 8192;
144   // FIXME this should be configurable.
145   private static final int SPINNER_COUNT = 8;
146   /**
147    * The spinner group singleton. We use this to add a small amount of
148    * randomness (in addition to the current time and the amount of free memory)
149    * based on the randomness (if any) present due to system load and thread
150    * scheduling.
151    */
152   private static final Spinner[] SPINNERS = new Spinner[SPINNER_COUNT];
153   private static final Thread[] SPINNER_THREADS = new Thread[SPINNER_COUNT];
154   static
155     {
156       for (int i = 0; i < SPINNER_COUNT; i++)
157         {
158           SPINNER_THREADS[i] = new Thread(SPINNERS[i] = new Spinner(),
159                                           "spinner-" + i);
160           SPINNER_THREADS[i].setDaemon(true);
161           SPINNER_THREADS[i].setPriority(Thread.MIN_PRIORITY);
162           SPINNER_THREADS[i].start();
163         }
164     }
165   /** The message digest (SHA-1) used in the mixing function. */
166   private final IMessageDigest hash;
167   /** The cipher (AES) used in the output masking function. */
168   private final IBlockCipher cipher;
169   /** The number of times the pool has been mixed. */
170   private int mixCount;
171   /** The entropy pool. */
172   private final byte[] pool;
173   /** The quality of the random pool (percentage). */
174   private double quality;
175   /** The index of the next byte in the entropy pool. */
176   private int index;
177   /** The pool for the X9.17-like generator. */
178   private byte[] x917pool;
179   /** The number of iterations of the X9.17-like generators. */
180   private int x917count;
181   /** Whether or not the X9.17-like generator is initialized. */
182   private boolean x917init;
183   /** The list of file soures. */
184   private final List files;
185   /** The list of URL sources. */
186   private final List urls;
187   /** The list of program sources. */
188   private final List progs;
189   /** The list of other sources. */
190   private final List other;
191   /** Whether or not to wait for the slow poll to complete. */
192   private boolean blocking;
193   /** The thread that polls for random data. */
194   private Poller poller;
195   private Thread pollerThread;
196
197   public CSPRNG()
198   {
199     super("CSPRNG");
200     pool = new byte[ALLOC_SIZE];
201     x917pool = new byte[X917_POOL_SIZE];
202     x917count = 0;
203     x917init = false;
204     quality = 0.0;
205     hash = HashFactory.getInstance(HASH_FUNCTION);
206     cipher = CipherFactory.getInstance(CIPHER);
207     buffer = new byte[OUTPUT_SIZE];
208     ndx = 0;
209     initialised = false;
210     files = new LinkedList();
211     urls = new LinkedList();
212     progs = new LinkedList();
213     other = new LinkedList();
214   }
215
216   /**
217    * Create and initialize a CSPRNG instance with the "system" parameters; the
218    * files, URLs, programs, and {@link EntropySource} sources used by the
219    * instance are derived from properties set in the system {@link Properties}.
220    * <p>
221    * All properties are of the from <i>name</i>.</i>N</i>, where <i>name</i>
222    * is the name of the source, and <i>N</i> is an integer (staring at 1) that
223    * indicates the preference number for that source.
224    * <p>
225    * The following vales for <i>name</i> are used here:
226    * <dl>
227    * <dt>gnu.crypto.csprng.file</dt>
228    * <dd>
229    * <p>
230    * These properties are file sources, passed as the {@link #FILE_SOURCES}
231    * parameter of the instance. The property value is a 4-tuple formatted as:
232    * </p>
233    * <blockquote><i>quality</i> ; <i>offset</i> ; <i>count</i> ; <i>path</i></blockquote>
234    * <p>
235    * The parameters are mapped to the parameters defined for {@link
236    * #FILE_SOURCES}. Leading or trailing spaces on any item are trimmed off.
237    * </p>
238    * </dd>
239    * <dt>gnu.crypto.csprng.url</dt>
240    * <dd>
241    * <p>
242    * These properties are URL sources, passed as the {@link #URL_SOURCES}
243    * parameter of the instance. The property is formatted the same way as file
244    * sources, but the <i>path</i> argument must be a valid URL.
245    * </p>
246    * </dd>
247    * <dt>gnu.crypto.csprng.program</dt>
248    * <dd>
249    * <p>
250    * These properties are program sources, passed as the {@link
251    * #PROGRAM_SOURCES} parameter of the instance. This property is formatted the
252    * same way as file and URL sources, but the last argument is a program and
253    * its arguments.
254    * </p>
255    * </dd>
256    * <dt>gnu.crypto.cspring.other</dt>
257    * <dd>
258    * <p>
259    * These properties are other sources, passed as the {@link #OTHER_SOURCES}
260    * parameter of the instance. The property value must be the full name of a
261    * class that implements the {@link EntropySource} interface and has a public
262    * no-argument constructor.
263    * </p>
264    * </dd>
265    * </dl>
266    * <p>
267    * Finally, a boolean property "gnu.crypto.csprng.blocking" can be set to the
268    * desired value of {@link #BLOCKING}.
269    * <p>
270    * An example of valid properties would be:
271    * <pre>
272    *  gnu.crypto.csprng.blocking=true
273    *
274    *  gnu.crypto.csprng.file.1=75.0;0;256;/dev/random
275    *  gnu.crypto.csprng.file.2=10.0;0;100;/home/user/file
276    *
277    *  gnu.crypto.csprng.url.1=5.0;0;256;http://www.random.org/cgi-bin/randbyte?nbytes=256
278    *  gnu.crypto.csprng.url.2=0;256;256;http://slashdot.org/
279    *
280    *  gnu.crypto.csprng.program.1=0.5;0;10;last -n 50
281    *  gnu.crypto.csprng.program.2=0.5;0;10;tcpdump -c 5
282    *
283    *  gnu.crypto.csprng.other.1=foo.bar.MyEntropySource
284    *  gnu.crypto.csprng.other.2=com.company.OtherEntropySource
285    * </pre>
286    */
287   public static IRandom getSystemInstance() throws ClassNotFoundException,
288       MalformedURLException, NumberFormatException
289   {
290     CSPRNG instance = new CSPRNG();
291     HashMap attrib = new HashMap();
292     attrib.put(BLOCKING, Boolean.valueOf(getProperty(BLOCK)));
293     String s = null;
294     // Get each file source "gnu.crypto.csprng.file.N".
295     List l = new LinkedList();
296     for (int i = 0; (s = getProperty(FILES + i)) != null; i++)
297       try
298         {
299           l.add(parseString(s.trim()));
300         }
301       catch (NumberFormatException nfe)
302         {
303         }
304     attrib.put(FILE_SOURCES, l);
305     l = new LinkedList();
306     for (int i = 0; (s = getProperty(URLS + i)) != null; i++)
307       try
308         {
309           l.add(parseURL(s.trim()));
310         }
311       catch (NumberFormatException nfe)
312         {
313         }
314       catch (MalformedURLException mue)
315         {
316         }
317     attrib.put(URL_SOURCES, l);
318     l = new LinkedList();
319     for (int i = 0; (s = getProperty(PROGS + i)) != null; i++)
320       try
321         {
322           l.add(parseString(s.trim()));
323         }
324       catch (NumberFormatException nfe)
325         {
326         }
327     attrib.put(PROGRAM_SOURCES, l);
328     l = new LinkedList();
329     for (int i = 0; (s = getProperty(OTHER + i)) != null; i++)
330       {
331         try
332           {
333             l.add((EntropySource)Class.forName(s.trim()).newInstance());
334           }
335         catch (ClassNotFoundException cnfe)
336           {
337             // ignore
338           }
339         catch (InstantiationException ie)
340           {
341             // ignore
342           }
343         catch (IllegalAccessException iae)
344           {
345             // ignore
346           }
347       }
348     attrib.put(OTHER_SOURCES, l);
349     instance.init(attrib);
350     return instance;
351   }
352
353   private static String getProperty(final String name)
354   {
355     return (String) AccessController.doPrivileged(new PrivilegedAction()
356     {
357       public Object run()
358       {
359         return Properties.getProperty(name);
360       }
361     });
362   }
363
364   private static List parseString(String s) throws NumberFormatException
365   {
366     StringTokenizer tok = new StringTokenizer(s, ";");
367     if (tok.countTokens() != 4)
368       throw new IllegalArgumentException("malformed property");
369     Double quality = new Double(tok.nextToken());
370     Integer offset = new Integer(tok.nextToken());
371     Integer length = new Integer(tok.nextToken());
372     String str = tok.nextToken();
373     return new SimpleList(quality, offset, length, str);
374   }
375
376   private static List parseURL(String s) throws MalformedURLException,
377       NumberFormatException
378   {
379     StringTokenizer tok = new StringTokenizer(s, ";");
380     if (tok.countTokens() != 4)
381       throw new IllegalArgumentException("malformed property");
382     Double quality = new Double(tok.nextToken());
383     Integer offset = new Integer(tok.nextToken());
384     Integer length = new Integer(tok.nextToken());
385     URL url = new URL(tok.nextToken());
386     return new SimpleList(quality, offset, length, url);
387   }
388
389   public Object clone()
390   {
391     return new CSPRNG();
392   }
393
394   public void setup(Map attrib)
395   {
396     List list = null;
397     if (Configuration.DEBUG)
398       log.fine("attrib=" + String.valueOf(attrib));
399     try
400       {
401         list = (List) attrib.get(FILE_SOURCES);
402         if (Configuration.DEBUG)
403           log.fine("list=" + String.valueOf(list));
404         if (list != null)
405           {
406             files.clear();
407             for (Iterator it = list.iterator(); it.hasNext();)
408               {
409                 List l = (List) it.next();
410                 if (Configuration.DEBUG)
411                   log.fine("l=" + l);
412                 if (l.size() != 4)
413                   {
414                     if (Configuration.DEBUG)
415                       log.fine("file list too small: " + l.size());
416                     throw new IllegalArgumentException("invalid file list");
417                   }
418                 Double quality = (Double) l.get(0);
419                 Integer offset = (Integer) l.get(1);
420                 Integer length = (Integer) l.get(2);
421                 String source = (String) l.get(3);
422                 files.add(new SimpleList(quality, offset, length, source));
423               }
424           }
425       }
426     catch (ClassCastException cce)
427       {
428         if (Configuration.DEBUG)
429           log.log(Level.FINE, "bad file list", cce);
430         throw new IllegalArgumentException("invalid file list");
431       }
432     try
433       {
434         list = (List) attrib.get(URL_SOURCES);
435         if (Configuration.DEBUG)
436           log.fine("list=" + String.valueOf(list));
437         if (list != null)
438           {
439             urls.clear();
440             for (Iterator it = list.iterator(); it.hasNext();)
441               {
442                 List l = (List) it.next();
443                 if (Configuration.DEBUG)
444                   log.fine("l=" + l);
445                 if (l.size() != 4)
446                   {
447                     if (Configuration.DEBUG)
448                       log.fine("URL list too small: " + l.size());
449                     throw new IllegalArgumentException("invalid URL list");
450                   }
451                 Double quality = (Double) l.get(0);
452                 Integer offset = (Integer) l.get(1);
453                 Integer length = (Integer) l.get(2);
454                 URL source = (URL) l.get(3);
455                 urls.add(new SimpleList(quality, offset, length, source));
456               }
457           }
458       }
459     catch (ClassCastException cce)
460       {
461         if (Configuration.DEBUG)
462           log.log(Level.FINE, "bad URL list", cce);
463         throw new IllegalArgumentException("invalid URL list");
464       }
465     try
466       {
467         list = (List) attrib.get(PROGRAM_SOURCES);
468         if (Configuration.DEBUG)
469           log.fine("list=" + String.valueOf(list));
470         if (list != null)
471           {
472             progs.clear();
473             for (Iterator it = list.iterator(); it.hasNext();)
474               {
475                 List l = (List) it.next();
476                 if (Configuration.DEBUG)
477                   log.fine("l=" + l);
478                 if (l.size() != 4)
479                   {
480                     if (Configuration.DEBUG)
481                       log.fine("program list too small: " + l.size());
482                     throw new IllegalArgumentException("invalid program list");
483                   }
484                 Double quality = (Double) l.get(0);
485                 Integer offset = (Integer) l.get(1);
486                 Integer length = (Integer) l.get(2);
487                 String source = (String) l.get(3);
488                 progs.add(new SimpleList(quality, offset, length, source));
489               }
490           }
491       }
492     catch (ClassCastException cce)
493       {
494         if (Configuration.DEBUG)
495           log.log(Level.FINE, "bad program list", cce);
496         throw new IllegalArgumentException("invalid program list");
497       }
498     try
499       {
500         list = (List) attrib.get(OTHER_SOURCES);
501         if (Configuration.DEBUG)
502           log.fine("list=" + String.valueOf(list));
503         if (list != null)
504           {
505             other.clear();
506             for (Iterator it = list.iterator(); it.hasNext();)
507               {
508                 EntropySource src = (EntropySource) it.next();
509                 if (Configuration.DEBUG)
510                   log.fine("src=" + src);
511                 if (src == null)
512                   throw new NullPointerException("null source in source list");
513                 other.add(src);
514               }
515           }
516       }
517     catch (ClassCastException cce)
518       {
519         throw new IllegalArgumentException("invalid source list");
520       }
521
522     try
523       {
524         Boolean block = (Boolean) attrib.get(BLOCKING);
525         if (block != null)
526           blocking = block.booleanValue();
527         else
528           blocking = true;
529       }
530     catch (ClassCastException cce)
531       {
532         throw new IllegalArgumentException("invalid blocking parameter");
533       }
534     poller = new Poller(files, urls, progs, other, this);
535     try
536       {
537         fillBlock();
538       }
539     catch (LimitReachedException lre)
540       {
541         throw new RuntimeException("bootstrapping CSPRNG failed");
542       }
543   }
544
545   public void fillBlock() throws LimitReachedException
546   {
547     if (Configuration.DEBUG)
548       log.fine("fillBlock");
549     if (getQuality() < 100.0)
550       {
551         if (Configuration.DEBUG)
552           log.fine("doing slow poll");
553         slowPoll();
554       }
555     do
556       {
557         fastPoll();
558         mixRandomPool();
559       }
560     while (mixCount < MIX_COUNT);
561     if (! x917init || x917count >= X917_LIFETIME)
562       {
563         mixRandomPool(pool);
564         Map attr = new HashMap();
565         byte[] key = new byte[32];
566         System.arraycopy(pool, 0, key, 0, 32);
567         cipher.reset();
568         attr.put(IBlockCipher.KEY_MATERIAL, key);
569         try
570           {
571             cipher.init(attr);
572           }
573         catch (InvalidKeyException ike)
574           {
575             throw new Error(ike.toString());
576           }
577         mixRandomPool(pool);
578         generateX917(pool);
579         mixRandomPool(pool);
580         generateX917(pool);
581         if (x917init)
582           quality = 0.0;
583         x917init = true;
584         x917count = 0;
585       }
586     byte[] export = new byte[ALLOC_SIZE];
587     for (int i = 0; i < ALLOC_SIZE; i++)
588       export[i] = (byte)(pool[i] ^ 0xFF);
589     mixRandomPool();
590     mixRandomPool(export);
591     generateX917(export);
592     for (int i = 0; i < OUTPUT_SIZE; i++)
593       buffer[i] = (byte)(export[i] ^ export[i + OUTPUT_SIZE]);
594     Arrays.fill(export, (byte) 0);
595   }
596
597   /**
598    * Add an array of bytes into the randomness pool. Note that this method will
599    * <i>not</i> increment the pool's quality counter (this can only be done via
600    * a source provided to the setup method).
601    *
602    * @param buf The byte array.
603    * @param off The offset from whence to start reading bytes.
604    * @param len The number of bytes to add.
605    * @throws ArrayIndexOutOfBoundsException If <i>off</i> or <i>len</i> are
606    *           out of the range of <i>buf</i>.
607    */
608   public synchronized void addRandomBytes(byte[] buf, int off, int len)
609   {
610     if (off < 0 || len < 0 || off + len > buf.length)
611       throw new ArrayIndexOutOfBoundsException();
612     if (Configuration.DEBUG)
613       {
614         log.fine("adding random bytes:");
615         log.fine(Util.toString(buf, off, len));
616       }
617     final int count = off + len;
618     for (int i = off; i < count; i++)
619       {
620         pool[index++] ^= buf[i];
621         if (index == pool.length)
622           {
623             mixRandomPool();
624             index = 0;
625           }
626       }
627   }
628
629   /**
630    * Add a single random byte to the randomness pool. Note that this method will
631    * <i>not</i> increment the pool's quality counter (this can only be done via
632    * a source provided to the setup method).
633    *
634    * @param b The byte to add.
635    */
636   public synchronized void addRandomByte(byte b)
637   {
638     if (Configuration.DEBUG)
639       log.fine("adding byte " + Integer.toHexString(b));
640     pool[index++] ^= b;
641     if (index >= pool.length)
642       {
643         mixRandomPool();
644         index = 0;
645       }
646   }
647
648   synchronized void addQuality(double quality)
649   {
650     if (Configuration.DEBUG)
651       log.fine("adding quality " + quality);
652     if (this.quality < 100)
653       this.quality += quality;
654     if (Configuration.DEBUG)
655       log.fine("quality now " + this.quality);
656   }
657
658   synchronized double getQuality()
659   {
660     return quality;
661   }
662
663   /**
664    * The mix operation. This method will, for every 20-byte block in the random
665    * pool, hash that block, the previous 20 bytes, and the next 44 bytes with
666    * SHA-1, writing the result back into that block.
667    */
668   private void mixRandomPool(byte[] buf)
669   {
670     int hashSize = hash.hashSize();
671     for (int i = 0; i < buf.length; i += hashSize)
672       {
673         // First update the bytes [p-19..p-1].
674         if (i == 0)
675           hash.update(buf, buf.length - hashSize, hashSize);
676         else
677           hash.update(buf, i - hashSize, hashSize);
678         // Now the next 64 bytes.
679         if (i + 64 < buf.length)
680           hash.update(buf, i, 64);
681         else
682           {
683             hash.update(buf, i, buf.length - i);
684             hash.update(buf, 0, 64 - (buf.length - i));
685           }
686         byte[] digest = hash.digest();
687         System.arraycopy(digest, 0, buf, i, hashSize);
688       }
689   }
690
691   private void mixRandomPool()
692   {
693     mixRandomPool(pool);
694     mixCount++;
695   }
696
697   private void generateX917(byte[] buf)
698   {
699     int off = 0;
700     for (int i = 0; i < buf.length; i += X917_POOL_SIZE)
701       {
702         int copy = Math.min(buf.length - i, X917_POOL_SIZE);
703         for (int j = 0; j < copy; j++)
704           x917pool[j] ^= pool[off + j];
705         cipher.encryptBlock(x917pool, 0, x917pool, 0);
706         System.arraycopy(x917pool, 0, buf, off, copy);
707         cipher.encryptBlock(x917pool, 0, x917pool, 0);
708         off += copy;
709         x917count++;
710       }
711   }
712
713   /**
714    * Add random data always immediately available into the random pool, such as
715    * the values of the eight asynchronous counters, the current time, the
716    * current memory usage, the calling thread name, and the current stack trace.
717    * <p>
718    * This method does not alter the quality counter, and is provided more to
719    * maintain randomness, not to seriously improve the current random state.
720    */
721   private void fastPoll()
722   {
723     byte b = 0;
724     for (int i = 0; i < SPINNER_COUNT; i++)
725       b ^= SPINNERS[i].counter;
726     addRandomByte(b);
727     addRandomByte((byte) System.currentTimeMillis());
728     addRandomByte((byte) Runtime.getRuntime().freeMemory());
729     String s = Thread.currentThread().getName();
730     if (s != null)
731       {
732         byte[] buf = s.getBytes();
733         addRandomBytes(buf, 0, buf.length);
734       }
735     ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
736     PrintStream pout = new PrintStream(bout);
737     Throwable t = new Throwable();
738     t.printStackTrace(pout);
739     pout.flush();
740     byte[] buf = bout.toByteArray();
741     addRandomBytes(buf, 0, buf.length);
742   }
743
744   private void slowPoll() throws LimitReachedException
745   {
746     if (Configuration.DEBUG)
747       log.fine("poller is alive? "
748                + (pollerThread == null ? false : pollerThread.isAlive()));
749     if (pollerThread == null || ! pollerThread.isAlive())
750       {
751         boolean interrupted = false;
752         pollerThread = new Thread(poller);
753         pollerThread.setDaemon(true);
754         pollerThread.setPriority(Thread.NORM_PRIORITY - 1);
755         pollerThread.start();
756         if (blocking)
757           try
758             {
759               pollerThread.join();
760             }
761           catch (InterruptedException ie)
762             {
763               interrupted = true;
764             }
765         // If the full slow poll has completed after we waited for it,
766         // and there in insufficient randomness, throw an exception.
767         if (! interrupted && blocking && quality < 100.0)
768           {
769             if (Configuration.DEBUG)
770               log.fine("insufficient quality: " + quality);
771             throw new LimitReachedException("insufficient randomness was polled");
772           }
773       }
774   }
775
776   protected void finalize() throws Throwable
777   {
778     if (poller != null && pollerThread != null && pollerThread.isAlive())
779       {
780         pollerThread.interrupt();
781         poller.stopUpdating();
782         pollerThread.interrupt();
783       }
784     Arrays.fill(pool, (byte) 0);
785     Arrays.fill(x917pool, (byte) 0);
786     Arrays.fill(buffer, (byte) 0);
787   }
788
789   /**
790    * A simple thread that constantly updates a byte counter. This class is used
791    * in a group of lowest-priority threads and the values of their counters
792    * (updated in competition with all other threads) is used as a source of
793    * entropy bits.
794    */
795   private static class Spinner
796       implements Runnable
797   {
798     protected byte counter;
799
800     private Spinner()
801     {
802     }
803
804     public void run()
805     {
806       while (true)
807         {
808           counter++;
809           try
810             {
811               Thread.sleep(100);
812             }
813           catch (InterruptedException ie)
814             {
815             }
816         }
817     }
818   }
819
820   private final class Poller
821       implements Runnable
822   {
823     private final List files;
824     private final List urls;
825     private final List progs;
826     private final List other;
827     private final CSPRNG pool;
828     private boolean running;
829
830     Poller(List files, List urls, List progs, List other, CSPRNG pool)
831     {
832       super();
833       this.files = Collections.unmodifiableList(files);
834       this.urls = Collections.unmodifiableList(urls);
835       this.progs = Collections.unmodifiableList(progs);
836       this.other = Collections.unmodifiableList(other);
837       this.pool = pool;
838     }
839
840     public void run()
841     {
842       running = true;
843       if (Configuration.DEBUG)
844         {
845           log.fine("files: " + files);
846           log.fine("URLs: " + urls);
847           log.fine("progs: " + progs);
848         }
849       Iterator files_it = files.iterator();
850       Iterator urls_it = urls.iterator();
851       Iterator prog_it = progs.iterator();
852       Iterator other_it = other.iterator();
853
854       while (files_it.hasNext() || urls_it.hasNext() || prog_it.hasNext()
855              || other_it.hasNext())
856         {
857           // There is enough random data. Go away.
858           if (pool.getQuality() >= 100.0 || ! running)
859             return;
860           if (files_it.hasNext())
861             try
862               {
863                 List l = (List) files_it.next();
864                 if (Configuration.DEBUG)
865                   log.fine(l.toString());
866                 double qual = ((Double) l.get(0)).doubleValue();
867                 int offset = ((Integer) l.get(1)).intValue();
868                 int count = ((Integer) l.get(2)).intValue();
869                 String src = (String) l.get(3);
870                 InputStream in = new FileInputStream(src);
871                 byte[] buf = new byte[count];
872                 if (offset > 0)
873                   in.skip(offset);
874                 int len = in.read(buf);
875                 if (len >= 0)
876                   {
877                     pool.addRandomBytes(buf, 0, len);
878                     pool.addQuality(qual * ((double) len / (double) count));
879                   }
880                 if (Configuration.DEBUG)
881                   log.fine("got " + len + " bytes from " + src);
882               }
883             catch (Exception x)
884               {
885                 if (Configuration.DEBUG)
886                   log.throwing(this.getClass().getName(), "run", x);
887               }
888           if (pool.getQuality() >= 100.0 || ! running)
889             return;
890           if (urls_it.hasNext())
891             try
892               {
893                 List l = (List) urls_it.next();
894                 if (Configuration.DEBUG)
895                   log.fine(l.toString());
896                 double qual = ((Double) l.get(0)).doubleValue();
897                 int offset = ((Integer) l.get(1)).intValue();
898                 int count = ((Integer) l.get(2)).intValue();
899                 URL src = (URL) l.get(3);
900                 InputStream in = src.openStream();
901                 byte[] buf = new byte[count];
902                 if (offset > 0)
903                   in.skip(offset);
904                 int len = in.read(buf);
905                 if (len >= 0)
906                   {
907                     pool.addRandomBytes(buf, 0, len);
908                     pool.addQuality(qual * ((double) len / (double) count));
909                   }
910                 if (Configuration.DEBUG)
911                   log.fine("got " + len + " bytes from " + src);
912               }
913             catch (Exception x)
914               {
915                 if (Configuration.DEBUG)
916                   log.throwing(this.getClass().getName(), "run", x);
917               }
918           if (pool.getQuality() >= 100.0 || ! running)
919             return;
920           Process proc = null;
921           if (prog_it.hasNext())
922             try
923               {
924                 List l = (List) prog_it.next();
925                 if (Configuration.DEBUG)
926                   log.finer(l.toString());
927                 double qual = ((Double) l.get(0)).doubleValue();
928                 int offset = ((Integer) l.get(1)).intValue();
929                 int count = ((Integer) l.get(2)).intValue();
930                 String src = (String) l.get(3);
931                 proc = null;
932                 proc = Runtime.getRuntime().exec(src);
933                 InputStream in = proc.getInputStream();
934                 byte[] buf = new byte[count];
935                 if (offset > 0)
936                   in.skip(offset);
937                 int len = in.read(buf);
938                 if (len >= 0)
939                   {
940                     pool.addRandomBytes(buf, 0, len);
941                     pool.addQuality(qual * ((double) len / (double) count));
942                   }
943                 proc.destroy();
944                 proc.waitFor();
945                 if (Configuration.DEBUG)
946                   log.fine("got " + len + " bytes from " + src);
947               }
948             catch (Exception x)
949               {
950                 if (Configuration.DEBUG)
951                   log.throwing(this.getClass().getName(), "run", x);
952                 try
953                   {
954                     if (proc != null)
955                       {
956                         proc.destroy();
957                         proc.waitFor();
958                       }
959                   }
960                 catch (Exception ignored)
961                   {
962                   }
963               }
964           if (pool.getQuality() >= 100.0 || ! running)
965             return;
966           if (other_it.hasNext())
967             try
968               {
969                 EntropySource src = (EntropySource) other_it.next();
970                 byte[] buf = src.nextBytes();
971                 if (pool == null)
972                   return;
973                 pool.addRandomBytes(buf, 0, buf.length);
974                 pool.addQuality(src.quality());
975                 if (Configuration.DEBUG)
976                   log.fine("got " + buf.length + " bytes from " + src);
977               }
978             catch (Exception x)
979               {
980                 if (Configuration.DEBUG)
981                   log.throwing(this.getClass().getName(), "run", x);
982               }
983         }
984     }
985
986     public void stopUpdating()
987     {
988       running = false;
989     }
990   }
991 }