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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8141039
  27  * @library /test/lib
  28  * @summary SecureRandom supports multiple getInstance method including
  29  *          getInstanceStrong() method. This test verifies a set of possible
  30  *          cases for getInstance with different SecureRandom mechanism
  31  *          supported in Java.
  32  * @run main GetInstanceTest
  33  */
  34 import java.security.NoSuchAlgorithmException;
  35 import java.security.NoSuchProviderException;
  36 import java.security.SecureRandom;
  37 import java.security.SecureRandomParameters;
  38 import java.security.DrbgParameters;
  39 import static java.security.DrbgParameters.Capability.*;
  40 import java.security.Security;
  41 import java.util.Arrays;
  42 import jdk.test.lib.Asserts;
  43 
  44 public class GetInstanceTest {
  45 
  46     private static final boolean PASS = true;
  47     private static final String INVALID_ALGO = "INVALID";
  48     private static final String SUN_PROVIDER = "SUN";
  49     private static final String INVALID_PROVIDER = "INVALID";
  50     private static final String STRONG_ALG_SEC_PROP
  51             = "securerandom.strongAlgorithms";
  52     private static final String DRBG_CONFIG = "securerandom.drbg.config";
  53     private static final String DRBG_CONFIG_VALUE
  54             = Security.getProperty(DRBG_CONFIG);
  55 
  56     public static void main(String[] args) throws Exception {
  57 
  58         boolean success = true;
  59         // Only accepted failure is NoSuchAlgorithmException.
  60         // For any other failure the test case will fail here.
  61         SecureRandom sr = matchExc(() -> SecureRandom.getInstanceStrong(),
  62                 PASS, NoSuchAlgorithmException.class,
  63                 "PASS - Undefined security Property "
  64                 + "'securerandom.strongAlgorithms'");
  65         System.out.format("Current platform supports mechanism: '%s' through "
  66                 + "provider: '%s' for the method getInstanceStrong().",
  67                 sr.getAlgorithm(), sr.getProvider().getName());
  68 
  69         // DRBG name should appear with "securerandom.strongAlgorithms"
  70         // security property.
  71         String origDRBGConfig = Security.getProperty(STRONG_ALG_SEC_PROP);
  72         if (!origDRBGConfig.contains("DRBG")) {
  73             throw new RuntimeException("DRBG is not associated with default "
  74                     + "strong algorithm through security Property: "
  75                     + "'securerandom.strongAlgorithms'.");
  76         }
  77         try {
  78             Security.setProperty(STRONG_ALG_SEC_PROP, "DRBG:SUN");
  79             sr = matchExc(() -> SecureRandom.getInstanceStrong(),
  80                     PASS, NoSuchAlgorithmException.class,
  81                     "PASS - Undefined security Property "
  82                     + "'securerandom.strongAlgorithms'");
  83             checkAttributes(sr, "DRBG");
  84         } finally {
  85             Security.setProperty(STRONG_ALG_SEC_PROP, origDRBGConfig);
  86         }
  87 
  88         for (String mech : new String[]{
  89             "SHA1PRNG", "Hash_DRBG", "HMAC_DRBG", "CTR_DRBG", INVALID_ALGO,}) {
  90             System.out.printf("%nTest SecureRandom mechanism: '%s'", mech);
  91             try {
  92                 if (isDRBG(mech)) {
  93                     Security.setProperty(DRBG_CONFIG, mech);
  94                 }
  95                 verifyInstance(mech);
  96             } catch (Exception e) {
  97                 e.printStackTrace(System.out);
  98                 success = false;
  99             } finally {
 100                 Security.setProperty(DRBG_CONFIG, DRBG_CONFIG_VALUE);
 101             }
 102         }
 103         if (!success) {
 104             throw new RuntimeException("At least one test failed.");
 105         }
 106     }
 107 
 108     private static void verifyInstance(String mech) throws Exception {
 109 
 110         String srAlgo = isDRBG(mech) ? "DRBG" : mech;
 111 
 112         // Test for getInstance(algorithm) method.
 113         // It should pass for all case other than invalid algorithm name.
 114         // If it fails then the expected exception type should be
 115         // NoSuchAlgorithmException. Any other Exception type occured will be
 116         // treated as failure.
 117         checkAttributes(
 118                 matchExc(() -> SecureRandom.getInstance(srAlgo), !(nsa(mech)),
 119                         NoSuchAlgorithmException.class,
 120                         String.format("PASS - It is expected to fail for"
 121                                 + " getInstance(algorithm) when algorithm: '%s'"
 122                                 + " is null or invalid.", mech)), mech);
 123         // Test for getInstance(algorithm, provider) method.
 124         checkAttributes(
 125                 matchExc(() -> SecureRandom.getInstance(srAlgo,
 126                                 Security.getProvider(SUN_PROVIDER)),
 127                         !(nsa(mech)),
 128                         NoSuchAlgorithmException.class,
 129                         String.format("PASS - It is expected to fail for"
 130                                 + " getInstance(algorithm, provider) when"
 131                                 + " algorithm:'%s' is null or invalid.", mech)),
 132                 mech);
 133         // Test for getInstance(algorithm, providerName) method.
 134         checkAttributes(
 135                 matchExc(() -> SecureRandom.getInstance(srAlgo, SUN_PROVIDER),
 136                         !(nsa(mech)), NoSuchAlgorithmException.class,
 137                         String.format("PASS - It is expected to fail for "
 138                                 + "getInstance(algorithm, providerName) when "
 139                                 + "algorithm: '%s' is null or invalid.", mech)),
 140                 mech);
 141         // Test for getInstance(algorithm, providerName) method.
 142         checkAttributes(
 143                 matchExc(() -> SecureRandom.getInstance(
 144                                 srAlgo, INVALID_PROVIDER),
 145                         !PASS, NoSuchProviderException.class,
 146                         String.format("PASS - It is expected to fail for "
 147                                 + "getInstance(algorithm, providerName) when "
 148                                 + "provider name: '%s' is invalid and "
 149                                 + "algorithm: '%s'", INVALID_PROVIDER, mech)),
 150                 mech);
 151 
 152         // Run the test for a set of SecureRandomParameters
 153         for (SecureRandomParameters param : Arrays.asList(null,
 154                 DrbgParameters.instantiation(-1, NONE, null))) {
 155 
 156             System.out.printf("%nRunning DRBG param getInstance() methods "
 157                     + "for algorithm: %s and DRBG param type: %s", mech,
 158                     (param != null) ? param.getClass().getName() : param);
 159 
 160             // Following Test are applicable for new DRBG methods only.
 161             // Test for getInstance(algorithm, params) method.
 162             // Tests are expected to pass for DRBG type with valid parameter
 163             // If it fails the expected exception type is derived from
 164             // getExcType(mech, param) method. If exception type is not
 165             // expected then the test will be considered as failure.
 166             checkAttributes(
 167                     matchExc(() -> SecureRandom.getInstance(srAlgo, param),
 168                             (isDRBG(mech)) && (isValidDRBGParam(param)),
 169                             getExcType(mech, param),
 170                             String.format("PASS - It is expected to fail "
 171                                     + "for getInstance(algorithm, params) "
 172                                     + "for algorithm: %s and parameter: %s",
 173                                     mech, param)),
 174                     mech);
 175             // Test for getInstance(algorithm, params, provider) method.
 176             checkAttributes(
 177                     matchExc(() -> SecureRandom.getInstance(srAlgo, param,
 178                                     Security.getProvider(SUN_PROVIDER)),
 179                             (isDRBG(mech)) && (isValidDRBGParam(param)),
 180                             getExcType(mech, param),
 181                             String.format("PASS - It is expected to fail "
 182                                     + "for getInstance(algorithm, params, "
 183                                     + "provider) for algorithm: %s and "
 184                                     + "parameter: %s", mech, param)),
 185                     mech);
 186             // Test for getInstance(algorithm, params, providerName) method.
 187             checkAttributes(
 188                     matchExc(() -> SecureRandom.getInstance(srAlgo, param,
 189                                     SUN_PROVIDER),
 190                             (isDRBG(mech)) && (isValidDRBGParam(param)),
 191                             getExcType(mech, param),
 192                             String.format("PASS - It is expected to fail "
 193                                     + "for getInstance(algorithm, params, "
 194                                     + "providerName) for algorithm: %s and "
 195                                     + "parameter: %s", mech, param)), mech);
 196             // getInstance(algorithm, params, providerName) when
 197             // providerName is invalid
 198             checkAttributes(
 199                     matchExc(() -> SecureRandom.getInstance(srAlgo, param,
 200                                     INVALID_PROVIDER),
 201                             !PASS, ((param == null)
 202                                     ? IllegalArgumentException.class
 203                                     : NoSuchProviderException.class),
 204                             String.format("PASS - It is expected to fail "
 205                                     + "for getInstance(algorithm, params, "
 206                                     + "providerName) when param is null or"
 207                                     + " provider: %s is invalid for "
 208                                     + "algorithm: '%s'", INVALID_PROVIDER,
 209                                     mech)), mech);
 210             // getInstance(algorithm, params, provider) when provider=null
 211             checkAttributes(
 212                     matchExc(() -> SecureRandom.getInstance(srAlgo, param,
 213                                     (String) null),
 214                             !PASS, IllegalArgumentException.class,
 215                             String.format("PASS - It is expected to fail "
 216                                     + "for getInstance(algorithm, params, "
 217                                     + "providerName) when provider name "
 218                                     + "is null")), mech);
 219             // getInstance(algorithm, params, providerName) when
 220             // providerName is empty.
 221             checkAttributes(
 222                     matchExc(() -> SecureRandom.getInstance(
 223                                     srAlgo, param, ""),
 224                             !PASS, IllegalArgumentException.class,
 225                             String.format("PASS - It is expected to fail "
 226                                     + "for getInstance(algorithm, params, "
 227                                     + "providerName) when provider name "
 228                                     + "is empty")), mech);
 229         }
 230     }
 231 
 232     private static boolean isValidDRBGParam(SecureRandomParameters param) {
 233         return (param instanceof DrbgParameters.Instantiation);
 234     }
 235 
 236     /**
 237      * If the mechanism should occur NoSuchAlgorithmException.
 238      */
 239     private static boolean nsa(String mech) {
 240         return mech.equals(INVALID_ALGO);
 241     }
 242 
 243     /**
 244      * Verify if the mechanism is DRBG type.
 245      * @param mech Mechanism name
 246      * @return True if the mechanism name is DRBG type else False.
 247      */
 248     private static boolean isDRBG(String mech) {
 249         return mech.contains("_DRBG");
 250     }
 251 
 252     /**
 253      * Type of exception expected for a SecureRandom instance when exception
 254      * occurred while calling getInstance method with a fixed set of parameter.
 255      * @param mech Mechanism used to create a SecureRandom instance
 256      * @param param Parameter to getInstance() method
 257      * @return Exception type expected
 258      */
 259     private static Class getExcType(String mech, SecureRandomParameters param) {
 260         return ((isDRBG(mech) && !isValidDRBGParam(param)) || param == null)
 261                 ? IllegalArgumentException.class
 262                 : NoSuchAlgorithmException.class;
 263     }
 264 
 265     private interface RunnableCode {
 266 
 267         SecureRandom run() throws Exception;
 268     }
 269 
 270     /**
 271      * Execute a given code block and verify, if the exception type is expected.
 272      * @param r Code block to run
 273      * @param ex Expected exception type
 274      * @param shouldPass If the code execution expected to pass without failure
 275      * @param msg Message to log in case of expected failure
 276      */
 277     private static SecureRandom matchExc(RunnableCode r, boolean shouldPass,
 278             Class ex, String msg) {
 279         SecureRandom sr = null;
 280         try {
 281             sr = r.run();
 282             if (!shouldPass) {
 283                 throw new RuntimeException("Excecution should fail here.");
 284             }
 285         } catch (Exception e) {
 286             System.out.printf("%nOccured exception: %s - Expected exception: %s"
 287                     + " : ", e.getClass(), ex.getCanonicalName());
 288             if (ex.isAssignableFrom(e.getClass())) {
 289                 System.out.printf("%n%s : Expected Exception: %s : ",
 290                         e.getClass(), msg);
 291             } else if (shouldPass) {
 292                 throw new RuntimeException(e);
 293             } else {
 294                 System.out.printf("%nIgnore the following exception: %s%n",
 295                         e.getMessage());
 296             }
 297         }
 298         return sr;
 299     }
 300 
 301     /**
 302      * Check specific attributes of a SecureRandom instance.
 303      */
 304     private static void checkAttributes(SecureRandom sr, String mech) {
 305         if (sr == null) {
 306             return;
 307         }
 308         Asserts.assertEquals(sr.getAlgorithm(), (isDRBG(mech) ? "DRBG" : mech));
 309         Asserts.assertEquals(sr.getProvider().getName(), SUN_PROVIDER);
 310     }
 311 
 312 }