Path: blob/master/src/java.base/share/classes/sun/security/provider/CtrDrbg.java
41159 views
/*1* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.provider;2627import javax.crypto.Cipher;28import javax.crypto.NoSuchPaddingException;29import javax.crypto.spec.SecretKeySpec;30import java.security.*;31import java.util.Arrays;32import java.util.HexFormat;33import java.util.Locale;3435public class CtrDrbg extends AbstractDrbg {3637private static final int AES_LIMIT;3839static {40try {41AES_LIMIT = Cipher.getMaxAllowedKeyLength("AES");42} catch (Exception e) {43// should not happen44throw new AssertionError("Cannot detect AES", e);45}46}4748private Cipher cipher;4950private String cipherAlg;51private String keyAlg;5253private int ctrLen;54private int blockLen;55private int keyLen;56private int seedLen;5758private byte[] v;59private byte[] k;6061public CtrDrbg(SecureRandomParameters params) {62mechName = "CTR_DRBG";63configure(params);64}6566private static int alg2strength(String algorithm) {67switch (algorithm.toUpperCase(Locale.ROOT)) {68case "AES-128":69return 128;70case "AES-192":71return 192;72case "AES-256":73return 256;74default:75throw new IllegalArgumentException(algorithm +76" not supported in CTR_DBRG");77}78}7980@Override81protected void chooseAlgorithmAndStrength() {82if (requestedAlgorithm != null) {83algorithm = requestedAlgorithm.toUpperCase(Locale.ROOT);84int supportedStrength = alg2strength(algorithm);85if (requestedInstantiationSecurityStrength >= 0) {86int tryStrength = getStandardStrength(87requestedInstantiationSecurityStrength);88if (tryStrength > supportedStrength) {89throw new IllegalArgumentException(algorithm +90" does not support strength " +91requestedInstantiationSecurityStrength);92}93this.securityStrength = tryStrength;94} else {95this.securityStrength = (DEFAULT_STRENGTH > supportedStrength) ?96supportedStrength : DEFAULT_STRENGTH;97}98} else {99int tryStrength = (requestedInstantiationSecurityStrength < 0) ?100DEFAULT_STRENGTH : requestedInstantiationSecurityStrength;101tryStrength = getStandardStrength(tryStrength);102// Default algorithm, use AES-128 if AES-256 is not available.103// Remember to sync with "securerandom.drbg.config" in java.security104if (tryStrength <= 128 && AES_LIMIT < 256) {105algorithm = "AES-128";106} else if (AES_LIMIT >= 256) {107algorithm = "AES-256";108} else {109throw new IllegalArgumentException("unsupported strength " +110requestedInstantiationSecurityStrength);111}112this.securityStrength = tryStrength;113}114switch (algorithm.toUpperCase(Locale.ROOT)) {115case "AES-128":116case "AES-192":117case "AES-256":118this.keyAlg = "AES";119this.cipherAlg = "AES/ECB/NoPadding";120switch (algorithm) {121case "AES-128":122this.keyLen = 128 / 8;123break;124case "AES-192":125this.keyLen = 192 / 8;126if (AES_LIMIT < 192) {127throw new IllegalArgumentException(algorithm +128" not available (because policy) in CTR_DBRG");129}130break;131case "AES-256":132this.keyLen = 256 / 8;133if (AES_LIMIT < 256) {134throw new IllegalArgumentException(algorithm +135" not available (because policy) in CTR_DBRG");136}137break;138default:139throw new IllegalArgumentException(algorithm +140" not supported in CTR_DBRG");141}142this.blockLen = 128 / 8;143break;144default:145throw new IllegalArgumentException(algorithm +146" not supported in CTR_DBRG");147}148this.seedLen = this.blockLen + this.keyLen;149this.ctrLen = this.blockLen; // TODO150if (usedf) {151this.minLength = this.securityStrength / 8;152} else {153this.minLength = this.maxLength =154this.maxPersonalizationStringLength =155this.maxAdditionalInputLength = seedLen;156}157}158159/**160* This call, used by the constructors, instantiates the digest.161*/162@Override163protected void initEngine() {164try {165/*166* Use the local SunJCE implementation to avoid native167* performance overhead.168*/169cipher = Cipher.getInstance(cipherAlg, "SunJCE");170} catch (NoSuchProviderException | NoSuchAlgorithmException171| NoSuchPaddingException e) {172// Fallback to any available.173try {174cipher = Cipher.getInstance(cipherAlg);175} catch (NoSuchAlgorithmException | NoSuchPaddingException exc) {176throw new InternalError(177"internal error: " + cipherAlg + " not available.", exc);178}179}180}181182private void status() {183if (debug != null) {184debug.println(this, "Key = " + HexFormat.of().formatHex(k));185debug.println(this, "V = " + HexFormat.of().formatHex(v));186debug.println(this, "reseed counter = " + reseedCounter);187}188}189190// 800-90Ar1 10.2.1.2. CTR_DRBG_Update191private void update(byte[] input) {192if (input.length != seedLen) {193// Should not happen194throw new IllegalArgumentException("input length not seedLen: "195+ input.length);196}197try {198199int m = (seedLen + blockLen - 1) / blockLen;200byte[] temp = new byte[m * blockLen];201202// Step 1. temp = Null.203204// Step 2. Loop205for (int i = 0; i < m; i++) {206// Step 2.1. Increment207addOne(v, ctrLen);208// Step 2.2. Block_Encrypt209cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));210// Step 2.3. Encrypt into right position, no need to cat211cipher.doFinal(v, 0, blockLen, temp, i * blockLen);212}213214// Step 3. Truncate215temp = Arrays.copyOf(temp, seedLen);216217// Step 4: Add218for (int i = 0; i < seedLen; i++) {219temp[i] ^= input[i];220}221222// Step 5: leftmost223k = Arrays.copyOf(temp, keyLen);224225// Step 6: rightmost226v = Arrays.copyOfRange(temp, seedLen - blockLen, seedLen);227228// Step 7. Return229} catch (GeneralSecurityException e) {230throw new InternalError(e);231}232}233234@Override235protected void instantiateAlgorithm(byte[] ei) {236if (debug != null) {237debug.println(this, "instantiate");238}239byte[] more;240if (usedf) {241// 800-90Ar1 10.2.1.3.2 Step 1-2. cat bytes242if (personalizationString == null) {243more = nonce;244} else {245if (nonce.length + personalizationString.length < 0) {246// Length must be represented as a 32 bit integer in df()247throw new IllegalArgumentException(248"nonce plus personalization string is too long");249}250more = Arrays.copyOf(251nonce, nonce.length + personalizationString.length);252System.arraycopy(personalizationString, 0, more, nonce.length,253personalizationString.length);254}255} else {256// 800-90Ar1 10.2.1.3.1257// Step 1-2, no need to expand personalizationString, we only XOR258// with shorter length259more = personalizationString;260}261reseedAlgorithm(ei, more);262}263264/**265* Block_cipher_df in 10.3.2266*267* @param input the input string268* @return the output block (always of seedLen)269*/270private byte[] df(byte[] input) {271// 800-90Ar1 10.3.2272// 2. L = len (input_string)/8273int l = input.length;274// 3. N = number_of_bits_to_return/8275int n = seedLen;276// 4. S = L || N || input_string || 0x80277byte[] ln = new byte[8];278ln[0] = (byte)(l >> 24);279ln[1] = (byte)(l >> 16);280ln[2] = (byte)(l >> 8);281ln[3] = (byte)(l);282ln[4] = (byte)(n >> 24);283ln[5] = (byte)(n >> 16);284ln[6] = (byte)(n >> 8);285ln[7] = (byte)(n);286287// 5. Zero padding of S288// Not necessary, see bcc289290// 8. K = leftmost (0x00010203...1D1E1F, keylen).291byte[] k = new byte[keyLen];292for (int i = 0; i < k.length; i++) {293k[i] = (byte)i;294}295296// 6. temp = the Null String297byte[] temp = new byte[seedLen];298299// 7. i = 0300for (int i = 0; i * blockLen < temp.length; i++) {301// 9.1 IV = i || 0^(outlen - len (i)). outLen is blockLen302byte[] iv = new byte[blockLen];303iv[0] = (byte)(i >> 24);304iv[1] = (byte)(i >> 16);305iv[2] = (byte)(i >> 8);306iv[3] = (byte)(i);307308int tailLen = temp.length - blockLen*i;309if (tailLen > blockLen) {310tailLen = blockLen;311}312// 9.2 temp = temp || BCC (K, (IV || S)).313System.arraycopy(bcc(k, iv, ln, input, new byte[]{(byte)0x80}),3140, temp, blockLen*i, tailLen);315}316317// 10. K = leftmost(temp, keylen)318k = Arrays.copyOf(temp, keyLen);319320// 11. x = select(temp, keylen+1, keylen+outlen)321byte[] x = Arrays.copyOfRange(temp, keyLen, temp.length);322323// 12. temp = the Null string324// No need to clean up, temp will be overwritten325326for (int i = 0; i * blockLen < seedLen; i++) {327try {328cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));329int tailLen = temp.length - blockLen*i;330// 14. requested_bits = leftmost(temp, nuumber_of_bits_to_return)331if (tailLen > blockLen) {332tailLen = blockLen;333}334x = cipher.doFinal(x);335System.arraycopy(x, 0, temp, blockLen * i, tailLen);336} catch (GeneralSecurityException e) {337throw new InternalError(e);338}339}340341// 15. Return342return temp;343}344345/**346* Block_Encrypt in 10.3.3347*348* @param k the key349* @param data after concatenated, the data to be operated upon. This is350* a series of byte[], each with an arbitrary length. Note351* that the full length is not necessarily a multiple of352* outlen. XOR with zero is no-op.353* @return the result354*/355private byte[] bcc(byte[] k, byte[]... data) {356byte[] chain = new byte[blockLen];357int n1 = 0; // index in data358int n2 = 0; // index in data[n1]359// pack blockLen of bytes into chain from data[][], again and again360while (n1 < data.length) {361int j;362out: for (j = 0; j < blockLen; j++) {363while (n2 >= data[n1].length) {364n1++;365if (n1 >= data.length) {366break out;367}368n2 = 0;369}370chain[j] ^= data[n1][n2];371n2++;372}373if (j == 0) { // all data happens to be consumed in the last loop374break;375}376try {377cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));378chain = cipher.doFinal(chain);379} catch (GeneralSecurityException e) {380throw new InternalError(e);381}382}383return chain;384}385386@Override387protected synchronized void reseedAlgorithm(388byte[] ei,389byte[] additionalInput) {390if (usedf) {391// 800-90Ar1 10.2.1.3.2 Instantiate.392// 800-90Ar1 10.2.1.4.2 Reseed.393394// Step 1: cat bytes395if (additionalInput != null) {396if (ei.length + additionalInput.length < 0) {397// Length must be represented as a 32 bit integer in df()398throw new IllegalArgumentException(399"entropy plus additional input is too long");400}401byte[] temp = Arrays.copyOf(402ei, ei.length + additionalInput.length);403System.arraycopy(additionalInput, 0, temp, ei.length,404additionalInput.length);405ei = temp;406}407// Step 2. df (seed_material, seedlen).408ei = df(ei);409} else {410// 800-90Ar1 10.2.1.3.1 Instantiate411// 800-90Ar1 10.2.1.4.1 Reseed412// Step 1-2. Needless413// Step 3. seed_material = entropy_input XOR more414if (additionalInput != null) {415// additionalInput.length <= seedLen416for (int i = 0; i < additionalInput.length; i++) {417ei[i] ^= additionalInput[i];418}419}420}421422if (v == null) {423// 800-90Ar1 10.2.1.3.2 Instantiate. Step 3-4424// 800-90Ar1 10.2.1.3.1 Instantiate. Step 4-5425k = new byte[keyLen];426v = new byte[blockLen];427}428//status();429430// 800-90Ar1 10.2.1.3.1 Instantiate. Step 6431// 800-90Ar1 10.2.1.3.2 Instantiate. Step 5432// 800-90Ar1 10.2.1.4.1 Reseed. Step 4433// 800-90Ar1 10.2.1.4.2 Reseed. Step 3434update(ei);435// 800-90Ar1 10.2.1.3.1 Instantiate. Step 7436// 800-90Ar1 10.2.1.3.2 Instantiate. Step 6437// 800-90Ar1 10.2.1.4.1 Reseed. Step 5438// 800-90Ar1 10.2.1.4.2 Reseed. Step 4439reseedCounter = 1;440//status();441442// Whatever step. Return443}444445/**446* Add one to data, only touch the last len bytes.447*/448private static void addOne(byte[] data, int len) {449for (int i = 0; i < len; i++) {450data[data.length - 1 - i]++;451if (data[data.length - 1 - i] != 0) {452break;453}454}455}456457@Override458public synchronized void generateAlgorithm(459byte[] result, byte[] additionalInput) {460461if (debug != null) {462debug.println(this, "generateAlgorithm");463}464465// 800-90Ar1 10.2.1.5.1 Generate466// 800-90Ar1 10.2.1.5.2 Generate467468// Step 1: Check reseed_counter. Will not fail. Already checked in469// AbstractDrbg#engineNextBytes.470471if (additionalInput != null) {472if (usedf) {473// 10.2.1.5.2 Step 2.1474additionalInput = df(additionalInput);475} else {476// 10.2.1.5.1 Step 2.1-2.2477additionalInput = Arrays.copyOf(additionalInput, seedLen);478}479// 10.2.1.5.1 Step 2.3480// 10.2.1.5.2 Step 2.2481update(additionalInput);482} else {483// 10.2.1.5.1 Step 2 Else484// 10.2.1.5.2 Step 2 Else485additionalInput = new byte[seedLen];486}487488// Step 3. temp = Null489int pos = 0;490int len = result.length;491492// Step 4. Loop493while (len > 0) {494// Step 4.1. Increment495addOne(v, ctrLen);496try {497cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));498// Step 4.2. Encrypt499// Step 4.3 and 5. Cat bytes and leftmost500if (len > blockLen) {501cipher.doFinal(v, 0, blockLen, result, pos);502} else {503byte[] out = cipher.doFinal(v);504System.arraycopy(out, 0, result, pos, len);505Arrays.fill(out, (byte)0);506}507} catch (GeneralSecurityException e) {508throw new InternalError(e);509}510len -= blockLen;511if (len <= 0) {512// shortcut, so that pos needn't be updated513break;514}515pos += blockLen;516}517518// Step 6. Update519update(additionalInput);520521// Step 7. reseed_counter++522reseedCounter++;523524//status();525526// Step 8. Return527}528529@Override530public String toString() {531return super.toString() + ","532+ (usedf ? "use_df" : "no_df");533}534}535536537