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 java.io.IOException;
29import java.security.AccessController;
30import java.security.DrbgParameters;
31import java.security.PrivilegedAction;
32import java.security.SecureRandomParameters;
33import java.security.SecureRandomSpi;
34import java.security.Security;
35import java.util.Locale;
36import static java.security.DrbgParameters.Capability.*;
37
38/**
39 * Implement the "SecureRandom.DRBG" algorithm.
40 *
41 * About the default "securerandom.drbg.config" value:
42 *
43 * The default value in java.security is set to "". This is because
44 * the default values of different aspects are dependent (For example,
45 * strength depends on algorithm) and if we write a full string there
46 * it will be difficult to modify one and keep all others legal.
47 *
48 * When changing default values, touch all places including:
49 *
50 * 1. comments of the security property in java.security
51 * 2. Default mech, cap, usedf set in this class
52 * 3. Default algorithm set in final implementation of each mech
53 * 4. Default strength set in AbstractDrbg, but the effective
54 *    value can be smaller if an algorithm does not support it.
55 *
56 * The default value is also mentioned in the @implNote part of
57 * {@link DrbgParameters} class.
58 */
59public final class DRBG extends SecureRandomSpi {
60
61    private static final String PROP_NAME = "securerandom.drbg.config";
62
63    private static final long serialVersionUID = 9L;
64
65    private transient AbstractDrbg impl;
66
67    /**
68     * @serial
69     */
70    private final MoreDrbgParameters mdp;
71
72    public DRBG(SecureRandomParameters params) {
73
74        // All parameters at unset status (null or -1).
75
76        // Configurable with the "securerandom.drbg.config" security property
77        String mech = null;
78        Boolean usedf = null;
79        String algorithm = null;
80
81        // Default instantiate parameters also configurable with
82        // "securerandom.drbg.config", and can be changed with params
83        // in getInstance("drbg", params)
84        int strength = -1;
85        DrbgParameters.Capability cap = null;
86        byte[] ps = null;
87
88        // Not configurable with public interfaces, but is a part of
89        // MoreDrbgParameters
90        EntropySource es = null;
91        byte[] nonce = null;
92
93        // Can be configured with a security property
94
95        String config = AccessController.doPrivileged((PrivilegedAction<String>)
96                () -> Security.getProperty(PROP_NAME));
97
98        if (config != null && !config.isEmpty()) {
99            for (String part : config.split(",")) {
100                part = part.trim();
101                switch (part.toLowerCase(Locale.ROOT)) {
102                    case "":
103                        throw new IllegalArgumentException(
104                                "aspect in " + PROP_NAME + " cannot be empty");
105                    case "pr_and_reseed":
106                        checkTwice(cap != null, "capability");
107                        cap = PR_AND_RESEED;
108                        break;
109                    case "reseed_only":
110                        checkTwice(cap != null, "capability");
111                        cap = RESEED_ONLY;
112                        break;
113                    case "none":
114                        checkTwice(cap != null, "capability");
115                        cap = NONE;
116                        break;
117                    case "hash_drbg":
118                    case "hmac_drbg":
119                    case "ctr_drbg":
120                        checkTwice(mech != null, "mechanism name");
121                        mech = part;
122                        break;
123                    case "no_df":
124                        checkTwice(usedf != null, "usedf flag");
125                        usedf = false;
126                        break;
127                    case "use_df":
128                        checkTwice(usedf != null, "usedf flag");
129                        usedf = true;
130                        break;
131                    default:
132                        // For all other parts of the property, it is
133                        // either an algorithm name or a strength
134                        try {
135                            int tmp = Integer.parseInt(part);
136                            if (tmp < 0) {
137                                throw new IllegalArgumentException(
138                                        "strength in " + PROP_NAME +
139                                                " cannot be negative: " + part);
140                            }
141                            checkTwice(strength >= 0, "strength");
142                            strength = tmp;
143                        } catch (NumberFormatException e) {
144                            checkTwice(algorithm != null, "algorithm name");
145                            algorithm = part;
146                        }
147                }
148            }
149        }
150
151        // Can be updated by params
152
153        if (params != null) {
154            // MoreDrbgParameters is used for testing.
155            if (params instanceof MoreDrbgParameters) {
156                MoreDrbgParameters m = (MoreDrbgParameters) params;
157                params = DrbgParameters.instantiation(m.strength,
158                        m.capability, m.personalizationString);
159
160                // No need to check null for es and nonce, they are still null
161                es = m.es;
162                nonce = m.nonce;
163
164                if (m.mech != null) {
165                    mech = m.mech;
166                }
167                if (m.algorithm != null) {
168                    algorithm = m.algorithm;
169                }
170                usedf = m.usedf;
171            }
172            if (params instanceof DrbgParameters.Instantiation) {
173                DrbgParameters.Instantiation dp =
174                        (DrbgParameters.Instantiation) params;
175
176                // ps is still null by now
177                ps = dp.getPersonalizationString();
178
179                int tmp = dp.getStrength();
180                if (tmp != -1) {
181                    strength = tmp;
182                }
183                cap = dp.getCapability();
184            } else {
185                throw new IllegalArgumentException("Unsupported params: "
186                        + params.getClass());
187            }
188        }
189
190        // Hardcoded defaults.
191        // Remember to sync with "securerandom.drbg.config" in java.security.
192
193        if (cap == null) {
194            cap = NONE;
195        }
196        if (mech == null) {
197            mech = "Hash_DRBG";
198        }
199        if (usedf == null) {
200            usedf = true;
201        }
202
203        mdp = new MoreDrbgParameters(
204                es, mech, algorithm, nonce, usedf,
205                DrbgParameters.instantiation(strength, cap, ps));
206
207        createImpl();
208    }
209
210    private void createImpl() {
211        switch (mdp.mech.toLowerCase(Locale.ROOT)) {
212            case "hash_drbg":
213                impl = new HashDrbg(mdp);
214                break;
215            case "hmac_drbg":
216                impl = new HmacDrbg(mdp);
217                break;
218            case "ctr_drbg":
219                impl = new CtrDrbg(mdp);
220                break;
221            default:
222                throw new IllegalArgumentException("Unsupported mech: " + mdp.mech);
223        }
224    }
225
226    @Override
227    protected void engineSetSeed(byte[] seed) {
228        impl.engineSetSeed(seed);
229    }
230
231    @Override
232    protected void engineNextBytes(byte[] bytes) {
233        impl.engineNextBytes(bytes);
234    }
235
236    @Override
237    protected byte[] engineGenerateSeed(int numBytes) {
238        return impl.engineGenerateSeed(numBytes);
239    }
240
241    @Override
242    protected void engineNextBytes(
243            byte[] bytes, SecureRandomParameters params) {
244        impl.engineNextBytes(bytes, params);
245    }
246
247    @Override
248    protected void engineReseed(SecureRandomParameters params) {
249        impl.engineReseed(params);
250    }
251
252    @Override
253    protected SecureRandomParameters engineGetParameters() {
254        return impl.engineGetParameters();
255    }
256
257    @Override
258    public String toString() {
259        return impl.toString();
260    }
261
262    /**
263     * Ensures an aspect is not set more than once.
264     *
265     * @param flag true if set more than once
266     * @param name the name of aspect shown in IAE
267     * @throws IllegalArgumentException if it happens
268     */
269    private static void checkTwice(boolean flag, String name) {
270        if (flag) {
271            throw new IllegalArgumentException(name
272                    + " cannot be provided more than once in " + PROP_NAME);
273        }
274    }
275
276    private void readObject(java.io.ObjectInputStream s)
277            throws IOException, ClassNotFoundException {
278        s.defaultReadObject();
279        if (mdp.mech == null) {
280            throw new IllegalArgumentException("Input data is corrupted");
281        }
282        createImpl();
283    }
284}
285