1 /* CSPRNG.java -- continuously-seeded pseudo-random number generator.
2 Copyright (C) 2004, 2006, 2010 Free Software Foundation, Inc.
4 This file is a part of GNU Classpath.
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.
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.
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
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
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. */
39 package gnu.javax.crypto.prng;
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;
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;
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;
71 import java.util.StringTokenizer;
72 import java.util.logging.Level;
73 import java.util.logging.Logger;
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>).
80 * The basic properties of this generator are:
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>
90 private static final Logger log = Configuration.DEBUG ?
91 Logger.getLogger(CSPRNG.class.getName()) : null;
94 * Property name for the list of files to read for random values. The mapped
95 * value is a list with the following values:
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>
105 * @see gnu.java.security.util.SimpleList
107 public static final String FILE_SOURCES = "gnu.crypto.prng.pool.files";
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}.
113 public static final String URL_SOURCES = "gnu.crypto.prng.pool.urls";
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.
120 public static final String PROGRAM_SOURCES = "gnu.crypto.prng.pool.programs";
122 * Property name for a list of other sources of entropy. The mapped value must
123 * be a list of {@link EntropySource} objects.
125 public static final String OTHER_SOURCES = "gnu.crypto.prng.pool.other";
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.
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;
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
152 private static final Spinner[] SPINNERS = new Spinner[SPINNER_COUNT];
153 private static final Thread[] SPINNER_THREADS = new Thread[SPINNER_COUNT];
156 for (int i = 0; i < SPINNER_COUNT; i++)
158 SPINNER_THREADS[i] = new Thread(SPINNERS[i] = new Spinner(),
160 SPINNER_THREADS[i].setDaemon(true);
161 SPINNER_THREADS[i].setPriority(Thread.MIN_PRIORITY);
162 SPINNER_THREADS[i].start();
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. */
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;
200 pool = new byte[ALLOC_SIZE];
201 x917pool = new byte[X917_POOL_SIZE];
205 hash = HashFactory.getInstance(HASH_FUNCTION);
206 cipher = CipherFactory.getInstance(CIPHER);
207 buffer = new byte[OUTPUT_SIZE];
210 files = new LinkedList();
211 urls = new LinkedList();
212 progs = new LinkedList();
213 other = new LinkedList();
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}.
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.
225 * The following vales for <i>name</i> are used here:
227 * <dt>gnu.crypto.csprng.file</dt>
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:
233 * <blockquote><i>quality</i> ; <i>offset</i> ; <i>count</i> ; <i>path</i></blockquote>
235 * The parameters are mapped to the parameters defined for {@link
236 * #FILE_SOURCES}. Leading or trailing spaces on any item are trimmed off.
239 * <dt>gnu.crypto.csprng.url</dt>
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.
247 * <dt>gnu.crypto.csprng.program</dt>
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
256 * <dt>gnu.crypto.cspring.other</dt>
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.
267 * Finally, a boolean property "gnu.crypto.csprng.blocking" can be set to the
268 * desired value of {@link #BLOCKING}.
270 * An example of valid properties would be:
272 * gnu.crypto.csprng.blocking=true
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
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/
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
283 * gnu.crypto.csprng.other.1=foo.bar.MyEntropySource
284 * gnu.crypto.csprng.other.2=com.company.OtherEntropySource
287 public static IRandom getSystemInstance() throws ClassNotFoundException,
288 MalformedURLException, NumberFormatException
290 CSPRNG instance = new CSPRNG();
291 HashMap attrib = new HashMap();
292 attrib.put(BLOCKING, Boolean.valueOf(getProperty(BLOCK)));
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++)
299 l.add(parseString(s.trim()));
301 catch (NumberFormatException nfe)
304 attrib.put(FILE_SOURCES, l);
305 l = new LinkedList();
306 for (int i = 0; (s = getProperty(URLS + i)) != null; i++)
309 l.add(parseURL(s.trim()));
311 catch (NumberFormatException nfe)
314 catch (MalformedURLException mue)
317 attrib.put(URL_SOURCES, l);
318 l = new LinkedList();
319 for (int i = 0; (s = getProperty(PROGS + i)) != null; i++)
322 l.add(parseString(s.trim()));
324 catch (NumberFormatException nfe)
327 attrib.put(PROGRAM_SOURCES, l);
328 l = new LinkedList();
329 for (int i = 0; (s = getProperty(OTHER + i)) != null; i++)
333 l.add((EntropySource)Class.forName(s.trim()).newInstance());
335 catch (ClassNotFoundException cnfe)
339 catch (InstantiationException ie)
343 catch (IllegalAccessException iae)
348 attrib.put(OTHER_SOURCES, l);
349 instance.init(attrib);
353 private static String getProperty(final String name)
355 return (String) AccessController.doPrivileged(new PrivilegedAction()
359 return Properties.getProperty(name);
364 private static List parseString(String s) throws NumberFormatException
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);
376 private static List parseURL(String s) throws MalformedURLException,
377 NumberFormatException
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);
389 public Object clone()
394 public void setup(Map attrib)
397 if (Configuration.DEBUG)
398 log.fine("attrib=" + String.valueOf(attrib));
401 list = (List) attrib.get(FILE_SOURCES);
402 if (Configuration.DEBUG)
403 log.fine("list=" + String.valueOf(list));
407 for (Iterator it = list.iterator(); it.hasNext();)
409 List l = (List) it.next();
410 if (Configuration.DEBUG)
414 if (Configuration.DEBUG)
415 log.fine("file list too small: " + l.size());
416 throw new IllegalArgumentException("invalid file list");
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));
426 catch (ClassCastException cce)
428 if (Configuration.DEBUG)
429 log.log(Level.FINE, "bad file list", cce);
430 throw new IllegalArgumentException("invalid file list");
434 list = (List) attrib.get(URL_SOURCES);
435 if (Configuration.DEBUG)
436 log.fine("list=" + String.valueOf(list));
440 for (Iterator it = list.iterator(); it.hasNext();)
442 List l = (List) it.next();
443 if (Configuration.DEBUG)
447 if (Configuration.DEBUG)
448 log.fine("URL list too small: " + l.size());
449 throw new IllegalArgumentException("invalid URL list");
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));
459 catch (ClassCastException cce)
461 if (Configuration.DEBUG)
462 log.log(Level.FINE, "bad URL list", cce);
463 throw new IllegalArgumentException("invalid URL list");
467 list = (List) attrib.get(PROGRAM_SOURCES);
468 if (Configuration.DEBUG)
469 log.fine("list=" + String.valueOf(list));
473 for (Iterator it = list.iterator(); it.hasNext();)
475 List l = (List) it.next();
476 if (Configuration.DEBUG)
480 if (Configuration.DEBUG)
481 log.fine("program list too small: " + l.size());
482 throw new IllegalArgumentException("invalid program list");
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));
492 catch (ClassCastException cce)
494 if (Configuration.DEBUG)
495 log.log(Level.FINE, "bad program list", cce);
496 throw new IllegalArgumentException("invalid program list");
500 list = (List) attrib.get(OTHER_SOURCES);
501 if (Configuration.DEBUG)
502 log.fine("list=" + String.valueOf(list));
506 for (Iterator it = list.iterator(); it.hasNext();)
508 EntropySource src = (EntropySource) it.next();
509 if (Configuration.DEBUG)
510 log.fine("src=" + src);
512 throw new NullPointerException("null source in source list");
517 catch (ClassCastException cce)
519 throw new IllegalArgumentException("invalid source list");
524 Boolean block = (Boolean) attrib.get(BLOCKING);
526 blocking = block.booleanValue();
530 catch (ClassCastException cce)
532 throw new IllegalArgumentException("invalid blocking parameter");
534 poller = new Poller(files, urls, progs, other, this);
539 catch (LimitReachedException lre)
541 throw new RuntimeException("bootstrapping CSPRNG failed");
545 public void fillBlock() throws LimitReachedException
547 if (Configuration.DEBUG)
548 log.fine("fillBlock");
549 if (getQuality() < 100.0)
551 if (Configuration.DEBUG)
552 log.fine("doing slow poll");
560 while (mixCount < MIX_COUNT);
561 if (! x917init || x917count >= X917_LIFETIME)
564 Map attr = new HashMap();
565 byte[] key = new byte[32];
566 System.arraycopy(pool, 0, key, 0, 32);
568 attr.put(IBlockCipher.KEY_MATERIAL, key);
573 catch (InvalidKeyException ike)
575 throw new Error(ike.toString());
586 byte[] export = new byte[ALLOC_SIZE];
587 for (int i = 0; i < ALLOC_SIZE; i++)
588 export[i] = (byte)(pool[i] ^ 0xFF);
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);
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).
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>.
608 public synchronized void addRandomBytes(byte[] buf, int off, int len)
610 if (off < 0 || len < 0 || off + len > buf.length)
611 throw new ArrayIndexOutOfBoundsException();
612 if (Configuration.DEBUG)
614 log.fine("adding random bytes:");
615 log.fine(Util.toString(buf, off, len));
617 final int count = off + len;
618 for (int i = off; i < count; i++)
620 pool[index++] ^= buf[i];
621 if (index == pool.length)
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).
634 * @param b The byte to add.
636 public synchronized void addRandomByte(byte b)
638 if (Configuration.DEBUG)
639 log.fine("adding byte " + Integer.toHexString(b));
641 if (index >= pool.length)
648 synchronized void addQuality(double quality)
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);
658 synchronized double getQuality()
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.
668 private void mixRandomPool(byte[] buf)
670 int hashSize = hash.hashSize();
671 for (int i = 0; i < buf.length; i += hashSize)
673 // First update the bytes [p-19..p-1].
675 hash.update(buf, buf.length - hashSize, hashSize);
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);
683 hash.update(buf, i, buf.length - i);
684 hash.update(buf, 0, 64 - (buf.length - i));
686 byte[] digest = hash.digest();
687 System.arraycopy(digest, 0, buf, i, hashSize);
691 private void mixRandomPool()
697 private void generateX917(byte[] buf)
700 for (int i = 0; i < buf.length; i += X917_POOL_SIZE)
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);
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.
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.
721 private void fastPoll()
724 for (int i = 0; i < SPINNER_COUNT; i++)
725 b ^= SPINNERS[i].counter;
727 addRandomByte((byte) System.currentTimeMillis());
728 addRandomByte((byte) Runtime.getRuntime().freeMemory());
729 String s = Thread.currentThread().getName();
732 byte[] buf = s.getBytes();
733 addRandomBytes(buf, 0, buf.length);
735 ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
736 PrintStream pout = new PrintStream(bout);
737 Throwable t = new Throwable();
738 t.printStackTrace(pout);
740 byte[] buf = bout.toByteArray();
741 addRandomBytes(buf, 0, buf.length);
744 private void slowPoll() throws LimitReachedException
746 if (Configuration.DEBUG)
747 log.fine("poller is alive? "
748 + (pollerThread == null ? false : pollerThread.isAlive()));
749 if (pollerThread == null || ! pollerThread.isAlive())
751 boolean interrupted = false;
752 pollerThread = new Thread(poller);
753 pollerThread.setDaemon(true);
754 pollerThread.setPriority(Thread.NORM_PRIORITY - 1);
755 pollerThread.start();
761 catch (InterruptedException ie)
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)
769 if (Configuration.DEBUG)
770 log.fine("insufficient quality: " + quality);
771 throw new LimitReachedException("insufficient randomness was polled");
776 protected void finalize() throws Throwable
778 if (poller != null && pollerThread != null && pollerThread.isAlive())
780 pollerThread.interrupt();
781 poller.stopUpdating();
782 pollerThread.interrupt();
784 Arrays.fill(pool, (byte) 0);
785 Arrays.fill(x917pool, (byte) 0);
786 Arrays.fill(buffer, (byte) 0);
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
795 private static class Spinner
798 protected byte counter;
813 catch (InterruptedException ie)
820 private final class Poller
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;
830 Poller(List files, List urls, List progs, List other, CSPRNG pool)
833 this.files = Collections.unmodifiableList(files);
834 this.urls = Collections.unmodifiableList(urls);
835 this.progs = Collections.unmodifiableList(progs);
836 this.other = Collections.unmodifiableList(other);
843 if (Configuration.DEBUG)
845 log.fine("files: " + files);
846 log.fine("URLs: " + urls);
847 log.fine("progs: " + progs);
849 Iterator files_it = files.iterator();
850 Iterator urls_it = urls.iterator();
851 Iterator prog_it = progs.iterator();
852 Iterator other_it = other.iterator();
854 while (files_it.hasNext() || urls_it.hasNext() || prog_it.hasNext()
855 || other_it.hasNext())
857 // There is enough random data. Go away.
858 if (pool.getQuality() >= 100.0 || ! running)
860 if (files_it.hasNext())
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];
874 int len = in.read(buf);
877 pool.addRandomBytes(buf, 0, len);
878 pool.addQuality(qual * ((double) len / (double) count));
880 if (Configuration.DEBUG)
881 log.fine("got " + len + " bytes from " + src);
885 if (Configuration.DEBUG)
886 log.throwing(this.getClass().getName(), "run", x);
888 if (pool.getQuality() >= 100.0 || ! running)
890 if (urls_it.hasNext())
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];
904 int len = in.read(buf);
907 pool.addRandomBytes(buf, 0, len);
908 pool.addQuality(qual * ((double) len / (double) count));
910 if (Configuration.DEBUG)
911 log.fine("got " + len + " bytes from " + src);
915 if (Configuration.DEBUG)
916 log.throwing(this.getClass().getName(), "run", x);
918 if (pool.getQuality() >= 100.0 || ! running)
921 if (prog_it.hasNext())
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);
932 proc = Runtime.getRuntime().exec(src);
933 InputStream in = proc.getInputStream();
934 byte[] buf = new byte[count];
937 int len = in.read(buf);
940 pool.addRandomBytes(buf, 0, len);
941 pool.addQuality(qual * ((double) len / (double) count));
945 if (Configuration.DEBUG)
946 log.fine("got " + len + " bytes from " + src);
950 if (Configuration.DEBUG)
951 log.throwing(this.getClass().getName(), "run", x);
960 catch (Exception ignored)
964 if (pool.getQuality() >= 100.0 || ! running)
966 if (other_it.hasNext())
969 EntropySource src = (EntropySource) other_it.next();
970 byte[] buf = src.nextBytes();
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);
980 if (Configuration.DEBUG)
981 log.throwing(this.getClass().getName(), "run", x);
986 public void stopUpdating()