1 /*
2  * Copyright (c) 2012, 2021, 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 package test.java.util.Random;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.PrimitiveIterator;
28 
29 import java.util.random.*;
30 
31 import java.util.Set;
32 import java.util.concurrent.CompletableFuture;
33 import java.util.concurrent.ExecutionException;
34 import java.util.concurrent.ThreadLocalRandom;
35 import java.util.concurrent.TimeUnit;
36 import java.util.concurrent.TimeoutException;
37 import java.util.function.IntSupplier;
38 import java.util.function.LongSupplier;
39 import java.util.function.BooleanSupplier;
40 import java.util.function.Supplier;
41 import java.util.stream.Stream;
42 
43 import static java.util.stream.Collectors.toList;
44 import static java.util.stream.Collectors.toSet;
45 
46 /**
47  * @test
48  * @summary test bit sequences produced by clases that implement interface RandomGenerator
49  * @bug 8248862
50  * @run main RandomTestBsi1999
51  * @key randomness
52  */
53 
54 public class RandomTestBsi1999 {
55 
56     /* A set of tests for pseudorandom number generators inspired by this report:
57      *
58      *   Werner Schindler.  Functionality Classes and Evaluation Methodology for
59      *   Deterministic Random Number Generators, Version 2.0.
60      *   Bundesamt fur Sicherheit in der Informationstechnik (BSI).  December 2, 1999.
61      *   https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Zertifizierung/Interpretationen/AIS_20_Functionality_Classes_Evaluation_Methodology_DRNG_e.pdf
62      *
63      * Section F of this report (pp. 19-20) recommends the use of five tests to evaluate a
64      * sequence of bits:
65      *
66      *        Monobit test
67      *        Poker test
68      *        Run test
69      *        Long run test
70      *        Autocorrelation test
71      *
72      * The first four of these tests are in turn taken from this report:
73      *
74      *   National Institute of Standards and Technology (NIST),
75      *   U.S. Department of Commerce.  Security Requirements for
76      *   Cryptographic Modules.  Federal Information Processing
77      *   Standard (FIPS) 140-1, January 11, 1994.
78      *   https://csrc.nist.gov/csrc/media/publications/fips/140/1/archive/1994-01-11/documents/fips1401.pdf
79      *
80      * The BSI report appears to contain a few typos in transcribing the FIPS 140-1
81      * requirements (pp. 44-45); specifically, the last three intervals for the runs test
82      * (for lengths 4, 5, and 6+) are given as "223 - 402, 90 - 223, 90 - 223" in the FIPS
83      * standard but as "233-402, 90-223, 90-233" in the BSI publication.  A quick
84      * mathematical check indicates that the FIPS 140-1 values are correct; therefore we
85      * use those values here. In addition, the BSI report specifies a test interval of
86      * 2326-2674 for the autocorrelation test, which provides an appropriately small
87      * rejection rate if the test were done for only a single value of tau; but because we
88      * wish to perform 5000 distinct tests, one for each value of tau in the range 1-5000,
89      * that test interval produces too many false positives.  Some calculation shows that
90      * the interval 2267-2733 used by the FIPS 140-1 run test for runs of length 1 is
91      * appropriate, so we use that interval here for each of the 5000 individual
92      * autocorrelation tests.
93      *
94      * Each of the four FIPS 140-1 tests examines a sequence of 20000 bits.  The
95      * autocorrelation test examines a sequence of 10000 bits.  It is convenient to
96      * generate a sequence of 20000 bits (which we represent as an array of 20000 bytes)
97      * and then apply all five tests, knowing that the autocorrelation test will examine
98      * only the first half of the byte array.
99      *
100      * The descriptions of the tests are quoted from the FIPS 140-1 and BSI reports
101      * (with some adjustments of punctuation and mathematical symbols, as well as
102      * for our specific choices of test intervals).
103      */
104 
105     static String currentRNG = "";
106     static int failCount = 0;
107 
exceptionOnFail()108     static void exceptionOnFail() {
109         if (failCount != 0) {
110             throw new RuntimeException(failCount + " fails detected");
111         }
112     }
113 
setRNG(String rng)114     static void setRNG(String rng) {
115         currentRNG = rng;
116     }
117 
fail(String format, Object... args)118     static void fail(String format, Object... args) {
119         if (currentRNG.length() != 0) {
120             System.err.println(currentRNG);
121             currentRNG = "";
122         }
123 
124         System.err.format("  " + format, args);
125         failCount++;
126     }
127 
128     private static final int SEQUENCE_SIZE = 20000;
129 
130     /* The Monobit Test
131      *
132      *   1. Count the number of ones in the 20,000 bit stream.  Denote this quantity by X.
133      *
134      *   2. The test is passed if 9,654 < X < 10,346.
135      */
monobitTest(String id, byte[] s)136     static int monobitTest(String id, byte[] s) {
137        // System.out.println("monobit test");
138        int count = 0;
139        for (int j = 0; j < s.length; j++) {
140            count += s[j];
141        }
142        int monobitFailure = ((9654 < count) && (count < 10346)) ? 0 : 1;
143        if (monobitFailure != 0) fail("monobit test failure for %s: count=%d (should be in [9654,10346])\n", id, count);
144        return monobitFailure;
145     }
146 
147     /* The Poker Test
148      *
149      *   1.  Divide the 20,000 bit stream into 5,000 contiguous 4-bit segments.  Count and
150      *   store the number of occurrences of each of the 16 possible 4-bit values.  Denote
151      *   f(i) as the number of each 4-bit value i where 0 <= i <= 15.
152      *
153      *   2.  Evaluate the following: X = (16/5000)(sum[i=0,15] (f(i))**2) - 5000
154      *
155      *   3.  The test is passed if 1.03 < X < 57.4.
156      */
157 
pokerTest(String id, byte[] s)158     static int pokerTest(String id, byte[] s) {
159        // System.out.println("poker test");
160        // Divide the bit sequence into 4-bit chunks, and count the number of times each 4-bit value appears.
161        int[] stats = new int[16];
162        int v = 0;
163        for (int j = 0; j < s.length; j++) {
164            v = (v << 1) | s[j];
165            if ((j & 3) == 3) {
166            ++stats[v];
167            v = 0;
168            }
169        }
170        int z = 0;
171        for (int k = 0; k < stats.length; k++) {
172            z += stats[k]*stats[k];
173        }
174        double x = (16.0 / (s.length / 4)) * z - (s.length / 4);
175        int pokerFailure = ((1.03 < x) && (x < 57.4)) ? 0 : 1;
176        if (pokerFailure != 0) fail("poker test failure for %s: x=%g (should be in [1.03,57.4])\n", id, x);
177        return pokerFailure;
178     }
179 
180     /* The Runs Test
181      *
182      *   1.  A run is defined as a maximal sequence of consecutive bits of either all ones
183      *   or all zeros, which is part of the 20,000 bit sample stream.  The incidences of
184      *   runs (for both consecutive zeros and consecutive ones) of all lengths (>= 1) in
185      *   the sample stream should be counted and stored.
186      *
187      *   2.  The test is passed if the number of runs that occur (of lengths 1 through 6)
188      *   is each within the corresponding interval specified below.  This must hold for
189      *   both the zeros and ones; that is, all 12 counts must lie in the specified
190      *   interval.  For the purpose of this test, runs of greater than 6 are considered to
191      *   be of length 6.
192      *
193      *        Length of run       Required Interval
194      *             1                2,267 - 2,733
195      *             2                1,079 - 1,421
196      *             3                  502 - 748
197      *             4                  223 - 402
198      *             5                   90 - 223
199      *             6+                  90 - 223
200      *
201      * The Long Run Test
202      *
203      *   1 . A long run is defined to be a run of length 34 or more (of either zeros or ones).
204      *
205      *   2.  On the sample of 20,000 bits, the test is passed if there are NO long runs.
206      */
runTestAndLongRunTest(String id, byte[] s)207     static int runTestAndLongRunTest(String id, byte[] s) {
208        // System.out.println("run test");
209        int[][] stats = new int[2][8];
210        int count = 0;
211        for (int j = 0; j < s.length; j++) {
212            ++count;
213            if ((j == (s.length - 1)) || (s[j+1] != s[j])) {
214            ++stats[s[j]][(count < 6) ? count : (count < 34) ? 6 : 7];
215            count = 0;
216            }
217        }
218        stats[0][6] += stats[0][7];
219        stats[1][6] += stats[1][7];
220        int runFailure = checkRunStats(stats[0]) | checkRunStats(stats[1]);
221        if (runFailure != 0) fail("run test failure for %s\n", id);
222        int longRunFailure = ((stats[0][7] == 0) && (stats[1][7] == 0)) ? 0 : 1;
223        if (longRunFailure != 0) fail("long run test failure for %s\n", id);
224        return (runFailure + longRunFailure);
225     }
226 
checkRunStats(int[] stats)227     static int checkRunStats(int[] stats) {
228        int runFailure = 0;
229        runFailure |= ((2267 <= stats[1]) && (stats[1] <= 2733)) ? 0 : 1;
230        runFailure |= ((1079 <= stats[2]) && (stats[2] <= 1421)) ? 0 : 1;
231        runFailure |= (( 502 <= stats[3]) && (stats[3] <=  748)) ? 0 : 1;
232        runFailure |= (( 223 <= stats[4]) && (stats[4] <=  402)) ? 0 : 1;
233        runFailure |= ((  90 <= stats[5]) && (stats[5] <=  223)) ? 0 : 1;
234        runFailure |= ((  90 <= stats[6]) && (stats[6] <=  223)) ? 0 : 1;
235        return runFailure;
236         }
237 
238         /* Autocorrelation Test
239          *
240          *   For tau in {1, ..., 5000}, Z[tau] := sum[j=1,5000] (b[j] ^ b[j+tau]).
241          *
242          *   The sequence passes the autocorrelation test if every Z[tau] lies within the
243          *   interval 2267-2733.
244          */
autocorrelationTest(String id, byte[] s)245         static int autocorrelationTest(String id, byte[] s) {
246        // System.out.println("autocorrelation test");
247        int autocorrelationFailure = 0;
248        int N = s.length / 4;
249        for (int tau = 1; tau <= N; tau++) {
250            int count = 0;
251            for (int j = 0; j < N; j++) {
252            count += (s[j] ^ s[j+tau]);
253            }
254            // We intentionally use bounds [2267, 2733], which are wider than
255            // the bounds [2326, 2674] specified by BSI for this test.
256            // The latter bounds produce way too many false positives.
257            int singleAutocorrelationFailure = ((2267 < count) && (count < 2733)) ? 0 : 1;
258            if (singleAutocorrelationFailure != 0) {
259            if (autocorrelationFailure < 8) {
260                fail("autocorrelation failure for %s: count=%d (should be in [2267,2733]), tau=%d\n", id, count, tau);
261                if (count < 100 || count > 4900) {
262                     System.out.print("    ");
263                for (int q = 0; q < 50; q++) System.out.print(s[q]);
264                     System.out.println();
265                }
266            }
267            }
268            autocorrelationFailure += singleAutocorrelationFailure;
269        }
270        return (autocorrelationFailure == 0) ? 0 : 1;
271     }
272 
entireBsi1999Test(String id, byte[] s)273     static int entireBsi1999Test(String id, byte[] s) {
274        return (monobitTest(id, s) +
275            pokerTest(id, s) +
276            runTestAndLongRunTest(id, s) +
277            autocorrelationTest(id, s)
278            );
279     }
280 
281     /* To test a sequence of boolean values from a BooleanSupplier,
282      * sequentially extract 20000 boolean values, convert to an array
283      * of bytes, and feed them to method {@code entireBsi1999Test}.
284      */
285 
testRngBsi1999BooleanOnce(String id, BooleanSupplier theSupplier)286     static int testRngBsi1999BooleanOnce(String id, BooleanSupplier theSupplier) {
287        int failureCount = 0;
288        byte[] s = new byte[SEQUENCE_SIZE];
289        // Take the next SEQUENCE_SIZE booleans and test them
290        for (int j = 0; j < s.length; j++) {
291            s[j] = (theSupplier.getAsBoolean() ? (byte)1 : (byte)0);
292        }
293        failureCount += entireBsi1999Test(id  + " consecutive", s);
294        return failureCount;
295     }
296 
297     /* To test a sequence of long values from a LongSupplier,
298      * two kinds of tests are performed.
299      *
300      * The first kind of test extracts 313=ceiling(20000/64) long
301      * values and concatenates all their bits; the first 20000 bits
302      * are converted to a byte array of bits to be tested.  This test is
303      * repeated 64 times.
304      *
305      * The second kind of test focuses on one bit position m (0 <= m < 64);
306      * it extracts 20000 long values and uses just bit m from each value
307      * to produce an array of bytes to be tested.  This test is performed
308      * once for each possible value of m (64 times in all).
309      */
testRngBsi1999LongOnce(String id, LongSupplier theSupplier)310     static int testRngBsi1999LongOnce(String id, LongSupplier theSupplier) {
311        int failureCount = 0;
312        byte[] s = new byte[SEQUENCE_SIZE];
313        // Part 1: 64 times, take the next SEQUENCE_SIZE bits and test them
314        for (int m = 0; m < 64; m++) {
315            long bits = 0;
316            int bitCount = 0;
317            for (int j = 0; j < s.length; j++) {
318            if ((j & 0x3f) == 0) {
319                bits = theSupplier.getAsLong();
320                // System.out.printf("0x%016x\n", bits);
321                bitCount += Long.bitCount((j == (20000 - 32)) ? ((bits << 32) >>> 32) : bits);
322            }
323            s[j] = (byte)(bits & 1);
324            bits >>>= 1;
325            }
326            // System.out.println(m + ": " + bitCount + " 1-bits");
327            failureCount += entireBsi1999Test(id + " consecutive (" + bitCount + " 1-bits)", s);
328        }
329        // Part 2: for 0 <= m < 64, use bit m from each of the next SEQUENCE_SIZE longs
330        for (int m = 0; m < 64; m++) {
331            for (int j = 0; j < s.length; j++) {
332            s[j] = (byte)((theSupplier.getAsLong() >>> m) & 1);
333            }
334            failureCount += entireBsi1999Test(id + " bit " + m, s);
335        }
336        return failureCount;
337     }
338 
339     /* To test a sequence of ing values from an IntSupplier,
340      * two kinds of tests are performed.
341      *
342      * The first kind of test extracts 625=20000/32 int values and
343      * concatenates all their bits; these 20000 bits are converted to
344      * a byte array of bits to be tested.  This test is repeated 64
345      * times.
346      *
347      * The second kind of test focuses on one bit position m (0 <= m < 32);
348      * it extracts 20000 int values and uses just bit m from each value
349      * to produce an array of bytes to be tested.  This test is performed
350      * once for each possible value of m (32 times in all).
351      */
testRngBsi1999IntOnce(String id, IntSupplier theSupplier)352     static int testRngBsi1999IntOnce(String id, IntSupplier theSupplier) {
353        int failureCount = 0;
354        byte[] s = new byte[SEQUENCE_SIZE];
355        // Part 1: 64 times, take the next SEQUENCE_SIZE bits and test them
356        for (int m = 0; m < 64; m++) {
357            int bits = 0;
358            int bitCount = 0;
359            for (int j = 0; j < s.length; j++) {
360            if ((j & 0x1f) == 0) {
361                bits = theSupplier.getAsInt();
362                bitCount += Integer.bitCount(bits);
363            }
364            s[j] = (byte)(bits & 1);
365            bits >>>= 1;
366            }
367            // System.out.println(m + ": " + bitCount + " 1-bits");
368            failureCount += entireBsi1999Test(id + " consecutive (" + bitCount + " 1-bits)", s);
369        }
370        // Part 2: for 0 <= m < 32, use bit m from each of the next SEQUENCE_SIZE ints
371        for (int m = 0; m < 32; m++) {
372            for (int j = 0; j < s.length; j++) {
373            s[j] = (byte)((theSupplier.getAsInt() >>> m) & 1);
374            }
375            failureCount += entireBsi1999Test(id + " bit " + m, s);
376        }
377        return failureCount;
378     }
379 
380     /* A call to {@code entireBsi1999Test} may report failure even if the source of random
381      * bits is quite good, because the test is statistical in nature.  To make the testing
382      * procedure more robust, if the first call to {@code entireBsi1999Test} fails, then
383      * the failure is ignored if two more calls to {@code entireBsi1999Test} both succeed.
384     */
385 
testRngBsi1999Boolean(String id, BooleanSupplier theSupplier, int failureTolerance)386     static boolean testRngBsi1999Boolean(String id, BooleanSupplier theSupplier, int failureTolerance) {
387        if (testRngBsi1999BooleanOnce(id, theSupplier) <= failureTolerance) return true;
388        fail("testRngBsi1999Boolean glitch");
389        return ((testRngBsi1999BooleanOnce(id, theSupplier) <= failureTolerance) &&
390            (testRngBsi1999BooleanOnce(id, theSupplier) <= failureTolerance));
391     }
392 
testRngBsi1999Long(String id, LongSupplier theSupplier, int failureTolerance)393     static boolean testRngBsi1999Long(String id, LongSupplier theSupplier, int failureTolerance) {
394        if (testRngBsi1999LongOnce(id, theSupplier) <= failureTolerance) return true;
395        fail("testRngBsi1999Long glitch");
396        return ((testRngBsi1999LongOnce(id, theSupplier) <= failureTolerance) &&
397            (testRngBsi1999LongOnce(id, theSupplier) <= failureTolerance));
398     }
399 
testRngBsi1999Int(String id, IntSupplier theSupplier, int failureTolerance)400     static boolean testRngBsi1999Int(String id, IntSupplier theSupplier, int failureTolerance) {
401        if (testRngBsi1999IntOnce(id, theSupplier) <= failureTolerance) return true;
402        fail("testRngBsi1999Int glitch");
403        return ((testRngBsi1999IntOnce(id, theSupplier) <= failureTolerance) &&
404            (testRngBsi1999IntOnce(id, theSupplier) <= failureTolerance));
405     }
406 
tryIt(RandomGenerator rng, String id, BooleanSupplier theSupplier)407     static void tryIt(RandomGenerator rng, String id, BooleanSupplier theSupplier) {
408        System.out.printf("Testing %s %s\n", rng.getClass().getName(), id);
409        boolean success = theSupplier.getAsBoolean();
410        if (!success) {
411            fail("*** Failure of %s %s\n", rng.getClass().getName(), id);
412        }
413     }
414 
testOneRng(RandomGenerator rng, int failureTolerance)415     static void testOneRng(RandomGenerator rng, int failureTolerance) {
416        String name = rng.getClass().getName();
417        tryIt(rng, "nextInt", () -> testRngBsi1999Int(name + " nextInt", rng::nextInt, failureTolerance));
418        tryIt(rng, "nextLong", () -> testRngBsi1999Long(name + " nextLong", rng::nextLong, failureTolerance));
419        tryIt(rng, "nextBoolean", () -> testRngBsi1999Boolean(name + " nextBoolean", rng::nextBoolean, failureTolerance));
420        tryIt(rng, "ints", () -> testRngBsi1999Int(name + " ints", rng.ints().iterator()::next, failureTolerance));
421        tryIt(rng, "longs", () -> testRngBsi1999Long(name + " longs", rng.longs().iterator()::next, failureTolerance));
422        {
423            PrimitiveIterator.OfDouble iter = rng.doubles().iterator();
424            tryIt(rng, "doubles",
425              () -> testRngBsi1999Int(name + " doubles",
426                          () -> (int)(long)Math.floor(iter.next() * 0x1.0p32),
427                          failureTolerance));
428        }
429        tryIt(rng, "nextDouble",
430              () -> testRngBsi1999Int(name + " nextDouble",
431                          () -> (int)(long)Math.floor(rng.nextDouble() * 0x1.0p32),
432                          failureTolerance));
433        tryIt(rng, "nextFloat",
434              () -> testRngBsi1999Int(name + " nextFloat",
435                          () -> (((int)(long)Math.floor(rng.nextFloat() * 0x1.0p16f) << 16)
436                             | (int)(long)Math.floor(rng.nextFloat() * 0x1.0p16f)),
437                          failureTolerance));
438     }
439 
440     // Android-added: do not run this test as it takes too much time and it's test cases
441     // are run in RandomTestBsi1999Split.
442     @org.testng.annotations.Test(enabled = false)
main(String[] args)443     public static void main(String[] args) {
444         RandomGeneratorFactory.all().forEach(factory -> {
445             setRNG(factory.name());
446 
447             if (currentRNG.equals("SecureRandom")) {
448                 // skip because stochastic
449             } else if (currentRNG.equals("Random")) {
450                 // testOneRng(factory.create(59), 1);
451                 // autocorrelation failure for java.util.Random longs bit 0: count=2207 (should be in [2267,2733]), tau=2819
452 
453             } else {
454                 testOneRng(factory.create(59), 0);
455             }
456         });
457 
458         exceptionOnFail();
459     }
460 }
461 
462