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 */
34import java.security.NoSuchAlgorithmException;
35import java.security.NoSuchProviderException;
36import java.security.SecureRandom;
37import java.security.SecureRandomParameters;
38import java.security.DrbgParameters;
39import static java.security.DrbgParameters.Capability.*;
40import java.security.Security;
41import java.util.Arrays;
42import jdk.testlibrary.Asserts;
43
44public 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}
313