1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.provider; 27 28 import javax.crypto.Cipher; 29 import javax.crypto.NoSuchPaddingException; 30 import javax.crypto.spec.SecretKeySpec; 31 import java.security.*; 32 import java.util.Arrays; 33 import java.util.Locale; 34 35 public class CtrDrbg extends AbstractDrbg { 36 37 private static final int AES_LIMIT; 38 39 static { 40 try { 41 AES_LIMIT = Cipher.getMaxAllowedKeyLength("AES"); 42 } catch (Exception e) { 43 // should not happen 44 throw new AssertionError("Cannot detect AES", e); 45 } 46 } 47 48 private Cipher cipher; 49 50 private String cipherAlg; 51 private String keyAlg; 52 53 private int ctrLen; 54 private int blockLen; 55 private int keyLen; 56 private int seedLen; 57 58 private byte[] v; 59 private byte[] k; 60 CtrDrbg(SecureRandomParameters params)61 public CtrDrbg(SecureRandomParameters params) { 62 mechName = "CTR_DRBG"; 63 configure(params); 64 } 65 alg2strength(String algorithm)66 private static int alg2strength(String algorithm) { 67 switch (algorithm.toUpperCase(Locale.ROOT)) { 68 case "AES-128": 69 return 128; 70 case "AES-192": 71 return 192; 72 case "AES-256": 73 return 256; 74 default: 75 throw new IllegalArgumentException(algorithm + 76 " not supported in CTR_DBRG"); 77 } 78 } 79 80 @Override chooseAlgorithmAndStrength()81 protected void chooseAlgorithmAndStrength() { 82 if (requestedAlgorithm != null) { 83 algorithm = requestedAlgorithm.toUpperCase(Locale.ROOT); 84 int supportedStrength = alg2strength(algorithm); 85 if (requestedInstantiationSecurityStrength >= 0) { 86 int tryStrength = getStandardStrength( 87 requestedInstantiationSecurityStrength); 88 if (tryStrength > supportedStrength) { 89 throw new IllegalArgumentException(algorithm + 90 " does not support strength " + 91 requestedInstantiationSecurityStrength); 92 } 93 this.securityStrength = tryStrength; 94 } else { 95 this.securityStrength = (DEFAULT_STRENGTH > supportedStrength) ? 96 supportedStrength : DEFAULT_STRENGTH; 97 } 98 } else { 99 int tryStrength = (requestedInstantiationSecurityStrength < 0) ? 100 DEFAULT_STRENGTH : requestedInstantiationSecurityStrength; 101 tryStrength = getStandardStrength(tryStrength); 102 // Default algorithm, use AES-128 if AES-256 is not available. 103 // Remember to sync with "securerandom.drbg.config" in java.security 104 if (tryStrength <= 128 && AES_LIMIT < 256) { 105 algorithm = "AES-128"; 106 } else if (AES_LIMIT >= 256) { 107 algorithm = "AES-256"; 108 } else { 109 throw new IllegalArgumentException("unsupported strength " + 110 requestedInstantiationSecurityStrength); 111 } 112 this.securityStrength = tryStrength; 113 } 114 switch (algorithm.toUpperCase(Locale.ROOT)) { 115 case "AES-128": 116 case "AES-192": 117 case "AES-256": 118 this.keyAlg = "AES"; 119 this.cipherAlg = "AES/ECB/NoPadding"; 120 switch (algorithm) { 121 case "AES-128": 122 this.keyLen = 128 / 8; 123 break; 124 case "AES-192": 125 this.keyLen = 192 / 8; 126 if (AES_LIMIT < 192) { 127 throw new IllegalArgumentException(algorithm + 128 " not available (because policy) in CTR_DBRG"); 129 } 130 break; 131 case "AES-256": 132 this.keyLen = 256 / 8; 133 if (AES_LIMIT < 256) { 134 throw new IllegalArgumentException(algorithm + 135 " not available (because policy) in CTR_DBRG"); 136 } 137 break; 138 default: 139 throw new IllegalArgumentException(algorithm + 140 " not supported in CTR_DBRG"); 141 } 142 this.blockLen = 128 / 8; 143 break; 144 default: 145 throw new IllegalArgumentException(algorithm + 146 " not supported in CTR_DBRG"); 147 } 148 this.seedLen = this.blockLen + this.keyLen; 149 this.ctrLen = this.blockLen; // TODO 150 if (usedf) { 151 this.minLength = this.securityStrength / 8; 152 } else { 153 this.minLength = this.maxLength = 154 this.maxPersonalizationStringLength = 155 this.maxAdditionalInputLength = seedLen; 156 } 157 } 158 159 /** 160 * This call, used by the constructors, instantiates the digest. 161 */ 162 @Override initEngine()163 protected void initEngine() { 164 try { 165 /* 166 * Use the local SunJCE implementation to avoid native 167 * performance overhead. 168 */ 169 cipher = Cipher.getInstance(cipherAlg, "SunJCE"); 170 } catch (NoSuchProviderException | NoSuchAlgorithmException 171 | NoSuchPaddingException e) { 172 // Fallback to any available. 173 try { 174 cipher = Cipher.getInstance(cipherAlg); 175 } catch (NoSuchAlgorithmException | NoSuchPaddingException exc) { 176 throw new InternalError( 177 "internal error: " + cipherAlg + " not available.", exc); 178 } 179 } 180 } 181 status()182 private void status() { 183 if (debug != null) { 184 debug.println(this, "Key = " + hex(k)); 185 debug.println(this, "V = " + hex(v)); 186 debug.println(this, "reseed counter = " + reseedCounter); 187 } 188 } 189 190 // 800-90Ar1 10.2.1.2. CTR_DRBG_Update update(byte[] input)191 private void update(byte[] input) { 192 if (input.length != seedLen) { 193 // Should not happen 194 throw new IllegalArgumentException("input length not seedLen: " 195 + input.length); 196 } 197 try { 198 199 int m = (seedLen + blockLen - 1) / blockLen; 200 byte[] temp = new byte[m * blockLen]; 201 202 // Step 1. temp = Null. 203 204 // Step 2. Loop 205 for (int i = 0; i < m; i++) { 206 // Step 2.1. Increment 207 addOne(v, ctrLen); 208 // Step 2.2. Block_Encrypt 209 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg)); 210 // Step 2.3. Encrypt into right position, no need to cat 211 cipher.doFinal(v, 0, blockLen, temp, i * blockLen); 212 } 213 214 // Step 3. Truncate 215 temp = Arrays.copyOf(temp, seedLen); 216 217 // Step 4: Add 218 for (int i = 0; i < seedLen; i++) { 219 temp[i] ^= input[i]; 220 } 221 222 // Step 5: leftmost 223 k = Arrays.copyOf(temp, keyLen); 224 225 // Step 6: rightmost 226 v = Arrays.copyOfRange(temp, seedLen - blockLen, seedLen); 227 228 // Step 7. Return 229 } catch (GeneralSecurityException e) { 230 throw new InternalError(e); 231 } 232 } 233 234 @Override instantiateAlgorithm(byte[] ei)235 protected void instantiateAlgorithm(byte[] ei) { 236 if (debug != null) { 237 debug.println(this, "instantiate"); 238 } 239 byte[] more; 240 if (usedf) { 241 // 800-90Ar1 10.2.1.3.2 Step 1-2. cat bytes 242 if (personalizationString == null) { 243 more = nonce; 244 } else { 245 if (nonce.length + personalizationString.length < 0) { 246 // Length must be represented as a 32 bit integer in df() 247 throw new IllegalArgumentException( 248 "nonce plus personalization string is too long"); 249 } 250 more = Arrays.copyOf( 251 nonce, nonce.length + personalizationString.length); 252 System.arraycopy(personalizationString, 0, more, nonce.length, 253 personalizationString.length); 254 } 255 } else { 256 // 800-90Ar1 10.2.1.3.1 257 // Step 1-2, no need to expand personalizationString, we only XOR 258 // with shorter length 259 more = personalizationString; 260 } 261 reseedAlgorithm(ei, more); 262 } 263 264 /** 265 * Block_cipher_df in 10.3.2 266 * 267 * @param input the input string 268 * @return the output block (always of seedLen) 269 */ df(byte[] input)270 private byte[] df(byte[] input) { 271 // 800-90Ar1 10.3.2 272 // 2. L = len (input_string)/8 273 int l = input.length; 274 // 3. N = number_of_bits_to_return/8 275 int n = seedLen; 276 // 4. S = L || N || input_string || 0x80 277 byte[] ln = new byte[8]; 278 ln[0] = (byte)(l >> 24); 279 ln[1] = (byte)(l >> 16); 280 ln[2] = (byte)(l >> 8); 281 ln[3] = (byte)(l); 282 ln[4] = (byte)(n >> 24); 283 ln[5] = (byte)(n >> 16); 284 ln[6] = (byte)(n >> 8); 285 ln[7] = (byte)(n); 286 287 // 5. Zero padding of S 288 // Not necessary, see bcc 289 290 // 8. K = leftmost (0x00010203...1D1E1F, keylen). 291 byte[] k = new byte[keyLen]; 292 for (int i = 0; i < k.length; i++) { 293 k[i] = (byte)i; 294 } 295 296 // 6. temp = the Null String 297 byte[] temp = new byte[seedLen]; 298 299 // 7. i = 0 300 for (int i = 0; i * blockLen < temp.length; i++) { 301 // 9.1 IV = i || 0^(outlen - len (i)). outLen is blockLen 302 byte[] iv = new byte[blockLen]; 303 iv[0] = (byte)(i >> 24); 304 iv[1] = (byte)(i >> 16); 305 iv[2] = (byte)(i >> 8); 306 iv[3] = (byte)(i); 307 308 int tailLen = temp.length - blockLen*i; 309 if (tailLen > blockLen) { 310 tailLen = blockLen; 311 } 312 // 9.2 temp = temp || BCC (K, (IV || S)). 313 System.arraycopy(bcc(k, iv, ln, input, new byte[]{(byte)0x80}), 314 0, temp, blockLen*i, tailLen); 315 } 316 317 // 10. K = leftmost(temp, keylen) 318 k = Arrays.copyOf(temp, keyLen); 319 320 // 11. x = select(temp, keylen+1, keylen+outlen) 321 byte[] x = Arrays.copyOfRange(temp, keyLen, temp.length); 322 323 // 12. temp = the Null string 324 // No need to clean up, temp will be overwritten 325 326 for (int i = 0; i * blockLen < seedLen; i++) { 327 try { 328 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg)); 329 int tailLen = temp.length - blockLen*i; 330 // 14. requested_bits = leftmost(temp, nuumber_of_bits_to_return) 331 if (tailLen > blockLen) { 332 tailLen = blockLen; 333 } 334 x = cipher.doFinal(x); 335 System.arraycopy(x, 0, temp, blockLen * i, tailLen); 336 } catch (GeneralSecurityException e) { 337 throw new InternalError(e); 338 } 339 } 340 341 // 15. Return 342 return temp; 343 } 344 345 /** 346 * Block_Encrypt in 10.3.3 347 * 348 * @param k the key 349 * @param data after concatenated, the data to be operated upon. This is 350 * a series of byte[], each with an arbitrary length. Note 351 * that the full length is not necessarily a multiple of 352 * outlen. XOR with zero is no-op. 353 * @return the result 354 */ bcc(byte[] k, byte[]... data)355 private byte[] bcc(byte[] k, byte[]... data) { 356 byte[] chain = new byte[blockLen]; 357 int n1 = 0; // index in data 358 int n2 = 0; // index in data[n1] 359 // pack blockLen of bytes into chain from data[][], again and again 360 while (n1 < data.length) { 361 int j; 362 out: for (j = 0; j < blockLen; j++) { 363 while (n2 >= data[n1].length) { 364 n1++; 365 if (n1 >= data.length) { 366 break out; 367 } 368 n2 = 0; 369 } 370 chain[j] ^= data[n1][n2]; 371 n2++; 372 } 373 if (j == 0) { // all data happens to be consumed in the last loop 374 break; 375 } 376 try { 377 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg)); 378 chain = cipher.doFinal(chain); 379 } catch (GeneralSecurityException e) { 380 throw new InternalError(e); 381 } 382 } 383 return chain; 384 } 385 386 @Override reseedAlgorithm( byte[] ei, byte[] additionalInput)387 protected synchronized void reseedAlgorithm( 388 byte[] ei, 389 byte[] additionalInput) { 390 if (usedf) { 391 // 800-90Ar1 10.2.1.3.2 Instantiate. 392 // 800-90Ar1 10.2.1.4.2 Reseed. 393 394 // Step 1: cat bytes 395 if (additionalInput != null) { 396 if (ei.length + additionalInput.length < 0) { 397 // Length must be represented as a 32 bit integer in df() 398 throw new IllegalArgumentException( 399 "entropy plus additional input is too long"); 400 } 401 byte[] temp = Arrays.copyOf( 402 ei, ei.length + additionalInput.length); 403 System.arraycopy(additionalInput, 0, temp, ei.length, 404 additionalInput.length); 405 ei = temp; 406 } 407 // Step 2. df (seed_material, seedlen). 408 ei = df(ei); 409 } else { 410 // 800-90Ar1 10.2.1.3.1 Instantiate 411 // 800-90Ar1 10.2.1.4.1 Reseed 412 // Step 1-2. Needless 413 // Step 3. seed_material = entropy_input XOR more 414 if (additionalInput != null) { 415 // additionalInput.length <= seedLen 416 for (int i = 0; i < additionalInput.length; i++) { 417 ei[i] ^= additionalInput[i]; 418 } 419 } 420 } 421 422 if (v == null) { 423 // 800-90Ar1 10.2.1.3.2 Instantiate. Step 3-4 424 // 800-90Ar1 10.2.1.3.1 Instantiate. Step 4-5 425 k = new byte[keyLen]; 426 v = new byte[blockLen]; 427 } 428 //status(); 429 430 // 800-90Ar1 10.2.1.3.1 Instantiate. Step 6 431 // 800-90Ar1 10.2.1.3.2 Instantiate. Step 5 432 // 800-90Ar1 10.2.1.4.1 Reseed. Step 4 433 // 800-90Ar1 10.2.1.4.2 Reseed. Step 3 434 update(ei); 435 // 800-90Ar1 10.2.1.3.1 Instantiate. Step 7 436 // 800-90Ar1 10.2.1.3.2 Instantiate. Step 6 437 // 800-90Ar1 10.2.1.4.1 Reseed. Step 5 438 // 800-90Ar1 10.2.1.4.2 Reseed. Step 4 439 reseedCounter = 1; 440 //status(); 441 442 // Whatever step. Return 443 } 444 445 /** 446 * Add one to data, only touch the last len bytes. 447 */ addOne(byte[] data, int len)448 private static void addOne(byte[] data, int len) { 449 for (int i = 0; i < len; i++) { 450 data[data.length - 1 - i]++; 451 if (data[data.length - 1 - i] != 0) { 452 break; 453 } 454 } 455 } 456 457 @Override generateAlgorithm( byte[] result, byte[] additionalInput)458 public synchronized void generateAlgorithm( 459 byte[] result, byte[] additionalInput) { 460 461 if (debug != null) { 462 debug.println(this, "generateAlgorithm"); 463 } 464 465 // 800-90Ar1 10.2.1.5.1 Generate 466 // 800-90Ar1 10.2.1.5.2 Generate 467 468 // Step 1: Check reseed_counter. Will not fail. Already checked in 469 // AbstractDrbg#engineNextBytes. 470 471 if (additionalInput != null) { 472 if (usedf) { 473 // 10.2.1.5.2 Step 2.1 474 additionalInput = df(additionalInput); 475 } else { 476 // 10.2.1.5.1 Step 2.1-2.2 477 additionalInput = Arrays.copyOf(additionalInput, seedLen); 478 } 479 // 10.2.1.5.1 Step 2.3 480 // 10.2.1.5.2 Step 2.2 481 update(additionalInput); 482 } else { 483 // 10.2.1.5.1 Step 2 Else 484 // 10.2.1.5.2 Step 2 Else 485 additionalInput = new byte[seedLen]; 486 } 487 488 // Step 3. temp = Null 489 int pos = 0; 490 int len = result.length; 491 492 // Step 4. Loop 493 while (len > 0) { 494 // Step 4.1. Increment 495 addOne(v, ctrLen); 496 try { 497 // Step 4.2. Encrypt 498 cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg)); 499 byte[] out = cipher.doFinal(v); 500 501 // Step 4.3 and 5. Cat bytes and leftmost 502 System.arraycopy(out, 0, result, pos, 503 (len > blockLen) ? blockLen : len); 504 } catch (GeneralSecurityException e) { 505 throw new InternalError(e); 506 } 507 len -= blockLen; 508 if (len <= 0) { 509 // shortcut, so that pos needn't be updated 510 break; 511 } 512 pos += blockLen; 513 } 514 515 // Step 6. Update 516 update(additionalInput); 517 518 // Step 7. reseed_counter++ 519 reseedCounter++; 520 521 //status(); 522 523 // Step 8. Return 524 } 525 526 @Override toString()527 public String toString() { 528 return super.toString() + "," 529 + (usedf ? "use_df" : "no_df"); 530 } 531 } 532