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