Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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}