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.security.AccessController;
  29 import java.security.DrbgParameters;
  30 import java.security.PrivilegedAction;
  31 import java.security.SecureRandomParameters;
  32 import java.security.SecureRandomSpi;
  33 import java.security.Security;
  34 import java.util.Locale;
  35 import static java.security.DrbgParameters.Capability.*;
  36 
  37 /**
  38  * Implement the "SecureRandom.DRBG" algorithm.
  39  */
  40 public final class DRBG extends SecureRandomSpi {
  41 
  42     private static final long serialVersionUID = 9L;
  43 
  44     private final AbstractDrbg impl;
  45 
  46     public DRBG(SecureRandomParameters params) {
  47 
  48         // All parameters at unset status (null or -1).
  49 
  50         // Configurable with security property "drbg"
  51         String mech = null;
  52         Boolean usedf = null;
  53         String algorithm = null;
  54 
  55         // Default instantiate parameters configurable with "drbg",
  56         // and can be changed with params in getInstance("drbg", params)
  57         int strength = -1;
  58         DrbgParameters.Capability cap = null;
  59         byte[] ps = null;
  60 
  61         // Not configurable with public interfaces, but is a part of
  62         // MoreDrbgParameters
  63         EntropySource es = null;
  64         byte[] nonce = null;
  65 
  66         // Can be configured with a security property
  67 
  68         String config = AccessController.doPrivileged((PrivilegedAction<String>)
  69                     () -> Security.getProperty("drbg"));
  70 
  71         if (config != null && !config.isEmpty()) {
  72             for (String part: config.split(",")) {
  73                 part = part.trim();
  74                 switch (part.toLowerCase(Locale.ROOT)) {
  75                     case "":
  76                         throw new IllegalArgumentException(
  77                                 "aspect in drbg cannot be empty");
  78                     case "pr_and_reseed":
  79                         checkTwice(cap != null, "capability");
  80                         cap = PR_AND_RESEED;
  81                         break;
  82                     case "reseed_only":
  83                         checkTwice(cap != null, "capability");
  84                         cap = RESEED_ONLY;
  85                         break;
  86                     case "none":
  87                         checkTwice(cap != null, "capability");
  88                         cap = NONE;
  89                         break;
  90                     case "hash_drbg":
  91                     case "hmac_drbg":
  92                     case "ctr_drbg":
  93                         checkTwice(mech != null, "mechanism name");
  94                         mech = part;
  95                         break;
  96                     case "no_df":
  97                         checkTwice(usedf != null, "usedf flag");
  98                         usedf = false;
  99                         break;
 100                     case "use_df":
 101                         checkTwice(usedf != null, "usedf flag");
 102                         usedf = true;
 103                         break;
 104                     default:
 105                         // For all other parts of the property, it is
 106                         // either an algorithm name or a strength
 107                         try {
 108                             checkTwice(strength >= 0, "strength");
 109                             strength = Integer.parseInt(part);
 110                             if (strength < 0) {
 111                                 throw new IllegalArgumentException(
 112                                         "strength in drbg cannot be negative");
 113                             }
 114                         } catch (NumberFormatException e) {
 115                             checkTwice(algorithm != null, "algorithm name");
 116                             algorithm = part;
 117                         }
 118                 }
 119             }
 120         }
 121 
 122         // Can be updated by params
 123 
 124         if (params != null) {
 125             if (params instanceof MoreDrbgParameters) {
 126                 MoreDrbgParameters m = (MoreDrbgParameters)params;
 127                 params = m.config;
 128 
 129                 // No need to check null for es and nonce, they are still null
 130                 es = m.es;
 131                 nonce = m.nonce;
 132 
 133                 if (m.mech != null) {
 134                     mech = m.mech;
 135                 }
 136                 if (m.algorithm != null) {
 137                     algorithm = m.algorithm;
 138                 }
 139                 usedf = m.usedf;
 140             }
 141             if (params instanceof DrbgParameters.Instantiate) {
 142                 DrbgParameters.Instantiate dp =
 143                         (DrbgParameters.Instantiate) params;
 144 
 145                 // ps is still null by now
 146                 ps = dp.getPersonalizationString();
 147 
 148                 if (dp.getStrength() != -1) {
 149                     strength = dp.getStrength();
 150                 }
 151                 cap = dp.getCapability();
 152             } else {
 153                 throw new IllegalArgumentException("Unsupported params");
 154             }
 155         }
 156 
 157         // Hardcoded defaults. The info below is duplicated in comments
 158         // of the "drbg" security property in java.security.
 159 
 160         if (cap == null) {
 161             cap = NONE;
 162         }
 163         if (mech == null) {
 164             mech = "Hash_DRBG";
 165         }
 166         if (usedf == null) {
 167             usedf = true;
 168         }
 169 
 170         MoreDrbgParameters m = new MoreDrbgParameters(
 171                 es, mech, algorithm, nonce, usedf,
 172                 DrbgParameters.instantiate(strength, cap, ps));
 173 
 174         switch (mech.toLowerCase(Locale.ROOT)) {
 175             case "hash_drbg":
 176                 impl = new HashDrbg(m);
 177                 break;
 178             case "hmac_drbg":
 179                 impl = new HmacDrbg(m);
 180                 break;
 181             case "ctr_drbg":
 182                 impl = new CtrDrbg(m);
 183                 break;
 184             default:
 185                 throw new IllegalArgumentException("Unsupported mech");
 186         }
 187     }
 188 
 189     @Override
 190     protected void engineSetSeed(byte[] seed) {
 191         impl.engineSetSeed(seed);
 192     }
 193 
 194     @Override
 195     protected void engineNextBytes(byte[] bytes) {
 196         impl.engineNextBytes(bytes);
 197     }
 198 
 199     @Override
 200     protected byte[] engineGenerateSeed(int numBytes) {
 201         return impl.engineGenerateSeed(numBytes);
 202     }
 203 
 204     @Override
 205     protected void engineNextBytes(
 206             byte[] bytes, SecureRandomParameters params) {
 207         impl.engineNextBytes(bytes, params);
 208     }
 209 
 210     @Override
 211     protected void engineReseed(SecureRandomParameters params) {
 212         impl.engineReseed(params);
 213     }
 214 
 215     @Override
 216     protected SecureRandomParameters engineGetParameters() {
 217         return impl.engineGetParameters();
 218     }
 219 
 220     @Override
 221     public String toString() {
 222         return impl.toString();
 223     }
 224 
 225     /**
 226      * Ensures an aspect is not set more than once.
 227      *
 228      * @param flag true if set more than once
 229      * @param name the name of aspect shown in IAE
 230      * @throws IllegalArgumentException if it happens
 231      */
 232     private static void checkTwice(boolean flag, String name) {
 233         if (flag) {
 234             throw new IllegalArgumentException(name
 235                     + " cannot be provided more than once in drbg");
 236         }
 237     }
 238 }