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