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