home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-03-20 | 10.0 KB | 293 lines |
- /*
- * @(#)SecureRandom.java 1.21 98/03/18
- *
- * Copyright 1996-1998 by Sun Microsystems, Inc.,
- * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
- * All rights reserved.
- *
- * This software is the confidential and proprietary information
- * of Sun Microsystems, Inc. ("Confidential Information"). You
- * shall not disclose such Confidential Information and shall use
- * it only in accordance with the terms of the license agreement
- * you entered into with Sun.
- */
-
- package java.security;
-
- import java.io.IOException;
- import java.util.*;
-
- /**
- * <p>This class provides a crytpographically strong pseudo-random number
- * generator based on the SHA-1 hash algorithm.
- *
- * <p>The calls inherited from Random will be implemented in terms of the
- * strengthened functionality.
- *
- * <p>Note that if a seed is not provided, we attempt to provide sufficient
- * seed bytes to completely randomize the internal state of the generator
- * (20 bytes). However, our seed generation algorithm has not been thoroughly
- * studied or widely deployed. It relies on counting the number of times that
- * the calling thread can yield while waiting for another thread to sleep for
- * a specified interval.
- *
- * <p>Also note that when a random object is deserialized,
- * <a href="#nextBytes(byte[])">nextBytes</a> invoked on the restored random
- * object will yield the exact same (random) bytes as the original object.
- * If this behaviour is not desired, the restored random object should be
- * seeded, using <a href="#setSeed(byte[])">setSeed</a>.
- *
- * @see java.util.Random
- *
- * @version 1.2 96/09/15
- * @author Benjamin Renaud
- * @author Josh Bloch
- */
-
- public class SecureRandom extends Random {
- private byte[] state;
- private transient MessageDigest digest;
-
- /**
- * Used by the empty constructor to seed the SecureRandom under
- * construction
- */
- private static SecureRandom generatorGenerator;
-
- /**
- * This empty constructor automatically seeds the generator. We attempt
- * to provide sufficient seed bytes to completely randomize the internal
- * state of the generator (20 bytes). Note, however, that our seed
- * generation algorithm has not been thoroughly studied or widely deployed.
- * It relies on counting the number of times that the calling thread
- * can yield while waiting for another thread to sleep for a specified
- * interval.
- *
- * <p>The first time this constructor is called in a given Virtual Machine,
- * it may take several seconds of CPU time to seed the generator, depending
- * on the underlying hardware. Successive calls run quickly because they
- * rely on the same (internal) pseudo-random number generator for their
- * seed bits.
- *
- * <p>The seeding procedure implemented by this constructor ensures that
- * the sequence of pseudo-random bytes produced by each SecureRandom
- * instance yields no useful information about the byte-sequence produced
- * by any other instance. If however, the user wishes to produce multiple
- * instances with truly unrelated seeds, the following code yields
- * the desired result (at substantial CPU cost per instance!):<p>
- *
- * <pre>
- * SecureRandom rnd = new SecureRandom(SecureRandom.getSeed(20));
- * </pre>
- */
- public SecureRandom() {
- this(nextSeed());
- }
-
- /**
- * This call, used exclusively by the empty constructor, returns 20 seed
- * bytes to seed the SecureRandom instance under construction. The first
- * time this method is called, creates a class-wide generator-generator.
- * This involves generating 20 "real-random" bytes with getSeed, which is
- * very time consuming!
- */
- private synchronized static byte[] nextSeed() {
- if (generatorGenerator == null)
- generatorGenerator = new SecureRandom(getSeed(20));
-
- byte seed[] = new byte[20];
- generatorGenerator.nextBytes(seed);
- return seed;
- }
-
-
- /**
- * This constructor uses a user-provided seed in preference to the
- * self-seeding algorithm referred to in the empty constructor
- * description. It may be preferable to the empty constructor if the
- * caller has access to high-quality random bytes from some physical
- * device (for example, a radiation detector or a noisy diode).
- *
- * @param seed the seed.
- */
- public SecureRandom(byte seed[]) {
- /*
- * This call to our superclass constructor will result in a call
- * to our own setSeed method, which will return immediately when
- * it is passed zero.
- */
- super(0);
-
- try {
- digest = MessageDigest.getInstance("SHA");
- } catch (NoSuchAlgorithmException e) {
- throw new InternalError("internal error: SHA-1 not available.");
- }
-
- setSeed(seed);
- }
-
- /**
- * Reseeds this random object. The given seed supplements, rather than
- * replaces, the existing seed. Thus, repeated calls are guaranteed
- * never to reduce randomness.
- *
- * @param seed the seed.
- */
- synchronized public void setSeed(byte[] seed) {
- if (state != null)
- digest.update(state);
- state = digest.digest(seed);
- }
-
- /**
- * Reseeds this random object, using the eight bytes contained
- * in the given <code>long seed</code>. The given seed supplements,
- * rather than replaces, the existing seed. Thus, repeated calls
- * are guaranteed never to reduce randomness.
- *
- * <p>This method is defined for compatibility with
- * <code>java.util.Random</code>.
- *
- * @param seed the seed.
- */
- public void setSeed(long seed) {
- /*
- * Ignore call from super constructor (as well as any other calls
- * unfortunate enough to be passing 0). It's critical that we
- * ignore call from superclass constructor, as digest has not
- * yet been initialized at that point.
- */
- if (seed != 0)
- setSeed(longToByteArray(seed));
- }
-
- private byte[] randomBytes = null;
- private int randomBytesUsed = 12;
- private long counter = 0; /* # of times we've generated randomBytes */
-
- /**
- * Generates a user-specified number of random bytes. This method is
- * used as the basis of all random entities returned by this class
- * (except seed bytes). Thus, it may be overridden to change the
- * behavior of the class.
- *
- * @param bytes the array to be filled in with random bytes.
- */
-
- synchronized public void nextBytes(byte[] bytes) {
- int numRequested = bytes.length;
- int numGot = 0;
-
- /*
- * This method follows IEEE P1363, Appendix G.7, page 88
- * (expansion of source bits):
- *
- * A cryptographically strong hash function (such as SHA-1)
- * computed over a true-random seed value concatenated with
- * a counter which is incremented for each operation. [For
- * example, one could have 256 bits of true-random seed and
- * a 64 bit counter, all hashed by SHA to yield 160 bits of
- * output, from which only 64 are used -- with the counter
- * incremented by 1 for each output batch. ]
- */
-
- while (numGot < numRequested) {
- /* If no more random bytes, make some more */
- if (randomBytes == null || randomBytesUsed == randomBytes.length) {
- digest.update(state);
- randomBytes = digest.digest(longToByteArray(counter++));
- randomBytesUsed = 12;
- }
-
- bytes[numGot++] = randomBytes[randomBytesUsed++];
- }
- }
-
- /**
- * Generates an integer containing the user-specified number of
- * pseudo-random bits (right justified, with leading zeros). This
- * method overrides a <code>java.util.Random</code> method, and serves
- * to provide a source of random bits to all of the methods inherited
- * from that class (for example, <code>nextInt</code>,
- * <code>nextLong</code>, and <code>nextFloat</code>).
- *
- * @param numBits number of pseudo-random bits to be generated, where
- * 0 <= <code>numBits</code> <= 32.
- */
- final protected int next(int numBits) {
- int numBytes = (numBits+7)/8;
- byte b[] = new byte[numBytes];
- int next = 0;
-
- nextBytes(b);
- for (int i=0; i<numBytes; i++)
- next = (next << 8) + (b[i] & 0xFF);
-
- return next >>> (numBytes*8 - numBits);
- }
-
- /**
- * Returns the given number of seed bytes, computed using the seed
- * generation algorithm that this class uses to seed itself. This
- * call may be used to seed other random number generators. While
- * we attempt to return a "truly random" sequence of bytes, we do not
- * know exactly how random the bytes returned by this call are. (See
- * the empty constructor <a href = "#SecureRandom">SecureRandom</a>
- * for a brief description of the underlying algorithm.)
- * The prudent user will err on the side of caution and get extra
- * seed bytes, although it should be noted that seed generation is
- * somewhat costly.
- *
- * @param numBytes the number of seed bytes to generate.
- *
- * @return the seed bytes.
- */
- public static byte[] getSeed(int numBytes) {
- byte[] retVal = new byte[numBytes];
-
- for (int i=0; i<numBytes; i++)
- retVal[i] = (byte) SeedGenerator.genSeed();
-
- return retVal;
- }
-
-
- /**
- * Helper function to convert a long into a byte array (least significant
- * byte first).
- */
- private static byte[] longToByteArray(long l) {
- byte[] retVal = new byte[8];
-
- for (int i=0; i<8; i++) {
- retVal[i] = (byte) l;
- l >>= 8;
- }
-
- return retVal;
- }
-
- /*
- * readObject is called to restore the state of the random object from
- * a stream. We have to create a new instance of MessageDigest, because
- * it is not included in the stream (it is marked "transient").
- *
- * Note that the nextBytes() method invoked on the restored random object
- * will yield the exact same (random) bytes as the original. If you do not
- * want this behaviour, you should re-seed the restored random object,
- * using setSeed().
- */
- private void readObject(java.io.ObjectInputStream s)
- throws IOException, ClassNotFoundException {
-
- s.defaultReadObject();
-
- try {
- digest = MessageDigest.getInstance("SHA");
- } catch (NoSuchAlgorithmException e) {
- throw new InternalError("internal error: SHA-1 not available");
- }
- }
- }
-