Path: blob/master/test/jdk/sun/security/provider/SecureRandom/DrbgCavp.java
41155 views
/*1* Copyright (c) 2016, 2018, 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*/2223import sun.security.provider.EntropySource;24import sun.security.provider.MoreDrbgParameters;2526import javax.crypto.Cipher;27import java.io.BufferedReader;28import java.io.ByteArrayOutputStream;29import java.io.File;30import java.io.InputStream;31import java.io.InputStreamReader;32import java.io.PrintStream;33import java.lang.*;34import java.security.NoSuchAlgorithmException;35import java.security.SecureRandom;36import java.security.DrbgParameters;37import java.util.ArrayDeque;38import java.util.Arrays;39import java.util.Queue;40import java.util.stream.Stream;41import java.util.zip.ZipEntry;42import java.util.zip.ZipFile;43import java.util.zip.ZipInputStream;4445import static java.security.DrbgParameters.Capability.*;4647/**48* The Known-output DRBG test. The test vector can be downloaded from49* https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip.50* The test is described on https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Random-Number-Generators.51*52* Manually run this test with53*54* java DrbgCavp drbgtestvectors.zip55*56*/57public class DrbgCavp {5859// the current nonce60private static byte[] nonce;6162// A buffer to store test materials for the current call and63// can be printed out of an error occurs.64private static ByteArrayOutputStream bout = new ByteArrayOutputStream();6566// Save err for restoring67private static PrintStream err = System.err;6869private static final int AES_LIMIT;7071static {72try {73AES_LIMIT = Cipher.getMaxAllowedKeyLength("AES");74} catch (Exception e) {75// should not happen76throw new AssertionError("Cannot detect AES");77}78}7980public static void main(String[] args) throws Exception {8182if (args.length != 1) {83System.out.println("Usage: java DrbgCavp drbgtestvectors.zip");84return;85}86File tv = new File(args[0]);8788EntropySource es = new TestEntropySource();89System.setErr(new PrintStream(bout));9091// The testsuite is a zip file containing more zip files for different92// working modes. Each internal zip file contains test materials for93// different mechanisms.9495try (ZipFile zf = new ZipFile(tv)) {96String[] modes = {"no_reseed", "pr_false", "pr_true"};97for (String mode : modes) {98try (ZipInputStream zis = new ZipInputStream(zf.getInputStream(99zf.getEntry("drbgvectors_" + mode + ".zip")))) {100while (true) {101ZipEntry ze = zis.getNextEntry();102if (ze == null) {103break;104}105String fname = ze.getName();106if (fname.equals("Hash_DRBG.txt")107|| fname.equals("HMAC_DRBG.txt")108|| fname.equals("CTR_DRBG.txt")) {109String algorithm110= fname.substring(0, fname.length() - 4);111test(mode, algorithm, es, zis);112}113}114}115}116} finally {117System.setErr(err);118}119}120121/**122* A special entropy source you can set entropy input at will.123*/124private static class TestEntropySource implements EntropySource {125126private static Queue<byte[]> data = new ArrayDeque<>();127128@Override129public byte[] getEntropy(int minEntropy, int minLength,130int maxLength, boolean pr) {131byte[] result = data.poll();132if (result == null133|| result.length < minLength134|| result.length > maxLength) {135throw new RuntimeException("Invalid entropy: " +136"need [" + minLength + ", " + maxLength + "], " +137(result == null ? "none" : "has " + result.length));138}139return result;140}141142private static void setEntropy(byte[] input) {143data.offer(input);144}145146private static void clearEntropy() {147data.clear();148}149}150151/**152* The test.153*154* // Algorithm line, might contain usedf flag155* [AES-128 use df]156* // Ignored, use mode argument157* [PredictionResistance = True]158* // Ignored, just read EntropyInput159* [EntropyInputLen = 128]160* // Ignored, just read Nonce161* [NonceLen = 64]162* // Ignored, just read PersonalizationString163* [PersonalizationStringLen = 128]164* // Ignored, just read AdditionalInput165* [AdditionalInputLen = 128]166* // Used to allocate buffer for nextBytes() call167* [ReturnedBitsLen = 512]168*169* // A sign we can ignore old unused entropy input170* COUNT = 0171*172* // Instantiate173* EntropyInput = 92898f...174* Nonce = c2a4d9...175* PersonalizationString = ea65ee... // Enough to call getInstance()176*177* // Reseed178* EntropyInputReseed = bfd503...179* AdditionalInputReseed = 009e0b... // Enough to call reseed()180*181* // Generation182* AdditionalInput = 1a40fa.... // Enough to call nextBytes() for PR off183* EntropyInputPR = 20728a... // Enough to call nextBytes() for PR on184* ReturnedBits = 5a3539... // Compare this to last nextBytes() output185*186* @param mode one of "no_reseed", "pr_false", "pr_true"187* @param mech one of "Hash_DRBG", "HMAC_DRBG", "CTR_DRBG"188* @param es our own entropy source189* @param is test material190*/191private static void test(String mode, String mech, EntropySource es,192InputStream is) throws Exception {193194SecureRandom hd = null;195196// Expected output length in bits as in [ReturnedBitsLen]197int outLen = 0;198199// DRBG algorithm as in the algorithm line200String algorithm = null;201202// When CTR_DRBG uses a derivation function as in the algorithm line203boolean usedf = false;204205// Additional input as in "AdditionalInput"206byte[] additional = null;207208// Random bits generated209byte[] output = null;210211// Prediction resistance flag, determined by mode212boolean isPr = false;213214StringBuilder sb = new StringBuilder();215216int lineno = 0;217218System.out.println(mode + "/" + mech);219220try (Stream<String> lines =221new BufferedReader(new InputStreamReader(is)).lines()) {222for (String s: (Iterable<String>) lines::iterator) {223lineno++;224err.print(hd == null ? '-' : '*');225Line l = new Line(s);226if (l.key.contains("no df") || l.key.contains("use df") ||227l.key.startsWith("SHA-")) {228sb = new StringBuilder();229bout.reset();230}231sb.append(String.format(232"%9s %4s %5d %s\n", mode, mech, lineno, s));233switch (l.key) {234case "3KeyTDEA no df":235case "AES-128 no df":236case "AES-192 no df":237case "AES-256 no df":238case "3KeyTDEA use df":239case "AES-128 use df":240case "AES-192 use df":241case "AES-256 use df":242algorithm = l.key.split(" ")[0];243usedf = l.key.contains("use df");244break;245case "ReturnedBitsLen":246outLen = l.vint();247output = new byte[outLen / 8];248break;249case "EntropyInput":250TestEntropySource.setEntropy(l.vdata());251break;252case "Nonce":253nonce = l.vdata();254break;255case "COUNT":256// Remove unused entropy (say, when AES-256 is skipped)257TestEntropySource.clearEntropy();258break;259case "PersonalizationString":260try {261isPr = mode.equals("pr_true");262byte[] ps = null;263if (l.vdata().length != 0) {264ps = l.vdata();265}266267// MoreDrbgParameters must be used because we268// want to set entropy input and nonce. Since269// it can also set mechanism, algorithm and usedf,270// we don't need to touch securerandom.drbg.config.271hd = SecureRandom.getInstance("DRBG",272new MoreDrbgParameters(es, mech, algorithm,273nonce, usedf,274DrbgParameters.instantiation(275-1,276isPr ? PR_AND_RESEED277: RESEED_ONLY,278ps)),279"SUN");280} catch (NoSuchAlgorithmException iae) {281// We don't support SHA-1 and 3KeyTDEA. AES-192 or282// AES-256 might not be available. This is OK.283if (algorithm.equals("SHA-1") ||284algorithm.equals("3KeyTDEA") ||285((algorithm.equals("AES-192")286|| algorithm.equals("AES-256"))287&& AES_LIMIT == 128)) {288hd = null;289} else {290throw iae;291}292}293break;294case "EntropyInputReseed":295TestEntropySource.setEntropy(l.vdata());296break;297case "AdditionalInputReseed":298if (l.vdata().length == 0) {299additional = null;300} else {301additional = l.vdata();302}303if (hd != null) {304if (additional == null) {305hd.reseed();306} else {307hd.reseed(DrbgParameters.reseed(308isPr, additional));309}310}311break;312case "EntropyInputPR":313if (l.vdata().length != 0) {314TestEntropySource.setEntropy(l.vdata());315}316if (mode.equals("pr_true")) {317if (hd != null) {318if (additional == null) {319hd.nextBytes(output);320} else {321hd.nextBytes(output,322DrbgParameters.nextBytes(323-1, isPr, additional));324}325}326}327break;328case "AdditionalInput":329if (l.vdata().length == 0) {330additional = null;331} else {332additional = l.vdata();333}334if (!mode.equals("pr_true")) {335if (hd != null) {336if (additional == null) {337hd.nextBytes(output);338} else {339hd.nextBytes(output,340DrbgParameters.nextBytes(341-1, isPr, additional));342}343}344}345break;346case "ReturnedBits":347if (hd != null) {348if (!Arrays.equals(output, l.vdata())) {349throw new Exception("\nExpected: " +350l.value + "\n Actual: " + hex(output));351}352}353break;354default:355// Algorithm line for Hash_DRBG and HMAC_DRBG356if (l.key.startsWith("SHA-")) {357algorithm = l.key;358}359}360}361err.println();362} catch (Exception e) {363err.println();364err.println(sb.toString());365err.println(bout.toString());366throw e;367}368}369370/**371* Parse a line from test material.372*373* Brackets are removed. Key and value separated.374*/375static class Line {376377final String key;378final String value;379380Line(String s) {381s = s.trim();382if (s.length() >= 2) {383if (s.charAt(0) == '[') {384s = s.substring(1, s.length() - 1);385}386}387if (s.indexOf('=') < 0) {388key = s;389value = null;390} else {391key = s.substring(0, s.indexOf('=')).trim();392value = s.substring(s.indexOf('=') + 1).trim();393}394}395396int vint() {397return Integer.parseInt(value);398}399400byte[] vdata() {401return xeh(value);402}403}404405// Bytes to HEX406private static String hex(byte[] in) {407StringBuilder sb = new StringBuilder();408for (byte b: in) {409sb.append(String.format("%02x", b&0xff));410}411return sb.toString();412}413414// HEX to bytes415private static byte[] xeh(String in) {416in = in.replaceAll(" ", "");417int len = in.length() / 2;418byte[] out = new byte[len];419for (int i = 0; i < len; i++) {420out[i] = (byte) Integer.parseInt(421in.substring(i * 2, i * 2 + 2), 16);422}423return out;424}425}426427428