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.Mac; 29 import javax.crypto.spec.SecretKeySpec; 30 import java.security.InvalidKeyException; 31 import java.security.NoSuchAlgorithmException; 32 import java.security.NoSuchProviderException; 33 import java.security.SecureRandomParameters; 34 import java.util.Arrays; 35 import java.util.Collections; 36 import java.util.List; 37 38 public class HmacDrbg extends AbstractHashDrbg { 39 40 private Mac mac; 41 42 private String macAlg; 43 44 private byte[] v; 45 private byte[] k; 46 47 public HmacDrbg(SecureRandomParameters params) { 48 mechName = "HMAC_DRBG"; 49 configure(params); 50 } 51 52 private void status() { 53 if (debug != null) { 54 debug.println(this, "V = " + hex(v)); 55 debug.println(this, "Key = " + hex(k)); 56 debug.println(this, "reseed counter = " + reseedCounter); 57 } 58 } 59 60 // 800-90Ar1 10.1.2.2: HMAC_DRBG Update Process 61 private void update(List<byte[]> inputs) { 62 try { 63 // Step 1. K = HMAC (K, V || 0x00 || provided_data). 64 mac.init(new SecretKeySpec(k, macAlg)); 65 mac.update(v); 66 mac.update((byte) 0); 67 for (byte[] input: inputs) { 68 mac.update(input); 69 } 70 k = mac.doFinal(); 71 72 // Step 2. V = HMAC (K, V). 73 mac.init(new SecretKeySpec(k, macAlg)); 74 v = mac.doFinal(v); 75 76 if (!inputs.isEmpty()) { 77 // Step 4. K = HMAC (K, V || 0x01 || provided_data). 78 mac.update(v); 79 mac.update((byte) 1); 80 for (byte[] input: inputs) { 81 mac.update(input); 82 } 83 k = mac.doFinal(); 84 85 // Step 5. V=HMAC(K,V). 86 mac.init(new SecretKeySpec(k, macAlg)); 87 v = mac.doFinal(v); 88 } // else Step 3 89 90 // Step 6. Return 91 } catch (InvalidKeyException e) { 92 throw new InternalError(e); 93 } 94 } 95 96 /** 97 * This call, used by the constructors, instantiates the digest. 98 */ 99 @Override 100 protected void initEngine() { 101 macAlg = "HmacSHA" + algorithm.substring(4); 102 try { 103 /* 104 * Use the local SunJCE implementation to avoid native 105 * performance overhead. 106 */ 107 mac = Mac.getInstance(macAlg, "SunJCE"); 108 } catch (NoSuchProviderException | NoSuchAlgorithmException e) { 109 // Fallback to any available. 110 try { 111 mac = Mac.getInstance(macAlg); 112 } catch (NoSuchAlgorithmException exc) { 113 throw new InternalError( 114 "internal error: " + macAlg + " not available.", exc); 115 } 116 } 117 } 118 119 // This method is used by both instantiation and reseeding. 120 @Override 121 protected final synchronized void hashReseedInternal(List<byte[]> input) { 122 123 // 800-90Ar1 10.1.2.3: Instantiate Process. 124 // 800-90Ar1 10.1.2.4: Reseed Process. 125 if (v == null) { 126 k = new byte[outLen]; 127 v = new byte[outLen]; 128 Arrays.fill(v, (byte) 1); 129 } 130 131 // Step 2: HMAC_DRBG_Update 132 update(input); 133 134 // Step 3: reseed_counter = 1. 135 reseedCounter = 1; 136 //status(); 137 138 // Step 4: Return 139 } 140 141 /** 142 * Generates a user-specified number of random bytes. 143 * 144 * @param result the array to be filled in with random bytes. 145 */ 146 @Override 147 public synchronized void generateAlgorithm( 148 byte[] result, byte[] additionalInput) { 149 150 if (debug != null) { 151 debug.println(this, "generateAlgorithm"); 152 } 153 154 // 800-90Ar1 10.1.2.5: HMAC_DRBG_Generate Process 155 156 // Step 1: Check reseed_counter. Will not fail. Already checked in 157 // AbstractDrbg#engineNextBytes. 158 159 // Step 2. HMAC_DRBG_Update 160 if (additionalInput != null) { 161 update(Collections.singletonList(additionalInput)); 162 } 163 164 // Step 3. temp = Null. 165 int pos = 0; 166 int len = result.length; 167 168 // Step 4. Loop 169 while (len > 0) { 170 // Step 4.1 V = HMAC (Key, V). 171 try { 172 mac.init(new SecretKeySpec(k, macAlg)); 173 } catch (InvalidKeyException e) { 174 throw new InternalError(e); 175 } 176 v = mac.doFinal(v); 177 // Step 4.2 temp = temp || V. 178 System.arraycopy(v, 0, result, pos, 179 len > outLen ? outLen : len); 180 181 len -= outLen; 182 if (len <= 0) { 183 // shortcut, so that pos needn't be updated 184 break; 185 } 186 pos += outLen; 187 } 188 189 // Step 5: No need to truncate 190 191 // Step 6. HMAC_DRBG_Update (additional_input, Key, V). 192 if (additionalInput != null) { 193 update(Collections.singletonList(additionalInput)); 194 } else { 195 update(Collections.emptyList()); 196 } 197 198 // Step 7. reseed_counter = reseed_counter + 1. 199 reseedCounter++; 200 201 //status(); 202 203 // Step 8. Return 204 } 205 }