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