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