home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / security / SeedGenerator.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  6.1 KB  |  185 lines

  1. /*
  2.  * @(#)SeedGenerator.java    1.8 98/03/18
  3.  *
  4.  * Copyright 1996, 1997 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.security;
  16.  
  17. /**
  18.  * This class exports a single static method (genSeed) that generates
  19.  * a seed for a cryptographically strong random number generator.  The
  20.  * goal is to provide a seed whose least significant byte is "truly random."
  21.  * The seed is produced by creating a "sleeper thread" that sleeps for a
  22.  * designated time period, and spinning on Thread.yield() while waiting
  23.  * for the sleeper thread to terminate.  The parent thread keeps track of
  24.  * the number of times that it yields, and returns this "spin count"
  25.  * as a seed.  Remarkably, the low order bits of this seed seem to be
  26.  * quite random.
  27.  *
  28.  * @version 1.8, 98/03/18
  29.  * @author Josh Bloch
  30.  */
  31.  
  32. class SeedGenerator {
  33.     private static int sleepTime;
  34.     static {
  35.     setSleepTime();
  36.     }
  37.  
  38.     private static final int TARGET_SPIN_COUNT = 55000;
  39.     private static final int MIN_SPIN_COUNT = (6*TARGET_SPIN_COUNT)/10;
  40.     private static final int MAX_SPIN_COUNT = 2*TARGET_SPIN_COUNT;
  41.     private static final int MAX_SLEEP_TIME = 30000;    // 30 seconds
  42.     private static final int MAX_ATTEMPTS = 5;
  43.  
  44.     /**
  45.      * This method calculates a sleep time that results in ~55000 thread
  46.      * yields.  Experimentally, this seems sufficient to generate one random
  47.      * byte.  Note that this  method (which "performs an experiment")requires
  48.      * approximately one second to execute.  It is called once when the class
  49.      * is loaded, and again if the computed sleep time "stops doing its job."
  50.      * (This will happen if the load on the machine changes drastically.)
  51.      *
  52.      * If the machine is heavily loaded, the calculated sleepTime may
  53.      * turn out to be quite high (possibly hours).  If such a value is
  54.      * generated, reset sleepTime to a more reasonble time, MAX_SLEEP_TIME;
  55.      */
  56.     private static void setSleepTime() {
  57.  
  58.     sleepTime = (1000*TARGET_SPIN_COUNT)/genSeed(1000);
  59.     if (sleepTime > MAX_SLEEP_TIME)
  60.         sleepTime = MAX_SLEEP_TIME;
  61.  
  62.     Security.debug("Resetting sleep time for seed generation: "
  63.                + sleepTime + " ms.");
  64.      }
  65.  
  66.     /**
  67.      * genSeed() - The sole exported method from this class; generates a
  68.      * random seed, an integer whose last byte is intended to be "truly
  69.      * random".  (Higher order bits may have some randomness as well.)
  70.      * This method is synchronized for two reasons: 1) it has the side effect
  71.      * of maintaining sleepTime, which is a static variable, and 2) having
  72.      * multiple threads generate seeds concurrently would likely result in
  73.      * unnecessary (and inefficent) increases in sleepTime.
  74.      */
  75.     public synchronized static int genSeed() {
  76.     int candidate = genSeed(sleepTime);
  77.  
  78.     /*
  79.      * If candidate is way too low, recalculate sleep time until it
  80.      * isn't.  This is necessary to thwart an attack where our adversary
  81.      * loads down the machine in order to reduce the quality of the
  82.      * seed generation.
  83.      *
  84.      * Only try this MAX_ATTEMPTS number of times so that we don't
  85.      * get into an infinite loop.
  86.      */
  87.     int attempts = 0;
  88.     while (candidate < MIN_SPIN_COUNT && attempts < MAX_ATTEMPTS) {
  89.         Security.debug("Candidate seed too low: "+ candidate +" ms.");
  90.         setSleepTime();
  91.         candidate = genSeed(sleepTime);
  92.         attempts++;
  93.     }
  94.  
  95.     if (attempts > MAX_ATTEMPTS)
  96.         throw new SecurityException("unable to generate a quality seed");
  97.  
  98.     /*
  99.      * If candidate is way too high, recalculate sleep time, but DO use
  100.      * the candidate (which is of HIGHER quality than necessary).  This
  101.      * step merely reduces the cost to calculate subsequent seed bytes.
  102.      * It isn't necessarily a win, as the recalculation is time consuming,
  103.      * but it prevents seed calculation time from unnecessarily
  104.      * "ratcheting up" over time.
  105.      */
  106.     if (candidate > MAX_SPIN_COUNT) {
  107.         Security.debug("Candidate seed too high: "+ candidate +" ms.");
  108.         setSleepTime();
  109.     }
  110.  
  111.     return candidate;
  112.     }
  113.  
  114.     /**
  115.      * Generate a random seed by spinning on thread yield while waiting for
  116.      * a child thread that sleeps for the designated period of time, in
  117.      * milliseconds.  The returned seed is the number of times we yield
  118.      * whilst waiting for the child.
  119.      */
  120.     private static int genSeed(int sleepTime) {
  121.     int counter = 1;
  122.     int priority = Thread.currentThread().getPriority();
  123.     Thread sleeper = null;
  124.  
  125.     try {
  126.         java.security.AccessController.beginPrivileged();
  127.         sleeper = new Sleeper(sleepTime);
  128.  
  129.         if (sleeper.getPriority() != priority) {
  130.         /*
  131.          * fix for bug 4065510:
  132.          *
  133.          * 1) Make sure the sleeper thread runs at the same priority
  134.          *    as the current thread.
  135.          * 2) Only modify the priority of the current thread
  136.          *    if it differs from that of the sleeper thread
  137.          *    (this will be true for applets but not applications).
  138.          *
  139.          * Note that we must lower the current thread's priority
  140.          * (as opposed to raising the sleeper thread's priority)
  141.          * because if this is an applet, the max priority the sleeper
  142.          * thread can have is the current thread's priority - 1.
  143.          */
  144.         Thread.currentThread().setPriority(sleeper.getPriority());
  145.         }
  146.  
  147.         sleeper.start();
  148.  
  149.         while (sleeper.isAlive()) {
  150.         counter++;
  151.         if (counter < 0)
  152.             counter = 1;
  153.         Thread.yield();
  154.         }
  155.  
  156.     } finally {
  157.         // only reset the priority of the current thread if we changed it
  158.         if (Thread.currentThread().getPriority() != priority)
  159.         Thread.currentThread().setPriority(priority);
  160.  
  161.         java.security.AccessController.endPrivileged();
  162.     }
  163.     return counter;
  164.     }
  165. }
  166.  
  167. /**
  168.  * This helper class exports a "sleeper thread" that sleeps for a designated
  169.  * period (in milliseconds) and terminates.
  170.  */
  171. class Sleeper extends Thread {
  172.     private int sleepTime;
  173.  
  174.     Sleeper(int sleepTime) {
  175.     this.sleepTime = sleepTime;
  176.     }
  177.  
  178.     final public void run() {
  179.     try {
  180.         Thread.sleep(sleepTime);
  181.     } catch (InterruptedException e) {
  182.     }
  183.     }
  184. }
  185.