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.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     private static final long serialVersionUID = 9L;
64 
65     private transient AbstractDrbg impl;
66 
67     /**
68      * @serial
69      */
70     private final MoreDrbgParameters mdp;
71 
DRBG(SecureRandomParameters params)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 
createImpl()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
engineSetSeed(byte[] seed)227     protected void engineSetSeed(byte[] seed) {
228         impl.engineSetSeed(seed);
229     }
230 
231     @Override
engineNextBytes(byte[] bytes)232     protected void engineNextBytes(byte[] bytes) {
233         impl.engineNextBytes(bytes);
234     }
235 
236     @Override
engineGenerateSeed(int numBytes)237     protected byte[] engineGenerateSeed(int numBytes) {
238         return impl.engineGenerateSeed(numBytes);
239     }
240 
241     @Override
engineNextBytes( byte[] bytes, SecureRandomParameters params)242     protected void engineNextBytes(
243             byte[] bytes, SecureRandomParameters params) {
244         impl.engineNextBytes(bytes, params);
245     }
246 
247     @Override
engineReseed(SecureRandomParameters params)248     protected void engineReseed(SecureRandomParameters params) {
249         impl.engineReseed(params);
250     }
251 
252     @Override
engineGetParameters()253     protected SecureRandomParameters engineGetParameters() {
254         return impl.engineGetParameters();
255     }
256 
257     @Override
toString()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      */
checkTwice(boolean flag, String name)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 
readObject(java.io.ObjectInputStream s)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