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