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
26package sun.security.provider;
27
28import sun.security.util.HexDumpEncoder;
29
30import java.util.ArrayList;
31import java.util.List;
32import java.util.Locale;
33
34public abstract class AbstractHashDrbg extends AbstractDrbg {
35
36    protected int outLen;
37    protected int seedLen;
38
39    private static int alg2strength(String algorithm) {
40        switch (algorithm.toUpperCase(Locale.ROOT)) {
41            case "SHA-224":
42            case "SHA-512/224":
43                return 192;
44            case "SHA-256":
45            case "SHA-512/256":
46            case "SHA-384":
47            case "SHA-512":
48                return 256;
49            default:
50                throw new IllegalArgumentException(algorithm +
51                        " not supported in Hash_DBRG");
52        }
53    }
54
55    protected void chooseAlgorithmAndStrength() {
56        if (requestedAlgorithm != null) {
57            algorithm = requestedAlgorithm.toUpperCase(Locale.ROOT);
58            int supportedStrength = alg2strength(algorithm);
59            if (requestedInstantiationSecurityStrength >= 0) {
60                int tryStrength = getStandardStrength(
61                        requestedInstantiationSecurityStrength);
62                if (tryStrength > supportedStrength) {
63                    throw new IllegalArgumentException(algorithm +
64                            " does not support strength " +
65                            requestedInstantiationSecurityStrength);
66                }
67                this.securityStrength = tryStrength;
68            } else {
69                this.securityStrength = DEFAULT_STRENGTH > supportedStrength ?
70                        supportedStrength : DEFAULT_STRENGTH;
71            }
72        } else {
73            int tryStrength = (requestedInstantiationSecurityStrength < 0) ?
74                    DEFAULT_STRENGTH : requestedInstantiationSecurityStrength;
75            tryStrength = getStandardStrength(tryStrength);
76            // The default algorithm which is enough for all strengths.
77            // Remember to sync with "securerandom.drbg.config" in java.security
78            algorithm = "SHA-256";
79            this.securityStrength = tryStrength;
80        }
81        switch (algorithm.toUpperCase(Locale.ROOT)) {
82            case "SHA-224":
83            case "SHA-512/224":
84                this.seedLen = 440 / 8;
85                this.outLen = 224 / 8;
86                break;
87            case "SHA-256":
88            case "SHA-512/256":
89                this.seedLen = 440 / 8;
90                this.outLen = 256 / 8;
91                break;
92            case "SHA-384":
93                this.seedLen = 888 / 8;
94                this.outLen = 384 / 8;
95                break;
96            case "SHA-512":
97                this.seedLen = 888 / 8;
98                this.outLen = 512 / 8;
99                break;
100            default:
101                throw new IllegalArgumentException(algorithm +
102                        " not supported in Hash_DBRG");
103        }
104        this.minLength = this.securityStrength / 8;
105    }
106
107    @Override
108    public void instantiateAlgorithm(byte[] entropy) {
109        if (debug != null) {
110            debug.println(this, "instantiate");
111        }
112
113        // 800-90Ar1 10.1.1.2: Hash_DRBG Instantiate Process.
114        // 800-90Ar1 10.1.2.3: Hmac_DRBG Instantiate Process.
115
116        // Step 1: entropy_input || nonce || personalization_string.
117        List<byte[]> inputs = new ArrayList<>(3);
118        inputs.add(entropy);
119        inputs.add(nonce);
120        if (personalizationString != null) {
121            inputs.add(personalizationString);
122        }
123        hashReseedInternal(inputs);
124    }
125
126    @Override
127    protected void reseedAlgorithm(
128            byte[] ei,
129            byte[] additionalInput) {
130        if (debug != null) {
131            debug.println(this, "reseedAlgorithm\n" +
132                    new HexDumpEncoder().encodeBuffer(ei) + "\n" +
133                    ((additionalInput == null) ? "" :
134                        new HexDumpEncoder().encodeBuffer(additionalInput)));
135        }
136
137        // 800-90Ar1 10.1.1.3: Hash_DRBG Reseed Process.
138        // 800-90Ar1 10.1.2.4: Hmac_DRBG Reseed Process.
139
140        // Step 1: entropy_input || additional_input.
141        List<byte[]> inputs = new ArrayList<>(2);
142        inputs.add(ei);
143        if (additionalInput != null) {
144            inputs.add(additionalInput);
145        }
146        hashReseedInternal(inputs);
147    }
148
149    /**
150     * Operates on multiple inputs.
151     * @param inputs not null, each element neither null
152     */
153    protected abstract void hashReseedInternal(List<byte[]> inputs);
154}
155