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