OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [javax/] [crypto/] [prng/] [CSPRNG.java] - Blame information for rev 769

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* 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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.