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.SecureRandomInstantiateParameters;
  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(SecureRandomInstantiateParameters 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 }