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 /lib/testlibrary
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.testlibrary.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 
main(String[] args)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 
verifyInstance(String mech)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 
isValidDRBGParam(SecureRandomParameters param)232     private static boolean isValidDRBGParam(SecureRandomParameters param) {
233         return (param instanceof DrbgParameters.Instantiation);
234     }
235 
236     /**
237      * If the mechanism should occur NoSuchAlgorithmException.
238      */
nsa(String mech)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      */
isDRBG(String mech)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      */
getExcType(String mech, SecureRandomParameters param)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 
run()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      */
matchExc(RunnableCode r, boolean shouldPass, Class ex, String msg)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      */
checkAttributes(SecureRandom sr, String mech)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 }
313