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 import java.math.BigInteger;
25 import java.security.NoSuchAlgorithmException;
26 import java.security.SecureRandom;
27 import java.security.Security;
28 import java.util.concurrent.Callable;
29 import java.util.concurrent.CompletionService;
30 import java.util.concurrent.CountDownLatch;
31 import java.util.concurrent.ExecutionException;
32 import java.util.concurrent.ExecutorCompletionService;
33 import java.util.concurrent.ExecutorService;
34 import java.util.concurrent.Executors;
35 import java.util.concurrent.ThreadFactory;
36 import static java.lang.Math.*;
37 
38 /*
39  * @test
40  * @bug 8141039
41  * @library /lib/testlibrary
42  * @summary Test behavior of a shared SecureRandom object when it is operated
43  *          by multiple threads concurrently.
44  * @run main/othervm -Djava.security.egd=file:/dev/urandom MultiThreadTest
45  */
46 public class MultiThreadTest {
47 
48     private static final byte[] GEN_RND_BYTES = {1};
49     private static final String DRBG_CONFIG = "securerandom.drbg.config";
50     private static final String DRBG_CONFIG_VALUE
51             = Security.getProperty(DRBG_CONFIG);
52 
53     private enum SEED {
54 
55         NONE, RESEED, SETSEED
56     }
57 
main(String[] args)58     public static void main(String[] args) {
59 
60         boolean success = true;
61         for (int byteLen : GEN_RND_BYTES) {
62             for (SEED reSeed : SEED.values()) {
63                 for (String mech : new String[]{
64                     "SHA1PRNG", "Hash_DRBG", "HMAC_DRBG", "CTR_DRBG"}) {
65                     try {
66                         forEachMech(mech, byteLen, reSeed);
67                     } catch (Exception e) {
68                         success = false;
69                         e.printStackTrace(System.out);
70                     } finally {
71                         Security.setProperty(DRBG_CONFIG, DRBG_CONFIG_VALUE);
72                     }
73                 }
74             }
75         }
76 
77         if (!success) {
78             throw new RuntimeException("At least one test failed.");
79         }
80     }
81 
82     /**
83      * Generate a number of threads to fetch random numbers of certain bits
84      * generated through a shared SecureRandom instance.
85      * @param mech Mechanism name
86      * @param byteLen Number of bytes of random number to produce
87      * @param reSeed Call reseed() before generating random numbers
88      * @throws NoSuchAlgorithmException
89      * @throws InterruptedException
90      * @throws ExecutionException
91      */
forEachMech(String mech, int byteLen, SEED reSeed)92     private static void forEachMech(String mech, int byteLen, SEED reSeed)
93             throws NoSuchAlgorithmException, InterruptedException,
94             ExecutionException {
95 
96         if ("SHA1PRNG".equals(mech) && SEED.RESEED.equals(reSeed)) {
97             System.out.printf(
98                     "%nreseed() api is not supported for '%s'", mech);
99             return;
100         }
101         System.out.printf("%nTest SecureRandom mechanism: '%s' with support of"
102                 + " reseed: '%s'", mech, reSeed);
103         int threadCount = (int) pow(2, 8 * byteLen);
104         System.out.printf("%nCreating %s number of threads to generate secure "
105                 + "random numbers concurrently.", threadCount);
106 
107         ExecutorService executor
108                 = Executors.newCachedThreadPool(new ThreadFactory() {
109                     @Override
110                     public Thread newThread(Runnable r) {
111                         Thread t = Executors.defaultThreadFactory()
112                         .newThread(r);
113                         t.setDaemon(true);
114                         return t;
115                     }
116                 });
117         CompletionService<Integer> completionService
118                 = new ExecutorCompletionService<Integer>(executor);
119 
120         CountDownLatch latch = new CountDownLatch(1);
121         SecureRandom rnd = null;
122         if (!mech.contains("_DRBG")) {
123             rnd = SecureRandom.getInstance(mech);
124         } else {
125             Security.setProperty(DRBG_CONFIG, mech);
126             rnd = SecureRandom.getInstance("DRBG");
127         }
128         try {
129             for (int i = 0; i < threadCount; i++) {
130                 completionService.submit(new Task(rnd, latch, byteLen, reSeed));
131             }
132             latch.countDown();
133 
134             for (int i = 0; i < threadCount; i++) {
135                 completionService.take();
136             }
137         } finally {
138             executor.shutdown();
139         }
140         System.out.printf("%nCompleted Test for algorithm '%s' with thread "
141                 + "counts to '%s' using reseeding '%s'",
142                 mech, threadCount, reSeed);
143 
144     }
145 
146     /**
147      * Define a Task to be executed by multiple thread to produce random numbers
148      * from a shared SecureRandom instance.
149      */
150     private static class Task implements Callable<Integer> {
151 
152         private final SecureRandom random;
153         private final CountDownLatch latch;
154         private final SEED reSeed;
155         private final int byteSize;
156 
Task(SecureRandom random, CountDownLatch latch, int byteSize, SEED reSeed)157         public Task(SecureRandom random, CountDownLatch latch, int byteSize,
158                 SEED reSeed) {
159             this.random = random;
160             this.latch = latch;
161             this.byteSize = byteSize;
162             this.reSeed = reSeed;
163         }
164 
165         @Override
call()166         public Integer call() throws Exception {
167             latch.await();
168             switch (this.reSeed) {
169                 case RESEED:
170                     this.random.reseed();
171                     break;
172                 case SETSEED:
173                     this.random.setSeed(1l);
174                     break;
175             }
176             byte[] bytes = new byte[byteSize];
177             random.nextBytes(bytes);
178             return new BigInteger(bytes).intValue();
179         }
180     }
181 
182 }
183