/* * 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 java.io.IOException; import java.math.BigInteger; import java.security.DigestException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandomInstantiateParameters; import java.util.Arrays; public class HashDrbg extends AbstractHashDrbg { private static final long serialVersionUID = 9L; private static final byte[] ZERO= new byte[1]; private static final byte[] ONE = new byte[]{1}; private transient MessageDigest digest; private byte[] v; private byte[] c; public HashDrbg(SecureRandomInstantiateParameters params) { mechName = "Hash_DRBG"; configureInternal(params); } /** * This call, used by the constructors, instantiates the digest. */ @Override protected void initEngine() { if (algorithm == null) { return; } try { /* * Use the local SUN implementation to avoid native * performance overhead. */ digest = MessageDigest.getInstance(algorithm, "SUN"); } catch (NoSuchProviderException | NoSuchAlgorithmException e) { // Fallback to any available. try { digest = MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException exc) { throw new InternalError( "internal error: " + algorithm + " not available.", exc); } } } private byte[] hashDf(int requested, byte[]... inputs) { return hashDf(digest, outLen, requested, inputs); } /** * A hash-based derivation function defined in NIST SP 800-90Ar1 10.3.1. * The function is used inside Hash_DRBG, and can also be used as an * approved conditioning function as described in 800-90B 6.4.2.2. * * @param digest a {@code MessageDigest} object in reset state * @param outLen {@link MessageDigest#getDigestLength} of {@code digest} * @param requested requested output length, in bytes * @param inputs input data * @return the condensed/expanded output */ public static byte[] hashDf(MessageDigest digest, int outLen, int requested, byte[]... inputs) { int len = (requested + outLen - 1) / outLen; byte[] temp = new byte[len * outLen]; int counter = 1; for (int i=0; i> 21)); // requested*8 as int32 digest.update((byte)(requested >> 13)); digest.update((byte)(requested >> 5)); digest.update((byte)(requested << 3)); for (byte[] input: inputs) { digest.update(input); } try { digest.digest(temp, i * outLen, outLen); } catch (DigestException e) { throw new AssertionError("will not happen"); } counter++; } return temp.length == requested? temp: Arrays.copyOf(temp, requested); } // For seeding, input is entropy_input || nonce || personalization_string; // for reseeding, input is entropy_input || additional_input @Override protected final void hashReseedInternal(byte[] input) { byte[] seed; if (v != null) { seed = hashDf(seedLen, ONE, v, input); } else { seed = hashDf(seedLen, input); } v = seed; c = hashDf(seedLen, ZERO, v); reseedCounter = 1; status(); } private void status() { if (debug != null) { debug.println("V = " + hex(v)); debug.println("C = " + hex(c)); debug.println("reseed counter = " + reseedCounter); } } /** * Adds byte arrays into an existing one. * * @param out existing array * @param data more arrays, can be of different length */ private static void addBytes(byte[] out, int len, byte[]... data) { for (byte[] d: data) { int dlen = d.length; int carry = 0; for (int i=0; i> 8; if (i >= dlen - 1 && carry == 0) break; } } } /** * Generates a user-specified number of random bytes. * * @param result the array to be filled in with random bytes. */ @Override public final synchronized void generateAlgorithm( byte[] result, byte[] additionalInput) { if (debug != null) { debug.println("generateAlgorithm"); } if (additionalInput != null) { digest.update((byte)2); digest.update(v); digest.update(additionalInput); addBytes(v, seedLen, digest.digest()); } // Step 3. hashGen(result, result.length, v); // Step 4. digest.update((byte)3); digest.update(v); byte[] h = digest.digest(); // Step 5. byte[] rcBytes; if (reseedCounter < 256) { rcBytes = new byte[]{(byte)reseedCounter}; } else { rcBytes = BigInteger.valueOf(reseedCounter).toByteArray(); } addBytes(v, seedLen, h, c, rcBytes); // Step 6. reseedCounter++; status(); } private void hashGen(byte[] output, int len, byte[] v) { int m = (len + outLen - 1) / outLen; byte[] data = v; for (int i=0; i