Index: modules/security/src/main/java/common/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java =================================================================== --- modules/security/src/main/java/common/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java (revision 0) +++ modules/security/src/main/java/common/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java (revision 0) @@ -0,0 +1,416 @@ +/* + * Copyright 2006 The Apache Software Software Foundation or its licensors, as applicable. + * + * Licensed 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. + */ + +/* + * TODO to add writeObject(ObjectOutputStream) and readObject(ObjectInputStream) for optimal serialization + */ + + +package org.apache.harmony.security.provider.crypto; + +import java.security.InvalidParameterException; +import java.security.ProviderException; +import java.security.SecureRandomSpi; + +import org.apache.harmony.security.provider.crypto.RandomBitsSupplier; +import org.apache.harmony.security.provider.crypto.SHA1Impl; + +import java.io.Serializable; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.IOException; + + +/** + * 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) algoritms" section, Appendix A, + * JavaTM Cryptography Architecure, API Specification&Reference
+ *
+ * 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; + + // miscellanous constans 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 spletting 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 othewise.
+ * + * The method overrides "engineSetSeed(byte[])" in class SecureRandomSpi. + * + * @param + * seed - byte array + * @throws + * NullPointerException - if null is passed to the "seed" argument + */ + protected void engineSetSeed(byte[] seed) { + + if ( seed == null ) { + throw new NullPointerException("null is passed to the 'seed' argument"); + } + + 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.
+ * + * 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 + */ + protected byte[] engineGenerateSeed(int numBytes) { + + byte[] myBytes; // byte[] for bytes returned by "nextBytes()" + + if ( numBytes < 0 ) { + throw new NegativeArraySizeException("numBytes=" + numBytes); + } + 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; + } + + + /** + * Writes random bytes into an array suppied. + * 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 + */ + protected 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("null is passed to the 'bytes' argument"); + } + + 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; + } + } + } + + +} Index: modules/security/src/main/java/linux/org/apache/harmony/security/provider/crypto/RandomBitsSupplier.java =================================================================== --- modules/security/src/main/java/linux/org/apache/harmony/security/provider/crypto/RandomBitsSupplier.java (revision 0) +++ modules/security/src/main/java/linux/org/apache/harmony/security/provider/crypto/RandomBitsSupplier.java (revision 0) @@ -0,0 +1,169 @@ +/* + * Copyright 2006 The Apache Software Software Foundation or its licensors, as applicable. + * + * Licensed 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.File; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.FileNotFoundException; + +import java.security.ProviderException; +import java.security.AccessController; +import java.security.PrivilegedActionException; + + +/** + * The static class providing access on Linux paltform + * to system means for generating true random bits.
+ * + * The source for true random bits is one of Linux's devices "/dev/urandom/" or + * "/dev/random" depends on which one is avalable; if both the first is used.
+ * + * If no device available the service is not avilable, + * that is, provider shouldn't register the algorithm.
+ */ + + +public class RandomBitsSupplier implements SHA1_Data { + + + /** + * BufferedInputStream to read from device + */ + private static BufferedInputStream bis = null; + + /** + * File to connect to device + */ + private static File randomFile = null; + + /** + * value of field is "true" only if a device is available + */ + private static boolean serviceAvailable; + + + static { + AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Object run() { + + for ( int i = 0 ; i < DEVICE_NAMES.length ; i++ ) { + File file = new File(DEVICE_NAMES[i]); + + try { + if ( file.canRead() ) { + bis = new BufferedInputStream( + new FileInputStream(file)); + randomFile = file; + return null; + } + } catch (FileNotFoundException e) { + } + } + return null; + } + } + ); + serviceAvailable = (bis != null); + } + + + /** + * The method is called by provider to determine if a device is available. + */ + static boolean isServiceAvailable() { + return serviceAvailable; + } + + + /** + * On the Linux platform with "random" devices available, + * the method reads random bytes from the device.
+ * + * In case of any runtime failure ProviderException gets thrown. + */ + private static synchronized byte[] getLinuxRandomBits(int numBytes) { + + byte[] bytes = new byte[numBytes]; + + int total = 0; + int bytesRead; + int offset = 0; + try { + for ( ; ; ) { + + bytesRead = bis.read(bytes, offset, numBytes-total); + + + // the below case should not occur because /dev/random or /dev/urandom is a special file + // hence, if it is happened there is some internal problem + // + if ( bytesRead == -1 ) { + throw new ProviderException( + "ATTENTION: 'bytesRead == -1' in getLinuxRandomBits()" ); + } + + total += bytesRead; + offset += bytesRead; + + if ( total >= numBytes ) { + break; + } + } + } catch (IOException e) { + + // actually there should be no IOException because device is a special file; + // hence, there is either some internal problem or, for instance, + // device was removed in runtime, or something else + // + throw new ProviderException( + "ATTENTION: IOException in RandomBitsSupplier.getLinuxRandomBits()\n", e ); + } + return bytes; + } + + + /** + * The method returns byte array of requested length provided service is available. + * ProviderException gets thrown otherwise. + * + * @param + * numBytes - length of bytes requested + * @return + * byte array + * @throws + * InvalidArgumentException - if numBytes <= 0 + */ + public static byte[] getRandomBits(int numBytes) { + + if ( numBytes <= 0 ) { + throw new IllegalArgumentException("numBytes <= 0 : " + numBytes); + } + + if ( !serviceAvailable ) { + throw new ProviderException( + "ATTENTION: service is not available : no random devices"); + } + + return getLinuxRandomBits(numBytes); + } + +}