--- /dev/null 2016-04-01 19:18:04.000000000 +0800 +++ new/src/java.base/share/classes/sun/security/provider/CtrDrbg.java 2016-04-01 19:18:03.000000000 +0800 @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.security.*; +import java.util.Arrays; +import java.util.Locale; + +public class CtrDrbg extends AbstractDrbg { + + private static final long serialVersionUID = 9L; + static final int aesLimit; + + static { + try { + aesLimit = Cipher.getMaxAllowedKeyLength("AES"); + } catch (Exception e) { + // should not happen + throw new AssertionError("Cannot detect AES"); + } + } + + private transient Cipher cipher; + + private String cipherAlg; + protected String keyAlg; + + private int ctrLen; + private int blockLen; + private int keyLen; + private int seedLen; + + private byte[] v; + private byte[] k; + + public CtrDrbg(SecureRandomInstantiateParameters params) { + mechName = "CTR_DRBG"; + configureInternal(params); + } + + private static int alg2strength(String algorithm) { + switch (algorithm.toUpperCase(Locale.ROOT)) { + case "TDEA": + case "3KEYTDEA": + case "3 KEY TDEA": + case "DESEDE": + return 112; + case "AES-128": + return 128; + case "AES-192": + return 192; + case "AES-256": + return 256; + default: + throw new IllegalArgumentException(algorithm + + " not supported in CTR_DBRG"); + } + } + + protected void chooseAlgorithmAndStrength() { + if (requestedAlgorithm != null) { + algorithm = requestedAlgorithm.toUpperCase(); + int supportedStrength = alg2strength(algorithm); + if (requestedStrength >= 0) { + int tryStrength = getStandardStrength(requestedStrength); + if (tryStrength > supportedStrength) { + throw new IllegalArgumentException( + algorithm + " does not support strength " + requestedStrength); + } + this.strength = tryStrength; + } else { + this.strength = defaultStrength > supportedStrength? + supportedStrength: defaultStrength; + } + } else { + int tryStrength = + requestedStrength<0? defaultStrength: requestedStrength; + tryStrength = getStandardStrength(tryStrength); + // Default algorithm, use AES-128 if AES-256 is not available + // Also described in comments of the "drbg" security property. + if (tryStrength <= 128 && aesLimit < 256) { + algorithm = "AES-128"; + } else if (aesLimit >= 256) { + algorithm = "AES-256"; + } else { + throw new IllegalArgumentException( + "unsupported strength " + requestedStrength); + } + this.strength = tryStrength; + } + switch (algorithm.toUpperCase(Locale.ROOT)) { + case "TDEA": + case "3KEYTDEA": + case "3 KEY TDEA": + case "DESEDE": + algorithm = "DESede"; + this.keyAlg = "DESede"; + this.cipherAlg = "DESede/ECB/NoPadding"; + this.blockLen = 64/8; + this.keyLen = 168/8; + break; + case "AES-128": + case "AES-192": + case "AES-256": + this.keyAlg = "AES"; + this.cipherAlg = "AES/ECB/NoPadding"; + switch (algorithm) { + case "AES-128": + this.keyLen = 128/8; + break; + case "AES-192": + this.keyLen = 192/8; + if (aesLimit < 192) { + throw new IllegalArgumentException(algorithm + + " not supported in CTR_DBRG"); + } + break; + case "AES-256": + this.keyLen = 256/8; + if (aesLimit < 256) { + throw new IllegalArgumentException(algorithm + + " not supported in CTR_DBRG"); + } + break; + default: + throw new IllegalArgumentException(algorithm + + " not supported in CTR_DBRG"); + } + this.blockLen = 128/8; + break; + default: + throw new IllegalArgumentException(algorithm + + " not supported in CTR_DBRG"); + } + this.seedLen = this.blockLen + this.keyLen; + this.ctrLen = this.blockLen; // TODO + if (usedf) { + this.minLength = this.strength / 8; + } else { + this.minLength = this.maxLength = + this.maxPsLength = this.maxAiLength = seedLen; + } + } + + /** + * This call, used by the constructors, instantiates the digest. + */ + @Override + protected void initEngine() { + if (cipherAlg == null) { + return; + } + try { + /* + * Use the local SUN implementation to avoid native + * performance overhead. + */ + cipher = Cipher.getInstance(cipherAlg, "SunJCE"); + } catch (NoSuchProviderException | NoSuchAlgorithmException + | NoSuchPaddingException e) { + // Fallback to any available. + try { + cipher = Cipher.getInstance(cipherAlg); + } catch (NoSuchAlgorithmException | NoSuchPaddingException exc) { + throw new InternalError( + "internal error: " + cipherAlg + " not available.", exc); + } + } + } + + private void status() { + if (debug != null) { + debug.println("Key = " + hex(k)); + debug.println("V = " + hex(v)); + debug.println("reseed counter = " + reseedCounter); + } + } + + private void update(byte[] input) { + if (input.length != seedLen) { + // Should not happen + throw new IllegalArgumentException("input must be of seedlen bytes"); + } + try { + int m = (seedLen + blockLen - 1) / blockLen; + byte[] temp = new byte[m * blockLen]; + for (int i = 0; i < m; i++) { + if (ctrLen < blockLen) { + byte[] inc = addBytes(ctrLen, v, new byte[]{1}); + System.arraycopy(inc, 0, v, blockLen - ctrLen, ctrLen); + } else { + v = addBytes(blockLen, v, new byte[]{1}); + } + cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); + cipher.doFinal(v, 0, blockLen, temp, i * blockLen); + } + temp = Arrays.copyOf(temp, seedLen); + for (int i=0; i> 24); + s[1] = (byte)(l >> 16); + s[2] = (byte)(l >> 8); + s[3] = (byte)(l); + s[4] = (byte)(n >> 24); + s[5] = (byte)(n >> 16); + s[6] = (byte)(n >> 8); + s[7] = (byte)(n); + System.arraycopy(input, 0, s, 8, l); + s[8+l] = (byte)0x80; + + byte[] k = new byte[keyLen]; + for (int i=0; i> 24); + iv[1] = (byte)(i >> 16); + iv[2] = (byte)(i >> 8); + iv[3] = (byte)(i); + System.arraycopy(s, 0, iv, blockLen, s.length); + int tailLen = temp.length - blockLen*i; + if (tailLen > blockLen) tailLen = blockLen; + System.arraycopy(bcc(k, iv), 0, temp, blockLen*i, tailLen); + } + + k = Arrays.copyOf(temp, keyLen); + byte[] x = Arrays.copyOfRange(temp, keyLen, temp.length); + + for (int i=0; i*blockLen < seedLen; i++) { + try { + cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); + int tailLen = temp.length - blockLen*i; + if (tailLen > blockLen) tailLen = blockLen; + x = cipher.doFinal(x); + System.arraycopy(x, 0, temp, blockLen * i, tailLen); + } catch (GeneralSecurityException e) { + throw new InternalError(e); + } + } + return temp; + } + + private byte[] bcc(byte[] k, byte[] data) { + byte[] chain = new byte[blockLen]; + int n = data.length / blockLen; + for (int i=0; i> 8; + if (i >= dlen && carry == 0) break; + } + } + return out; + } + + @Override + public synchronized void generateAlgorithm( + byte[] result, byte[] additionalInput) { + + if (additionalInput != null) { + if (usedf) { + additionalInput = df(additionalInput); + } else { + additionalInput = Arrays.copyOf(additionalInput, seedLen); + } + update(additionalInput); + } + + // Step 3. + int pos = 0; + + // Step 4. + while (pos < result.length) { + int tailLen = result.length - pos; + // Step 4.1. + if (ctrLen < blockLen) { + byte[] inc = addBytes(ctrLen, v, new byte[]{1}); + System.arraycopy(inc, 0, v, blockLen - ctrLen, ctrLen); + } else { + v = addBytes(blockLen, v, new byte[]{1}); + } + try { + // Step 4.2. + cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); + byte[] out = cipher.doFinal(v); + + // Step 4.3 and 5. + System.arraycopy(out, 0, result, pos, + tailLen > blockLen ? blockLen: tailLen); + } catch (GeneralSecurityException e) { + throw new InternalError(e); + } + pos += blockLen; + } + + // Step 6. + update(additionalInput != null? additionalInput: new byte[seedLen]); + + // Step 7. + reseedCounter++; + + status(); + } + + private static void des7to8(byte[] key56, int off56, byte[] key64, int off64) { + key64[off64+0] = (byte) (key56[off56+0] & 0xFE); // << 0 + key64[off64+1] = (byte) ((key56[off56+0] << 7) | ((key56[off56+1] & 0xFF) >>> 1)); + key64[off64+2] = (byte) ((key56[off56+1] << 6) | ((key56[off56+2] & 0xFF) >>> 2)); + key64[off64+3] = (byte) ((key56[off56+2] << 5) | ((key56[off56+3] & 0xFF) >>> 3)); + key64[off64+4] = (byte) ((key56[off56+3] << 4) | ((key56[off56+4] & 0xFF) >>> 4)); + key64[off64+5] = (byte) ((key56[off56+4] << 3) | ((key56[off56+5] & 0xFF) >>> 5)); + key64[off64+6] = (byte) ((key56[off56+5] << 2) | ((key56[off56+6] & 0xFF) >>> 6)); + key64[off64+7] = (byte) (key56[off56+6] << 1); + + for (int i = 0; i < 8; i++) { + // if even # bits, make uneven, take last bit of count so XOR with 1 + // for uneven # bits, make even, take last bit of count so XOR with 0 + key64[off64+i] ^= Integer.bitCount(key64[off64+i] ^ 1) & 1; + } + } + + private static SecretKey getKey(String keyAlg, byte[] k) { + if (keyAlg.equals("DESede")) { + byte[] k2 = new byte[24]; + des7to8(k, 0, k2, 0); + des7to8(k, 7, k2, 8); + des7to8(k, 14, k2, 16); + k = k2; + } + return new SecretKeySpec(k, keyAlg); + } + + private void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject (); + initEngine(); + } + + @Override + public String toString() { + return super.toString() + "/" + + (usedf?"use_df":"no_df"); + } +}