Path: blob/master/test/jdk/java/util/Random/RandomTestBsi1999.java
41152 views
/*1* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/22import java.util.ArrayList;23import java.util.List;24import java.util.PrimitiveIterator;2526import java.util.random.*;2728import java.util.Set;29import java.util.concurrent.CompletableFuture;30import java.util.concurrent.ExecutionException;31import java.util.concurrent.ThreadLocalRandom;32import java.util.concurrent.TimeUnit;33import java.util.concurrent.TimeoutException;34import java.util.function.IntSupplier;35import java.util.function.LongSupplier;36import java.util.function.BooleanSupplier;37import java.util.function.Supplier;38import java.util.stream.Stream;3940import static java.util.stream.Collectors.toList;41import static java.util.stream.Collectors.toSet;4243/**44* @test45* @summary test bit sequences produced by clases that implement interface RandomGenerator46* @bug 824886247* @run main RandomTestBsi199948* @key randomness49*/5051public class RandomTestBsi1999 {5253/* A set of tests for pseudorandom number generators inspired by this report:54*55* Werner Schindler. Functionality Classes and Evaluation Methodology for56* Deterministic Random Number Generators, Version 2.0.57* Bundesamt fur Sicherheit in der Informationstechnik (BSI). December 2, 1999.58* https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Zertifizierung/Interpretationen/AIS_20_Functionality_Classes_Evaluation_Methodology_DRNG_e.pdf59*60* Section F of this report (pp. 19-20) recommends the use of five tests to evaluate a61* sequence of bits:62*63* Monobit test64* Poker test65* Run test66* Long run test67* Autocorrelation test68*69* The first four of these tests are in turn taken from this report:70*71* National Institute of Standards and Technology (NIST),72* U.S. Department of Commerce. Security Requirements for73* Cryptographic Modules. Federal Information Processing74* Standard (FIPS) 140-1, January 11, 1994.75* https://csrc.nist.gov/csrc/media/publications/fips/140/1/archive/1994-01-11/documents/fips1401.pdf76*77* The BSI report appears to contain a few typos in transcribing the FIPS 140-178* requirements (pp. 44-45); specifically, the last three intervals for the runs test79* (for lengths 4, 5, and 6+) are given as "223 - 402, 90 - 223, 90 - 223" in the FIPS80* standard but as "233-402, 90-223, 90-233" in the BSI publication. A quick81* mathematical check indicates that the FIPS 140-1 values are correct; therefore we82* use those values here. In addition, the BSI report specifies a test interval of83* 2326-2674 for the autocorrelation test, which provides an appropriately small84* rejection rate if the test were done for only a single value of tau; but because we85* wish to perform 5000 distinct tests, one for each value of tau in the range 1-5000,86* that test interval produces too many false positives. Some calculation shows that87* the interval 2267-2733 used by the FIPS 140-1 run test for runs of length 1 is88* appropriate, so we use that interval here for each of the 5000 individual89* autocorrelation tests.90*91* Each of the four FIPS 140-1 tests examines a sequence of 20000 bits. The92* autocorrelation test examines a sequence of 10000 bits. It is convenient to93* generate a sequence of 20000 bits (which we represent as an array of 20000 bytes)94* and then apply all five tests, knowing that the autocorrelation test will examine95* only the first half of the byte array.96*97* The descriptions of the tests are quoted from the FIPS 140-1 and BSI reports98* (with some adjustments of punctuation and mathematical symbols, as well as99* for our specific choices of test intervals).100*/101102static String currentRNG = "";103static int failCount = 0;104105static void exceptionOnFail() {106if (failCount != 0) {107throw new RuntimeException(failCount + " fails detected");108}109}110111static void setRNG(String rng) {112currentRNG = rng;113}114115static void fail(String format, Object... args) {116if (currentRNG.length() != 0) {117System.err.println(currentRNG);118currentRNG = "";119}120121System.err.format(" " + format, args);122failCount++;123}124125private static final int SEQUENCE_SIZE = 20000;126127/* The Monobit Test128*129* 1. Count the number of ones in the 20,000 bit stream. Denote this quantity by X.130*131* 2. The test is passed if 9,654 < X < 10,346.132*/133static int monobitTest(String id, byte[] s) {134// System.out.println("monobit test");135int count = 0;136for (int j = 0; j < s.length; j++) {137count += s[j];138}139int monobitFailure = ((9654 < count) && (count < 10346)) ? 0 : 1;140if (monobitFailure != 0) fail("monobit test failure for %s: count=%d (should be in [9654,10346])\n", id, count);141return monobitFailure;142}143144/* The Poker Test145*146* 1. Divide the 20,000 bit stream into 5,000 contiguous 4-bit segments. Count and147* store the number of occurrences of each of the 16 possible 4-bit values. Denote148* f(i) as the number of each 4-bit value i where 0 <= i <= 15.149*150* 2. Evaluate the following: X = (16/5000)(sum[i=0,15] (f(i))**2) - 5000151*152* 3. The test is passed if 1.03 < X < 57.4.153*/154155static int pokerTest(String id, byte[] s) {156// System.out.println("poker test");157// Divide the bit sequence into 4-bit chunks, and count the number of times each 4-bit value appears.158int[] stats = new int[16];159int v = 0;160for (int j = 0; j < s.length; j++) {161v = (v << 1) | s[j];162if ((j & 3) == 3) {163++stats[v];164v = 0;165}166}167int z = 0;168for (int k = 0; k < stats.length; k++) {169z += stats[k]*stats[k];170}171double x = (16.0 / (s.length / 4)) * z - (s.length / 4);172int pokerFailure = ((1.03 < x) && (x < 57.4)) ? 0 : 1;173if (pokerFailure != 0) fail("poker test failure for %s: x=%g (should be in [1.03,57.4])\n", id, x);174return pokerFailure;175}176177/* The Runs Test178*179* 1. A run is defined as a maximal sequence of consecutive bits of either all ones180* or all zeros, which is part of the 20,000 bit sample stream. The incidences of181* runs (for both consecutive zeros and consecutive ones) of all lengths (>= 1) in182* the sample stream should be counted and stored.183*184* 2. The test is passed if the number of runs that occur (of lengths 1 through 6)185* is each within the corresponding interval specified below. This must hold for186* both the zeros and ones; that is, all 12 counts must lie in the specified187* interval. For the purpose of this test, runs of greater than 6 are considered to188* be of length 6.189*190* Length of run Required Interval191* 1 2,267 - 2,733192* 2 1,079 - 1,421193* 3 502 - 748194* 4 223 - 402195* 5 90 - 223196* 6+ 90 - 223197*198* The Long Run Test199*200* 1 . A long run is defined to be a run of length 34 or more (of either zeros or ones).201*202* 2. On the sample of 20,000 bits, the test is passed if there are NO long runs.203*/204static int runTestAndLongRunTest(String id, byte[] s) {205// System.out.println("run test");206int[][] stats = new int[2][8];207int count = 0;208for (int j = 0; j < s.length; j++) {209++count;210if ((j == (s.length - 1)) || (s[j+1] != s[j])) {211++stats[s[j]][(count < 6) ? count : (count < 34) ? 6 : 7];212count = 0;213}214}215stats[0][6] += stats[0][7];216stats[1][6] += stats[1][7];217int runFailure = checkRunStats(stats[0]) | checkRunStats(stats[1]);218if (runFailure != 0) fail("run test failure for %s\n", id);219int longRunFailure = ((stats[0][7] == 0) && (stats[1][7] == 0)) ? 0 : 1;220if (longRunFailure != 0) fail("long run test failure for %s\n", id);221return (runFailure + longRunFailure);222}223224static int checkRunStats(int[] stats) {225int runFailure = 0;226runFailure |= ((2267 <= stats[1]) && (stats[1] <= 2733)) ? 0 : 1;227runFailure |= ((1079 <= stats[2]) && (stats[2] <= 1421)) ? 0 : 1;228runFailure |= (( 502 <= stats[3]) && (stats[3] <= 748)) ? 0 : 1;229runFailure |= (( 223 <= stats[4]) && (stats[4] <= 402)) ? 0 : 1;230runFailure |= (( 90 <= stats[5]) && (stats[5] <= 223)) ? 0 : 1;231runFailure |= (( 90 <= stats[6]) && (stats[6] <= 223)) ? 0 : 1;232return runFailure;233}234235/* Autocorrelation Test236*237* For tau in {1, ..., 5000}, Z[tau] := sum[j=1,5000] (b[j] ^ b[j+tau]).238*239* The sequence passes the autocorrelation test if every Z[tau] lies within the240* interval 2267-2733.241*/242static int autocorrelationTest(String id, byte[] s) {243// System.out.println("autocorrelation test");244int autocorrelationFailure = 0;245int N = s.length / 4;246for (int tau = 1; tau <= N; tau++) {247int count = 0;248for (int j = 0; j < N; j++) {249count += (s[j] ^ s[j+tau]);250}251// We intentionally use bounds [2267, 2733], which are wider than252// the bounds [2326, 2674] specified by BSI for this test.253// The latter bounds produce way too many false positives.254int singleAutocorrelationFailure = ((2267 < count) && (count < 2733)) ? 0 : 1;255if (singleAutocorrelationFailure != 0) {256if (autocorrelationFailure < 8) {257fail("autocorrelation failure for %s: count=%d (should be in [2267,2733]), tau=%d\n", id, count, tau);258if (count < 100 || count > 4900) {259System.out.print(" ");260for (int q = 0; q < 50; q++) System.out.print(s[q]);261System.out.println();262}263}264}265autocorrelationFailure += singleAutocorrelationFailure;266}267return (autocorrelationFailure == 0) ? 0 : 1;268}269270static int entireBsi1999Test(String id, byte[] s) {271return (monobitTest(id, s) +272pokerTest(id, s) +273runTestAndLongRunTest(id, s) +274autocorrelationTest(id, s)275);276}277278/* To test a sequence of boolean values from a BooleanSupplier,279* sequentially extract 20000 boolean values, convert to an array280* of bytes, and feed them to method {@code entireBsi1999Test}.281*/282283static int testRngBsi1999BooleanOnce(String id, BooleanSupplier theSupplier) {284int failureCount = 0;285byte[] s = new byte[SEQUENCE_SIZE];286// Take the next SEQUENCE_SIZE booleans and test them287for (int j = 0; j < s.length; j++) {288s[j] = (theSupplier.getAsBoolean() ? (byte)1 : (byte)0);289}290failureCount += entireBsi1999Test(id + " consecutive", s);291return failureCount;292}293294/* To test a sequence of long values from a LongSupplier,295* two kinds of tests are performed.296*297* The first kind of test extracts 313=ceiling(20000/64) long298* values and concatenates all their bits; the first 20000 bits299* are converted to a byte array of bits to be tested. This test is300* repeated 64 times.301*302* The second kind of test focuses on one bit position m (0 <= m < 64);303* it extracts 20000 long values and uses just bit m from each value304* to produce an array of bytes to be tested. This test is performed305* once for each possible value of m (64 times in all).306*/307static int testRngBsi1999LongOnce(String id, LongSupplier theSupplier) {308int failureCount = 0;309byte[] s = new byte[SEQUENCE_SIZE];310// Part 1: 64 times, take the next SEQUENCE_SIZE bits and test them311for (int m = 0; m < 64; m++) {312long bits = 0;313int bitCount = 0;314for (int j = 0; j < s.length; j++) {315if ((j & 0x3f) == 0) {316bits = theSupplier.getAsLong();317// System.out.printf("0x%016x\n", bits);318bitCount += Long.bitCount((j == (20000 - 32)) ? ((bits << 32) >>> 32) : bits);319}320s[j] = (byte)(bits & 1);321bits >>>= 1;322}323// System.out.println(m + ": " + bitCount + " 1-bits");324failureCount += entireBsi1999Test(id + " consecutive (" + bitCount + " 1-bits)", s);325}326// Part 2: for 0 <= m < 64, use bit m from each of the next SEQUENCE_SIZE longs327for (int m = 0; m < 64; m++) {328for (int j = 0; j < s.length; j++) {329s[j] = (byte)((theSupplier.getAsLong() >>> m) & 1);330}331failureCount += entireBsi1999Test(id + " bit " + m, s);332}333return failureCount;334}335336/* To test a sequence of ing values from an IntSupplier,337* two kinds of tests are performed.338*339* The first kind of test extracts 625=20000/32 int values and340* concatenates all their bits; these 20000 bits are converted to341* a byte array of bits to be tested. This test is repeated 64342* times.343*344* The second kind of test focuses on one bit position m (0 <= m < 32);345* it extracts 20000 int values and uses just bit m from each value346* to produce an array of bytes to be tested. This test is performed347* once for each possible value of m (32 times in all).348*/349static int testRngBsi1999IntOnce(String id, IntSupplier theSupplier) {350int failureCount = 0;351byte[] s = new byte[SEQUENCE_SIZE];352// Part 1: 64 times, take the next SEQUENCE_SIZE bits and test them353for (int m = 0; m < 64; m++) {354int bits = 0;355int bitCount = 0;356for (int j = 0; j < s.length; j++) {357if ((j & 0x1f) == 0) {358bits = theSupplier.getAsInt();359bitCount += Integer.bitCount(bits);360}361s[j] = (byte)(bits & 1);362bits >>>= 1;363}364// System.out.println(m + ": " + bitCount + " 1-bits");365failureCount += entireBsi1999Test(id + " consecutive (" + bitCount + " 1-bits)", s);366}367// Part 2: for 0 <= m < 32, use bit m from each of the next SEQUENCE_SIZE ints368for (int m = 0; m < 32; m++) {369for (int j = 0; j < s.length; j++) {370s[j] = (byte)((theSupplier.getAsInt() >>> m) & 1);371}372failureCount += entireBsi1999Test(id + " bit " + m, s);373}374return failureCount;375}376377/* A call to {@code entireBsi1999Test} may report failure even if the source of random378* bits is quite good, because the test is statistical in nature. To make the testing379* procedure more robust, if the first call to {@code entireBsi1999Test} fails, then380* the failure is ignored if two more calls to {@code entireBsi1999Test} both succeed.381*/382383static boolean testRngBsi1999Boolean(String id, BooleanSupplier theSupplier, int failureTolerance) {384if (testRngBsi1999BooleanOnce(id, theSupplier) <= failureTolerance) return true;385fail("testRngBsi1999Boolean glitch");386return ((testRngBsi1999BooleanOnce(id, theSupplier) <= failureTolerance) &&387(testRngBsi1999BooleanOnce(id, theSupplier) <= failureTolerance));388}389390static boolean testRngBsi1999Long(String id, LongSupplier theSupplier, int failureTolerance) {391if (testRngBsi1999LongOnce(id, theSupplier) <= failureTolerance) return true;392fail("testRngBsi1999Long glitch");393return ((testRngBsi1999LongOnce(id, theSupplier) <= failureTolerance) &&394(testRngBsi1999LongOnce(id, theSupplier) <= failureTolerance));395}396397static boolean testRngBsi1999Int(String id, IntSupplier theSupplier, int failureTolerance) {398if (testRngBsi1999IntOnce(id, theSupplier) <= failureTolerance) return true;399fail("testRngBsi1999Int glitch");400return ((testRngBsi1999IntOnce(id, theSupplier) <= failureTolerance) &&401(testRngBsi1999IntOnce(id, theSupplier) <= failureTolerance));402}403404static void tryIt(RandomGenerator rng, String id, BooleanSupplier theSupplier) {405System.out.printf("Testing %s %s\n", rng.getClass().getName(), id);406boolean success = theSupplier.getAsBoolean();407if (!success) {408fail("*** Failure of %s %s\n", rng.getClass().getName(), id);409}410}411412static void testOneRng(RandomGenerator rng, int failureTolerance) {413String name = rng.getClass().getName();414tryIt(rng, "nextInt", () -> testRngBsi1999Int(name + " nextInt", rng::nextInt, failureTolerance));415tryIt(rng, "nextLong", () -> testRngBsi1999Long(name + " nextLong", rng::nextLong, failureTolerance));416tryIt(rng, "nextBoolean", () -> testRngBsi1999Boolean(name + " nextBoolean", rng::nextBoolean, failureTolerance));417tryIt(rng, "ints", () -> testRngBsi1999Int(name + " ints", rng.ints().iterator()::next, failureTolerance));418tryIt(rng, "longs", () -> testRngBsi1999Long(name + " longs", rng.longs().iterator()::next, failureTolerance));419{420PrimitiveIterator.OfDouble iter = rng.doubles().iterator();421tryIt(rng, "doubles",422() -> testRngBsi1999Int(name + " doubles",423() -> (int)(long)Math.floor(iter.next() * 0x1.0p32),424failureTolerance));425}426tryIt(rng, "nextDouble",427() -> testRngBsi1999Int(name + " nextDouble",428() -> (int)(long)Math.floor(rng.nextDouble() * 0x1.0p32),429failureTolerance));430tryIt(rng, "nextFloat",431() -> testRngBsi1999Int(name + " nextFloat",432() -> (((int)(long)Math.floor(rng.nextFloat() * 0x1.0p16f) << 16)433| (int)(long)Math.floor(rng.nextFloat() * 0x1.0p16f)),434failureTolerance));435}436437public static void main(String[] args) {438RandomGeneratorFactory.all().forEach(factory -> {439setRNG(factory.name());440441if (currentRNG.equals("SecureRandom")) {442// skip because stochastic443} else if (currentRNG.equals("Random")) {444// testOneRng(factory.create(59), 1);445// autocorrelation failure for java.util.Random longs bit 0: count=2207 (should be in [2267,2733]), tau=2819446447} else {448testOneRng(factory.create(59), 0);449}450});451452exceptionOnFail();453}454}455456457458