/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.security.provider.crypto; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.security.InvalidParameterException; import java.security.SecureRandomSpi; import libcore.io.Streams; import libcore.util.EmptyArray; /** * This class extends the SecureRandomSpi class implementing all its abstract methods. <BR> * <BR> * 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 <BR> * <BR> * The class implements the Serializable interface. */ public class SHA1PRNG_SecureRandomImpl extends SecureRandomSpi implements Serializable, SHA1_Data { private static final long serialVersionUID = 283736797212159675L; // constants to use in expressions operating on bytes in int and long variables: // END_FLAGS - final bytes in words to append to message; // see "ch.5.1 Padding the Message, FIPS 180-2" // RIGHT1 - shifts to right for left half of long // RIGHT2 - shifts to right for right half of long // LEFT - shifts to left for bytes // MASK - mask to select counter's bytes after shift to right private static final int[] END_FLAGS = { 0x80000000, 0x800000, 0x8000, 0x80 }; private static final int[] RIGHT1 = { 0, 40, 48, 56 }; private static final int[] RIGHT2 = { 0, 8, 16, 24 }; private static final int[] LEFT = { 0, 24, 16, 8 }; private static final int[] MASK = { 0xFFFFFFFF, 0x00FFFFFF, 0x0000FFFF, 0x000000FF }; // HASHBYTES_TO_USE defines # of bytes returned by "computeHash(byte[])" // to use to form byte array returning by the "nextBytes(byte[])" method // Note, that this implementation uses more bytes than it is defined // in the above specification. private static final int HASHBYTES_TO_USE = 20; // value of 16 defined in the "SECURE HASH STANDARD", FIPS PUB 180-2 private static final int FRAME_LENGTH = 16; // miscellaneous constants defined in this implementation: // COUNTER_BASE - initial value to set to "counter" before computing "nextBytes(..)"; // note, that the exact value is not defined in STANDARD // HASHCOPY_OFFSET - offset for copy of current hash in "copies" array // EXTRAFRAME_OFFSET - offset for extra frame in "copies" array; // as the extra frame follows the current hash frame, // EXTRAFRAME_OFFSET is equal to length of current hash frame // FRAME_OFFSET - offset for frame in "copies" array // MAX_BYTES - maximum # of seed bytes processing which doesn't require extra frame // see (1) comments on usage of "seed" array below and // (2) comments in "engineNextBytes(byte[])" method // // UNDEFINED - three states of engine; initially its state is "UNDEFINED" // SET_SEED call to "engineSetSeed" sets up "SET_SEED" state, // NEXT_BYTES call to "engineNextByte" sets up "NEXT_BYTES" state private static final int COUNTER_BASE = 0; private static final int HASHCOPY_OFFSET = 0; private static final int EXTRAFRAME_OFFSET = 5; private static final int FRAME_OFFSET = 21; private static final int MAX_BYTES = 48; private static final int UNDEFINED = 0; private static final int SET_SEED = 1; private static final int NEXT_BYTES = 2; private static SHA1PRNG_SecureRandomImpl myRandom; // Structure of "seed" array: // - 0-79 - words for computing hash // - 80 - unused // - 81 - # of seed bytes in current seed frame // - 82-86 - 5 words, current seed hash private transient int[] seed; // total length of seed bytes, including all processed private transient long seedLength; // Structure of "copies" array // - 0-4 - 5 words, copy of current seed hash // - 5-20 - extra 16 words frame; // is used if final padding exceeds 512-bit length // - 21-36 - 16 word frame to store a copy of remaining bytes private transient int[] copies; // ready "next" bytes; needed because words are returned private transient byte[] nextBytes; // index of used bytes in "nextBytes" array private transient int nextBIndex; // variable required according to "SECURE HASH STANDARD" private transient long counter; // contains int value corresponding to engine's current state private transient int state; // 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. /** * Creates object and sets implementation variables to their initial values */ public SHA1PRNG_SecureRandomImpl() { 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; } /* * The method invokes the SHA1Impl's "updateHash(..)" method * to update current seed frame and * to compute new intermediate hash value if the frame is full. * * After that it computes a length of whole seed. */ private void updateSeed(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; } /** * Changes current seed by supplementing a seed argument to the current seed, * if this already set; * the argument is used as first seed otherwise. <BR> * * The method overrides "engineSetSeed(byte[])" in class SecureRandomSpi. * * @param * seed - byte array * @throws * NullPointerException - if null is passed to the "seed" argument */ protected synchronized void engineSetSeed(byte[] seed) { if (seed == null) { throw new NullPointerException("seed == null"); } 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); } } /** * Returns a required number of random bytes. <BR> * * The method overrides "engineGenerateSeed (int)" in class SecureRandomSpi. <BR> * * @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 */ protected synchronized byte[] engineGenerateSeed(int numBytes) { byte[] myBytes; // byte[] for bytes returned by "nextBytes()" if (numBytes < 0) { throw new NegativeArraySizeException(Integer.toString(numBytes)); } if (numBytes == 0) { return EmptyArray.BYTE; } if (myRandom == null) { myRandom = new SHA1PRNG_SecureRandomImpl(); myRandom.engineSetSeed(RandomBitsSupplier.getRandomBits(DIGEST_LENGTH)); } myBytes = new byte[numBytes]; myRandom.engineNextBytes(myBytes); return myBytes; } /** * Writes random bytes into an array supplied. * Bits in a byte are from left to right. <BR> * * 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. <BR> * * 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 */ protected synchronized void engineNextBytes(byte[] bytes) { 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("bytes == null"); } 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; } } } private void writeObject(ObjectOutputStream oos) throws IOException { 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); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 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(); Streams.readFully(ois, nextBytes, nextBIndex, HASHBYTES_TO_USE - nextBIndex); } }