Source for gnu.javax.crypto.prng.CSPRNG

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