FileDocCategorySizeDatePackage
SHA1PRNG_SecureRandomImpl.javaAPI DocAndroid 1.5 API20598Wed May 06 22:41:06 BST 2009org.apache.harmony.security.provider.crypto

SHA1PRNG_SecureRandomImpl

public class SHA1PRNG_SecureRandomImpl extends SecureRandomSpi implements Serializable, SHA1_Data
This class extends the SecureRandomSpi class implementing all its abstract methods.

To generate pseudo-random bits, the implementation uses technique described in the "Random Number Generator (RNG) algorithms" section, Appendix A, JavaTM Cryptography Architecture, API Specification&Reference

The class implements the Serializable interface.

Fields Summary
private static final long
serialVersionUID
private static final int[]
END_FLAGS
private static final int[]
RIGHT1
private static final int[]
RIGHT2
private static final int[]
LEFT
private static final int[]
MASK
private static final int
HASHBYTES_TO_USE
private static final int
FRAME_LENGTH
private static final int
COUNTER_BASE
private static final int
HASHCOPY_OFFSET
private static final int
EXTRAFRAME_OFFSET
private static final int
FRAME_OFFSET
private static final int
MAX_BYTES
private static final int
UNDEFINED
private static final int
SET_SEED
private static final int
NEXT_BYTES
private static SHA1PRNG_SecureRandomImpl
myRandom
private transient int[]
seed
private transient long
seedLength
private transient int[]
copies
private transient byte[]
nextBytes
private transient int
nextBIndex
private transient long
counter
private transient int
state
Constructors Summary
public SHA1PRNG_SecureRandomImpl()
Creates object and sets implementation variables to their initial values


    // The "seed" array is used to compute both "current seed hash" and "next bytes".
    //
    // As the "SHA1" algorithm computes a hash of entire seed by splitting it into
    // a number of the 512-bit length frames (512 bits = 64 bytes = 16 words),
    // "current seed hash" is a hash (5 words, 20 bytes) for all previous full frames;
    // remaining bytes are stored in the 0-15 word frame of the "seed" array.
    //
    // As for calculating "next bytes",
    // both remaining bytes and "current seed hash" are used,
    // to preserve the latter for following "setSeed(..)" commands,
    // the following technique is used:
    // - upon getting "nextBytes(byte[])" invoked, single or first in row,
    //   which requires computing new hash, that is, 
    //   there is no more bytes remaining from previous "next bytes" computation,
    //   remaining bytes are copied into the 21-36 word frame of the "copies" array;
    // - upon getting "setSeed(byte[])" invoked, single or first in row,
    //   remaining bytes are copied back. 

                    
      

        seed = new int[HASH_OFFSET + EXTRAFRAME_OFFSET];
        seed[HASH_OFFSET] = H0;
        seed[HASH_OFFSET + 1] = H1;
        seed[HASH_OFFSET + 2] = H2;
        seed[HASH_OFFSET + 3] = H3;
        seed[HASH_OFFSET + 4] = H4;

        seedLength = 0;
        copies = new int[2 * FRAME_LENGTH + EXTRAFRAME_OFFSET];
        nextBytes = new byte[DIGEST_LENGTH];
        nextBIndex = HASHBYTES_TO_USE;
        counter = COUNTER_BASE;
        state = UNDEFINED;
    
Methods Summary
protected byte[]engineGenerateSeed(int numBytes)
Returns a required number of random bytes.
The method overrides "engineGenerateSeed (int)" in class SecureRandomSpi.

param
numBytes - number of bytes to return; should be >= 0.
return
byte array containing bits in order from left to right
throws
InvalidParameterException - if numBytes < 0


        byte[] myBytes; // byte[] for bytes returned by "nextBytes()"

        if (numBytes < 0) {
            throw new NegativeArraySizeException(Messages.getString("security.171", numBytes)); //$NON-NLS-1$
        }
        if (numBytes == 0) {
            return new byte[0];
        }

        if (myRandom == null) {
            myRandom = new SHA1PRNG_SecureRandomImpl();
            myRandom.engineSetSeed(RandomBitsSupplier
                    .getRandomBits(DIGEST_LENGTH));
        }

        myBytes = new byte[numBytes];
        myRandom.engineNextBytes(myBytes);

        return myBytes;
    
protected voidengineNextBytes(byte[] bytes)
Writes random bytes into an array supplied. Bits in a byte are from left to right.
To generate random bytes, the "expansion of source bits" method is used, that is, the current seed with a 64-bit counter appended is used to compute new bits. The counter is incremented by 1 for each 20-byte output.
The method overrides engineNextBytes in class SecureRandomSpi.

param
bytes - byte array to be filled in with bytes
throws
NullPointerException - if null is passed to the "bytes" argument


        int i, n;

        long bits; // number of bits required by Secure Hash Standard
        int nextByteToReturn; // index of ready bytes in "bytes" array
        int lastWord; // index of last word in frame containing bytes
        final int extrabytes = 7;// # of bytes to add in order to computer # of 8 byte words

        if (bytes == null) {
            throw new NullPointerException(
                    Messages.getString("security.83", "bytes")); //$NON-NLS-1$ //$NON-NLS-2$
        }

        lastWord = seed[BYTES_OFFSET] == 0 ? 0
                : (seed[BYTES_OFFSET] + extrabytes) >> 3 - 1;

        if (state == UNDEFINED) {

            // no seed supplied by user, hence it is generated thus randomizing internal state
            updateSeed(RandomBitsSupplier.getRandomBits(DIGEST_LENGTH));
            nextBIndex = HASHBYTES_TO_USE;

        } else if (state == SET_SEED) {

            System.arraycopy(seed, HASH_OFFSET, copies, HASHCOPY_OFFSET,
                    EXTRAFRAME_OFFSET);

            // possible cases for 64-byte frame:
            //
            // seed bytes < 48      - remaining bytes are enough for all, 8 counter bytes, 
            //                        0x80, and 8 seedLength bytes; no extra frame required
            // 48 < seed bytes < 56 - remaining 9 bytes are for 0x80 and 8 counter bytes
            //                        extra frame contains only seedLength value at the end
            // seed bytes > 55      - extra frame contains both counter's bytes
            //                        at the beginning and seedLength value at the end;
            //                        note, that beginning extra bytes are not more than 8,
            //                        that is, only 2 extra words may be used

            // no need to set to "0" 3 words after "lastWord" and  
            // more than two words behind frame 
            for (i = lastWord + 3; i < FRAME_LENGTH + 2; i++) {
                seed[i] = 0;
            }

            bits = seedLength << 3 + 64; // transforming # of bytes into # of bits

            // putting # of bits into two last words (14,15) of 16 word frame in 
            // seed or copies array depending on total length after padding
            if (seed[BYTES_OFFSET] < MAX_BYTES) {
                seed[14] = (int) (bits >>> 32);
                seed[15] = (int) (bits & 0xFFFFFFFF);
            } else {
                copies[EXTRAFRAME_OFFSET + 14] = (int) (bits >>> 32);
                copies[EXTRAFRAME_OFFSET + 15] = (int) (bits & 0xFFFFFFFF);
            }

            nextBIndex = HASHBYTES_TO_USE; // skipping remaining random bits
        }
        state = NEXT_BYTES;

        if (bytes.length == 0) {
            return;
        }

        nextByteToReturn = 0;

        // possibly not all of HASHBYTES_TO_USE bytes were used previous time 
        n = (HASHBYTES_TO_USE - nextBIndex) < (bytes.length - nextByteToReturn) ? HASHBYTES_TO_USE
                - nextBIndex
                : bytes.length - nextByteToReturn;
        if (n > 0) {
            System.arraycopy(nextBytes, nextBIndex, bytes, nextByteToReturn, n);
            nextBIndex += n;
            nextByteToReturn += n;
        }

        if (nextByteToReturn >= bytes.length) {
            return; // return because "bytes[]" are filled in
        }

        n = seed[BYTES_OFFSET] & 0x03;
        for (;;) {
            if (n == 0) {

                seed[lastWord] = (int) (counter >>> 32);
                seed[lastWord + 1] = (int) (counter & 0xFFFFFFFF);
                seed[lastWord + 2] = END_FLAGS[0];

            } else {

                seed[lastWord] |= (int) ((counter >>> RIGHT1[n]) & MASK[n]);
                seed[lastWord + 1] = (int) ((counter >>> RIGHT2[n]) & 0xFFFFFFFF);
                seed[lastWord + 2] = (int) ((counter << LEFT[n]) | END_FLAGS[n]);
            }
            if (seed[BYTES_OFFSET] > MAX_BYTES) {
                copies[EXTRAFRAME_OFFSET] = seed[FRAME_LENGTH];
                copies[EXTRAFRAME_OFFSET + 1] = seed[FRAME_LENGTH + 1];
            }

            SHA1Impl.computeHash(seed);

            if (seed[BYTES_OFFSET] > MAX_BYTES) {

                System.arraycopy(seed, 0, copies, FRAME_OFFSET, FRAME_LENGTH);
                System.arraycopy(copies, EXTRAFRAME_OFFSET, seed, 0,
                        FRAME_LENGTH);

                SHA1Impl.computeHash(seed);
                System.arraycopy(copies, FRAME_OFFSET, seed, 0, FRAME_LENGTH);
            }
            counter++;

            int j = 0;
            for (i = 0; i < EXTRAFRAME_OFFSET; i++) {
                int k = seed[HASH_OFFSET + i];
                nextBytes[j] = (byte) (k >>> 24); // getting first  byte from left
                nextBytes[j + 1] = (byte) (k >>> 16); // getting second byte from left
                nextBytes[j + 2] = (byte) (k >>> 8); // getting third  byte from left
                nextBytes[j + 3] = (byte) (k); // getting fourth byte from left
                j += 4;
            }

            nextBIndex = 0;
            j = HASHBYTES_TO_USE < (bytes.length - nextByteToReturn) ? HASHBYTES_TO_USE
                    : bytes.length - nextByteToReturn;

            if (j > 0) {
                System.arraycopy(nextBytes, 0, bytes, nextByteToReturn, j);
                nextByteToReturn += j;
                nextBIndex += j;
            }

            if (nextByteToReturn >= bytes.length) {
                break;
            }
        }
    
protected voidengineSetSeed(byte[] seed)
Changes current seed by supplementing a seed argument to the current seed, if this already set; the argument is used as first seed otherwise.
The method overrides "engineSetSeed(byte[])" in class SecureRandomSpi.

param
seed - byte array
throws
NullPointerException - if null is passed to the "seed" argument


        if (seed == null) {
            throw new NullPointerException(
                    Messages.getString("security.83", "seed")); //$NON-NLS-1$ //$NON-NLS-2$
        }

        if (state == NEXT_BYTES) { // first setSeed after NextBytes; restoring hash
            System.arraycopy(copies, HASHCOPY_OFFSET, this.seed, HASH_OFFSET,
                    EXTRAFRAME_OFFSET);
        }
        state = SET_SEED;

        if (seed.length != 0) {
            updateSeed(seed);
        }
    
private voidreadObject(java.io.ObjectInputStream ois)


        seed = new int[HASH_OFFSET + EXTRAFRAME_OFFSET];
        copies = new int[2 * FRAME_LENGTH + EXTRAFRAME_OFFSET];
        nextBytes = new byte[DIGEST_LENGTH];

        seedLength = ois.readLong();
        counter = ois.readLong();
        state = ois.readInt();
        seed[BYTES_OFFSET] = ois.readInt();

        int nRemaining = (seed[BYTES_OFFSET] + 3) >> 2; // converting bytes in words

        if (state != NEXT_BYTES) {

            for (int i = 0; i < nRemaining; i++) {
                seed[i] = ois.readInt();
            }
            for (int i = 0; i < EXTRAFRAME_OFFSET; i++) {
                seed[HASH_OFFSET + i] = ois.readInt();
            }
        } else {
            if (seed[BYTES_OFFSET] >= MAX_BYTES) {

                // reading next bytes in seed extra frame
                seed[FRAME_LENGTH] = ois.readInt();
                seed[FRAME_LENGTH + 1] = ois.readInt();
                seed[FRAME_LENGTH + 14] = ois.readInt();
                seed[FRAME_LENGTH + 15] = ois.readInt();
            }
            // reading next bytes in seed frame
            for (int i = 0; i < FRAME_LENGTH; i++) {
                seed[i] = ois.readInt();
            }
            // reading remaining seed bytes 
            for (int i = 0; i < nRemaining; i++) {
                copies[FRAME_LENGTH + EXTRAFRAME_OFFSET + i] = ois.readInt();
            }
            // reading copy of current hash
            for (int i = 0; i < EXTRAFRAME_OFFSET; i++) {
                copies[i] = ois.readInt();
            }
            // reading current hash
            for (int i = 0; i < EXTRAFRAME_OFFSET; i++) {
                seed[HASH_OFFSET + i] = ois.readInt();
            }
        }

        nextBIndex = ois.readInt();
        ois.read(nextBytes, nextBIndex, HASHBYTES_TO_USE - nextBIndex);
    
private voidupdateSeed(byte[] bytes)


        // on call:   "seed" contains current bytes and current hash;
        // on return: "seed" contains new current bytes and possibly new current hash
        //            if after adding, seed bytes overfill its buffer
        SHA1Impl.updateHash(seed, bytes, 0, bytes.length - 1);

        seedLength += bytes.length;
    
private voidwriteObject(java.io.ObjectOutputStream oos)


        int[] intData = null;

        final int only_hash = EXTRAFRAME_OFFSET;
        final int hashes_and_frame = EXTRAFRAME_OFFSET * 2 + FRAME_LENGTH;
        final int hashes_and_frame_extra = EXTRAFRAME_OFFSET * 2 + FRAME_LENGTH
                * 2;

        oos.writeLong(seedLength);
        oos.writeLong(counter);
        oos.writeInt(state);
        oos.writeInt(seed[BYTES_OFFSET]);

        int nRemaining = (seed[BYTES_OFFSET] + 3) >> 2; // converting bytes in words
        // result may be 0
        if (state != NEXT_BYTES) {

            // either the state is UNDEFINED or previous method was "setSeed(..)" 
            // so in "seed[]" to serialize are remaining bytes (seed[0-nRemaining]) and
            // current hash (seed[82-86])

            intData = new int[only_hash + nRemaining];

            System.arraycopy(seed, 0, intData, 0, nRemaining);
            System.arraycopy(seed, HASH_OFFSET, intData, nRemaining,
                    EXTRAFRAME_OFFSET);

        } else {
            // previous method was "nextBytes(..)"
            // so, data to serialize are all the above (two first are in "copies" array)
            // and current words in both frame and extra frame (as if)

            int offset = 0;
            if (seed[BYTES_OFFSET] < MAX_BYTES) { // no extra frame

                intData = new int[hashes_and_frame + nRemaining];

            } else { // extra frame is used

                intData = new int[hashes_and_frame_extra + nRemaining];

                intData[offset] = seed[FRAME_LENGTH];
                intData[offset + 1] = seed[FRAME_LENGTH + 1];
                intData[offset + 2] = seed[FRAME_LENGTH + 14];
                intData[offset + 3] = seed[FRAME_LENGTH + 15];
                offset += 4;
            }

            System.arraycopy(seed, 0, intData, offset, FRAME_LENGTH);
            offset += FRAME_LENGTH;

            System.arraycopy(copies, FRAME_LENGTH + EXTRAFRAME_OFFSET, intData,
                    offset, nRemaining);
            offset += nRemaining;

            System.arraycopy(copies, 0, intData, offset, EXTRAFRAME_OFFSET);
            offset += EXTRAFRAME_OFFSET;

            System.arraycopy(seed, HASH_OFFSET, intData, offset,
                    EXTRAFRAME_OFFSET);
        }
        for (int i = 0; i < intData.length; i++) {
            oos.writeInt(intData[i]);
        }

        oos.writeInt(nextBIndex);
        oos.write(nextBytes, nextBIndex, HASHBYTES_TO_USE - nextBIndex);