Home | History | Annotate | Download | only in crypto
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 
     19 package org.apache.harmony.security.provider.crypto;
     20 
     21 import dalvik.system.BlockGuard;
     22 import java.io.File;
     23 import java.io.FileInputStream;
     24 import java.io.IOException;
     25 import java.io.ObjectInputStream;
     26 import java.io.ObjectOutputStream;
     27 import java.io.Serializable;
     28 import java.security.InvalidParameterException;
     29 import java.security.ProviderException;
     30 import java.security.SecureRandomSpi;
     31 import libcore.io.Streams;
     32 import libcore.util.EmptyArray;
     33 
     34 import static org.apache.harmony.security.provider.crypto.SHA1Constants.*;
     35 
     36 /**
     37  * This class extends the SecureRandomSpi class implementing all its abstract methods.
     38  *
     39  * <p>To generate pseudo-random bits, the implementation uses technique described in
     40  * the "Random Number Generator (RNG) algorithms" section, Appendix A,
     41  * JavaTM Cryptography Architecture, API Specification & Reference.
     42  */
     43 public class SHA1PRNG_SecureRandomImpl extends SecureRandomSpi implements Serializable {
     44 
     45     private static final long serialVersionUID = 283736797212159675L;
     46 
     47     private static FileInputStream devURandom;
     48     static {
     49         try {
     50             devURandom = new FileInputStream(new File("/dev/urandom"));
     51         } catch (IOException ex) {
     52             throw new RuntimeException(ex);
     53         }
     54     }
     55 
     56     // constants to use in expressions operating on bytes in int and long variables:
     57     // END_FLAGS - final bytes in words to append to message;
     58     //             see "ch.5.1 Padding the Message, FIPS 180-2"
     59     // RIGHT1    - shifts to right for left half of long
     60     // RIGHT2    - shifts to right for right half of long
     61     // LEFT      - shifts to left for bytes
     62     // MASK      - mask to select counter's bytes after shift to right
     63 
     64     private static final int[] END_FLAGS = { 0x80000000, 0x800000, 0x8000, 0x80 };
     65 
     66     private static final int[] RIGHT1 = { 0, 40, 48, 56 };
     67 
     68     private static final int[] RIGHT2 = { 0, 8, 16, 24 };
     69 
     70     private static final int[] LEFT = { 0, 24, 16, 8 };
     71 
     72     private static final int[] MASK = { 0xFFFFFFFF, 0x00FFFFFF, 0x0000FFFF,
     73             0x000000FF };
     74 
     75     // HASHBYTES_TO_USE defines # of bytes returned by "computeHash(byte[])"
     76     // to use to form byte array returning by the "nextBytes(byte[])" method
     77     // Note, that this implementation uses more bytes than it is defined
     78     // in the above specification.
     79     private static final int HASHBYTES_TO_USE = 20;
     80 
     81     // value of 16 defined in the "SECURE HASH STANDARD", FIPS PUB 180-2
     82     private static final int FRAME_LENGTH = 16;
     83 
     84     // miscellaneous constants defined in this implementation:
     85     // COUNTER_BASE - initial value to set to "counter" before computing "nextBytes(..)";
     86     //                note, that the exact value is not defined in STANDARD
     87     // HASHCOPY_OFFSET   - offset for copy of current hash in "copies" array
     88     // EXTRAFRAME_OFFSET - offset for extra frame in "copies" array;
     89     //                     as the extra frame follows the current hash frame,
     90     //                     EXTRAFRAME_OFFSET is equal to length of current hash frame
     91     // FRAME_OFFSET      - offset for frame in "copies" array
     92     // MAX_BYTES - maximum # of seed bytes processing which doesn't require extra frame
     93     //             see (1) comments on usage of "seed" array below and
     94     //             (2) comments in "engineNextBytes(byte[])" method
     95     //
     96     // UNDEFINED  - three states of engine; initially its state is "UNDEFINED"
     97     // SET_SEED     call to "engineSetSeed"  sets up "SET_SEED" state,
     98     // NEXT_BYTES   call to "engineNextByte" sets up "NEXT_BYTES" state
     99 
    100     private static final int COUNTER_BASE = 0;
    101 
    102     private static final int HASHCOPY_OFFSET = 0;
    103 
    104     private static final int EXTRAFRAME_OFFSET = 5;
    105 
    106     private static final int FRAME_OFFSET = 21;
    107 
    108     private static final int MAX_BYTES = 48;
    109 
    110     private static final int UNDEFINED = 0;
    111 
    112     private static final int SET_SEED = 1;
    113 
    114     private static final int NEXT_BYTES = 2;
    115 
    116     private static SHA1PRNG_SecureRandomImpl myRandom;
    117 
    118     // Structure of "seed" array:
    119     // -  0-79 - words for computing hash
    120     // - 80    - unused
    121     // - 81    - # of seed bytes in current seed frame
    122     // - 82-86 - 5 words, current seed hash
    123     private transient int[] seed;
    124 
    125     // total length of seed bytes, including all processed
    126     private transient long seedLength;
    127 
    128     // Structure of "copies" array
    129     // -  0-4  - 5 words, copy of current seed hash
    130     // -  5-20 - extra 16 words frame;
    131     //           is used if final padding exceeds 512-bit length
    132     // - 21-36 - 16 word frame to store a copy of remaining bytes
    133     private transient int[] copies;
    134 
    135     // ready "next" bytes; needed because words are returned
    136     private transient byte[] nextBytes;
    137 
    138     // index of used bytes in "nextBytes" array
    139     private transient int nextBIndex;
    140 
    141     // variable required according to "SECURE HASH STANDARD"
    142     private transient long counter;
    143 
    144     // contains int value corresponding to engine's current state
    145     private transient int state;
    146 
    147     // The "seed" array is used to compute both "current seed hash" and "next bytes".
    148     //
    149     // As the "SHA1" algorithm computes a hash of entire seed by splitting it into
    150     // a number of the 512-bit length frames (512 bits = 64 bytes = 16 words),
    151     // "current seed hash" is a hash (5 words, 20 bytes) for all previous full frames;
    152     // remaining bytes are stored in the 0-15 word frame of the "seed" array.
    153     //
    154     // As for calculating "next bytes",
    155     // both remaining bytes and "current seed hash" are used,
    156     // to preserve the latter for following "setSeed(..)" commands,
    157     // the following technique is used:
    158     // - upon getting "nextBytes(byte[])" invoked, single or first in row,
    159     //   which requires computing new hash, that is,
    160     //   there is no more bytes remaining from previous "next bytes" computation,
    161     //   remaining bytes are copied into the 21-36 word frame of the "copies" array;
    162     // - upon getting "setSeed(byte[])" invoked, single or first in row,
    163     //   remaining bytes are copied back.
    164 
    165     /**
    166      *  Creates object and sets implementation variables to their initial values
    167      */
    168     public SHA1PRNG_SecureRandomImpl() {
    169 
    170         seed = new int[HASH_OFFSET + EXTRAFRAME_OFFSET];
    171         seed[HASH_OFFSET] = H0;
    172         seed[HASH_OFFSET + 1] = H1;
    173         seed[HASH_OFFSET + 2] = H2;
    174         seed[HASH_OFFSET + 3] = H3;
    175         seed[HASH_OFFSET + 4] = H4;
    176 
    177         seedLength = 0;
    178         copies = new int[2 * FRAME_LENGTH + EXTRAFRAME_OFFSET];
    179         nextBytes = new byte[DIGEST_LENGTH];
    180         nextBIndex = HASHBYTES_TO_USE;
    181         counter = COUNTER_BASE;
    182         state = UNDEFINED;
    183     }
    184 
    185     /*
    186      * The method invokes the SHA1Impl's "updateHash(..)" method
    187      * to update current seed frame and
    188      * to compute new intermediate hash value if the frame is full.
    189      *
    190      * After that it computes a length of whole seed.
    191      */
    192     private void updateSeed(byte[] bytes) {
    193 
    194         // on call:   "seed" contains current bytes and current hash;
    195         // on return: "seed" contains new current bytes and possibly new current hash
    196         //            if after adding, seed bytes overfill its buffer
    197         SHA1Impl.updateHash(seed, bytes, 0, bytes.length - 1);
    198 
    199         seedLength += bytes.length;
    200     }
    201 
    202     /**
    203      * Changes current seed by supplementing a seed argument to the current seed,
    204      * if this already set;
    205      * the argument is used as first seed otherwise. <BR>
    206      *
    207      * The method overrides "engineSetSeed(byte[])" in class SecureRandomSpi.
    208      *
    209      * @param
    210      *       seed - byte array
    211      * @throws
    212      *       NullPointerException - if null is passed to the "seed" argument
    213      */
    214     protected synchronized void engineSetSeed(byte[] seed) {
    215 
    216         if (seed == null) {
    217             throw new NullPointerException("seed == null");
    218         }
    219 
    220         if (state == NEXT_BYTES) { // first setSeed after NextBytes; restoring hash
    221             System.arraycopy(copies, HASHCOPY_OFFSET, this.seed, HASH_OFFSET,
    222                     EXTRAFRAME_OFFSET);
    223         }
    224         state = SET_SEED;
    225 
    226         if (seed.length != 0) {
    227             updateSeed(seed);
    228         }
    229     }
    230 
    231     /**
    232      * Returns a required number of random bytes. <BR>
    233      *
    234      * The method overrides "engineGenerateSeed (int)" in class SecureRandomSpi. <BR>
    235      *
    236      * @param
    237      *       numBytes - number of bytes to return; should be >= 0.
    238      * @return
    239      *       byte array containing bits in order from left to right
    240      * @throws
    241      *       InvalidParameterException - if numBytes < 0
    242      */
    243     protected synchronized byte[] engineGenerateSeed(int numBytes) {
    244 
    245         byte[] myBytes; // byte[] for bytes returned by "nextBytes()"
    246 
    247         if (numBytes < 0) {
    248             throw new NegativeArraySizeException(Integer.toString(numBytes));
    249         }
    250         if (numBytes == 0) {
    251             return EmptyArray.BYTE;
    252         }
    253 
    254         if (myRandom == null) {
    255             myRandom = new SHA1PRNG_SecureRandomImpl();
    256             myRandom.engineSetSeed(getRandomBytes(DIGEST_LENGTH));
    257         }
    258 
    259         myBytes = new byte[numBytes];
    260         myRandom.engineNextBytes(myBytes);
    261 
    262         return myBytes;
    263     }
    264 
    265     /**
    266      * Writes random bytes into an array supplied.
    267      * Bits in a byte are from left to right. <BR>
    268      *
    269      * To generate random bytes, the "expansion of source bits" method is used,
    270      * that is,
    271      * the current seed with a 64-bit counter appended is used to compute new bits.
    272      * The counter is incremented by 1 for each 20-byte output. <BR>
    273      *
    274      * The method overrides engineNextBytes in class SecureRandomSpi.
    275      *
    276      * @param
    277      *       bytes - byte array to be filled in with bytes
    278      * @throws
    279      *       NullPointerException - if null is passed to the "bytes" argument
    280      */
    281     protected synchronized void engineNextBytes(byte[] bytes) {
    282 
    283         int i, n;
    284 
    285         long bits; // number of bits required by Secure Hash Standard
    286         int nextByteToReturn; // index of ready bytes in "bytes" array
    287         int lastWord; // index of last word in frame containing bytes
    288         final int extrabytes = 7;// # of bytes to add in order to computer # of 8 byte words
    289 
    290         if (bytes == null) {
    291             throw new NullPointerException("bytes == null");
    292         }
    293 
    294         lastWord = seed[BYTES_OFFSET] == 0 ? 0
    295                 : (seed[BYTES_OFFSET] + extrabytes) >> 3 - 1;
    296 
    297         if (state == UNDEFINED) {
    298 
    299             // no seed supplied by user, hence it is generated thus randomizing internal state
    300             updateSeed(getRandomBytes(DIGEST_LENGTH));
    301             nextBIndex = HASHBYTES_TO_USE;
    302 
    303             // updateSeed(...) updates where the last word of the seed is, so we
    304             // have to read it again.
    305             lastWord = seed[BYTES_OFFSET] == 0 ? 0
    306                     : (seed[BYTES_OFFSET] + extrabytes) >> 3 - 1;
    307 
    308         } else if (state == SET_SEED) {
    309 
    310             System.arraycopy(seed, HASH_OFFSET, copies, HASHCOPY_OFFSET,
    311                     EXTRAFRAME_OFFSET);
    312 
    313             // possible cases for 64-byte frame:
    314             //
    315             // seed bytes < 48      - remaining bytes are enough for all, 8 counter bytes,
    316             //                        0x80, and 8 seedLength bytes; no extra frame required
    317             // 48 < seed bytes < 56 - remaining 9 bytes are for 0x80 and 8 counter bytes
    318             //                        extra frame contains only seedLength value at the end
    319             // seed bytes > 55      - extra frame contains both counter's bytes
    320             //                        at the beginning and seedLength value at the end;
    321             //                        note, that beginning extra bytes are not more than 8,
    322             //                        that is, only 2 extra words may be used
    323 
    324             // no need to set to "0" 3 words after "lastWord" and
    325             // more than two words behind frame
    326             for (i = lastWord + 3; i < FRAME_LENGTH + 2; i++) {
    327                 seed[i] = 0;
    328             }
    329 
    330             bits = (seedLength << 3) + 64; // transforming # of bytes into # of bits
    331 
    332             // putting # of bits into two last words (14,15) of 16 word frame in
    333             // seed or copies array depending on total length after padding
    334             if (seed[BYTES_OFFSET] < MAX_BYTES) {
    335                 seed[14] = (int) (bits >>> 32);
    336                 seed[15] = (int) (bits & 0xFFFFFFFF);
    337             } else {
    338                 copies[EXTRAFRAME_OFFSET + 14] = (int) (bits >>> 32);
    339                 copies[EXTRAFRAME_OFFSET + 15] = (int) (bits & 0xFFFFFFFF);
    340             }
    341 
    342             nextBIndex = HASHBYTES_TO_USE; // skipping remaining random bits
    343         }
    344         state = NEXT_BYTES;
    345 
    346         if (bytes.length == 0) {
    347             return;
    348         }
    349 
    350         nextByteToReturn = 0;
    351 
    352         // possibly not all of HASHBYTES_TO_USE bytes were used previous time
    353         n = (HASHBYTES_TO_USE - nextBIndex) < (bytes.length - nextByteToReturn) ? HASHBYTES_TO_USE
    354                 - nextBIndex
    355                 : bytes.length - nextByteToReturn;
    356         if (n > 0) {
    357             System.arraycopy(nextBytes, nextBIndex, bytes, nextByteToReturn, n);
    358             nextBIndex += n;
    359             nextByteToReturn += n;
    360         }
    361 
    362         if (nextByteToReturn >= bytes.length) {
    363             return; // return because "bytes[]" are filled in
    364         }
    365 
    366         n = seed[BYTES_OFFSET] & 0x03;
    367         for (;;) {
    368             if (n == 0) {
    369 
    370                 seed[lastWord] = (int) (counter >>> 32);
    371                 seed[lastWord + 1] = (int) (counter & 0xFFFFFFFF);
    372                 seed[lastWord + 2] = END_FLAGS[0];
    373 
    374             } else {
    375 
    376                 seed[lastWord] |= (int) ((counter >>> RIGHT1[n]) & MASK[n]);
    377                 seed[lastWord + 1] = (int) ((counter >>> RIGHT2[n]) & 0xFFFFFFFF);
    378                 seed[lastWord + 2] = (int) ((counter << LEFT[n]) | END_FLAGS[n]);
    379             }
    380             if (seed[BYTES_OFFSET] > MAX_BYTES) {
    381                 copies[EXTRAFRAME_OFFSET] = seed[FRAME_LENGTH];
    382                 copies[EXTRAFRAME_OFFSET + 1] = seed[FRAME_LENGTH + 1];
    383             }
    384 
    385             SHA1Impl.computeHash(seed);
    386 
    387             if (seed[BYTES_OFFSET] > MAX_BYTES) {
    388 
    389                 System.arraycopy(seed, 0, copies, FRAME_OFFSET, FRAME_LENGTH);
    390                 System.arraycopy(copies, EXTRAFRAME_OFFSET, seed, 0,
    391                         FRAME_LENGTH);
    392 
    393                 SHA1Impl.computeHash(seed);
    394                 System.arraycopy(copies, FRAME_OFFSET, seed, 0, FRAME_LENGTH);
    395             }
    396             counter++;
    397 
    398             int j = 0;
    399             for (i = 0; i < EXTRAFRAME_OFFSET; i++) {
    400                 int k = seed[HASH_OFFSET + i];
    401                 nextBytes[j] = (byte) (k >>> 24); // getting first  byte from left
    402                 nextBytes[j + 1] = (byte) (k >>> 16); // getting second byte from left
    403                 nextBytes[j + 2] = (byte) (k >>> 8); // getting third  byte from left
    404                 nextBytes[j + 3] = (byte) (k); // getting fourth byte from left
    405                 j += 4;
    406             }
    407 
    408             nextBIndex = 0;
    409             j = HASHBYTES_TO_USE < (bytes.length - nextByteToReturn) ? HASHBYTES_TO_USE
    410                     : bytes.length - nextByteToReturn;
    411 
    412             if (j > 0) {
    413                 System.arraycopy(nextBytes, 0, bytes, nextByteToReturn, j);
    414                 nextByteToReturn += j;
    415                 nextBIndex += j;
    416             }
    417 
    418             if (nextByteToReturn >= bytes.length) {
    419                 break;
    420             }
    421         }
    422     }
    423 
    424     private void writeObject(ObjectOutputStream oos) throws IOException {
    425 
    426         int[] intData = null;
    427 
    428         final int only_hash = EXTRAFRAME_OFFSET;
    429         final int hashes_and_frame = EXTRAFRAME_OFFSET * 2 + FRAME_LENGTH;
    430         final int hashes_and_frame_extra = EXTRAFRAME_OFFSET * 2 + FRAME_LENGTH
    431                 * 2;
    432 
    433         oos.writeLong(seedLength);
    434         oos.writeLong(counter);
    435         oos.writeInt(state);
    436         oos.writeInt(seed[BYTES_OFFSET]);
    437 
    438         int nRemaining = (seed[BYTES_OFFSET] + 3) >> 2; // converting bytes in words
    439         // result may be 0
    440         if (state != NEXT_BYTES) {
    441 
    442             // either the state is UNDEFINED or previous method was "setSeed(..)"
    443             // so in "seed[]" to serialize are remaining bytes (seed[0-nRemaining]) and
    444             // current hash (seed[82-86])
    445 
    446             intData = new int[only_hash + nRemaining];
    447 
    448             System.arraycopy(seed, 0, intData, 0, nRemaining);
    449             System.arraycopy(seed, HASH_OFFSET, intData, nRemaining,
    450                     EXTRAFRAME_OFFSET);
    451 
    452         } else {
    453             // previous method was "nextBytes(..)"
    454             // so, data to serialize are all the above (two first are in "copies" array)
    455             // and current words in both frame and extra frame (as if)
    456 
    457             int offset = 0;
    458             if (seed[BYTES_OFFSET] < MAX_BYTES) { // no extra frame
    459 
    460                 intData = new int[hashes_and_frame + nRemaining];
    461 
    462             } else { // extra frame is used
    463 
    464                 intData = new int[hashes_and_frame_extra + nRemaining];
    465 
    466                 intData[offset] = seed[FRAME_LENGTH];
    467                 intData[offset + 1] = seed[FRAME_LENGTH + 1];
    468                 intData[offset + 2] = seed[FRAME_LENGTH + 14];
    469                 intData[offset + 3] = seed[FRAME_LENGTH + 15];
    470                 offset += 4;
    471             }
    472 
    473             System.arraycopy(seed, 0, intData, offset, FRAME_LENGTH);
    474             offset += FRAME_LENGTH;
    475 
    476             System.arraycopy(copies, FRAME_LENGTH + EXTRAFRAME_OFFSET, intData,
    477                     offset, nRemaining);
    478             offset += nRemaining;
    479 
    480             System.arraycopy(copies, 0, intData, offset, EXTRAFRAME_OFFSET);
    481             offset += EXTRAFRAME_OFFSET;
    482 
    483             System.arraycopy(seed, HASH_OFFSET, intData, offset,
    484                     EXTRAFRAME_OFFSET);
    485         }
    486         for (int i = 0; i < intData.length; i++) {
    487             oos.writeInt(intData[i]);
    488         }
    489 
    490         oos.writeInt(nextBIndex);
    491         oos.write(nextBytes, nextBIndex, HASHBYTES_TO_USE - nextBIndex);
    492     }
    493 
    494     private void readObject(ObjectInputStream ois) throws IOException,
    495             ClassNotFoundException {
    496 
    497         seed = new int[HASH_OFFSET + EXTRAFRAME_OFFSET];
    498         copies = new int[2 * FRAME_LENGTH + EXTRAFRAME_OFFSET];
    499         nextBytes = new byte[DIGEST_LENGTH];
    500 
    501         seedLength = ois.readLong();
    502         counter = ois.readLong();
    503         state = ois.readInt();
    504         seed[BYTES_OFFSET] = ois.readInt();
    505 
    506         int nRemaining = (seed[BYTES_OFFSET] + 3) >> 2; // converting bytes in words
    507 
    508         if (state != NEXT_BYTES) {
    509 
    510             for (int i = 0; i < nRemaining; i++) {
    511                 seed[i] = ois.readInt();
    512             }
    513             for (int i = 0; i < EXTRAFRAME_OFFSET; i++) {
    514                 seed[HASH_OFFSET + i] = ois.readInt();
    515             }
    516         } else {
    517             if (seed[BYTES_OFFSET] >= MAX_BYTES) {
    518 
    519                 // reading next bytes in seed extra frame
    520                 seed[FRAME_LENGTH] = ois.readInt();
    521                 seed[FRAME_LENGTH + 1] = ois.readInt();
    522                 seed[FRAME_LENGTH + 14] = ois.readInt();
    523                 seed[FRAME_LENGTH + 15] = ois.readInt();
    524             }
    525             // reading next bytes in seed frame
    526             for (int i = 0; i < FRAME_LENGTH; i++) {
    527                 seed[i] = ois.readInt();
    528             }
    529             // reading remaining seed bytes
    530             for (int i = 0; i < nRemaining; i++) {
    531                 copies[FRAME_LENGTH + EXTRAFRAME_OFFSET + i] = ois.readInt();
    532             }
    533             // reading copy of current hash
    534             for (int i = 0; i < EXTRAFRAME_OFFSET; i++) {
    535                 copies[i] = ois.readInt();
    536             }
    537             // reading current hash
    538             for (int i = 0; i < EXTRAFRAME_OFFSET; i++) {
    539                 seed[HASH_OFFSET + i] = ois.readInt();
    540             }
    541         }
    542 
    543         nextBIndex = ois.readInt();
    544         Streams.readFully(ois, nextBytes, nextBIndex, HASHBYTES_TO_USE - nextBIndex);
    545     }
    546 
    547     private static byte[] getRandomBytes(int byteCount) {
    548         if (byteCount <= 0) {
    549             throw new IllegalArgumentException("Too few bytes requested: " + byteCount);
    550         }
    551 
    552         BlockGuard.Policy originalPolicy = BlockGuard.getThreadPolicy();
    553         try {
    554             BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
    555             byte[] result = new byte[byteCount];
    556             Streams.readFully(devURandom, result, 0, byteCount);
    557             return result;
    558         } catch (Exception ex) {
    559             throw new ProviderException("Couldn't read " + byteCount + " random bytes", ex);
    560         } finally {
    561             BlockGuard.setThreadPolicy(originalPolicy);
    562         }
    563     }
    564 }
    565