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.SecretKey; 31 import javax.crypto.spec.SecretKeySpec; 32 import java.io.IOException; 33 import java.security.*; 34 import java.util.Arrays; 35 import java.util.Locale; 36 37 public class CtrDrbg extends AbstractDrbg { 38 39 private static final long serialVersionUID = 9L; 40 static final int aesLimit; 41 42 static { 43 try { 44 aesLimit = Cipher.getMaxAllowedKeyLength("AES"); 45 } catch (Exception e) { 46 // should not happen 47 throw new AssertionError("Cannot detect AES"); 48 } 49 } 50 51 private transient Cipher cipher; 52 53 private String cipherAlg; 54 protected String keyAlg; 55 56 private int ctrLen; 57 private int blockLen; 58 private int keyLen; 59 private int seedLen; 60 61 private byte[] v; 62 private byte[] k; 63 64 public CtrDrbg(SecureRandomParameters params) { 65 mechName = "CTR_DRBG"; 66 configureInternal(params); 67 } 68 69 private static int alg2strength(String algorithm) { 70 switch (algorithm.toUpperCase(Locale.ROOT)) { 71 case "TDEA": 72 case "3KEYTDEA": 73 case "3 KEY TDEA": 74 case "DESEDE": 75 return 112; 76 case "AES-128": 77 return 128; 78 case "AES-192": 79 return 192; 80 case "AES-256": 81 return 256; 82 default: 83 throw new IllegalArgumentException(algorithm + 84 " not supported in CTR_DBRG"); 85 } 86 } 87 88 protected void chooseAlgorithmAndStrength() { 89 if (requestedAlgorithm != null) { 90 algorithm = requestedAlgorithm.toUpperCase(); 91 int supportedStrength = alg2strength(algorithm); 92 if (requestedStrength >= 0) { 93 int tryStrength = getStandardStrength(requestedStrength); 94 if (tryStrength > supportedStrength) { 95 throw new IllegalArgumentException( 96 algorithm + " does not support strength " + requestedStrength); 97 } 98 this.strength = tryStrength; 99 } else { 100 this.strength = defaultStrength > supportedStrength? 101 supportedStrength: defaultStrength; 102 } 103 } else { 104 int tryStrength = 105 requestedStrength<0? defaultStrength: requestedStrength; 106 tryStrength = getStandardStrength(tryStrength); 107 // Default algorithm, use AES-128 if AES-256 is not available 108 // Also described in comments of the "drbg" security property. 109 if (tryStrength <= 128 && aesLimit < 256) { 110 algorithm = "AES-128"; 111 } else if (aesLimit >= 256) { 112 algorithm = "AES-256"; 113 } else { 114 throw new IllegalArgumentException( 115 "unsupported strength " + requestedStrength); 116 } 117 this.strength = tryStrength; 118 } 119 switch (algorithm.toUpperCase(Locale.ROOT)) { 120 case "TDEA": 121 case "3KEYTDEA": 122 case "3 KEY TDEA": 123 case "DESEDE": 124 algorithm = "DESede"; 125 this.keyAlg = "DESede"; 126 this.cipherAlg = "DESede/ECB/NoPadding"; 127 this.blockLen = 64/8; 128 this.keyLen = 168/8; 129 break; 130 case "AES-128": 131 case "AES-192": 132 case "AES-256": 133 this.keyAlg = "AES"; 134 this.cipherAlg = "AES/ECB/NoPadding"; 135 switch (algorithm) { 136 case "AES-128": 137 this.keyLen = 128/8; 138 break; 139 case "AES-192": 140 this.keyLen = 192/8; 141 if (aesLimit < 192) { 142 throw new IllegalArgumentException(algorithm + 143 " not supported in CTR_DBRG"); 144 } 145 break; 146 case "AES-256": 147 this.keyLen = 256/8; 148 if (aesLimit < 256) { 149 throw new IllegalArgumentException(algorithm + 150 " not supported in CTR_DBRG"); 151 } 152 break; 153 default: 154 throw new IllegalArgumentException(algorithm + 155 " not supported in CTR_DBRG"); 156 } 157 this.blockLen = 128/8; 158 break; 159 default: 160 throw new IllegalArgumentException(algorithm + 161 " not supported in CTR_DBRG"); 162 } 163 this.seedLen = this.blockLen + this.keyLen; 164 this.ctrLen = this.blockLen; // TODO 165 if (usedf) { 166 this.minLength = this.strength / 8; 167 } else { 168 this.minLength = this.maxLength = 169 this.maxPsLength = this.maxAiLength = seedLen; 170 } 171 } 172 173 /** 174 * This call, used by the constructors, instantiates the digest. 175 */ 176 @Override 177 protected void initEngine() { 178 if (cipherAlg == null) { 179 return; 180 } 181 try { 182 /* 183 * Use the local SUN implementation to avoid native 184 * performance overhead. 185 */ 186 cipher = Cipher.getInstance(cipherAlg, "SunJCE"); 187 } catch (NoSuchProviderException | NoSuchAlgorithmException 188 | NoSuchPaddingException e) { 189 // Fallback to any available. 190 try { 191 cipher = Cipher.getInstance(cipherAlg); 192 } catch (NoSuchAlgorithmException | NoSuchPaddingException exc) { 193 throw new InternalError( 194 "internal error: " + cipherAlg + " not available.", exc); 195 } 196 } 197 } 198 199 private void status() { 200 if (debug != null) { 201 debug.println("Key = " + hex(k)); 202 debug.println("V = " + hex(v)); 203 debug.println("reseed counter = " + reseedCounter); 204 } 205 } 206 207 private void update(byte[] input) { 208 if (input.length != seedLen) { 209 // Should not happen 210 throw new IllegalArgumentException("input must be of seedlen bytes"); 211 } 212 try { 213 int m = (seedLen + blockLen - 1) / blockLen; 214 byte[] temp = new byte[m * blockLen]; 215 for (int i = 0; i < m; i++) { 216 if (ctrLen < blockLen) { 217 byte[] inc = addBytes(ctrLen, v, new byte[]{1}); 218 System.arraycopy(inc, 0, v, blockLen - ctrLen, ctrLen); 219 } else { 220 v = addBytes(blockLen, v, new byte[]{1}); 221 } 222 cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); 223 cipher.doFinal(v, 0, blockLen, temp, i * blockLen); 224 } 225 temp = Arrays.copyOf(temp, seedLen); 226 for (int i=0; i<seedLen; i++) { 227 temp[i] ^= input[i]; 228 } 229 k = Arrays.copyOf(temp, keyLen); 230 v = Arrays.copyOfRange(temp, seedLen-blockLen, seedLen); 231 } catch (GeneralSecurityException e) { 232 throw new InternalError(e); 233 } 234 } 235 236 @Override 237 protected void instantiateAlgorithm(byte[] ei) { 238 byte[] more; 239 if (usedf) { 240 if (ps == null) { 241 more = nonce; 242 } else { 243 more = Arrays.copyOf(nonce, nonce.length + ps.length); 244 System.arraycopy(ps, 0, more, nonce.length, ps.length); 245 } 246 } else { 247 more = ps; 248 } 249 reseedAlgorithm(ei, more); 250 } 251 252 private byte[] df(byte[] input) { 253 int l = input.length; 254 int n = seedLen; 255 int slen = 4 + 4 + l + 1; 256 byte[] s = new byte[(slen + blockLen - 1) / blockLen * blockLen]; 257 s[0] = (byte)(l >> 24); 258 s[1] = (byte)(l >> 16); 259 s[2] = (byte)(l >> 8); 260 s[3] = (byte)(l); 261 s[4] = (byte)(n >> 24); 262 s[5] = (byte)(n >> 16); 263 s[6] = (byte)(n >> 8); 264 s[7] = (byte)(n); 265 System.arraycopy(input, 0, s, 8, l); 266 s[8+l] = (byte)0x80; 267 268 byte[] k = new byte[keyLen]; 269 for (int i=0; i<k.length; i++) { 270 k[i] = (byte)i; 271 } 272 273 byte[] temp = new byte[seedLen]; 274 275 for (int i=0; i*blockLen < temp.length; i++) { 276 byte[] iv = new byte[blockLen + s.length]; 277 iv[0] = (byte)(i >> 24); 278 iv[1] = (byte)(i >> 16); 279 iv[2] = (byte)(i >> 8); 280 iv[3] = (byte)(i); 281 System.arraycopy(s, 0, iv, blockLen, s.length); 282 int tailLen = temp.length - blockLen*i; 283 if (tailLen > blockLen) tailLen = blockLen; 284 System.arraycopy(bcc(k, iv), 0, temp, blockLen*i, tailLen); 285 } 286 287 k = Arrays.copyOf(temp, keyLen); 288 byte[] x = Arrays.copyOfRange(temp, keyLen, temp.length); 289 290 for (int i=0; i*blockLen < seedLen; i++) { 291 try { 292 cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); 293 int tailLen = temp.length - blockLen*i; 294 if (tailLen > blockLen) tailLen = blockLen; 295 x = cipher.doFinal(x); 296 System.arraycopy(x, 0, temp, blockLen * i, tailLen); 297 } catch (GeneralSecurityException e) { 298 throw new InternalError(e); 299 } 300 } 301 return temp; 302 } 303 304 private byte[] bcc(byte[] k, byte[] data) { 305 byte[] chain = new byte[blockLen]; 306 int n = data.length / blockLen; 307 for (int i=0; i<n; i++) { 308 byte[] inputBlock = Arrays.copyOfRange( 309 data, i*blockLen, i*blockLen+blockLen); 310 for (int j=0; j<blockLen; j++) { 311 inputBlock[j] ^= chain[j]; 312 } 313 try { 314 cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); 315 chain = cipher.doFinal(inputBlock); 316 } catch (GeneralSecurityException e) { 317 throw new InternalError(e); 318 } 319 } 320 return chain; 321 } 322 323 @Override 324 protected void reseedAlgorithm( 325 byte[] ei, 326 byte[] additionalInput) { 327 if (usedf) { 328 if (additionalInput != null) { 329 byte[] temp = Arrays.copyOf( 330 ei, ei.length + additionalInput.length); 331 System.arraycopy(additionalInput, 0, temp, ei.length, additionalInput.length); 332 ei = temp; 333 } 334 ei = df(ei); 335 } else { 336 if (additionalInput != null) { 337 for (int i = 0; i < seedLen && i < additionalInput.length; i++) { 338 ei[i] ^= additionalInput[i]; 339 } 340 } 341 } 342 343 // Also called by instantiateAlgorithm. 344 if (v == null) { 345 k = new byte[keyLen]; 346 v = new byte[blockLen]; 347 } 348 status(); 349 update(ei); 350 reseedCounter = 1; 351 status(); 352 } 353 354 private static byte[] addBytes(int len, byte[]... data) { 355 byte[] out = new byte[len]; 356 for (byte[] d: data) { 357 int dlen = d.length; 358 int carry = 0; 359 for (int i=0; i<len; i++) { 360 int sum = (out[len-i-1] & 0xff) + carry; 361 if (i < dlen) { 362 sum += (d[dlen - i - 1] & 0xff); 363 } 364 out[len - i - 1] = (byte) sum; 365 carry = sum >> 8; 366 if (i >= dlen && carry == 0) break; 367 } 368 } 369 return out; 370 } 371 372 @Override 373 public synchronized void generateAlgorithm( 374 byte[] result, byte[] additionalInput) { 375 376 if (additionalInput != null) { 377 if (usedf) { 378 additionalInput = df(additionalInput); 379 } else { 380 additionalInput = Arrays.copyOf(additionalInput, seedLen); 381 } 382 update(additionalInput); 383 } 384 385 // Step 3. 386 int pos = 0; 387 388 // Step 4. 389 while (pos < result.length) { 390 int tailLen = result.length - pos; 391 // Step 4.1. 392 if (ctrLen < blockLen) { 393 byte[] inc = addBytes(ctrLen, v, new byte[]{1}); 394 System.arraycopy(inc, 0, v, blockLen - ctrLen, ctrLen); 395 } else { 396 v = addBytes(blockLen, v, new byte[]{1}); 397 } 398 try { 399 // Step 4.2. 400 cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); 401 byte[] out = cipher.doFinal(v); 402 403 // Step 4.3 and 5. 404 System.arraycopy(out, 0, result, pos, 405 tailLen > blockLen ? blockLen: tailLen); 406 } catch (GeneralSecurityException e) { 407 throw new InternalError(e); 408 } 409 pos += blockLen; 410 } 411 412 // Step 6. 413 update(additionalInput != null? additionalInput: new byte[seedLen]); 414 415 // Step 7. 416 reseedCounter++; 417 418 status(); 419 } 420 421 private static void des7to8(byte[] key56, int off56, byte[] key64, int off64) { 422 key64[off64+0] = (byte) (key56[off56+0] & 0xFE); // << 0 423 key64[off64+1] = (byte) ((key56[off56+0] << 7) | ((key56[off56+1] & 0xFF) >>> 1)); 424 key64[off64+2] = (byte) ((key56[off56+1] << 6) | ((key56[off56+2] & 0xFF) >>> 2)); 425 key64[off64+3] = (byte) ((key56[off56+2] << 5) | ((key56[off56+3] & 0xFF) >>> 3)); 426 key64[off64+4] = (byte) ((key56[off56+3] << 4) | ((key56[off56+4] & 0xFF) >>> 4)); 427 key64[off64+5] = (byte) ((key56[off56+4] << 3) | ((key56[off56+5] & 0xFF) >>> 5)); 428 key64[off64+6] = (byte) ((key56[off56+5] << 2) | ((key56[off56+6] & 0xFF) >>> 6)); 429 key64[off64+7] = (byte) (key56[off56+6] << 1); 430 431 for (int i = 0; i < 8; i++) { 432 // if even # bits, make uneven, take last bit of count so XOR with 1 433 // for uneven # bits, make even, take last bit of count so XOR with 0 434 key64[off64+i] ^= Integer.bitCount(key64[off64+i] ^ 1) & 1; 435 } 436 } 437 438 private static SecretKey getKey(String keyAlg, byte[] k) { 439 if (keyAlg.equals("DESede")) { 440 byte[] k2 = new byte[24]; 441 des7to8(k, 0, k2, 0); 442 des7to8(k, 7, k2, 8); 443 des7to8(k, 14, k2, 16); 444 k = k2; 445 } 446 return new SecretKeySpec(k, keyAlg); 447 } 448 449 private void readObject(java.io.ObjectInputStream s) 450 throws IOException, ClassNotFoundException { 451 s.defaultReadObject (); 452 initEngine(); 453 } 454 455 @Override 456 public String toString() { 457 return super.toString() + "/" 458 + (usedf?"use_df":"no_df"); 459 } 460 }