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 java.io.IOException; 29 import java.math.BigInteger; 30 import java.security.DigestException; 31 import java.security.MessageDigest; 32 import java.security.NoSuchAlgorithmException; 33 import java.security.NoSuchProviderException; 34 import java.security.SecureRandomInstantiateParameters; 35 import java.util.Arrays; 36 37 public class HashDrbg extends AbstractHashDrbg { 38 39 private static final long serialVersionUID = 9L; 40 41 private static final byte[] ZERO= new byte[1]; 42 private static final byte[] ONE = new byte[]{1}; 43 44 private transient MessageDigest digest; 45 46 private byte[] v; 47 private byte[] c; 48 49 public HashDrbg(SecureRandomInstantiateParameters params) { 50 mechName = "Hash_DRBG"; 51 configureInternal(params); 52 } 53 54 /** 55 * This call, used by the constructors, instantiates the digest. 56 */ 57 @Override 58 protected void initEngine() { 59 if (algorithm == null) { 60 return; 61 } 62 try { 63 /* 64 * Use the local SUN implementation to avoid native 65 * performance overhead. 66 */ 67 digest = MessageDigest.getInstance(algorithm, "SUN"); 68 } catch (NoSuchProviderException | NoSuchAlgorithmException e) { 69 // Fallback to any available. 70 try { 71 digest = MessageDigest.getInstance(algorithm); 72 } catch (NoSuchAlgorithmException exc) { 73 throw new InternalError( 74 "internal error: " + algorithm + " not available.", exc); 75 } 76 } 77 } 78 79 private byte[] hashDf(int requested, byte[]... inputs) { 80 return hashDf(digest, outLen, requested, inputs); 81 } 82 83 /** 84 * A hash-based derivation function defined in NIST SP 800-90Ar1 10.3.1. 85 * The function is used inside Hash_DRBG, and can also be used as an 86 * approved conditioning function as described in 800-90B 6.4.2.2. 87 * 88 * @param digest a {@code MessageDigest} object in reset state 89 * @param outLen {@link MessageDigest#getDigestLength} of {@code digest} 90 * @param requested requested output length, in bytes 91 * @param inputs input data 92 * @return the condensed/expanded output 93 */ 94 public static byte[] hashDf(MessageDigest digest, int outLen, 95 int requested, byte[]... inputs) { 96 int len = (requested + outLen - 1) / outLen; 97 byte[] temp = new byte[len * outLen]; 98 int counter = 1; 99 100 for (int i=0; i<len; i++) { 101 digest.update((byte) counter); 102 digest.update((byte)(requested >> 21)); // requested*8 as int32 103 digest.update((byte)(requested >> 13)); 104 digest.update((byte)(requested >> 5)); 105 digest.update((byte)(requested << 3)); 106 for (byte[] input: inputs) { 107 digest.update(input); 108 } 109 try { 110 digest.digest(temp, i * outLen, outLen); 111 } catch (DigestException e) { 112 throw new AssertionError("will not happen"); 113 } 114 counter++; 115 } 116 return temp.length == requested? temp: Arrays.copyOf(temp, requested); 117 } 118 119 // For seeding, input is entropy_input || nonce || personalization_string; 120 // for reseeding, input is entropy_input || additional_input 121 @Override 122 protected final void hashReseedInternal(byte[] input) { 123 byte[] seed; 124 if (v != null) { 125 seed = hashDf(seedLen, ONE, v, input); 126 } else { 127 seed = hashDf(seedLen, input); 128 } 129 v = seed; 130 c = hashDf(seedLen, ZERO, v); 131 reseedCounter = 1; 132 status(); 133 } 134 135 private void status() { 136 if (debug != null) { 137 debug.println("V = " + hex(v)); 138 debug.println("C = " + hex(c)); 139 debug.println("reseed counter = " + reseedCounter); 140 } 141 } 142 143 /** 144 * Adds byte arrays into an existing one. 145 * 146 * @param out existing array 147 * @param data more arrays, can be of different length 148 */ 149 private static void addBytes(byte[] out, int len, byte[]... data) { 150 for (byte[] d: data) { 151 int dlen = d.length; 152 int carry = 0; 153 for (int i=0; i<len; i++) { 154 int sum = (out[len-i-1] & 0xff) + carry; 155 if (i < dlen) { 156 sum += (d[dlen - i - 1] & 0xff); 157 } 158 out[len - i - 1] = (byte) sum; 159 carry = sum >> 8; 160 if (i >= dlen - 1 && carry == 0) break; 161 } 162 } 163 } 164 165 /** 166 * Generates a user-specified number of random bytes. 167 * 168 * @param result the array to be filled in with random bytes. 169 */ 170 @Override 171 public final synchronized void generateAlgorithm( 172 byte[] result, byte[] additionalInput) { 173 174 if (debug != null) { 175 debug.println("generateAlgorithm"); 176 } 177 if (additionalInput != null) { 178 digest.update((byte)2); 179 digest.update(v); 180 digest.update(additionalInput); 181 addBytes(v, seedLen, digest.digest()); 182 } 183 184 // Step 3. 185 hashGen(result, result.length, v); 186 187 // Step 4. 188 digest.update((byte)3); 189 digest.update(v); 190 byte[] h = digest.digest(); 191 192 // Step 5. 193 byte[] rcBytes; 194 if (reseedCounter < 256) { 195 rcBytes = new byte[]{(byte)reseedCounter}; 196 } else { 197 rcBytes = BigInteger.valueOf(reseedCounter).toByteArray(); 198 } 199 addBytes(v, seedLen, h, c, rcBytes); 200 201 // Step 6. 202 reseedCounter++; 203 204 status(); 205 } 206 207 private void hashGen(byte[] output, int len, byte[] v) { 208 int m = (len + outLen - 1) / outLen; 209 byte[] data = v; 210 for (int i=0; i<m; i++) { 211 int tailLen = len - i * outLen; 212 if (tailLen < outLen) { 213 System.arraycopy(digest.digest(data), 0, output, i * outLen, 214 tailLen); 215 } else { 216 try { 217 digest.update(data); 218 digest.digest(output, i*outLen, outLen); 219 } catch (DigestException e) { 220 throw new AssertionError("will not happen"); 221 } 222 } 223 // Unless this is the last around, we will need to increment data. 224 // but we cannot change v, so a copy is made. 225 if (i != m - 1) { 226 if (data == v) { 227 data = Arrays.copyOf(v, v.length); 228 } 229 addBytes(data, seedLen, ONE); 230 } 231 } 232 } 233 234 private void readObject(java.io.ObjectInputStream s) 235 throws IOException, ClassNotFoundException { 236 s.defaultReadObject (); 237 initEngine(); 238 } 239 }