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.io.IOException; 31 import java.security.InvalidKeyException; 32 import java.security.NoSuchAlgorithmException; 33 import java.security.NoSuchProviderException; 34 import java.security.SecureRandomParameters; 35 import java.util.Arrays; 36 37 public class HmacDrbg extends AbstractHashDrbg { 38 39 private static final long serialVersionUID = 9L; 40 41 private transient Mac mac; 42 43 private String macAlg; 44 45 private byte[] v; 46 private byte[] k; 47 48 public HmacDrbg(SecureRandomParameters params) { 49 mechName = "HMAC_DRBG"; 50 configureInternal(params); 51 } 52 53 private void status() { 54 if (debug != null) { 55 debug.println("V = " + hex(v)); 56 debug.println("Key = " + hex(k)); 57 debug.println("reseed counter = " + reseedCounter); 58 } 59 } 60 61 private void update(byte[]... inputs) { 62 try { 63 // Step 1. 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 // Step 2. 72 mac.init(new SecretKeySpec(k, macAlg)); 73 v = mac.doFinal(v); 74 // Step 4. 75 if (inputs.length != 0) { 76 mac.update(v); 77 mac.update((byte) 1); 78 for (byte[] input: inputs) { 79 mac.update(input); 80 } 81 k = mac.doFinal(); 82 mac.init(new SecretKeySpec(k, macAlg)); 83 v = mac.doFinal(v); 84 } 85 } catch (InvalidKeyException e) { 86 throw new InternalError(e); 87 } 88 } 89 90 /** 91 * This call, used by the constructors, instantiates the digest. 92 */ 93 @Override 94 protected void initEngine() { 95 if (algorithm == null) { 96 return; 97 } 98 macAlg = "HmacSHA" + algorithm.substring(4); 99 try { 100 mac = Mac.getInstance(macAlg, "SunJCE"); 101 } catch (NoSuchProviderException | NoSuchAlgorithmException e) { 102 // Fallback to any available. 103 try { 104 mac = Mac.getInstance(macAlg); 105 } catch (NoSuchAlgorithmException exc) { 106 throw new InternalError( 107 "internal error: " + macAlg + " not available.", exc); 108 } 109 } 110 } 111 112 // For seeding, input is entropy_input || nonce || personalization_string; 113 // for reseeding, input is entropy_input || additional_input 114 @Override 115 protected final void hashReseedInternal(byte[] input) { 116 if (v == null) { 117 k = new byte[outLen]; 118 v = new byte[outLen]; 119 Arrays.fill(v, (byte) 1); 120 } 121 update(input); 122 reseedCounter = 1; 123 status(); 124 } 125 126 /** 127 * Generates a user-specified number of random bytes. 128 * 129 * @param result the array to be filled in with random bytes. 130 */ 131 @Override 132 public synchronized void generateAlgorithm( 133 byte[] result, byte[] additionalInput) { 134 135 // Step 2. 136 if (additionalInput != null) { 137 update(additionalInput); 138 } 139 140 // Step 3. 141 int pos = 0; 142 143 // Step 4. 144 while (pos < result.length) { 145 int tailLen = result.length - pos; 146 // Step 4.1. 147 try { 148 mac.init(new SecretKeySpec(k, macAlg)); 149 } catch (InvalidKeyException e) { 150 throw new InternalError(e); 151 } 152 v = mac.doFinal(v); 153 // Step 4.2 and 5. 154 System.arraycopy(v, 0, result, pos, 155 tailLen > outLen ? outLen : tailLen); 156 pos += outLen; 157 } 158 159 // Step 6. 160 if (additionalInput != null) { 161 update(additionalInput); 162 } else { 163 update(); 164 } 165 166 // Step 7. 167 reseedCounter++; 168 169 status(); 170 } 171 172 private void readObject(java.io.ObjectInputStream s) 173 throws IOException, ClassNotFoundException { 174 s.defaultReadObject (); 175 initEngine(); 176 } 177 }