# HG changeset patch # User weijun # Date 1462505924 -28800 # Node ID 256c45c4af5d768bb1b4129ff6ff60adab2502e9 # Parent c5dc5ab60139bac5b6b16d71350a57e543581dea 8051408: NIST SP 800-90A SecureRandom implementations Reviewed-by: wetmore, xuelei, coffeys diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/HmacCore.java Fri May 06 11:38:44 2016 +0800 @@ -262,4 +262,14 @@ super("SHA-512", 128); } } + public static final class HmacSHA512_224 extends HmacCore { + public HmacSHA512_224() throws NoSuchAlgorithmException { + super("SHA-512/224", 128); + } + } + public static final class HmacSHA512_256 extends HmacCore { + public HmacSHA512_256() throws NoSuchAlgorithmException { + super("SHA-512/256", 128); + } + } } diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java Fri May 06 11:38:44 2016 +0800 @@ -704,6 +704,12 @@ put("Alg.Alias.Mac.OID.1.2.840.113549.2.11", "HmacSHA512"); put("Alg.Alias.Mac.1.2.840.113549.2.11", "HmacSHA512"); + // TODO: aliases with OIDs + put("Mac.HmacSHA512/224", + "com.sun.crypto.provider.HmacCore$HmacSHA512_224"); + put("Mac.HmacSHA512/256", + "com.sun.crypto.provider.HmacCore$HmacSHA512_256"); + put("Mac.HmacPBESHA1", "com.sun.crypto.provider.HmacPKCS12PBESHA1"); diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/java/security/DrbgParameters.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/security/DrbgParameters.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.security; + +import java.util.Locale; +import java.util.Objects; + +/** + * This class specifies the parameters used by a DRBG (Deterministic + * Random Bit Generator). + *

+ * According to + * + * NIST Special Publication 800-90A Revision 1, Recommendation for Random + * Number Generation Using Deterministic Random Bit Generators (800-90Ar1), + *

+ * A DRBG is based on a DRBG mechanism as specified in this Recommendation + * and includes a source of randomness. A DRBG mechanism uses an algorithm + * (i.e., a DRBG algorithm) that produces a sequence of bits from an initial + * value that is determined by a seed that is determined from the output of + * the randomness source." + *
+ *

+ * The 800-90Ar1 specification allows for a variety of DRBG implementation + * choices, such as: + *

+ *

+ * These choices are set in each implementation and are not directly + * managed by the {@code SecureRandom} API. Check your DRBG provider's + * documentation to find an appropriate implementation for the situation. + *

+ * On the other hand, the 800-90Ar1 specification does have some configurable + * options, such as: + *

+ *

+ * A DRBG instance can be instantiated with parameters from an + * {@link DrbgParameters.Instantiation} object and other information + * (for example, the nonce, which is not managed by this API). This maps + * to the {@code Instantiate_function} defined in NIST SP 800-90Ar1. + *

+ * A DRBG instance can be reseeded with parameters from a + * {@link DrbgParameters.Reseed} object. This maps to the + * {@code Reseed_function} defined in NIST SP 800-90Ar1. Calling + * {@link SecureRandom#reseed()} is equivalent to calling + * {@link SecureRandom#reseed(SecureRandomParameters)} with the effective + * instantiated prediction resistance flag (as returned by + * {@link SecureRandom#getParameters()}) with no additional input. + *

+ * A DRBG instance generates data with additional parameters from a + * {@link DrbgParameters.NextBytes} object. This maps to the + * {@code Generate_function} defined in NIST SP 800-90Ar1. Calling + * {@link SecureRandom#nextBytes(byte[])} is equivalent to calling + * {@link SecureRandom#nextBytes(byte[], SecureRandomParameters)} + * with the effective instantiated strength and prediction resistance flag + * (as returned by {@link SecureRandom#getParameters()}) with no + * additional input. + *

+ * A DRBG should be implemented as a subclass of {@link SecureRandomSpi}. + * It is recommended that the implementation contain the 1-arg + * {@linkplain SecureRandomSpi#SecureRandomSpi(SecureRandomParameters) constructor} + * that takes a {@code DrbgParameters.Instantiation} argument. If implemented + * this way, this implementation can be chosen by any + * {@code SecureRandom.getInstance()} method. If it is chosen by a + * {@code SecureRandom.getInstance()} with a {@link SecureRandomParameters} + * parameter, the parameter is passed into this constructor. If it is chosen + * by a {@code SecureRandom.getInstance()} without a + * {@code SecureRandomParameters} parameter, the constructor is called with + * a {@code null} argument and the implementation should choose its own + * parameters. Its {@link SecureRandom#getParameters()} must always return a + * non-null effective {@code DrbgParameters.Instantiation} object that reflects + * how the DRBG is actually instantiated. A caller can use this information + * to determine whether a {@code SecureRandom} object is a DRBG and what + * features it supports. Please note that the returned value does not + * necessarily equal to the {@code DrbgParameters.Instantiation} object passed + * into the {@code SecureRandom.getInstance()} call. For example, + * the requested capability can be {@link DrbgParameters.Capability#NONE} + * but the effective value can be {@link DrbgParameters.Capability#RESEED_ONLY} + * if the implementation supports reseeding. The implementation must implement + * the {@link SecureRandomSpi#engineNextBytes(byte[], SecureRandomParameters)} + * method which takes a {@code DrbgParameters.NextBytes} parameter. Unless + * the result of {@link SecureRandom#getParameters()} has its + * {@linkplain DrbgParameters.Instantiation#getCapability() capability} being + * {@link Capability#NONE NONE}, it must implement + * {@link SecureRandomSpi#engineReseed(SecureRandomParameters)} which takes + * a {@code DrbgParameters.Reseed} parameter. + *

+ * On the other hand, if a DRBG implementation does not contain a constructor + * that has an {@code DrbgParameters.Instantiation} argument (not recommended), + * it can only be chosen by a {@code SecureRandom.getInstance()} without + * a {@code SecureRandomParameters} parameter, but will not be chosen if + * a {@code getInstance} method with a {@code SecureRandomParameters} parameter + * is called. If implemented this way, its {@link SecureRandom#getParameters()} + * must return {@code null}, and it does not need to implement either + * {@link SecureRandomSpi#engineNextBytes(byte[], SecureRandomParameters)} + * or {@link SecureRandomSpi#engineReseed(SecureRandomParameters)}. + *

+ * A DRBG might reseed itself automatically if the seed period is bigger + * than the maximum seed life defined by the DRBG mechanism. + *

+ * A DRBG implementation should support serialization and deserialization + * by retaining the configuration and effective parameters, but the internal + * state must not be serialized and the deserialized object must be + * reinstantiated. + *

+ * Examples: + *

+ * SecureRandom drbg;
+ * byte[] buffer = new byte[32];
+ *
+ * // Any DRBG is OK
+ * drbg = SecureRandom.getInstance("DRBG");
+ * drbg.nextBytes(buffer);
+ *
+ * SecureRandomParameters params = drbg.getParameters();
+ * if (params instanceof DrbgParameters.Instantiation) {
+ *     DrbgParameters.Instantiation ins = (DrbgParameters.Instantiation) params;
+ *     if (ins.getCapability().supportsReseeding()) {
+ *         drbg.reseed();
+ *     }
+ * }
+ *
+ * // The following call requests a weak DRBG instance. It is only
+ * // guaranteed to support 112 bits of security strength.
+ * drbg = SecureRandom.getInstance("DRBG",
+ *         DrbgParameters.instantiation(112, NONE, null));
+ *
+ * // Both the next two calls will likely fail, because drbg could be
+ * // instantiated with a smaller strength with no prediction resistance
+ * // support.
+ * drbg.nextBytes(buffer,
+ *         DrbgParameters.nextBytes(256, false, "more".getBytes()));
+ * drbg.nextBytes(buffer,
+ *         DrbgParameters.nextBytes(112, true, "more".getBytes()));
+ *
+ * // The following call requests a strong DRBG instance, with a
+ * // personalization string. If it successfully returns an instance,
+ * // that instance is guaranteed to support 256 bits of security strength
+ * // with prediction resistance available.
+ * drbg = SecureRandom.getInstance("DRBG", DrbgParameters.instantiation(
+ *         256, PR_AND_RESEED, "hello".getBytes()));
+ *
+ * // Prediction resistance is not requested in this single call,
+ * // but an additional input is used.
+ * drbg.nextBytes(buffer,
+ *         DrbgParameters.nextBytes(-1, false, "more".getBytes()));
+ *
+ * // Same for this call.
+ * drbg.reseed(DrbgParameters.reseed(false, "extra".getBytes()));
+ *
+ * + * @implSpec + * By convention, a provider should name its primary DRBG implementation + * with the + * standard {@code SecureRandom} algorithm name "DRBG". + * + * @implNote + * The following notes apply to the "DRBG" implementation in the SUN provider + * of the JDK reference implementation. + *

+ * This implementation supports the Hash_DRBG and HMAC_DRBG mechanisms with + * DRBG algorithm SHA-1, SHA-224, SHA-512/224, SHA-256, SHA-512/256, + * SHA-384 and SHA-512, and CTR_DRBG (both using derivation function and + * not using derivation function) with DRBG algorithm 3KeyTDEA + * (also known as DESede in JCE), AES-128, AES-192 and AES-256. + *

+ * The mechanism name and DRBG algorithm name are determined by the + * {@linkplain Security#getProperty(String) security property} + * {@code securerandom.drbg.config}. The default choice is Hash_DRBG + * with SHA-256. + *

+ * For each combination, the security strength can be requested from 112 + * up to the highest strength it supports. Both reseeding and prediction + * resistance are supported. + *

+ * Personalization string is supported through the + * {@link DrbgParameters.Instantiation} class and additional input is supported + * through the {@link DrbgParameters.NextBytes} and + * {@link DrbgParameters.Reseed} classes. + *

+ * If a DRBG is not instantiated with a {@link DrbgParameters.Instantiation} + * object explicitly, this implementation instantiates it with a default + * requested strength of 128 bits (112 bits for CTR_DRBG with 3KeyTDEA), + * no prediction resistance request, and no personalization string. + * These default instantiation parameters can also be customized with + * the {@code securerandom.drbg.config} security property. + *

+ * This implementation reads fresh entropy from the system default entropy + * source determined by the security property {@code securerandom.source}. + *

+ * Calling {@link SecureRandom#generateSeed(int)} will directly read + * from this system default entropy source. + *

+ * This implementation has passed all tests included in the 20151104 version of + * + * The DRBG Test Vectors. + * + * @since 9 + */ +public class DrbgParameters { + + private DrbgParameters() { + // This class should not be instantiated + } + + /** + * The reseedable and prediction resistance capabilities of a DRBG. + *

+ * When this object is passed to a {@code SecureRandom.getInstance()} call, + * it is the requested minimum capability. When it's returned from + * {@code SecureRandom.getParameters()}, it is the effective capability. + *

+ * Please note that while the {@code Instantiate_function} defined in + * NIST SP 800-90Ar1 only includes a {@code prediction_resistance_flag} + * parameter, the {@code Capability} type includes an extra value + * {@link #RESEED_ONLY} because reseeding is an optional function. + * If {@code NONE} is used in an {@code Instantiation} object in calling the + * {@code SecureRandom.getInstance} method, the returned DRBG instance + * is not guaranteed to support reseeding. If {@code RESEED_ONLY} or + * {@code PR_AND_RESEED} is used, the instance must support reseeding. + *

+ * The table below lists possible effective values if a certain + * capability is requested, i.e. + *

+     * Capability requested = ...;
+     * SecureRandom s = SecureRandom.getInstance("DRBG",
+     *         DrbgParameters(-1, requested, null));
+     * Capability effective = ((DrbgParametes.Initiate) s.getParameters())
+     *         .getCapability();
+ *
+ * + * + * + * + * + * + * + * + *
Requested ValuePossible Effective Values
NONENONE, RESEED_ONLY, PR_AND_RESEED
RESEED_ONLYRESEED_ONLY, PR_AND_RESEED
PR_AND_RESEEDPR_AND_RESEED
+ *

+ * A DRBG implementation supporting prediction resistance must also + * support reseeding. + * + * @since 9 + */ + public enum Capability { + + /** + * Both prediction resistance and reseed. + */ + PR_AND_RESEED, + + /** + * Reseed but no prediction resistance. + */ + RESEED_ONLY, + + /** + * Neither prediction resistance nor reseed. + */ + NONE; + + @Override + public String toString() { + return name().toLowerCase(Locale.ROOT); + } + + /** + * Returns whether this capability supports reseeding. + * + * @return {@code true} for {@link #PR_AND_RESEED} and + * {@link #RESEED_ONLY}, and {@code false} for {@link #NONE} + */ + public boolean supportsReseeding() { + return this != NONE; + } + + /** + * Returns whether this capability supports prediction resistance. + * + * @return {@code true} for {@link #PR_AND_RESEED}, and {@code false} + * for {@link #RESEED_ONLY} and {@link #NONE} + */ + public boolean supportsPredictionResistance() { + return this == PR_AND_RESEED; + } + } + + /** + * DRBG parameters for instantiation. + *

+ * When used in + * {@link SecureRandom#getInstance(String, SecureRandomParameters)} + * or one of the other similar {@code getInstance} calls that take a + * {@code SecureRandomParameters} parameter, it means the + * requested instantiate parameters the newly created {@code SecureRandom} + * object must minimally support. When used as the return value of the + * {@link SecureRandom#getParameters()} method, it means the effective + * instantiate parameters of the {@code SecureRandom} object. + * + * @since 9 + */ + public static final class Instantiation + implements SecureRandomParameters { + + private final int strength; + private final Capability capability; + private final byte[] personalizationString; + + /** + * Returns the security strength in bits. + * + * @return If used in {@code getInstance}, returns the minimum strength + * requested, or -1 if there is no specific request on the strength. + * If used in {@code getParameters}, returns the effective strength. + * The effective strength must be greater than or equal to the minimum + * strength requested. + */ + public int getStrength() { + return strength; + } + + /** + * Returns the capability. + * + * @return If used in {@code getInstance}, returns the minimum + * capability requested. If used in {@code getParameters}, returns + * information on the effective prediction resistance flag and + * whether it supports reseeding. + */ + public Capability getCapability() { + return capability; + } + + /** + * Returns the personalization string as a byte array. + * + * @return If used in {@code getInstance}, returns the requested + * personalization string as a newly allocated array, or {@code null} + * if no personalization string is requested. The same string should + * be returned in {@code getParameters} as a new copy, or {@code null} + * if no personalization string is requested in {@code getInstance}. + */ + public byte[] getPersonalizationString() { + return (personalizationString == null) ? + null : personalizationString.clone(); + } + + private Instantiation(int strength, Capability capability, + byte[] personalizationString) { + this.strength = strength; + this.capability = capability; + this.personalizationString = (personalizationString == null) ? + null : personalizationString.clone(); + } + + /** + * Returns a Human-readable string representation of this + * {@code Instantiation}. + * + * @return the string representation + */ + @Override + public String toString() { + // I don't care what personalizationString looks like + return strength + "," + capability + "," + personalizationString; + } + } + + /** + * DRBG parameters for random bits generation. It is used in + * {@link SecureRandom#nextBytes(byte[], SecureRandomParameters)}. + * + * @since 9 + */ + public static final class NextBytes + implements SecureRandomParameters { + private final int strength; + private final boolean predictionResistance; + private final byte[] additionalInput; + + /** + * Returns the security strength requested in bits. + * + * @return the strength requested, or -1 if the effective strength + * should be used. + */ + public int getStrength() { + return strength; + } + + /** + * Returns whether prediction resistance is requested. + * + * @return whether prediction resistance is requested + */ + public boolean getPredictionResistance() { + return predictionResistance; + } + + /** + * Returns the requested additional input. + * + * @return the requested additional input, {@code null} if not + * requested. A new byte array is returned each time this method + * is called. + */ + public byte[] getAdditionalInput() { + return additionalInput == null? null: additionalInput.clone(); + } + + private NextBytes(int strength, boolean predictionResistance, + byte[] additionalInput) { + this.strength = strength; + this.predictionResistance = predictionResistance; + this.additionalInput = (additionalInput == null) ? + null : additionalInput.clone(); + } + } + + /** + * DRBG parameters for reseed. It is used in + * {@link SecureRandom#reseed(SecureRandomParameters)}. + * + * @since 9 + */ + public static final class Reseed implements SecureRandomParameters { + + private final byte[] additionalInput; + private final boolean predictionResistance; + + /** + * Returns whether prediction resistance is requested. + * + * @return whether prediction resistance is requested + */ + public boolean getPredictionResistance() { + return predictionResistance; + } + + /** + * Returns the requested additional input. + * + * @return the requested additional input, or {@code null} if + * not requested. A new byte array is returned each time this method + * is called. + */ + public byte[] getAdditionalInput() { + return additionalInput == null ? null : additionalInput.clone(); + } + + private Reseed(boolean predictionResistance, byte[] additionalInput) { + this.predictionResistance = predictionResistance; + this.additionalInput = (additionalInput == null) ? + null : additionalInput.clone(); + } + } + + /** + * Generates a {@link DrbgParameters.Instantiation} object. + * + * @param strength security strength in bits, -1 for default strength + * if used in {@code getInstance}. + * @param capability capability + * @param personalizationString personalization string as a byte array, + * can be {@code null}. The content of this + * byte array will be copied. + * @return a new {@code Instantiation} object + * @throws NullPointerException if {@code capability} is {@code null} + */ + public static Instantiation instantiation(int strength, + Capability capability, + byte[] personalizationString) { + return new Instantiation(strength, Objects.requireNonNull(capability), + personalizationString); + } + + /** + * Generates a {@link NextBytes} object. + * + * @param strength requested security strength in bits. If set to -1, the + * effective strength will be used. + * @param predictionResistance prediction resistance requested + * @param additionalInput additional input, can be {@code null}. + * The content of this byte array will be copied. + * @return a new {@code NextBytes} object + */ + public static NextBytes nextBytes(int strength, + boolean predictionResistance, + byte[] additionalInput) { + return new NextBytes(strength, predictionResistance, additionalInput); + } + + /** + * Generates a {@link Reseed} object. + * + * @param predictionResistance prediction resistance requested + * @param additionalInput additional input, can be {@code null}. + * The content of this byte array will be copied. + * @return a new {@code Reseed} object + */ + public static Reseed reseed( + boolean predictionResistance, byte[] additionalInput) { + return new Reseed(predictionResistance, additionalInput); + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/java/security/Provider.java --- a/jdk/src/java.base/share/classes/java/security/Provider.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/security/Provider.java Fri May 06 11:38:44 2016 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved + * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,8 +142,35 @@ Constructor con = clazz.getConstructor(); return con.newInstance(); } else { - Constructor con = clazz.getConstructor(ctrParamClz); - return con.newInstance(ctorParamObj); + // Looking for the constructor with a params first and fallback + // to one without if not found. This is to support the enhanced + // SecureRandom where both styles of constructors are supported. + // Before jdk9, there was no params support (only getInstance(alg)) + // and an impl only had the params-less constructor. Since jdk9, + // there is getInstance(alg,params) and an impl can contain + // an Impl(params) constructor. + try { + Constructor con = clazz.getConstructor(ctrParamClz); + return con.newInstance(ctorParamObj); + } catch (NoSuchMethodException nsme) { + // For pre-jdk9 SecureRandom implementations, they only + // have params-less constructors which still works when + // the input ctorParamObj is null. + // + // For other primitives using params, ctorParamObj should not + // be null and nsme is thrown, just like before. + if (ctorParamObj == null) { + try { + Constructor con = clazz.getConstructor(); + return con.newInstance(); + } catch (NoSuchMethodException nsme2) { + nsme.addSuppressed(nsme2); + throw nsme; + } + } else { + throw nsme; + } + } } } @@ -1384,7 +1411,8 @@ addEngine("KeyPairGenerator", false, null); addEngine("KeyStore", false, null); addEngine("MessageDigest", false, null); - addEngine("SecureRandom", false, null); + addEngine("SecureRandom", false, + "java.security.SecureRandomParameters"); addEngine("Signature", true, null); addEngine("CertificateFactory", false, null); addEngine("CertPathBuilder", false, null); @@ -1678,6 +1706,7 @@ } } } + // constructorParameter can be null if not provided return newInstanceUtil(getImplClass(), ctrParamClz, constructorParameter); } catch (NoSuchAlgorithmException e) { throw e; diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/java/security/SecureRandom.java --- a/jdk/src/java.base/share/classes/java/security/SecureRandom.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/security/SecureRandom.java Fri May 06 11:38:44 2016 +0800 @@ -38,52 +38,87 @@ * This class provides a cryptographically strong random number * generator (RNG). * - *

A cryptographically strong random number - * minimally complies with the statistical random number generator tests - * specified in + *

A cryptographically strong random number minimally complies with the + * statistical random number generator tests specified in * * FIPS 140-2, Security Requirements for Cryptographic Modules, * section 4.9.1. - * Additionally, SecureRandom must produce non-deterministic output. - * Therefore any seed material passed to a SecureRandom object must be - * unpredictable, and all SecureRandom output sequences must be + * Additionally, {@code SecureRandom} must produce non-deterministic output. + * Therefore any seed material passed to a {@code SecureRandom} object must be + * unpredictable, and all {@code SecureRandom} output sequences must be * cryptographically strong, as described in * * RFC 4086: Randomness Requirements for Security. * - *

A caller obtains a SecureRandom instance via the - * no-argument constructor or one of the {@code getInstance} methods: - * - *

- *      SecureRandom random = new SecureRandom();
- * 
- * - *

Many SecureRandom implementations are in the form of a pseudo-random - * number generator (PRNG), which means they use a deterministic algorithm - * to produce a pseudo-random sequence from a true random seed. + *

Many {@code SecureRandom} implementations are in the form of a + * pseudo-random number generator (PRNG, also known as deterministic random + * bits generator or DRBG), which means they use a deterministic algorithm + * to produce a pseudo-random sequence from a random seed. * Other implementations may produce true random numbers, * and yet others may use a combination of both techniques. * - *

Typical callers of SecureRandom invoke the following methods + *

A caller obtains a {@code SecureRandom} instance via the + * no-argument constructor or one of the {@code getInstance} methods. + * For example: + * + *

+ * SecureRandom r1 = new SecureRandom();
+ * SecureRandom r2 = SecureRandom.getInstance("NativePRNG");
+ * SecureRandom r3 = SecureRandom("DRBG",
+ *         DrbgParameters.Instantiation(128, RESEED_ONLY, null));
+ *
+ * + *

The third statement above returns a {@code SecureRandom} object of the + * specific algorithm supporting the specific instantiate parameters. The + * implementation's effective instantiated parameters must match this minimum + * request but is not necessarily the same. For example, even if the request + * does not require a certain feature, the actual instantiation can provide + * the feature. An implementation may lazily instantiate a {@code SecureRandom} + * until it's actually used, but the effective instantiate parameters must be + * determined right after it's created and {@link #getParameters()} should + * always return the same result unchanged. + * + *

Typical callers of {@code SecureRandom} invoke the following methods * to retrieve random bytes: * - *

- *      SecureRandom random = new SecureRandom();
- *      byte[] bytes = new byte[20];
- *      random.nextBytes(bytes);
- * 
+ *
+ * SecureRandom random = new SecureRandom();
+ * byte[] bytes = new byte[20];
+ * random.nextBytes(bytes);
+ *
* - *

Callers may also invoke the {@code generateSeed} method + *

Callers may also invoke the {@link #generateSeed} method * to generate a given number of seed bytes (to seed other random number * generators, for example): - *

- *      byte[] seed = random.generateSeed(20);
- * 
+ * + *
+ * byte[] seed = random.generateSeed(20);
+ *
+ * + *

A newly created PRNG {@code SecureRandom} object is not seeded (except + * if it is created by {@link #SecureRandom(byte[])}). The first call to + * {@code nextBytes} will force it to seed itself from an implementation- + * specific entropy source. This self-seeding will not occur if {@code setSeed} + * was previously called. * - * Note: Depending on the implementation, the {@code generateSeed} and - * {@code nextBytes} methods may block as entropy is being gathered, - * for example, if they need to read from /dev/random on various Unix-like - * operating systems. + *

A {@code SecureRandom} can be reseeded at any time by calling the + * {@code reseed} or {@code setSeed} method. The {@code reseed} method + * reads entropy input from its entropy source to reseed itself. + * The {@code setSeed} method requires the caller to provide the seed. + * + *

Please note that {@code reseed} may not be supported by all + * {@code SecureRandom} implementations. + * + *

Some {@code SecureRandom} implementations may accept a + * {@link SecureRandomParameters} parameter in its + * {@link #nextBytes(byte[], SecureRandomParameters)} and + * {@link #reseed(SecureRandomParameters)} methods to further + * control the behavior of the methods. + * + *

Note: Depending on the implementation, the {@code generateSeed}, + * {@code reseed} and {@code nextBytes} methods may block as entropy is being + * gathered, for example, if the entropy source is /dev/random on various + * Unix-like operating systems. * * @see java.security.SecureRandomSpi * @see java.util.Random @@ -132,26 +167,19 @@ * *

This constructor traverses the list of registered security Providers, * starting with the most preferred Provider. - * A new SecureRandom object encapsulating the - * SecureRandomSpi implementation from the first - * Provider that supports a SecureRandom (RNG) algorithm is returned. + * A new {@code SecureRandom} object encapsulating the + * {@code SecureRandomSpi} implementation from the first + * Provider that supports a {@code SecureRandom} (RNG) algorithm is returned. * If none of the Providers support a RNG algorithm, * then an implementation-specific default is returned. * *

Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * - *

See the SecureRandom section in the See the {@code SecureRandom} section in the * Java Cryptography Architecture Standard Algorithm Name Documentation * for information about standard RNG algorithm names. - * - *

The returned SecureRandom object has not been seeded. To seed the - * returned object, call the {@code setSeed} method. - * If {@code setSeed} is not called, the first call to - * {@code nextBytes} will force the SecureRandom object to seed itself. - * This self-seeding will not occur if {@code setSeed} was - * previously called. */ public SecureRandom() { /* @@ -166,20 +194,20 @@ /** * Constructs a secure random number generator (RNG) implementing the * default random number algorithm. - * The SecureRandom instance is seeded with the specified seed bytes. + * The {@code SecureRandom} instance is seeded with the specified seed bytes. * *

This constructor traverses the list of registered security Providers, * starting with the most preferred Provider. - * A new SecureRandom object encapsulating the - * SecureRandomSpi implementation from the first - * Provider that supports a SecureRandom (RNG) algorithm is returned. + * A new {@code SecureRandom} object encapsulating the + * {@code SecureRandomSpi} implementation from the first + * Provider that supports a {@code SecureRandom} (RNG) algorithm is returned. * If none of the Providers support a RNG algorithm, * then an implementation-specific default is returned. * *

Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * - *

See the SecureRandom section in the See the {@code SecureRandom} section in the * Java Cryptography Architecture Standard Algorithm Name Documentation * for information about standard RNG algorithm names. @@ -225,9 +253,9 @@ } /** - * Creates a SecureRandom object. + * Creates a {@code SecureRandom} object. * - * @param secureRandomSpi the SecureRandom implementation. + * @param secureRandomSpi the {@code SecureRandom} implementation. * @param provider the provider. */ protected SecureRandom(SecureRandomSpi secureRandomSpi, @@ -249,25 +277,18 @@ } /** - * Returns a SecureRandom object that implements the specified + * Returns a {@code SecureRandom} object that implements the specified * Random Number Generator (RNG) algorithm. * *

This method traverses the list of registered security Providers, * starting with the most preferred Provider. - * A new SecureRandom object encapsulating the - * SecureRandomSpi implementation from the first + * A new {@code SecureRandom} object encapsulating the + * {@code SecureRandomSpi} implementation from the first * Provider that supports the specified algorithm is returned. * *

Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * - *

The returned SecureRandom object has not been seeded. To seed the - * returned object, call the {@code setSeed} method. - * If {@code setSeed} is not called, the first call to - * {@code nextBytes} will force the SecureRandom object to seed itself. - * This self-seeding will not occur if {@code setSeed} was - * previously called. - * * @implNote * The JDK Reference Implementation additionally uses the * {@code jdk.security.provider.preferred} @@ -277,15 +298,15 @@ * {@link Security#getProviders() Security.getProviders()}. * * @param algorithm the name of the RNG algorithm. - * See the SecureRandom section in the * Java Cryptography Architecture Standard Algorithm Name Documentation * for information about standard RNG algorithm names. * - * @return the new SecureRandom object. + * @return the new {@code SecureRandom} object. * * @exception NoSuchAlgorithmException if no Provider supports a - * SecureRandomSpi implementation for the + * {@code SecureRandomSpi} implementation for the * specified algorithm. * * @see Provider @@ -295,49 +316,42 @@ public static SecureRandom getInstance(String algorithm) throws NoSuchAlgorithmException { Instance instance = GetInstance.getInstance("SecureRandom", - SecureRandomSpi.class, algorithm); + SecureRandomSpi.class, algorithm); return new SecureRandom((SecureRandomSpi)instance.impl, - instance.provider, algorithm); + instance.provider, algorithm); } /** - * Returns a SecureRandom object that implements the specified + * Returns a {@code SecureRandom} object that implements the specified * Random Number Generator (RNG) algorithm. * - *

A new SecureRandom object encapsulating the - * SecureRandomSpi implementation from the specified provider + *

A new {@code SecureRandom} object encapsulating the + * {@code SecureRandomSpi} implementation from the specified provider * is returned. The specified provider must be registered * in the security provider list. * *

Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * - *

The returned SecureRandom object has not been seeded. To seed the - * returned object, call the {@code setSeed} method. - * If {@code setSeed} is not called, the first call to - * {@code nextBytes} will force the SecureRandom object to seed itself. - * This self-seeding will not occur if {@code setSeed} was - * previously called. - * * @param algorithm the name of the RNG algorithm. - * See the SecureRandom section in the * Java Cryptography Architecture Standard Algorithm Name Documentation * for information about standard RNG algorithm names. * * @param provider the name of the provider. * - * @return the new SecureRandom object. + * @return the new {@code SecureRandom} object. * - * @exception NoSuchAlgorithmException if a SecureRandomSpi - * implementation for the specified algorithm is not - * available from the specified provider. + * @throws NoSuchAlgorithmException if a {@code SecureRandomSpi} + * implementation for the specified algorithm is not + * available from the specified provider. * - * @exception NoSuchProviderException if the specified provider is not - * registered in the security provider list. + * @throws NoSuchProviderException if the specified provider is not + * registered in the security provider list. * - * @exception IllegalArgumentException if the provider name is null - * or empty. + * @throws IllegalArgumentException if the provider name is null + * or empty. * * @see Provider * @@ -352,36 +366,29 @@ } /** - * Returns a SecureRandom object that implements the specified + * Returns a {@code SecureRandom} object that implements the specified * Random Number Generator (RNG) algorithm. * - *

A new SecureRandom object encapsulating the - * SecureRandomSpi implementation from the specified Provider - * object is returned. Note that the specified Provider object + *

A new {@code SecureRandom} object encapsulating the + * {@code SecureRandomSpi} implementation from the specified {@code Provider} + * object is returned. Note that the specified {@code Provider} object * does not have to be registered in the provider list. * - *

The returned SecureRandom object has not been seeded. To seed the - * returned object, call the {@code setSeed} method. - * If {@code setSeed} is not called, the first call to - * {@code nextBytes} will force the SecureRandom object to seed itself. - * This self-seeding will not occur if {@code setSeed} was - * previously called. - * * @param algorithm the name of the RNG algorithm. - * See the SecureRandom section in the * Java Cryptography Architecture Standard Algorithm Name Documentation * for information about standard RNG algorithm names. * * @param provider the provider. * - * @return the new SecureRandom object. + * @return the new {@code SecureRandom} object. * - * @exception NoSuchAlgorithmException if a SecureRandomSpi - * implementation for the specified algorithm is not available - * from the specified Provider object. + * @throws NoSuchAlgorithmException if a {@code SecureRandomSpi} + * implementation for the specified algorithm is not available + * from the specified {@code Provider} object. * - * @exception IllegalArgumentException if the specified provider is null. + * @throws IllegalArgumentException if the specified provider is null. * * @see Provider * @@ -396,24 +403,178 @@ } /** - * Returns the SecureRandomSpi of this SecureRandom object. + * Returns a {@code SecureRandom} object that implements the specified + * Random Number Generator (RNG) algorithm and supports the specified + * {@code SecureRandomParameters} request. + * + *

This method traverses the list of registered security Providers, + * starting with the most preferred Provider. + * A new {@code SecureRandom} object encapsulating the + * {@code SecureRandomSpi} implementation from the first + * Provider that supports the specified algorithm and the specified + * {@code SecureRandomParameters} is returned. + * + *

Note that the list of registered providers may be retrieved via + * the {@link Security#getProviders() Security.getProviders()} method. + * + * @implNote + * The JDK Reference Implementation additionally uses the + * {@code jdk.security.provider.preferred} property to determine + * the preferred provider order for the specified algorithm. This + * may be different than the order of providers returned by + * {@link Security#getProviders() Security.getProviders()}. + * + * @param algorithm the name of the RNG algorithm. + * See the {@code SecureRandom} section in the + * Java Cryptography Architecture Standard Algorithm Name Documentation + * for information about standard RNG algorithm names. + * + * @param params the {@code SecureRandomParameters} + * the newly created {@code SecureRandom} object must support. + * + * @return the new {@code SecureRandom} object. + * + * @throws NoSuchAlgorithmException if no Provider supports a + * {@code SecureRandomSpi} implementation for the specified + * algorithm and parameters. + * + * @throws IllegalArgumentException if the specified params is null. + * + * @see Provider + * + * @since 9 + */ + public static SecureRandom getInstance( + String algorithm, SecureRandomParameters params) + throws NoSuchAlgorithmException { + if (params == null) { + throw new IllegalArgumentException("params cannot be null"); + } + Instance instance = GetInstance.getInstance("SecureRandom", + SecureRandomSpi.class, algorithm, params); + return new SecureRandom((SecureRandomSpi)instance.impl, + instance.provider, algorithm); + } + + /** + * Returns a {@code SecureRandom} object that implements the specified + * Random Number Generator (RNG) algorithm and supports the specified + * {@code SecureRandomParameters} request. + * + *

A new {@code SecureRandom} object encapsulating the + * {@code SecureRandomSpi} implementation from the specified provider + * is returned. The specified provider must be registered + * in the security provider list. + * + *

Note that the list of registered providers may be retrieved via + * the {@link Security#getProviders() Security.getProviders()} method. + * + * @param algorithm the name of the RNG algorithm. + * See the {@code SecureRandom} section in the + * Java Cryptography Architecture Standard Algorithm Name Documentation + * for information about standard RNG algorithm names. + * + * @param params the {@code SecureRandomParameters} + * the newly created {@code SecureRandom} object must support. + * + * @param provider the name of the provider. + * + * @return the new {@code SecureRandom} object. + * + * @throws NoSuchAlgorithmException if the specified provider does not + * support a {@code SecureRandomSpi} implementation for the + * specified algorithm and parameters. + * + * @throws NoSuchProviderException if the specified provider is not + * registered in the security provider list. + * + * @throws IllegalArgumentException if the provider name is null + * or empty, or params is null. + * + * @see Provider + * + * @since 9 + */ + public static SecureRandom getInstance(String algorithm, + SecureRandomParameters params, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { + if (params == null) { + throw new IllegalArgumentException("params cannot be null"); + } + Instance instance = GetInstance.getInstance("SecureRandom", + SecureRandomSpi.class, algorithm, params, provider); + return new SecureRandom((SecureRandomSpi)instance.impl, + instance.provider, algorithm); + } + + /** + * Returns a {@code SecureRandom} object that implements the specified + * Random Number Generator (RNG) algorithm and supports the specified + * {@code SecureRandomParameters} request. + * + *

A new {@code SecureRandom} object encapsulating the + * {@code SecureRandomSpi} implementation from the specified + * {@code Provider} object is returned. Note that the specified + * {@code Provider} object does not have to be registered in the + * provider list. + * + * @param algorithm the name of the RNG algorithm. + * See the {@code SecureRandom} section in the + * Java Cryptography Architecture Standard Algorithm Name Documentation + * for information about standard RNG algorithm names. + * + * @param params the {@code SecureRandomParameters} + * the newly created {@code SecureRandom} object must support. + * + * @param provider the provider. + * + * @return the new {@code SecureRandom} object. + * + * @throws NoSuchAlgorithmException if the specified provider does not + * support a {@code SecureRandomSpi} implementation for the + * specified algorithm and parameters. + * + * @throws IllegalArgumentException if the specified provider or params + * is null. + * + * @see Provider + * + * @since 9 + */ + public static SecureRandom getInstance(String algorithm, + SecureRandomParameters params, Provider provider) + throws NoSuchAlgorithmException { + if (params == null) { + throw new IllegalArgumentException("params cannot be null"); + } + Instance instance = GetInstance.getInstance("SecureRandom", + SecureRandomSpi.class, algorithm, params, provider); + return new SecureRandom((SecureRandomSpi)instance.impl, + instance.provider, algorithm); + } + + /** + * Returns the {@code SecureRandomSpi} of this {@code SecureRandom} object. */ SecureRandomSpi getSecureRandomSpi() { return secureRandomSpi; } /** - * Returns the provider of this SecureRandom object. + * Returns the provider of this {@code SecureRandom} object. * - * @return the provider of this SecureRandom object. + * @return the provider of this {@code SecureRandom} object. */ public final Provider getProvider() { return provider; } /** - * Returns the name of the algorithm implemented by this SecureRandom - * object. + * Returns the name of the algorithm implemented by this + * {@code SecureRandom} object. * * @return the name of the algorithm or {@code unknown} * if the algorithm name cannot be determined. @@ -424,9 +585,49 @@ } /** - * Reseeds this random object. The given seed supplements, rather than - * replaces, the existing seed. Thus, repeated calls are guaranteed - * never to reduce randomness. + * Returns a Human-readable string representation of this + * {@code SecureRandom}. + * + * @return the string representation + * + * @since 9 + */ + @Override + public String toString() { + return secureRandomSpi.toString(); + } + + /** + * Returns the effective {@link SecureRandomParameters} for this + * {@code SecureRandom} instance. + *

+ * The returned value can be different from the + * {@code SecureRandomParameters} object passed into a {@code getInstance} + * method, but it cannot change during the lifetime of this + * {@code SecureRandom} object. + *

+ * A caller can use the returned value to find out what features this + * {@code SecureRandom} supports. + * + * @return the effective {@link SecureRandomParameters} parameters, + * or {@code null} if no parameters were used. + * + * @since 9 + * @see SecureRandomSpi + */ + public SecureRandomParameters getParameters() { + return secureRandomSpi.engineGetParameters(); + } + + /** + * Reseeds this random object with the given seed. The seed supplements, + * rather than replaces, the existing seed. Thus, repeated calls are + * guaranteed never to reduce randomness. + *

+ * A PRNG {@code SecureRandom} will not seed itself automatically if + * {@code setSeed} is called before any {@code nextBytes} or {@code reseed} + * calls. The caller should make sure that the {@code seed} argument + * contains enough entropy for the security of this {@code SecureRandom}. * * @param seed the seed. * @@ -458,18 +659,13 @@ * yet been initialized at that point. */ if (seed != 0) { - secureRandomSpi.engineSetSeed(longToByteArray(seed)); + this.secureRandomSpi.engineSetSeed(longToByteArray(seed)); } } /** * Generates a user-specified number of random bytes. * - *

If a call to {@code setSeed} had not occurred previously, - * the first call to this method forces this SecureRandom object - * to seed itself. This self-seeding will not occur if - * {@code setSeed} was previously called. - * * @param bytes the array to be filled in with random bytes. */ @Override @@ -478,6 +674,28 @@ } /** + * Generates a user-specified number of random bytes with + * additional parameters. + * + * @param bytes the array to be filled in with random bytes + * @param params additional parameters + * @throws NullPointerException if {@code bytes} is null + * @throws UnsupportedOperationException if the underlying provider + * implementation has not overridden this method + * @throws IllegalArgumentException if {@code params} is {@code null}, + * illegal or unsupported by this {@code SecureRandom} + * + * @since 9 + */ + public synchronized void nextBytes( + byte[] bytes, SecureRandomParameters params) { + if (params == null) { + throw new IllegalArgumentException("params cannot be null"); + } + secureRandomSpi.engineNextBytes(Objects.requireNonNull(bytes), params); + } + + /** * Generates an integer containing the user-specified number of * pseudo-random bits (right justified, with leading zeros). This * method overrides a {@code java.util.Random} method, and serves @@ -512,7 +730,7 @@ * *

This method is only included for backwards compatibility. * The caller is encouraged to use one of the alternative - * {@code getInstance} methods to obtain a SecureRandom object, and + * {@code getInstance} methods to obtain a {@code SecureRandom} object, and * then call the {@code generateSeed} method to obtain seed bytes * from that object. * @@ -537,10 +755,13 @@ * call may be used to seed other random number generators. * * @param numBytes the number of seed bytes to generate. - * + * @throws IllegalArgumentException if {@code numBytes} is negative * @return the seed bytes. */ public byte[] generateSeed(int numBytes) { + if (numBytes < 0) { + throw new IllegalArgumentException("numBytes cannot be negative"); + } return secureRandomSpi.engineGenerateSeed(numBytes); } @@ -562,8 +783,8 @@ /** * Gets a default PRNG algorithm by looking through all registered * providers. Returns the first PRNG algorithm of the first provider that - * has registered a SecureRandom implementation, or null if none of the - * registered providers supplies a SecureRandom implementation. + * has registered a {@code SecureRandom} implementation, or null if none of + * the registered providers supplies a {@code SecureRandom} implementation. */ private static String getPrngAlgorithm() { for (Provider p : Providers.getProviderList().providers()) { @@ -667,6 +888,42 @@ "No strong SecureRandom impls available: " + property); } + /** + * Reseeds this {@code SecureRandom} with entropy input read from its + * entropy source. + * + * @throws UnsupportedOperationException if the underlying provider + * implementation has not overridden this method. + * + * @since 9 + */ + public synchronized void reseed() { + secureRandomSpi.engineReseed(null); + } + + /** + * Reseeds this {@code SecureRandom} with entropy input read from its + * entropy source with additional parameters. + *

+ * Note that entropy is obtained from an entropy source. While + * some data in {@code params} may contain entropy, its main usage is to + * provide diversity. + * + * @param params extra parameters + * @throws UnsupportedOperationException if the underlying provider + * implementation has not overridden this method. + * @throws IllegalArgumentException if {@code params} is {@code null}, + * illegal or unsupported by this {@code SecureRandom} + * + * @since 9 + */ + public synchronized void reseed(SecureRandomParameters params) { + if (params == null) { + throw new IllegalArgumentException("params cannot be null"); + } + secureRandomSpi.engineReseed(params); + } + // Declare serialVersionUID to be compatible with JDK1.1 static final long serialVersionUID = 4940670005562187L; @@ -685,7 +942,7 @@ * We know that the MessageDigest class does not implement * java.io.Serializable. However, since this field is no longer * used, it will always be NULL and won't affect the serialization - * of the SecureRandom class itself. + * of the {@code SecureRandom} class itself. */ private byte[] randomBytes; /** diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/java/security/SecureRandomParameters.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/security/SecureRandomParameters.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.security; + +/** + * A marker interface for parameters used in various {@code SecureRandom} + * methods. + *

+ * Some {@code SecureRandom} implementations might require additional + * operational parameters. Objects of classes which implement this interface + * can be passed to those implementations that support them. + * + * @see DrbgParameters + */ +public interface SecureRandomParameters { +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/java/security/SecureRandomSpi.java --- a/jdk/src/java.base/share/classes/java/security/SecureRandomSpi.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/security/SecureRandomSpi.java Fri May 06 11:38:44 2016 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,38 @@ /** * This class defines the Service Provider Interface (SPI) - * for the {@code SecureRandom} class. + * for the {@link SecureRandom} class. + *

* All the abstract methods in this class must be implemented by each * service provider who wishes to supply the implementation * of a cryptographically strong pseudo-random number generator. * + * @implSpec + * If the {@link #SecureRandomSpi(SecureRandomParameters)} + * constructor is overridden in an implementation, it will always be called + * whenever a {@code SecureRandom} is instantiated. Precisely, if an object is + * instantiated with one of {@code SecureRandom}'s {@code getInstance} methods + * without a {@link SecureRandomParameters} parameter, + * the constructor will be called with a {@code null} argument and the + * implementation is responsible for creating its own + * {@code SecureRandomParameters} parameter for use when + * {@link #engineGetParameters()} is called. If an object + * is instantiated with one of {@code SecureRandom}'s {@code getInstance} + * methods with a {@code SecureRandomParameters} argument, + * the constructor will be called with that argument. The + * {@link #engineGetParameters()} method must not return {@code null}. + *

+ * Otherwise, if the {@code SecureRandomSpi(SecureRandomParameters)} + * constructor is not overridden in an implementation, the + * {@link #SecureRandomSpi()} constructor must be overridden and it will be + * called if an object is instantiated with one of {@code SecureRandom}'s + * {@code getInstance} methods without a + * {@code SecureRandomParameters} argument. Calling one of + * {@code SecureRandom}'s {@code getInstance} methods with + * a {@code SecureRandomParameters} argument will never + * return an instance of this implementation. The + * {@link #engineGetParameters()} method must return {@code null}. * - * @see SecureRandom * @since 1.2 */ @@ -42,9 +67,30 @@ private static final long serialVersionUID = -2991854161009191830L; /** - * Reseeds this random object. The given seed supplements, rather than - * replaces, the existing seed. Thus, repeated calls are guaranteed - * never to reduce randomness. + * Constructor without a parameter. + */ + public SecureRandomSpi() { + // ignored + } + + /** + * Constructor with a parameter. + * + * @param params the {@link SecureRandomParameters} object. + * This argument can be {@code null}. + * @throws IllegalArgumentException if {@code params} is + * unrecognizable or unsupported by this {@code SecureRandom} + * + * @since 9 + */ + protected SecureRandomSpi(SecureRandomParameters params) { + // ignored + } + + /** + * Reseeds this random object with the given seed. The seed supplements, + * rather than replaces, the existing seed. Thus, repeated calls + * are guaranteed never to reduce randomness. * * @param seed the seed. */ @@ -52,17 +98,45 @@ /** * Generates a user-specified number of random bytes. - * - *

If a call to {@code engineSetSeed} had not occurred previously, - * the first call to this method forces this SecureRandom implementation - * to seed itself. This self-seeding will not occur if - * {@code engineSetSeed} was previously called. + *

+ * Some random number generators can only generate a limited amount + * of random bytes per invocation. If the size of {@code bytes} + * is greater than this limit, the implementation should invoke + * its generation process multiple times to completely fill the + * buffer before returning from this method. * * @param bytes the array to be filled in with random bytes. */ protected abstract void engineNextBytes(byte[] bytes); /** + * Generates a user-specified number of random bytes with + * additional parameters. + *

+ * Some random number generators can only generate a limited amount + * of random bytes per invocation. If the size of {@code bytes} + * is greater than this limit, the implementation should invoke + * its generation process multiple times to completely fill the + * buffer before returning from this method. + * + * @implSpec The default implementation throws + * an {@link UnsupportedOperationException}. + * + * @param bytes the array to be filled in with random bytes + * @param params additional parameters + * @throws UnsupportedOperationException if the implementation + * has not overridden this method + * @throws IllegalArgumentException if {@code params} is {@code null}, + * illegal or unsupported by this {@code SecureRandom} + * + * @since 9 + */ + protected void engineNextBytes( + byte[] bytes, SecureRandomParameters params) { + throw new UnsupportedOperationException(); + } + + /** * Returns the given number of seed bytes. This call may be used to * seed other random number generators. * @@ -70,5 +144,58 @@ * * @return the seed bytes. */ - protected abstract byte[] engineGenerateSeed(int numBytes); + protected abstract byte[] engineGenerateSeed(int numBytes); + + /** + * Reseeds this random object with entropy input read from its + * entropy source with additional parameters. + *

+ * If this method is called by {@link SecureRandom#reseed()}, + * {@code params} will be {@code null}. + *

+ * Do not override this method if the implementation does not + * support reseeding. + * + * @implSpec The default implementation throws + * an {@link UnsupportedOperationException}. + * + * @param params extra parameters, can be {@code null}. + * @throws UnsupportedOperationException if the implementation + * has not overridden this method + * @throws IllegalArgumentException if {@code params} is + * illegal or unsupported by this {@code SecureRandom} + * + * @since 9 + */ + protected void engineReseed(SecureRandomParameters params) { + throw new UnsupportedOperationException(); + } + + /** + * Returns the effective {@link SecureRandomParameters} for this + * {@code SecureRandom} instance. + * + * @implSpec The default implementation returns {@code null}. + * + * @return the effective {@link SecureRandomParameters} parameters, + * or {@code null} if no parameters were used. + * + * @since 9 + */ + protected SecureRandomParameters engineGetParameters() { + return null; + } + + /** + * Returns a Human-readable string representation of this + * {@code SecureRandom}. + * + * @return the string representation + * + * @since 9 + */ + @Override + public String toString() { + return getClass().getSimpleName(); + } } diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/AbstractDrbg.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/provider/AbstractDrbg.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,771 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import sun.security.util.Debug; + +import java.security.*; +import java.util.Arrays; +import java.util.Objects; +import static java.security.DrbgParameters.Capability.*; + +/** + * The abstract base class for all DRBGs. + *

+ * This class creates 5 new abstract methods. 3 are defined by the SP800-90A: + *

    + *
  1. {@link #generateAlgorithm(byte[], byte[])} + *
  2. {@link #reseedAlgorithm(byte[], byte[])} (might not be supported) + *
  3. {@link #instantiateAlgorithm(byte[])} + *
+ * and 2 for implementation purpose: + *
    + *
  1. {@link #initEngine()} + *
  2. {@link #chooseAlgorithmAndStrength} + *
+ * All existing {@link SecureRandomSpi} methods are implemented based on the + * methods above as final. The initialization process is divided into 2 phases: + * configuration is eagerly called to set up parameters, and instantiation + * is lazily called only when nextBytes or reseed is called. + *

+ * Synchronized keyword should be added to all externally callable engine + * methods including {@link #engineReseed}, {@link #engineSetSeed}, and + * {@link #engineNextBytes} (but not {@link #engineGenerateSeed}). + * Internal methods like {@link #configure} and {@link #instantiateAlgorithm} + * are not synchronized. They will either be called in a constructor or + * in another synchronized method. + */ +public abstract class AbstractDrbg extends SecureRandomSpi { + + private static final long serialVersionUID = 9L; + + /** + * This field is not null if {@code -Djava.security.debug=securerandom} is + * specified on the command line. An implementation can print useful + * debug info. + */ + protected static final Debug debug = Debug.getInstance( + "securerandom", "drbg"); + + // Common working status + + private transient boolean instantiated = false; + + /** + * Reseed counter of a DRBG instance. A mechanism should increment it + * after each random bits generation and reset it in reseed. A mechanism + * does not need to compare it to {@link #reseedInterval}. + */ + protected transient int reseedCounter = 0; + + // Mech features. If not same as below, must be redefined in constructor. + + /** + * Default strength of a DRBG instance if it is not configured. + * 128 is considered secure enough now. A mechanism + * can change it in a constructor. + * + * Remember to sync with "securerandom.drbg.config" in java.security. + */ + protected static final int DEFAULT_STRENGTH = 128; + + /** + * Mechanism name, say, {@code HashDRBG}. Must be set in constructor. + * This value will be used in {@code toString}. + */ + protected String mechName = "DRBG"; + + /** + * highest_supported_security_strength of this mechanism for all algorithms + * it supports. A mechanism should update the value in its constructor + * if the value is not 256. + */ + protected int highestSupportedSecurityStrength = 256; + + /** + * Whether prediction resistance is supported. A mechanism should update + * the value in its constructor if it is not supported. + */ + protected boolean supportPredictionResistance = true; + + /** + * Whether reseed is supported. A mechanism should update + * the value in its constructor if it is not supported. + */ + protected boolean supportReseeding = true; + + // Strength features. If not same as below, must be redefined in + // chooseAlgorithmAndStrength. Among these, minLength and seedLen have no + // default value and must be redefined. If personalization string or + // additional input is not supported, set maxPersonalizationStringLength + // or maxAdditionalInputLength to -1. + + /** + * Minimum entropy input length in bytes for this DRBG instance. + * Must be assigned in {@link #chooseAlgorithmAndStrength}. + */ + protected int minLength; + + /** + * Maximum entropy input length in bytes for this DRBG instance. + * Should be assigned in {@link #chooseAlgorithmAndStrength} if it is not + * {@link Integer#MAX_VALUE}. + *

+ * In theory this value (and the values below) can be bigger than + * {@code Integer.MAX_VALUE} but a Java array can only have an int32 index. + */ + protected int maxLength = Integer.MAX_VALUE; + + /** + * Maximum personalization string length in bytes for this DRBG instance. + * Should be assigned in {@link #chooseAlgorithmAndStrength} if it is not + * {@link Integer#MAX_VALUE}. + */ + protected int maxPersonalizationStringLength = Integer.MAX_VALUE; + + /** + * Maximum additional input length in bytes for this DRBG instance. + * Should be assigned in {@link #chooseAlgorithmAndStrength} if it is not + * {@link Integer#MAX_VALUE}. + */ + protected int maxAdditionalInputLength = Integer.MAX_VALUE; + + /** + * max_number_of_bits_per_request in bytes for this DRBG instance. + * Should be assigned in {@link #chooseAlgorithmAndStrength} if it is not + * {@link Integer#MAX_VALUE}. + */ + protected int maxNumberOfBytesPerRequest = Integer.MAX_VALUE; + + /** + * Maximum number of requests between reseeds for this DRBG instance. + * Should be assigned in {@link #chooseAlgorithmAndStrength} if it is not + * {@link Integer#MAX_VALUE}. + */ + protected int reseedInterval = Integer.MAX_VALUE; + + + /** + * Algorithm used by this instance (SHA-512 or AES-256). Must be assigned + * in {@link #chooseAlgorithmAndStrength}. This field is used in + * {@link #toString()} and {@link DRBG#algorithmName}. + */ + protected String algorithm; + + // Configurable parameters + + /** + * Security strength for this instance. Must be assigned in + * {@link #chooseAlgorithmAndStrength}. Should be at least the requested + * strength. Might be smaller than the highest strength + * {@link #algorithm} supports. Must not be -1. + */ + protected int securityStrength; // in bits + + /** + * Strength requested in {@link DrbgParameters.Instantiation}. + * The real strength is based on it. Do not modify it in a mechanism. + */ + protected int requestedInstantiationSecurityStrength = -1; + + /** + * The personalization string used by this instance. Set inside + * {@link #configure(SecureRandomParameters)} and + * can be used in a mechanism. Do not modify it in a mechanism. + */ + protected byte[] personalizationString; + + /** + * The prediction resistance flag used by this instance. Set inside + * {@link #configure(SecureRandomParameters)}. + */ + private boolean predictionResistanceFlag; + + // Non-standard configurable parameters + + /** + * Whether a derivation function is used. Requested in + * {@link MoreDrbgParameters}. Only CtrDRBG uses it. + * Do not modify it in a mechanism. + */ + protected boolean usedf; + + /** + * The nonce for this instance. Set in {@link #instantiateIfNecessary}. + * After instantiation, this field is not null. Do not modify it + * in a mechanism. + */ + protected transient byte[] nonce; + + /** + * Requested nonce in {@link MoreDrbgParameters}. If set to null, + * nonce will be chosen by system, and a reinstantiated DRBG will get a + * new system-provided nonce. + */ + private byte[] requestedNonce; + + /** + * Requested algorithm in {@link MoreDrbgParameters}. + * Do not modify it in a mechanism. + */ + protected String requestedAlgorithm; + + /** + * The entropy source used by this instance. Set inside + * {@link #configure(SecureRandomParameters)}. This field + * can be null. {@link #getEntropyInput} will take care of null check. + */ + private transient EntropySource es; + + // Five abstract methods for SP 800-90A DRBG + + /** + * Decides what algorithm and strength to use (SHA-256 or AES-256, + * 128 or 256). Strength related fields must also be defined or redefined + * here. Called in {@link #configure}. A mechanism uses + * {@link #requestedAlgorithm}, + * {@link #requestedInstantiationSecurityStrength}, and + * {@link #DEFAULT_STRENGTH} to decide which algorithm and strength to use. + *

+ * If {@code requestedAlgorithm} is provided, it will always be used. + * If {@code requestedInstantiationSecurityStrength} is also provided, + * the algorithm will use the strength (an exception will be thrown if + * the strength is not supported), otherwise, the smaller one of + * the highest supported strength of the algorithm and the default strength + * will be used. + *

+ * If {@code requestedAlgorithm} is not provided, an algorithm will be + * chosen that supports {@code requestedInstantiationSecurityStrength} + * (or {@code DEFAULT_STRENGTH} if there is no request). + *

+ * Since every call to {@link #configure} will call this method, + * make sure to the calls do not contradict with each other. + *

+ * Here are some examples of the algorithm and strength chosen (suppose + * {@code DEFAULT_STRENGTH} is 128) for HashDRBG: + *

+     * requested             effective
+     * (SHA-1, -1)           (SHA-1,128)
+     * (SHA-1, 112)          (SHA-1,112)
+     * (SHA-1, 192)          IAE
+     * (SHA-256, -1)         (SHA-256,128)
+     * (SHA-256, 128)        (SHA-256,128)
+     * (SHA-3, -1)           IAE
+     * (null, -1)            (SHA-256,128)
+     * (null, 112)           (SHA-256,112)
+     * (null, 192)           (SHA-256,192)
+     * (null, 256)           (SHA-256,256)
+     * (null, 384)           IAE
+     * 
+ * + * @throws IllegalArgumentException if the requested parameters + * can not be supported or contradict with each other. + */ + protected abstract void chooseAlgorithmAndStrength(); + + /** + * Initiates security engines ({@code MessageDigest}, {@code Mac}, + * or {@code Cipher}). Must be called in deserialization. Please note + * that before instantiation the algorithm might not be available yet. + * In this case, just return and this method will be called + * automatically at instantiation. + */ + protected abstract void initEngine(); + + /** + * Instantiates a DRBG. Called automatically before the first + * {@code nextBytes} call. + *

+ * Note that the other parameters (nonce, strength, ps) are already + * stored inside at configuration. + * + * @param ei the entropy input, its length is already conditioned to be + * between {@link #minLength} and {@link #maxLength}. + */ + protected abstract void instantiateAlgorithm(byte[] ei); + + /** + * The generate function. + * + * @param result fill result here, not null + * @param additionalInput additional input, can be null. If not null, + * its length is smaller than {@link #maxAdditionalInputLength} + */ + protected abstract void generateAlgorithm( + byte[] result, byte[] additionalInput); + + /** + * The reseed function. + * + * @param ei the entropy input, its length is already conditioned to be + * between {@link #minLength} and {@link #maxLength}. + * @param additionalInput additional input, can be null. If not null, + * its length is smaller than {@link #maxAdditionalInputLength} + * @throws UnsupportedOperationException if reseed is not supported + */ + protected void reseedAlgorithm( + byte[] ei, byte[] additionalInput) { + throw new UnsupportedOperationException("No reseed function"); + } + + // SecureRandomSpi methods taken care of here. All final. + + @Override + protected final void engineNextBytes(byte[] result) { + engineNextBytes(result, DrbgParameters.nextBytes( + -1, predictionResistanceFlag, null)); + } + + @Override + protected final void engineNextBytes( + byte[] result, SecureRandomParameters params) { + + Objects.requireNonNull(result); + + if (debug != null) { + debug.println(this, "nextBytes"); + } + if (params instanceof DrbgParameters.NextBytes) { + + // 800-90Ar1 9.3: Generate Process. + + DrbgParameters.NextBytes dp = (DrbgParameters.NextBytes) params; + + // Step 2: max_number_of_bits_per_request + if (result.length > maxNumberOfBytesPerRequest) { + // generateAlgorithm should be called multiple times to fill + // up result. Unimplemented since maxNumberOfBytesPerRequest + // is now Integer.MAX_VALUE. + } + + // Step 3: check requested_security_strength + if (dp.getStrength() > securityStrength) { + throw new IllegalArgumentException("strength too high: " + + dp.getStrength()); + } + + // Step 4: check max_additional_input_length + byte[] ai = dp.getAdditionalInput(); + if (ai != null && ai.length > maxAdditionalInputLength) { + throw new IllegalArgumentException("ai too long: " + + ai.length); + } + + // Step 5: check prediction_resistance_flag + boolean pr = dp.getPredictionResistance(); + if (!predictionResistanceFlag && pr) { + throw new IllegalArgumentException("pr not available"); + } + + instantiateIfNecessary(null); + + // Step 7: Auto reseed + if (reseedCounter > reseedInterval || pr) { + reseedAlgorithm(getEntropyInput(pr), ai); + ai = null; + } + + // Step 8, 10: Generate_algorithm + // Step 9: Unnecessary. reseedCounter only updated after generation + generateAlgorithm(result, ai); + + // Step 11: Return + } else { + throw new IllegalArgumentException("unknown params type:" + + params.getClass()); + } + } + + @Override + public final void engineReseed(SecureRandomParameters params) { + if (debug != null) { + debug.println(this, "reseed with params"); + } + if (!supportReseeding) { + throw new UnsupportedOperationException("Reseed not supported"); + } + if (params == null) { + params = DrbgParameters.reseed(predictionResistanceFlag, null); + } + if (params instanceof DrbgParameters.Reseed) { + DrbgParameters.Reseed dp = (DrbgParameters.Reseed) params; + + // 800-90Ar1 9.2: Reseed Process. + + // Step 2: Check prediction_resistance_request + boolean pr = dp.getPredictionResistance(); + if (!predictionResistanceFlag && pr) { + throw new IllegalArgumentException("pr not available"); + } + + // Step 3: Check additional_input length + byte[] ai = dp.getAdditionalInput(); + if (ai != null && ai.length > maxAdditionalInputLength) { + throw new IllegalArgumentException("ai too long: " + + ai.length); + } + instantiateIfNecessary(null); + + // Step 4: Get_entropy_input + // Step 5: Check step 4 + // Step 6-7: Reseed_algorithm + reseedAlgorithm(getEntropyInput(pr), ai); + + // Step 8: Return + } else { + throw new IllegalArgumentException("unknown params type: " + + params.getClass()); + } + } + + /** + * Returns the given number of seed bytes. A DRBG always uses + * {@link SeedGenerator} to get an array with full-entropy. + *

+ * The implementation is identical to SHA1PRNG's + * {@link SecureRandom#engineGenerateSeed}. + * + * @param numBytes the number of seed bytes to generate. + * @return the seed bytes. + */ + @Override + public final byte[] engineGenerateSeed(int numBytes) { + byte[] b = new byte[numBytes]; + SeedGenerator.generateSeed(b); + return b; + } + + /** + * Reseeds this random object with the given seed. A DRBG always expands + * or truncates the input to be between {@link #minLength} and + * {@link #maxLength} and uses it to instantiate or reseed itself + * (depending on whether the DRBG is instantiated). + * + * @param input the seed + */ + @Override + public final synchronized void engineSetSeed(byte[] input) { + if (debug != null) { + debug.println(this, "setSeed"); + } + if (input.length < minLength) { + input = Arrays.copyOf(input, minLength); + } else if (input.length > maxLength) { + input = Arrays.copyOf(input, maxLength); + } + if (!instantiated) { + instantiateIfNecessary(input); + } else { + reseedAlgorithm(input, null); + } + } + + // get_entropy_input + + private byte[] getEntropyInput(boolean isPr) { + // Should the 1st arg be minEntropy or minLength? + // + // Technically it should be minEntropy, but CtrDRBG + // (not using derivation function) is so confusing + // (does it need only strength or seedlen of entropy?) + // that it's safer to assume minLength. In all other + // cases minLength equals to minEntropy. + return getEntropyInput(minLength, minLength, maxLength, isPr); + } + + private byte[] getEntropyInput(int minEntropy, int minLength, + int maxLength, boolean pr) { + if (debug != null) { + debug.println(this, "getEntropy(" + minEntropy + "," + minLength + + "," + maxLength + "," + pr + ")"); + } + EntropySource esNow = es; + if (esNow == null) { + esNow = pr ? SeederHolder.prseeder : SeederHolder.seeder; + } + return esNow.getEntropy(minEntropy, minLength, maxLength, pr); + } + + // Defaults + + /** + * The default {@code EntropySource} determined by system property + * "java.security.egd" or security property "securerandom.source". + *

+ * This object uses {@link SeedGenerator#generateSeed(byte[])} to + * return a byte array containing {@code minLength} bytes. It is + * assumed to support prediction resistance and always contains + * full-entropy. A trusted application can update this field. + */ + private final static EntropySource defaultES = + (minE, minLen, maxLen, pr) -> { + byte[] result = new byte[minLen]; + SeedGenerator.generateSeed(result); + return result; + }; + + private static class SeederHolder { + + /** + * Default EntropySource for SecureRandom with prediction resistance, + */ + static final EntropySource prseeder; + + /** + * Default EntropySource for SecureRandom without prediction resistance, + * which is backed by a DRBG whose EntropySource is {@link #prseeder}. + */ + static final EntropySource seeder; + + static { + prseeder = defaultES; + // According to SP800-90C section 7, a DRBG without live + // entropy (drbg here, with pr being false) can instantiate + // another DRBG with weaker strength. So we choose highest + // strength we support. + HashDrbg first = new HashDrbg(new MoreDrbgParameters( + prseeder, null, "SHA-256", null, false, + DrbgParameters.instantiation( + 256, NONE, + SeedGenerator.getSystemEntropy()))); + seeder = (entropy, minLen, maxLen, pr) -> { + if (pr) { + // This SEI does not support pr + throw new IllegalArgumentException("pr not supported"); + } + byte[] result = new byte[minLen]; + first.engineNextBytes(result); + return result; + }; + } + } + + // Constructor called by overridden methods, initializer... + + /** + * A constructor without argument so that an implementation does not + * need to always write {@code super(params)}. + */ + protected AbstractDrbg() { + // Nothing + } + + /** + * A mechanism shall override this constructor to setup {@link #mechName}, + * {@link #highestSupportedSecurityStrength}, + * {@link #supportPredictionResistance}, {@link #supportReseeding} + * or other features like {@link #DEFAULT_STRENGTH}. Finally it shall + * call {@link #configure} on {@code params}. + * + * @param params the {@link SecureRandomParameters} object. + * This argument can be {@code null}. + * @throws IllegalArgumentException if {@code params} is + * inappropriate for this SecureRandom. + */ + protected AbstractDrbg(SecureRandomParameters params) { + // Nothing + } + + /** + * Returns the current configuration as a {@link DrbgParameters.Instantiation} + * object. + * + * @return the curent configuration + */ + @Override + protected SecureRandomParameters engineGetParameters() { + // Or read from variable. + return DrbgParameters.instantiation( + securityStrength, + predictionResistanceFlag ? PR_AND_RESEED : + (supportReseeding ? RESEED_ONLY : NONE), + personalizationString); + } + + /** + * Configure this DRBG. This method calls + * {@link #chooseAlgorithmAndStrength()} and {@link #initEngine()} + * but does not do the actual instantiation. + * + * @param params configuration, if null, default configuration (default + * strength, pr_false, no personalization string) is used. + * @throws IllegalArgumentException if {@code params} is + * inappropriate for this SecureRandom. + */ + protected final synchronized void configure( + SecureRandomParameters params) { + if (debug != null) { + debug.println(this, "configure " + this + " with " + params); + } + if (params == null) { + params = DrbgParameters.instantiation(-1, RESEED_ONLY, null); + } + if (params instanceof MoreDrbgParameters) { + MoreDrbgParameters m = (MoreDrbgParameters)params; + this.requestedNonce = m.nonce; + this.es = m.es; + this.requestedAlgorithm = m.algorithm; + this.usedf = m.usedf; + params = m.config; + } + if (params != null) { + if (params instanceof DrbgParameters.Instantiation) { + DrbgParameters.Instantiation inst = + (DrbgParameters.Instantiation) params; + + // 800-90Ar1 9.1: Instantiate Process. Steps 1-5. + + // Step 1: Check requested_instantiation_security_strength + if (inst.getStrength() > highestSupportedSecurityStrength) { + throw new IllegalArgumentException("strength too big: " + + inst.getStrength()); + } + + // Step 2: Check prediction_resistance_flag + if (inst.getCapability().supportsPredictionResistance() + && !supportPredictionResistance) { + throw new IllegalArgumentException("pr not supported"); + } + + // Step 3: Check personalization_string + byte[] ps = inst.getPersonalizationString(); + if (ps != null && ps.length > maxPersonalizationStringLength) { + throw new IllegalArgumentException("ps too long: " + + ps.length); + } + + if (inst.getCapability().supportsReseeding() + && !supportReseeding) { + throw new IllegalArgumentException("reseed not supported"); + } + this.personalizationString = ps; + this.predictionResistanceFlag = + inst.getCapability().supportsPredictionResistance(); + this.requestedInstantiationSecurityStrength = inst.getStrength(); + } else { + throw new IllegalArgumentException("unknown params: " + + params.getClass()); + } + } + + // Step 4: Set security_strength + chooseAlgorithmAndStrength(); + instantiated = false; + + // Step 5: no-op. + + if (debug != null) { + debug.println(this, "configured " + this); + } + } + + /** + * Instantiate if necessary, + * + * @param entropy a user-provided entropy, the length is already good. + * If null, will fetch entropy input automatically. + */ + private synchronized void instantiateIfNecessary(byte[] entropy) { + if (!instantiated) { + + // 800-90Ar1 9.1: Instantiate Process. Steps 6-12. + + // Step 6: Get_entropy_input + // Step 7: check error (getEntropyInput throw no exception now) + if (entropy == null) { + entropy = getEntropyInput(predictionResistanceFlag); + } + + // Step 8. nonce + if (requestedNonce != null) { + nonce = requestedNonce; + } else { + nonce = NonceProvider.next(); + } + initEngine(); + + // Step 9-11: Instantiate_algorithm + instantiateAlgorithm(entropy); + instantiated = true; + + // Step 12: Return + } + } + + // Nonce provider + + private static class NonceProvider { + + // 128 bits of nonce can be used by 256-bit strength DRBG + private static final byte[] block = new byte[16]; + + private static synchronized byte[] next() { + int k = 15; + while ((k >= 0) && (++block[k] == 0)) { + k--; + } + return block.clone(); + } + } + + // Misc + + /** A handy method returning hexdump string with no colon or new line. + * + * @param in input byte array + * @return the hexdump string + */ + protected static String hex(byte[] in) { + StringBuilder sb = new StringBuilder(); + for (byte b : in) { + sb.append(String.format("%02x", b&0xff)); + } + return sb.toString(); + } + + /** + * Returns the smallest standard strength (112, 128, 192, 256) that is + * greater or equal to the input. + * + * @param input the input strength + * @return the standard strength + */ + protected static int getStandardStrength(int input) { + if (input <= 112) return 112; + if (input <= 128) return 128; + if (input <= 192) return 192; + if (input <= 256) return 256; + throw new IllegalArgumentException("input too big: " + input); + } + + @Override + public String toString() { + return mechName + "," + algorithm + + "," + securityStrength + "," + + (predictionResistanceFlag ? "pr_and_reseed" + : (supportReseeding ? "reseed_only" : "none")); + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/AbstractHashDrbg.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/provider/AbstractHashDrbg.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import sun.security.util.HexDumpEncoder; + +import java.util.Arrays; +import java.util.Locale; + +public abstract class AbstractHashDrbg extends AbstractDrbg { + + private static final long serialVersionUID = 9L; + + protected int outLen; + protected int seedLen; + + private static int alg2strength(String algorithm) { + switch (algorithm.toUpperCase(Locale.ROOT)) { + case "SHA-1": + return 128; + case "SHA-224": + case "SHA-512/224": + return 192; + case "SHA-256": + case "SHA-512/256": + case "SHA-384": + case "SHA-512": + return 256; + default: + throw new IllegalArgumentException(algorithm + + " not supported in Hash_DBRG"); + } + } + + protected void chooseAlgorithmAndStrength() { + if (requestedAlgorithm != null) { + algorithm = requestedAlgorithm.toUpperCase(Locale.ROOT); + int supportedStrength = alg2strength(algorithm); + if (requestedInstantiationSecurityStrength >= 0) { + int tryStrength = getStandardStrength( + requestedInstantiationSecurityStrength); + if (tryStrength > supportedStrength) { + throw new IllegalArgumentException(algorithm + + " does not support strength " + + requestedInstantiationSecurityStrength); + } + this.securityStrength = tryStrength; + } else { + this.securityStrength = DEFAULT_STRENGTH > supportedStrength ? + supportedStrength : DEFAULT_STRENGTH; + } + } else { + int tryStrength = (requestedInstantiationSecurityStrength < 0) ? + DEFAULT_STRENGTH : requestedInstantiationSecurityStrength; + tryStrength = getStandardStrength(tryStrength); + // The default algorithm which is enough for all strengths. + // Remember to sync with "securerandom.drbg.config" in java.security + algorithm = "SHA-256"; + this.securityStrength = tryStrength; + } + switch (algorithm.toUpperCase(Locale.ROOT)) { + case "SHA-1": + this.seedLen = 440 / 8; + this.outLen = 160 / 8; + break; + case "SHA-224": + case "SHA-512/224": + this.seedLen = 440 / 8; + this.outLen = 224 / 8; + break; + case "SHA-256": + case "SHA-512/256": + this.seedLen = 440 / 8; + this.outLen = 256 / 8; + break; + case "SHA-384": + this.seedLen = 888 / 8; + this.outLen = 384 / 8; + break; + case "SHA-512": + this.seedLen = 888 / 8; + this.outLen = 512 / 8; + break; + default: + throw new IllegalArgumentException(algorithm + + " not supported in Hash_DBRG"); + } + this.minLength = this.securityStrength / 8; + } + + @Override + public void instantiateAlgorithm(byte[] entropy) { + if (debug != null) { + debug.println(this, "instantiate"); + } + + // 800-90Ar1 10.1.1.2: Hash_DRBG Instantiate Process. + // 800-90Ar1 10.1.2.3: Hmac_DRBG Instantiate Process. + + // Step 1: entropy_input || nonce || personalization_string. + byte[] seed = Arrays.copyOf(entropy, entropy.length + nonce.length + + ((personalizationString == null) ? 0 + : personalizationString.length)); + System.arraycopy(nonce, 0, seed, entropy.length, nonce.length); + if (personalizationString != null) { + System.arraycopy(personalizationString, 0, + seed, entropy.length + nonce.length, + personalizationString.length); + } + hashReseedInternal(seed); + } + + @Override + protected void reseedAlgorithm( + byte[] ei, + byte[] additionalInput) { + if (debug != null) { + debug.println(this, "reseedAlgorithm\n" + + new HexDumpEncoder().encodeBuffer(ei) + "\n" + + ((additionalInput == null) ? "" : + new HexDumpEncoder().encodeBuffer(additionalInput))); + } + + // 800-90Ar1 10.1.1.3: Hash_DRBG Reseed Process. + // 800-90Ar1 10.1.2.4: Hmac_DRBG Reseed Process. + + // Step 1: entropy_input || additional_input. + if (additionalInput != null) { + ei = Arrays.copyOf(ei, ei.length + additionalInput.length); + System.arraycopy(additionalInput, 0, ei, + ei.length - additionalInput.length, additionalInput.length); + } + hashReseedInternal(ei); + } + + protected abstract void hashReseedInternal(byte[] seed); +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/CtrDrbg.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/provider/CtrDrbg.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.security.*; +import java.util.Arrays; +import java.util.Locale; + +public class CtrDrbg extends AbstractDrbg { + + private static final long serialVersionUID = 9L; + private static final int AES_LIMIT; + + static { + try { + AES_LIMIT = Cipher.getMaxAllowedKeyLength("AES"); + } catch (Exception e) { + // should not happen + throw new AssertionError("Cannot detect AES", e); + } + } + + private transient Cipher cipher; + + private String cipherAlg; + private String keyAlg; + + private int ctrLen; + private int blockLen; + private int keyLen; + private int seedLen; + + private transient byte[] v; + private transient byte[] k; + + public CtrDrbg(SecureRandomParameters params) { + mechName = "CTR_DRBG"; + configure(params); + } + + private static int alg2strength(String algorithm) { + switch (algorithm.toUpperCase(Locale.ROOT)) { + case "TDEA": + case "3KEYTDEA": + case "3 KEY TDEA": + case "DESEDE": + return 112; + case "AES-128": + return 128; + case "AES-192": + return 192; + case "AES-256": + return 256; + default: + throw new IllegalArgumentException(algorithm + + " not supported in CTR_DBRG"); + } + } + + @Override + protected void chooseAlgorithmAndStrength() { + if (requestedAlgorithm != null) { + algorithm = requestedAlgorithm.toUpperCase(); + int supportedStrength = alg2strength(algorithm); + if (requestedInstantiationSecurityStrength >= 0) { + int tryStrength = getStandardStrength( + requestedInstantiationSecurityStrength); + if (tryStrength > supportedStrength) { + throw new IllegalArgumentException(algorithm + + " does not support strength " + + requestedInstantiationSecurityStrength); + } + this.securityStrength = tryStrength; + } else { + this.securityStrength = (DEFAULT_STRENGTH > supportedStrength) ? + supportedStrength : DEFAULT_STRENGTH; + } + } else { + int tryStrength = (requestedInstantiationSecurityStrength < 0) ? + DEFAULT_STRENGTH : requestedInstantiationSecurityStrength; + tryStrength = getStandardStrength(tryStrength); + // Default algorithm, use AES-128 if AES-256 is not available. + // Remember to sync with "securerandom.drbg.config" in java.security + if (tryStrength <= 128 && AES_LIMIT < 256) { + algorithm = "AES-128"; + } else if (AES_LIMIT >= 256) { + algorithm = "AES-256"; + } else { + throw new IllegalArgumentException("unsupported strength " + + requestedInstantiationSecurityStrength); + } + this.securityStrength = tryStrength; + } + switch (algorithm.toUpperCase(Locale.ROOT)) { + case "TDEA": + case "3KEYTDEA": + case "3 KEY TDEA": + case "DESEDE": + algorithm = "DESede"; + this.keyAlg = "DESede"; + this.cipherAlg = "DESede/ECB/NoPadding"; + this.blockLen = 64 / 8; + this.keyLen = 168 / 8; + break; + case "AES-128": + case "AES-192": + case "AES-256": + this.keyAlg = "AES"; + this.cipherAlg = "AES/ECB/NoPadding"; + switch (algorithm) { + case "AES-128": + this.keyLen = 128 / 8; + break; + case "AES-192": + this.keyLen = 192 / 8; + if (AES_LIMIT < 192) { + throw new IllegalArgumentException(algorithm + + " not available (because policy) in CTR_DBRG"); + } + break; + case "AES-256": + this.keyLen = 256 / 8; + if (AES_LIMIT < 256) { + throw new IllegalArgumentException(algorithm + + " not available (because policy) in CTR_DBRG"); + } + break; + default: + throw new IllegalArgumentException(algorithm + + " not supported in CTR_DBRG"); + } + this.blockLen = 128 / 8; + break; + default: + throw new IllegalArgumentException(algorithm + + " not supported in CTR_DBRG"); + } + this.seedLen = this.blockLen + this.keyLen; + this.ctrLen = this.blockLen; // TODO + if (usedf) { + this.minLength = this.securityStrength / 8; + } else { + this.minLength = this.maxLength = + this.maxPersonalizationStringLength = + this.maxAdditionalInputLength = seedLen; + } + } + + /** + * This call, used by the constructors, instantiates the digest. + */ + @Override + protected void initEngine() { + try { + /* + * Use the local SUN implementation to avoid native + * performance overhead. + */ + cipher = Cipher.getInstance(cipherAlg, "SunJCE"); + } catch (NoSuchProviderException | NoSuchAlgorithmException + | NoSuchPaddingException e) { + // Fallback to any available. + try { + cipher = Cipher.getInstance(cipherAlg); + } catch (NoSuchAlgorithmException | NoSuchPaddingException exc) { + throw new InternalError( + "internal error: " + cipherAlg + " not available.", exc); + } + } + } + + private void status() { + if (debug != null) { + debug.println(this, "Key = " + hex(k)); + debug.println(this, "V = " + hex(v)); + debug.println(this, "reseed counter = " + reseedCounter); + } + } + + // 800-90Ar1 10.2.1.2. CTR_DRBG_Update + private void update(byte[] input) { + if (input.length != seedLen) { + // Should not happen + throw new IllegalArgumentException("input length not seedLen: " + + input.length); + } + try { + + int m = (seedLen + blockLen - 1) / blockLen; + byte[] temp = new byte[m * blockLen]; + + // Step 1. temp = Null. + + // Step 2. Loop + for (int i = 0; i < m; i++) { + // Step 2.1. Increment + addOne(v, ctrLen); + // Step 2.2. Block_Encrypt + cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); + // Step 2.3. Encrypt into right position, no need to cat + cipher.doFinal(v, 0, blockLen, temp, i * blockLen); + } + + // Step 3. Truncate + temp = Arrays.copyOf(temp, seedLen); + + // Step 4: Add + for (int i = 0; i < seedLen; i++) { + temp[i] ^= input[i]; + } + + // Step 5: leftmost + k = Arrays.copyOf(temp, keyLen); + + // Step 6: rightmost + v = Arrays.copyOfRange(temp, seedLen - blockLen, seedLen); + + // Step 7. Return + } catch (GeneralSecurityException e) { + throw new InternalError(e); + } + } + + @Override + protected void instantiateAlgorithm(byte[] ei) { + if (debug != null) { + debug.println(this, "instantiate"); + } + byte[] more; + if (usedf) { + // 800-90Ar1 10.2.1.3.2 Step 1-2. cat bytes + if (personalizationString == null) { + more = nonce; + } else { + more = Arrays.copyOf( + nonce, nonce.length + personalizationString.length); + System.arraycopy(personalizationString, 0, more, nonce.length, + personalizationString.length); + } + } else { + // 800-90Ar1 10.2.1.3.1 + // Step 1-2, no need to expand personalizationString, we only XOR + // with shorter length + more = personalizationString; + } + reseedAlgorithm(ei, more); + } + + private byte[] df(byte[] input) { + int l = input.length; + int n = seedLen; + int slen = 4 + 4 + l + 1; + byte[] s = new byte[(slen + blockLen - 1) / blockLen * blockLen]; + s[0] = (byte)(l >> 24); + s[1] = (byte)(l >> 16); + s[2] = (byte)(l >> 8); + s[3] = (byte)(l); + s[4] = (byte)(n >> 24); + s[5] = (byte)(n >> 16); + s[6] = (byte)(n >> 8); + s[7] = (byte)(n); + System.arraycopy(input, 0, s, 8, l); + s[8+l] = (byte)0x80; + + byte[] k = new byte[keyLen]; + for (int i = 0; i < k.length; i++) { + k[i] = (byte)i; + } + + byte[] temp = new byte[seedLen]; + + for (int i = 0; i * blockLen < temp.length; i++) { + byte[] iv = new byte[blockLen + s.length]; + iv[0] = (byte)(i >> 24); + iv[1] = (byte)(i >> 16); + iv[2] = (byte)(i >> 8); + iv[3] = (byte)(i); + System.arraycopy(s, 0, iv, blockLen, s.length); + int tailLen = temp.length - blockLen*i; + if (tailLen > blockLen) { + tailLen = blockLen; + } + System.arraycopy(bcc(k, iv), 0, temp, blockLen*i, tailLen); + } + + k = Arrays.copyOf(temp, keyLen); + byte[] x = Arrays.copyOfRange(temp, keyLen, temp.length); + + for (int i = 0; i * blockLen < seedLen; i++) { + try { + cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); + int tailLen = temp.length - blockLen*i; + if (tailLen > blockLen) { + tailLen = blockLen; + } + x = cipher.doFinal(x); + System.arraycopy(x, 0, temp, blockLen * i, tailLen); + } catch (GeneralSecurityException e) { + throw new InternalError(e); + } + } + return temp; + } + + private byte[] bcc(byte[] k, byte[] data) { + byte[] chain = new byte[blockLen]; + int n = data.length / blockLen; + for (int i = 0; i < n; i++) { + byte[] inputBlock = Arrays.copyOfRange( + data, i * blockLen, i * blockLen + blockLen); + for (int j = 0; j < blockLen; j++) { + inputBlock[j] ^= chain[j]; + } + try { + cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); + chain = cipher.doFinal(inputBlock); + } catch (GeneralSecurityException e) { + throw new InternalError(e); + } + } + return chain; + } + + @Override + protected void reseedAlgorithm( + byte[] ei, + byte[] additionalInput) { + if (usedf) { + // 800-90Ar1 10.2.1.3.2 Instantiate. + // 800-90Ar1 10.2.1.4.2 Reseed. + + // Step 1: cat bytes + if (additionalInput != null) { + byte[] temp = Arrays.copyOf( + ei, ei.length + additionalInput.length); + System.arraycopy(additionalInput, 0, temp, ei.length, + additionalInput.length); + ei = temp; + } + // Step 2. df (seed_material, seedlen). + ei = df(ei); + } else { + // 800-90Ar1 10.2.1.3.1 Instantiate + // 800-90Ar1 10.2.1.4.1 Reseed + // Step 1-2. Needless + // Step 3. seed_material = entropy_input XOR more + if (additionalInput != null) { + // additionalInput.length <= seedLen + for (int i = 0; i < additionalInput.length; i++) { + ei[i] ^= additionalInput[i]; + } + } + } + + if (v == null) { + // 800-90Ar1 10.2.1.3.2 Instantiate. Step 3-4 + // 800-90Ar1 10.2.1.3.1 Instantiate. Step 4-5 + k = new byte[keyLen]; + v = new byte[blockLen]; + } + //status(); + + // 800-90Ar1 10.2.1.3.1 Instantiate. Step 6 + // 800-90Ar1 10.2.1.3.2 Instantiate. Step 5 + // 800-90Ar1 10.2.1.4.1 Reseed. Step 4 + // 800-90Ar1 10.2.1.4.2 Reseed. Step 3 + update(ei); + // 800-90Ar1 10.2.1.3.1 Instantiate. Step 7 + // 800-90Ar1 10.2.1.3.2 Instantiate. Step 6 + // 800-90Ar1 10.2.1.4.1 Reseed. Step 5 + // 800-90Ar1 10.2.1.4.2 Reseed. Step 4 + reseedCounter = 1; + //status(); + + // Whatever step. Return + } + + /** + * Add one to data, only touch the last len bytes. + */ + private static void addOne(byte[] data, int len) { + for (int i = 0; i < len; i++) { + data[data.length - 1 - i]++; + if (data[data.length - 1 - i] != 0) { + break; + } + } + } + + @Override + public synchronized void generateAlgorithm( + byte[] result, byte[] additionalInput) { + + if (debug != null) { + debug.println(this, "generateAlgorithm"); + } + + // 800-90Ar1 10.2.1.5.1 Generate + // 800-90Ar1 10.2.1.5.2 Generate + + // Step 1: Check reseed_counter. Will not fail. Already checked in + // AbstractDrbg#engineNextBytes. + + if (additionalInput != null) { + if (usedf) { + // 10.2.1.5.2 Step 2.1 + additionalInput = df(additionalInput); + } else { + // 10.2.1.5.1 Step 2.1-2.2 + additionalInput = Arrays.copyOf(additionalInput, seedLen); + } + // 10.2.1.5.1 Step 2.3 + // 10.2.1.5.2 Step 2.2 + update(additionalInput); + } else { + // 10.2.1.5.1 Step 2 Else + // 10.2.1.5.2 Step 2 Else + additionalInput = new byte[seedLen]; + } + + // Step 3. temp = Null + int pos = 0; + + // Step 4. Loop + while (pos < result.length) { + int tailLen = result.length - pos; + // Step 4.1. Increment + addOne(v, ctrLen); + try { + // Step 4.2. Encrypt + cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k)); + byte[] out = cipher.doFinal(v); + + // Step 4.3 and 5. Cat bytes and leftmost + System.arraycopy(out, 0, result, pos, + (tailLen > blockLen) ? blockLen : tailLen); + } catch (GeneralSecurityException e) { + throw new InternalError(e); + } + pos += blockLen; + } + + // Step 6. Update + update(additionalInput); + + // Step 7. reseed_counter++ + reseedCounter++; + + //status(); + + // Step 8. Return + } + + private static void des7to8( + byte[] key56, int off56, byte[] key64, int off64) { + key64[off64 + 0] = (byte) + (key56[off56 + 0] & 0xFE); // << 0 + key64[off64 + 1] = (byte) + ((key56[off56 + 0] << 7) | ((key56[off56 + 1] & 0xFF) >>> 1)); + key64[off64 + 2] = (byte) + ((key56[off56 + 1] << 6) | ((key56[off56 + 2] & 0xFF) >>> 2)); + key64[off64 + 3] = (byte) + ((key56[off56 + 2] << 5) | ((key56[off56 + 3] & 0xFF) >>> 3)); + key64[off64 + 4] = (byte) + ((key56[off56 + 3] << 4) | ((key56[off56 + 4] & 0xFF) >>> 4)); + key64[off64 + 5] = (byte) + ((key56[off56 + 4] << 3) | ((key56[off56 + 5] & 0xFF) >>> 5)); + key64[off64 + 6] = (byte) + ((key56[off56 + 5] << 2) | ((key56[off56 + 6] & 0xFF) >>> 6)); + key64[off64 + 7] = (byte) + (key56[off56 + 6] << 1); + + for (int i = 0; i < 8; i++) { + // if even # bits, make uneven, XOR with 1 (uneven & 1) + // for uneven # bits, make even, XOR with 0 (even & 1) + key64[off64 + i] ^= Integer.bitCount(key64[off64 + i] ^ 1) & 1; + } + } + + private static SecretKey getKey(String keyAlg, byte[] k) { + if (keyAlg.equals("DESede")) { + byte[] k2 = new byte[24]; + des7to8(k, 0, k2, 0); + des7to8(k, 7, k2, 8); + des7to8(k, 14, k2, 16); + k = k2; + } + return new SecretKeySpec(k, keyAlg); + } + + private void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject (); + initEngine(); + } + + @Override + public String toString() { + return super.toString() + "/" + + (usedf ? "use_df" : "no_df"); + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/DRBG.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/provider/DRBG.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.security.AccessController; +import java.security.DrbgParameters; +import java.security.PrivilegedAction; +import java.security.SecureRandomParameters; +import java.security.SecureRandomSpi; +import java.security.Security; +import java.util.Locale; +import static java.security.DrbgParameters.Capability.*; + +/** + * Implement the "SecureRandom.DRBG" algorithm. + * + * About the default "securerandom.drbg.config" value: + * + * The default value in java.security is set to "". This is because + * the default values of different aspects are dependent (For example, + * strength depends on algorithm) and if we write a full string there + * it will be difficult to modify one and keep all others legal. + * + * When changing default values, touch all places including: + * + * 1. comments of the security property in java.security + * 2. Default mech, cap, usedf set in this class + * 3. Default algorithm set in final implementation of each mech + * 4. Default strength set in AbstractDrbg, but the effective + * value can be smaller if an algorithm does not support it. + * + * The default value is also mentioned in the @implNote part of + * {@link DrbgParameters} class. + */ +public final class DRBG extends SecureRandomSpi { + + private static final String PROP_NAME = "securerandom.drbg.config"; + + private static final long serialVersionUID = 9L; + + private final AbstractDrbg impl; + + private final String mechName; + + private final String algorithmName; + + public DRBG(SecureRandomParameters params) { + + // All parameters at unset status (null or -1). + + // Configurable with the "securerandom.drbg.config" security property + String mech = null; + Boolean usedf = null; + String algorithm = null; + + // Default instantiate parameters also configurable with + // "securerandom.drbg.config", and can be changed with params + // in getInstance("drbg", params) + int strength = -1; + DrbgParameters.Capability cap = null; + byte[] ps = null; + + // Not configurable with public interfaces, but is a part of + // MoreDrbgParameters + EntropySource es = null; + byte[] nonce = null; + + // Can be configured with a security property + + String config = AccessController.doPrivileged((PrivilegedAction) + () -> Security.getProperty(PROP_NAME)); + + if (config != null && !config.isEmpty()) { + for (String part : config.split(",")) { + part = part.trim(); + switch (part.toLowerCase(Locale.ROOT)) { + case "": + throw new IllegalArgumentException( + "aspect in " + PROP_NAME + " cannot be empty"); + case "pr_and_reseed": + checkTwice(cap != null, "capability"); + cap = PR_AND_RESEED; + break; + case "reseed_only": + checkTwice(cap != null, "capability"); + cap = RESEED_ONLY; + break; + case "none": + checkTwice(cap != null, "capability"); + cap = NONE; + break; + case "hash_drbg": + case "hmac_drbg": + case "ctr_drbg": + checkTwice(mech != null, "mechanism name"); + mech = part; + break; + case "no_df": + checkTwice(usedf != null, "usedf flag"); + usedf = false; + break; + case "use_df": + checkTwice(usedf != null, "usedf flag"); + usedf = true; + break; + default: + // For all other parts of the property, it is + // either an algorithm name or a strength + try { + int tmp = Integer.parseInt(part); + if (tmp < 0) { + throw new IllegalArgumentException( + "strength in " + PROP_NAME + + " cannot be negative: " + part); + } + checkTwice(strength >= 0, "strength"); + strength = tmp; + } catch (NumberFormatException e) { + checkTwice(algorithm != null, "algorithm name"); + algorithm = part; + } + } + } + } + + // Can be updated by params + + if (params != null) { + // MoreDrbgParameters is used for testing. + if (params instanceof MoreDrbgParameters) { + MoreDrbgParameters m = (MoreDrbgParameters)params; + params = m.config; + + // No need to check null for es and nonce, they are still null + es = m.es; + nonce = m.nonce; + + if (m.mech != null) { + mech = m.mech; + } + if (m.algorithm != null) { + algorithm = m.algorithm; + } + usedf = m.usedf; + } + if (params instanceof DrbgParameters.Instantiation) { + DrbgParameters.Instantiation dp = + (DrbgParameters.Instantiation) params; + + // ps is still null by now + ps = dp.getPersonalizationString(); + + int tmp = dp.getStrength(); + if (tmp != -1) { + strength = tmp; + } + cap = dp.getCapability(); + } else { + throw new IllegalArgumentException("Unsupported params: " + + params.getClass()); + } + } + + // Hardcoded defaults. + // Remember to sync with "securerandom.drbg.config" in java.security. + + if (cap == null) { + cap = NONE; + } + if (mech == null) { + mech = "Hash_DRBG"; + } + if (usedf == null) { + usedf = true; + } + + MoreDrbgParameters m = new MoreDrbgParameters( + es, mech, algorithm, nonce, usedf, + DrbgParameters.instantiation(strength, cap, ps)); + + switch (mech.toLowerCase(Locale.ROOT)) { + case "hash_drbg": + impl = new HashDrbg(m); + break; + case "hmac_drbg": + impl = new HmacDrbg(m); + break; + case "ctr_drbg": + impl = new CtrDrbg(m); + break; + default: + throw new IllegalArgumentException("Unsupported mech: " + mech); + } + + mechName = mech; + algorithmName = impl.algorithm; + } + + @Override + protected void engineSetSeed(byte[] seed) { + impl.engineSetSeed(seed); + } + + @Override + protected void engineNextBytes(byte[] bytes) { + impl.engineNextBytes(bytes); + } + + @Override + protected byte[] engineGenerateSeed(int numBytes) { + return impl.engineGenerateSeed(numBytes); + } + + @Override + protected void engineNextBytes( + byte[] bytes, SecureRandomParameters params) { + impl.engineNextBytes(bytes, params); + } + + @Override + protected void engineReseed(SecureRandomParameters params) { + impl.engineReseed(params); + } + + @Override + protected SecureRandomParameters engineGetParameters() { + return impl.engineGetParameters(); + } + + @Override + public String toString() { + return impl.toString(); + } + + /** + * Ensures an aspect is not set more than once. + * + * @param flag true if set more than once + * @param name the name of aspect shown in IAE + * @throws IllegalArgumentException if it happens + */ + private static void checkTwice(boolean flag, String name) { + if (flag) { + throw new IllegalArgumentException(name + + " cannot be provided more than once in " + PROP_NAME); + } + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/EntropySource.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/provider/EntropySource.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +/** + * An interface of a source of entropy input. + * + * @since 9 + */ +public interface EntropySource { + /** + * Returns a byte array containing entropy. + *

+ * This maps to the {@code Get_entropy_input} function defined in + * Section 9 of NIST SP 800-90Ar1. + * + * @param minEntropy minimum entropy required, in bytes + * @param minLength minimum length of output, in bytes + * @param maxLength maximum length of output, in bytes + * @param pr whether prediction resistance is required + * @return the byte array containing entropy + */ + byte[] getEntropy(int minEntropy, int minLength, int maxLength, boolean pr); +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/HashDrbg.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/provider/HashDrbg.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandomParameters; +import java.util.Arrays; + +public class HashDrbg extends AbstractHashDrbg { + + private static final long serialVersionUID = 9L; + + private static final byte[] ZERO = new byte[1]; + private static final byte[] ONE = new byte[]{1}; + + private transient MessageDigest digest; + + private transient byte[] v; + private transient byte[] c; + + public HashDrbg(SecureRandomParameters params) { + mechName = "Hash_DRBG"; + configure(params); + } + + /** + * This call, used by the constructors, instantiates the digest. + */ + @Override + protected void initEngine() { + try { + /* + * Use the local SUN implementation to avoid native + * performance overhead. + */ + digest = MessageDigest.getInstance(algorithm, "SUN"); + } catch (NoSuchProviderException | NoSuchAlgorithmException e) { + // Fallback to any available. + try { + digest = MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException exc) { + throw new InternalError( + "internal error: " + algorithm + " not available.", exc); + } + } + } + + private byte[] hashDf(int requested, byte[]... inputs) { + return hashDf(digest, outLen, requested, inputs); + } + + /** + * A hash-based derivation function defined in NIST SP 800-90Ar1 10.3.1. + * The function is used inside Hash_DRBG, and can also be used as an + * approved conditioning function as described in 800-90B 6.4.2.2. + * + * @param digest a {@code MessageDigest} object in reset state + * @param outLen {@link MessageDigest#getDigestLength} of {@code digest} + * @param requested requested output length, in bytes + * @param inputs input data + * @return the condensed/expanded output + */ + public static byte[] hashDf(MessageDigest digest, int outLen, + int requested, byte[]... inputs) { + int len = (requested + outLen - 1) / outLen; + byte[] temp = new byte[len * outLen]; + int counter = 1; + + for (int i=0; i> 21)); // requested*8 as int32 + digest.update((byte)(requested >> 13)); + digest.update((byte)(requested >> 5)); + digest.update((byte)(requested << 3)); + for (byte[] input : inputs) { + digest.update(input); + } + try { + digest.digest(temp, i * outLen, outLen); + } catch (DigestException e) { + throw new AssertionError("will not happen", e); + } + counter++; + } + return temp.length == requested? temp: Arrays.copyOf(temp, requested); + } + + // This method is used by both instantiation and reseeding. + @Override + protected final void hashReseedInternal(byte[] input) { + + // 800-90Ar1 10.1.1.2: Instantiate Process. + // 800-90Ar1 10.1.1.3: Reseed Process. + byte[] seed; + + // Step 2: seed = Hash_df (seed_material, seedlen). + if (v != null) { + // Step 1 of 10.1.1.3: Prepend 0x01 || V + seed = hashDf(seedLen, ONE, v, input); + } else { + seed = hashDf(seedLen, input); + } + + // Step 3. V = seed. + v = seed; + + // Step 4. C = Hash_df ((0x00 || V), seedlen). + c = hashDf(seedLen, ZERO, v); + + // Step 5. reseed_counter = 1. + reseedCounter = 1; + + //status(); + + // Step 6: Return + } + + private void status() { + if (debug != null) { + debug.println(this, "V = " + hex(v)); + debug.println(this, "C = " + hex(c)); + debug.println(this, "reseed counter = " + reseedCounter); + } + } + + /** + * Adds byte arrays into an existing one. + * + * @param out existing array + * @param data more arrays, can be of different length + */ + private static void addBytes(byte[] out, int len, byte[]... data) { + for (byte[] d: data) { + int dlen = d.length; + int carry = 0; + for (int i = 0; i < len; i++) { + int sum = (out[len - i - 1] & 0xff) + carry; + if (i < dlen) { + sum += (d[dlen - i - 1] & 0xff); + } + out[len - i - 1] = (byte) sum; + carry = sum >> 8; + if (i >= dlen - 1 && carry == 0) break; + } + } + } + + /** + * Generates a user-specified number of random bytes. + * + * @param result the array to be filled in with random bytes. + */ + @Override + public final synchronized void generateAlgorithm( + byte[] result, byte[] additionalInput) { + + if (debug != null) { + debug.println(this, "generateAlgorithm"); + } + + // 800-90Ar1 10.1.1.4: Hash_DRBG_Generate Process + + // Step 1: Check reseed_counter. Will not fail. Already checked in + // AbstractDrbg#engineNextBytes. + + // Step 2: additional_input + if (additionalInput != null) { + digest.update((byte)2); + digest.update(v); + digest.update(additionalInput); + addBytes(v, seedLen, digest.digest()); + } + + // Step 3. Hashgen (requested_number_of_bits, V). + hashGen(result, result.length, v); + + // Step 4. H = Hash (0x03 || V). + digest.update((byte)3); + digest.update(v); + byte[] h = digest.digest(); + + // Step 5. V = (V + H + C + reseed_counter) mod 2seedlen. + byte[] rcBytes; + if (reseedCounter < 256) { + rcBytes = new byte[]{(byte)reseedCounter}; + } else { + rcBytes = BigInteger.valueOf(reseedCounter).toByteArray(); + } + addBytes(v, seedLen, h, c, rcBytes); + + // Step 6. reseed_counter = reseed_counter + 1. + reseedCounter++; + + //status(); + + // Step 7: Return. + } + + // 800-90Ar1 10.1.1.4: Hashgen + private void hashGen(byte[] output, int len, byte[] v) { + + // Step 1. m + int m = (len + outLen - 1) / outLen; + + // Step 2. data = V + byte[] data = v; + + // Step 3: W is output not filled + + // Step 4: For i = 1 to m + for (int i = 0; i < m; i++) { + int tailLen = len - i * outLen; + if (tailLen < outLen) { + // Step 4.1 w = Hash (data). + // Step 4.2 W = W || w. + System.arraycopy(digest.digest(data), 0, output, i * outLen, + tailLen); + } else { + try { + // Step 4.1 w = Hash (data). + digest.update(data); + // Step 4.2 digest into right position, no need to cat + digest.digest(output, i*outLen, outLen); + } catch (DigestException e) { + throw new AssertionError("will not happen", e); + } + } + // Unless this is the last around, we will need to increment data. + // but we cannot change v, so a copy is made. + if (i != m - 1) { + if (data == v) { + data = Arrays.copyOf(v, v.length); + } + // Step 4.3 data = (data + 1) mod 2^seedlen. + addBytes(data, seedLen, ONE); + } + } + + // Step 5: No need to truncate + // Step 6: Return + } + + private void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject (); + initEngine(); + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/HmacDrbg.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/provider/HmacDrbg.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandomParameters; +import java.util.Arrays; + +public class HmacDrbg extends AbstractHashDrbg { + + private static final long serialVersionUID = 9L; + + private transient Mac mac; + + private String macAlg; + + private transient byte[] v; + private transient byte[] k; + + public HmacDrbg(SecureRandomParameters params) { + mechName = "HMAC_DRBG"; + configure(params); + } + + private void status() { + if (debug != null) { + debug.println(this, "V = " + hex(v)); + debug.println(this, "Key = " + hex(k)); + debug.println(this, "reseed counter = " + reseedCounter); + } + } + + // 800-90Ar1 10.1.2.2: HMAC_DRBG Update Process + private void update(byte[]... inputs) { + try { + // Step 1. K = HMAC (K, V || 0x00 || provided_data). + mac.init(new SecretKeySpec(k, macAlg)); + mac.update(v); + mac.update((byte) 0); + for (byte[] input: inputs) { + mac.update(input); + } + k = mac.doFinal(); + + // Step 2. V = HMAC (K, V). + mac.init(new SecretKeySpec(k, macAlg)); + v = mac.doFinal(v); + + if (inputs.length != 0) { + // Step 4. K = HMAC (K, V || 0x01 || provided_data). + mac.update(v); + mac.update((byte) 1); + for (byte[] input: inputs) { + mac.update(input); + } + k = mac.doFinal(); + + // Step 5. V=HMAC(K,V). + mac.init(new SecretKeySpec(k, macAlg)); + v = mac.doFinal(v); + } // else Step 3 + + // Step 6. Return + } catch (InvalidKeyException e) { + throw new InternalError(e); + } + } + + /** + * This call, used by the constructors, instantiates the digest. + */ + @Override + protected void initEngine() { + macAlg = "HmacSHA" + algorithm.substring(4); + try { + mac = Mac.getInstance(macAlg, "SunJCE"); + } catch (NoSuchProviderException | NoSuchAlgorithmException e) { + // Fallback to any available. + try { + mac = Mac.getInstance(macAlg); + } catch (NoSuchAlgorithmException exc) { + throw new InternalError( + "internal error: " + macAlg + " not available.", exc); + } + } + } + + // This method is used by both instantiation and reseeding. + @Override + protected final void hashReseedInternal(byte[] input) { + + // 800-90Ar1 10.1.2.3: Instantiate Process. + // 800-90Ar1 10.1.2.4: Reseed Process. + if (v == null) { + k = new byte[outLen]; + v = new byte[outLen]; + Arrays.fill(v, (byte) 1); + } + + // Step 2: HMAC_DRBG_Update + update(input); + + // Step 3: reseed_counter = 1. + reseedCounter = 1; + //status(); + + // Step 4: Return + } + + /** + * Generates a user-specified number of random bytes. + * + * @param result the array to be filled in with random bytes. + */ + @Override + public synchronized void generateAlgorithm( + byte[] result, byte[] additionalInput) { + + if (debug != null) { + debug.println(this, "generateAlgorithm"); + } + + // 800-90Ar1 10.1.2.5: HMAC_DRBG_Generate Process + + // Step 1: Check reseed_counter. Will not fail. Already checked in + // AbstractDrbg#engineNextBytes. + + // Step 2. HMAC_DRBG_Update + if (additionalInput != null) { + update(additionalInput); + } + + // Step 3. temp = Null. + int pos = 0; + + // Step 4. Loop + while (pos < result.length) { + int tailLen = result.length - pos; + + // Step 4.1 V = HMAC (Key, V). + try { + mac.init(new SecretKeySpec(k, macAlg)); + } catch (InvalidKeyException e) { + throw new InternalError(e); + } + v = mac.doFinal(v); + // Step 4.2 temp = temp || V. + System.arraycopy(v, 0, result, pos, + tailLen > outLen ? outLen : tailLen); + pos += outLen; + } + + // Step 5: No need to truncate + + // Step 6. HMAC_DRBG_Update (additional_input, Key, V). + if (additionalInput != null) { + update(additionalInput); + } else { + update(); + } + + // Step 7. reseed_counter = reseed_counter + 1. + reseedCounter++; + + //status(); + + // Step 8. Return + } + + private void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException { + s.defaultReadObject (); + initEngine(); + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/MoreDrbgParameters.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/provider/MoreDrbgParameters.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.security.DrbgParameters; +import java.security.SecureRandomParameters; + +/** + * Extra non-standard parameters that can be used by DRBGs. + */ +public class MoreDrbgParameters implements SecureRandomParameters { + + final String mech; + final String algorithm; + final EntropySource es; + final byte[] nonce; + final boolean usedf; + final DrbgParameters.Instantiation config; + + /** + * Creates a new {@code MoreDrbgParameters} object. + * + * @param es the {@link EntropySource} to use. If set to {@code null}, + * a default entropy source will be used. + * @param mech mech name. If set to {@code null}, the one in + * securerandom.drbg.config is used. This argument is ignored + * when passing to HashDrbg/HmacDrbg/CtrDrbg. + * @param algorithm the requested algorithm to use. If set to {@code null}, + * the algorithm will be decided by strength. + * @param nonce the nonce to use. If set to {@code null}, + * a nonce will be assigned. + * @param usedf whether a derivation function should be used + * @param config a {@link DrbgParameters.Instantiation} object + */ + public MoreDrbgParameters(EntropySource es, String mech, + String algorithm, byte[] nonce, boolean usedf, + DrbgParameters.Instantiation config) { + this.mech = mech; + this.algorithm = algorithm; + this.es = es; + this.nonce = nonce; + this.usedf = usedf; + this.config = config; + } + + @Override + public String toString() { + return mech + "," + algorithm + "," + usedf + "," + config; + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/SHA5.java --- a/jdk/src/java.base/share/classes/sun/security/provider/SHA5.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/SHA5.java Fri May 06 11:38:44 2016 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,7 @@ package sun.security.provider; -import java.security.*; import java.util.Objects; -import java.math.BigInteger; import jdk.internal.HotSpotIntrinsicCandidate; import static sun.security.provider.ByteArrayAccess.*; @@ -118,7 +116,14 @@ i2bBig4((int)bitsProcessed, buffer, 124); implCompress(buffer, 0); - l2bBig(state, 0, out, ofs, engineGetDigestLength()); + int len = engineGetDigestLength(); + if (len == 28) { + // Special case for SHA-512/224 + l2bBig(state, 0, out, ofs, 24); + i2bBig4((int)(state[3] >> 32), out, ofs + 24); + } else { + l2bBig(state, 0, out, ofs, len); + } } /** @@ -306,4 +311,31 @@ super("SHA-384", 48, INITIAL_HASHES); } } + public static final class SHA512_224 extends SHA5 { + + private static final long[] INITIAL_HASHES = { + 0x8C3D37C819544DA2L, 0x73E1996689DCD4D6L, + 0x1DFAB7AE32FF9C82L, 0x679DD514582F9FCFL, + 0x0F6D2B697BD44DA8L, 0x77E36F7304C48942L, + 0x3F9D85A86A1D36C8L, 0x1112E6AD91D692A1L + }; + + public SHA512_224() { + super("SHA-512/224", 28, INITIAL_HASHES); + } + } + + public static final class SHA512_256 extends SHA5 { + + private static final long[] INITIAL_HASHES = { + 0x22312194FC2BF72CL, 0x9F555FA3C84C64C2L, + 0x2393B86B6F53B151L, 0x963877195940EABDL, + 0x96283EE2A88EFFE3L, 0xBE5E1E2553863992L, + 0x2B0199FC2C85B8AAL, 0x0EB72DDC81C52CA2L + }; + + public SHA512_256() { + super("SHA-512/256", 32, INITIAL_HASHES); + } + } } diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/provider/SunEntries.java --- a/jdk/src/java.base/share/classes/sun/security/provider/SunEntries.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/provider/SunEntries.java Fri May 06 11:38:44 2016 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -97,6 +97,9 @@ map.put("SecureRandom.NativePRNG", "sun.security.provider.NativePRNG"); } + + map.put("SecureRandom.DRBG", "sun.security.provider.DRBG"); + map.put("SecureRandom.SHA1PRNG", "sun.security.provider.SecureRandom"); if (nativeAvailable && !useNativePRNG) { @@ -199,6 +202,14 @@ map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.3", "SHA-512"); map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.3", "SHA-512"); + map.put("MessageDigest.SHA-512/224", "sun.security.provider.SHA5$SHA512_224"); + map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.5", "SHA-512/224"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.5", + "SHA-512/224"); + map.put("MessageDigest.SHA-512/256", "sun.security.provider.SHA5$SHA512_256"); + map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.6", "SHA-512/256"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.6", + "SHA-512/256"); /* * Algorithm Parameter Generator engines diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/classes/sun/security/util/Debug.java --- a/jdk/src/java.base/share/classes/sun/security/util/Debug.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/src/java.base/share/classes/sun/security/util/Debug.java Fri May 06 11:38:44 2016 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -87,6 +87,7 @@ System.err.println("pkcs12 PKCS12 KeyStore debugging"); System.err.println("sunpkcs11 SunPKCS11 provider debugging"); System.err.println("scl permissions SecureClassLoader assigns"); + System.err.println("securerandom SecureRandom"); System.err.println("ts timestamping"); System.err.println(); System.err.println("The following can be used with access:"); @@ -175,6 +176,16 @@ } /** + * print a message to stderr that is prefixed with the prefix + * created from the call to getInstance and obj. + */ + public void println(Object obj, String message) + { + System.err.println(prefix + " [" + obj.getClass().getSimpleName() + + "@" + System.identityHashCode(obj) + "]: "+message); + } + + /** * print a blank line to stderr that is prefixed with the prefix. */ diff -r c5dc5ab60139 -r 256c45c4af5d jdk/src/java.base/share/conf/security/java.security --- a/jdk/src/java.base/share/conf/security/java.security Thu May 05 16:36:06 2016 -0700 +++ b/jdk/src/java.base/share/conf/security/java.security Fri May 06 11:38:44 2016 +0800 @@ -120,30 +120,30 @@ # # Sun Provider SecureRandom seed source. # -# Select the primary source of seed data for the "SHA1PRNG" and -# "NativePRNG" SecureRandom implementations in the "Sun" provider. +# Select the primary source of seed data for the "NativePRNG", "SHA1PRNG" +# and "DRBG" SecureRandom implementations in the "Sun" provider. # (Other SecureRandom implementations might also use this property.) # # On Unix-like systems (for example, Solaris/Linux/MacOS), the -# "NativePRNG" and "SHA1PRNG" implementations obtains seed data from +# "NativePRNG", "SHA1PRNG" and "DRBG" implementations obtains seed data from # special device files such as file:/dev/random. # # On Windows systems, specifying the URLs "file:/dev/random" or # "file:/dev/urandom" will enable the native Microsoft CryptoAPI seeding -# mechanism for SHA1PRNG. +# mechanism for SHA1PRNG and DRBG. # # By default, an attempt is made to use the entropy gathering device # specified by the "securerandom.source" Security property. If an # exception occurs while accessing the specified URL: # -# SHA1PRNG: -# the traditional system/thread activity algorithm will be used. -# # NativePRNG: # a default value of /dev/random will be used. If neither # are available, the implementation will be disabled. # "file" is the only currently supported protocol type. # +# SHA1PRNG and DRBG: +# the traditional system/thread activity algorithm will be used. +# # The entropy gathering device can also be specified with the System # property "java.security.egd". For example: # @@ -154,7 +154,7 @@ # # In addition, if "file:/dev/random" or "file:/dev/urandom" is # specified, the "NativePRNG" implementation will be more preferred than -# SHA1PRNG in the Sun provider. +# DRBG and SHA1PRNG in the Sun provider. # securerandom.source=file:/dev/random @@ -169,13 +169,79 @@ # entries. # #ifdef windows -securerandom.strongAlgorithms=Windows-PRNG:SunMSCAPI,SHA1PRNG:SUN +securerandom.strongAlgorithms=Windows-PRNG:SunMSCAPI,DRBG:SUN #endif #ifndef windows -securerandom.strongAlgorithms=NativePRNGBlocking:SUN +securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN #endif # +# Sun provider DRBG configuration and default instantiation request. +# +# NIST SP 800-90Ar1 lists several DRBG mechanisms. Each can be configured +# with a DRBG algorithm name, and can be instantiated with a security strength, +# prediction resistance support, etc. This property defines the configuration +# and the default instantiation request of "DRBG" SecureRandom implementations +# in the SUN provider. (Other DRBG implementations can also use this property.) +# Applications can request different instantiation parameters like security +# strength, capability, personalization string using one of the +# getInstance(...,SecureRandomParameters,...) methods with a +# DrbgParameters.Instantiation argument, but other settings such as the +# mechanism and DRBG algorithm names are not currently configurable by any API. +# +# Please note that the SUN implementation of DRBG always supports reseeding. +# +# The value of this property is a comma-separated list of all configurable +# aspects. The aspects can appear in any order but the same aspect can only +# appear at most once. Its BNF-style definition is: +# +# Value: +# aspect { "," aspect } +# +# aspect: +# mech_name | algorithm_name | strength | capability | df +# +# // The DRBG mechanism to use. Default "Hash_DRBG" +# mech_name: +# "Hash_DRBG" | "HMAC_DRBG" | "CTR_DRBG" +# +# // The DRBG algorithm name. The "SHA-***" names are for Hash_DRBG and +# // HMAC_DRBG, default "SHA-256". "3KeyTDEA" and "AES-***" names are for +# // CTR_DRBG, default "AES-128" when using the limited cryptographic +# // or "AES-256" when using the unlimited. +# algorithm_name: +# "SHA-1" | "SHA-224" | "SHA-512/224" | "SHA-256" | +# "SHA-512/256" | "SHA-384" | "SHA-512" | +# "3KeyTDEA" | "AES-128" | "AES-192" | "AES-256" +# +# // Security strength requested. Default "128", or "112" +# // if mech_name is CTR_DRBG and algorithm_name is "3KeyTDEA" +# strength: +# "112" | "128" | "192" | "256" +# +# // Prediction resistance and reseeding request. Default "none" +# // "pr_and_reseed" - Both prediction resistance and reseeding +# // support requested +# // "reseed_only" - Only reseeding support requested +# // "none" - Neither prediction resistance not reseeding +# // support requested +# pr: +# "pr_and_reseed" | "reseed_only" | "none" +# +# // Whether a derivation function should be used. only applicable +# // to CTR_DRBG. Default "use_df" +# df: +# "use_df" | "no_df" +# +# Examples, +# securerandom.drbg.config=Hash_DRBG,SHA-1,112,none +# securerandom.drbg.config=CTR_DRBG,AES-256,192,pr_and_reseed,use_df +# +# The default value is an empty string, which is equivalent to +# securerandom.drbg.config=Hash_DRBG,SHA-256,128,none +securerandom.drbg.config= + +# # Class to instantiate as the javax.security.auth.login.Configuration # provider. # diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/com/sun/crypto/provider/Mac/HmacSHA512.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/com/sun/crypto/provider/Mac/HmacSHA512.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.Asserts; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.util.Arrays; + +/** + * @test + * @bug 8051408 + * @library /lib/testlibrary + * @summary testing HmacSHA512/224 and HmacSHA512/256. + */ +public class HmacSHA512 { + public static void main(String[] args) throws Exception { + + Mac mac; + + // Test vectors obtained from + // https://groups.google.com/d/msg/sci.crypt/OolWgsgQD-8/IUR2KhCcfEkJ + mac = Mac.getInstance("HmacSHA512/224"); + mac.init(new SecretKeySpec(xeh("4a656665"), "HmacSHA512/224")); + mac.update("what do ya want for nothing?".getBytes()); + Asserts.assertTrue(Arrays.equals(mac.doFinal(), + xeh("4a530b31a79ebcce36916546317c45f247d83241dfb818fd37254bde"))); + + mac = Mac.getInstance("HmacSHA512/256"); + mac.init(new SecretKeySpec(xeh("4a656665"), "HmacSHA512/256")); + mac.update("what do ya want for nothing?".getBytes()); + Asserts.assertTrue(Arrays.equals(mac.doFinal(), + xeh("6df7b24630d5ccb2ee335407081a87188c221489768fa2020513b2d593359456"))); + + mac = Mac.getInstance("HmacSHA512/224"); + mac.init(new SecretKeySpec(xeh("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + "HmacSHA512/224")); + mac.update("Hi There".getBytes()); + Asserts.assertTrue(Arrays.equals(mac.doFinal(), + xeh("b244ba01307c0e7a8ccaad13b1067a4cf6b961fe0c6a20bda3d92039"))); + + mac = Mac.getInstance("HmacSHA512/256"); + mac.init(new SecretKeySpec(xeh("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + "HmacSHA512/256")); + mac.update("Hi There".getBytes()); + Asserts.assertTrue(Arrays.equals(mac.doFinal(), + xeh("9f9126c3d9c3c330d760425ca8a217e31feae31bfe70196ff81642b868402eab"))); + } + + static byte[] xeh(String in) { + in = in.replaceAll(" ", ""); + int len = in.length() / 2; + byte[] out = new byte[len]; + for (int i = 0; i < len; i++) { + out[i] = (byte)Integer.parseInt(in.substring(i * 2, i * 2 + 2), 16); + } + return out; + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/java/security/SecureRandom/DrbgParametersSpec.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/security/SecureRandom/DrbgParametersSpec.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8051408 + * @summary Make sure DrbgParameters coded as specified + * @library /test/lib/share/classes + */ + +import jdk.test.lib.Asserts; + +import java.security.DrbgParameters; +import java.util.Arrays; + +import static java.security.DrbgParameters.Capability.*; + +public class DrbgParametersSpec { + + public static void main(String args[]) throws Exception { + + byte[] p, np1, np2; + + // Capability + Asserts.assertTrue(PR_AND_RESEED.supportsPredictionResistance()); + Asserts.assertTrue(PR_AND_RESEED.supportsReseeding()); + Asserts.assertFalse(RESEED_ONLY.supportsPredictionResistance()); + Asserts.assertTrue(RESEED_ONLY.supportsReseeding()); + Asserts.assertFalse(NONE.supportsPredictionResistance()); + Asserts.assertFalse(NONE.supportsReseeding()); + + // Instantiation + p = "Instantiation".getBytes(); + DrbgParameters.Instantiation ins = DrbgParameters + .instantiation(192, RESEED_ONLY, p); + Asserts.assertTrue(ins.getStrength() == 192); + Asserts.assertTrue(ins.getCapability() == RESEED_ONLY); + np1 = ins.getPersonalizationString(); + np2 = ins.getPersonalizationString(); + // Getter outputs have same content but not the same object + Asserts.assertTrue(Arrays.equals(np1, p)); + Asserts.assertTrue(Arrays.equals(np2, p)); + Asserts.assertNE(np1, np2); + // Changes to original input has no affect on object + p[0] = 'X'; + np2 = ins.getPersonalizationString(); + Asserts.assertTrue(Arrays.equals(np1, np2)); + + ins = DrbgParameters.instantiation(-1, NONE, null); + Asserts.assertNull(ins.getPersonalizationString()); + + // NextBytes + p = "NextBytes".getBytes(); + DrbgParameters.NextBytes nb = DrbgParameters + .nextBytes(192, true, p); + Asserts.assertTrue(nb.getStrength() == 192); + Asserts.assertTrue(nb.getPredictionResistance()); + np1 = nb.getAdditionalInput(); + np2 = nb.getAdditionalInput(); + // Getter outputs have same content but not the same object + Asserts.assertTrue(Arrays.equals(np1, p)); + Asserts.assertTrue(Arrays.equals(np2, p)); + Asserts.assertNE(np1, np2); + // Changes to original input has no affect on object + p[0] = 'X'; + np2 = nb.getAdditionalInput(); + Asserts.assertTrue(Arrays.equals(np1, np2)); + + // Reseed + p = "Reseed".getBytes(); + DrbgParameters.Reseed rs = DrbgParameters + .reseed(true, p); + Asserts.assertTrue(rs.getPredictionResistance()); + np1 = rs.getAdditionalInput(); + np2 = rs.getAdditionalInput(); + // Getter outputs have same content but not the same object + Asserts.assertTrue(Arrays.equals(np1, p)); + Asserts.assertTrue(Arrays.equals(np2, p)); + Asserts.assertNE(np1, np2); + // Changes to original input has no affect on object + p[0] = 'X'; + np2 = rs.getAdditionalInput(); + Asserts.assertTrue(Arrays.equals(np1, np2)); + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/java/security/SecureRandom/Serialize.java --- a/jdk/test/java/security/SecureRandom/Serialize.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/test/java/security/SecureRandom/Serialize.java Fri May 06 11:38:44 2016 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,16 +31,73 @@ public class Serialize { - public static void main(String args[]) throws IOException { + public static void main(String args[]) throws Exception { + for (String alg: new String[]{ + "SHA1PRNG", "DRBG", "Hash_DRBG", "HMAC_DRBG", "CTR_DRBG", + "Hash_DRBG,SHA-512,192,pr_and_reseed"}) { + + System.out.println("Testing " + alg); + SecureRandom s1; + + // A SecureRandom can be s11ned and des11ned at any time. - FileOutputStream fos = new FileOutputStream("t.tmp"); - ObjectOutputStream oos = new ObjectOutputStream(fos); + // Brand new. + s1 = getInstance(alg); + revive(s1).nextInt(); + if (alg.contains("DRBG")) { + revive(s1).reseed(); + } + + // Used + s1 = getInstance(alg); + s1.nextInt(); // state set + revive(s1).nextInt(); + if (alg.contains("DRBG")) { + revive(s1).reseed(); + } - SecureRandom secRandom = new SecureRandom(); + // Automatically reseeded + s1 = getInstance(alg); + if (alg.contains("DRBG")) { + s1.reseed(); + } + revive(s1).nextInt(); + if (alg.contains("DRBG")) { + revive(s1).reseed(); + } + + // Manually seeded + s1 = getInstance(alg); + s1.setSeed(1L); + revive(s1).nextInt(); + if (alg.contains("DRBG")) { + revive(s1).reseed(); + } + } + } - // serialize and write out - oos.writeObject(secRandom); - oos.flush(); - oos.close(); + private static SecureRandom getInstance(String alg) throws Exception { + if (alg.equals("SHA1PRNG") || alg.equals("DRBG")) { + return SecureRandom.getInstance(alg); + } else { + String old = Security.getProperty("securerandom.drbg.config"); + try { + Security.setProperty("securerandom.drbg.config", alg); + return SecureRandom.getInstance("DRBG"); + } finally { + Security.setProperty("securerandom.drbg.config", old); + } + } + } + + private static SecureRandom revive(SecureRandom oldOne) throws Exception { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + new ObjectOutputStream(bout).writeObject(oldOne); + SecureRandom newOne = (SecureRandom) new ObjectInputStream( + new ByteArrayInputStream(bout.toByteArray())).readObject(); + if (!oldOne.toString().equals(newOne.toString())) { + throw new Exception(newOne + " is not " + oldOne); + } + return newOne; } } diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/sun/security/provider/MessageDigest/SHA512.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/provider/MessageDigest/SHA512.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.testlibrary.Asserts; + +import java.security.MessageDigest; +import java.util.Arrays; + +/** + * @test + * @bug 8051408 + * @library /lib/testlibrary + * @summary testing SHA-512/224 and SHA-512/256. + */ +public class SHA512 { + public static void main(String[] args) throws Exception { + + MessageDigest md; + + // Test vectors obtained from + // http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512_224.pdf + md = MessageDigest.getInstance("SHA-512/224"); + Asserts.assertTrue(Arrays.equals(md.digest("abc".getBytes()), + xeh("4634270F 707B6A54 DAAE7530 460842E2 0E37ED26 5CEEE9A4 3E8924AA"))); + Asserts.assertTrue(Arrays.equals(md.digest(( + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu").getBytes()), + xeh("23FEC5BB 94D60B23 30819264 0B0C4533 35D66473 4FE40E72 68674AF9"))); + + // Test vectors obtained from + // http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512_256.pdf + md = MessageDigest.getInstance("SHA-512/256"); + Asserts.assertTrue(Arrays.equals(md.digest("abc".getBytes()), + xeh("53048E26 81941EF9 9B2E29B7 6B4C7DAB E4C2D0C6 34FC6D46 E0E2F131 07E7AF23"))); + Asserts.assertTrue(Arrays.equals(md.digest(( + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu").getBytes()), + xeh("3928E184 FB8690F8 40DA3988 121D31BE 65CB9D3E F83EE614 6FEAC861 E19B563A"))); + } + + static byte[] xeh(String in) { + in = in.replaceAll(" ", ""); + int len = in.length() / 2; + byte[] out = new byte[len]; + for (int i = 0; i < len; i++) { + out[i] = (byte)Integer.parseInt(in.substring(i * 2, i * 2 + 2), 16); + } + return out; + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/sun/security/provider/SecureRandom/AbstractDrbgSpec.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/provider/SecureRandom/AbstractDrbgSpec.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8051408 + * @modules java.base/sun.security.provider + * @summary check the AbstractDrbg API etc + */ + +import java.security.*; +import sun.security.provider.AbstractDrbg; +import static java.security.DrbgParameters.Capability.*; + +/** + * This test makes sure the AbstractDrbg API works as specified. It also + * checks the SecureRandom API. + */ +public class AbstractDrbgSpec { + + public static void main(String args[]) throws Exception { + + // getInstance from a provider. + + Provider p = new All("A", 0, ""); + byte[] bytes = new byte[100]; + + // A non-DRBG + iae(() -> SecureRandom.getInstance("S1", null, p)); + nsae(() -> SecureRandom.getInstance("S1", + new SecureRandomParameters() {}, p)); + + SecureRandom s1 = SecureRandom.getInstance("S1", p); + if (s1.getParameters() != null) { + throw new Exception(); + } + + iae(() -> s1.nextBytes(bytes, null)); + uoe(() -> s1.nextBytes(bytes, new SecureRandomParameters() {})); + uoe(() -> s1.reseed()); + iae(() -> s1.reseed(null)); + uoe(() -> s1.reseed(new SecureRandomParameters() {})); + + // A weak DRBG + iae(() -> SecureRandom.getInstance("S2", null, p)); + nsae(() -> SecureRandom.getInstance("S2", + new SecureRandomParameters() {}, p)); + nsae(() -> SecureRandom.getInstance("S2", + DrbgParameters.instantiation(256, NONE, null), p)); + nsae(() -> SecureRandom.getInstance("S2", + DrbgParameters.instantiation(-1, PR_AND_RESEED, null), p)); + nsae(() -> SecureRandom.getInstance("S2", + DrbgParameters.instantiation(-1, RESEED_ONLY, null), p)); + + SecureRandom s2 = SecureRandom.getInstance("S2", + DrbgParameters.instantiation(-1, NONE, null), p); + equals(s2, "S2,SQUEEZE,128,none"); + equals(s2.getParameters(), "128,none,null"); + + npe(() -> s2.nextBytes(null)); + iae(() -> s2.nextBytes(bytes, null)); + iae(() -> s2.nextBytes(bytes, new SecureRandomParameters() {})); + uoe(() -> s2.reseed()); + iae(() -> s2.reseed(null)); + + iae(() -> s2.nextBytes(bytes, + DrbgParameters.nextBytes(-1, false, new byte[101]))); + s2.nextBytes(new byte[101], + DrbgParameters.nextBytes(-1, false, new byte[100])); + s2.nextBytes(bytes, + DrbgParameters.nextBytes(-1, false, new byte[100])); + + // A strong DRBG + iae(() -> SecureRandom.getInstance("S3", null, p)); + nsae(() -> SecureRandom.getInstance("S3", + new SecureRandomParameters() {}, p)); + SecureRandom.getInstance("S3", + DrbgParameters.instantiation(192, PR_AND_RESEED, null), p); + + SecureRandom s3 = SecureRandom.getInstance("S3", p); + equals(s3, "S3,SQUEEZE,128,reseed_only"); + equals(s3.getParameters(), "128,reseed_only,null"); + + iae(() -> s3.nextBytes(bytes, + DrbgParameters.nextBytes(192, false, null))); + iae(() -> s3.nextBytes(bytes, + DrbgParameters.nextBytes(112, true, null))); + iae(() -> s3.reseed(new SecureRandomParameters() {})); + + SecureRandom s32 = SecureRandom.getInstance( + "S3", DrbgParameters.instantiation(192, PR_AND_RESEED, null), p); + equals(s32, "S3,SQUEEZE,192,pr_and_reseed"); + equals(s32.getParameters(), "192,pr_and_reseed,null"); + + s32.nextBytes(bytes, DrbgParameters.nextBytes(192, false, null)); + s32.nextBytes(bytes, DrbgParameters.nextBytes(112, true, null)); + s32.reseed(); + s32.reseed(DrbgParameters.reseed(true, new byte[100])); + + // getInstance from competitive providers. + + Provider l = new Legacy("L", 0, ""); + Provider w = new Weak("W", 0, ""); + Provider s = new Strong("S", 0, ""); + + Security.addProvider(l); + Security.addProvider(w); + Security.addProvider(s); + + SecureRandom s4; + + try { + s4 = SecureRandom.getInstance("S"); + if (s4.getProvider() != l) { + throw new Exception(); + } + + nsae(() -> SecureRandom.getInstance( + "S", DrbgParameters.instantiation(256, NONE, null))); + + s4 = SecureRandom.getInstance( + "S", DrbgParameters.instantiation(192, NONE, null)); + if (s4.getProvider() != s) { + throw new Exception(); + } + + s4 = SecureRandom.getInstance( + "S", DrbgParameters.instantiation(128, PR_AND_RESEED, null)); + if (s4.getProvider() != s) { + throw new Exception(); + } + + s4 = SecureRandom.getInstance( + "S", DrbgParameters.instantiation(128, RESEED_ONLY, null)); + if (s4.getProvider() != s) { + throw new Exception(); + } + + s4 = SecureRandom.getInstance( + "S", DrbgParameters.instantiation(128, NONE, null)); + if (s4.getProvider() != w) { + throw new Exception(); + } + } finally { + Security.removeProvider("L"); + Security.removeProvider("W"); + Security.removeProvider("S"); + } + } + + public static class All extends Provider { + protected All(String name, double version, String info) { + super(name, version, info); + put("SecureRandom.S1", S1.class.getName()); + put("SecureRandom.S2", S2.class.getName()); + put("SecureRandom.S3", S3.class.getName()); + } + } + + // Providing S with no params support + public static class Legacy extends Provider { + protected Legacy(String name, double version, String info) { + super(name, version, info); + put("SecureRandom.S", S1.class.getName()); + } + } + + public static class Weak extends Provider { + protected Weak(String name, double version, String info) { + super(name, version, info); + put("SecureRandom.S", S2.class.getName()); + } + } + + public static class Strong extends Provider { + protected Strong(String name, double version, String info) { + super(name, version, info); + put("SecureRandom.S", S3.class.getName()); + } + } + + // This is not a DRBG. + public static class S1 extends SecureRandomSpi { + @Override + protected void engineSetSeed(byte[] seed) { + } + + @Override + protected void engineNextBytes(byte[] bytes) { + } + + @Override + protected byte[] engineGenerateSeed(int numBytes) { + return new byte[numBytes]; + } + } + + // This is a strong DRBG. + public static class S3 extends AbstractDrbg { + + public S3(SecureRandomParameters params) { + supportPredictionResistance = true; + supportReseeding = true; + highestSupportedSecurityStrength = 192; + mechName = "S3"; + algorithm = "SQUEEZE"; + configure(params); + } + protected void chooseAlgorithmAndStrength() { + if (requestedInstantiationSecurityStrength < 0) { + securityStrength = DEFAULT_STRENGTH; + } else { + securityStrength = requestedInstantiationSecurityStrength; + } + minLength = securityStrength / 8; + maxAdditionalInputLength = maxPersonalizationStringLength = 100; + } + + @Override + protected void initEngine() { + + } + + @Override + protected void instantiateAlgorithm(byte[] ei) { + + } + + @Override + protected void generateAlgorithm(byte[] result, byte[] additionalInput) { + + } + + @Override + protected void reseedAlgorithm(byte[] ei, byte[] additionalInput) { + + } + } + + // This is a weak DRBG. maximum strength is 128 and does + // not support prediction resistance or reseed. + public static class S2 extends S3 { + public S2(SecureRandomParameters params) { + super(null); + mechName = "S2"; + highestSupportedSecurityStrength = 128; + supportPredictionResistance = false; + supportReseeding = false; + configure(params); + } + } + + static void nsae(RunnableWithException r) throws Exception { + checkException(r, NoSuchAlgorithmException.class); + } + + static void iae(RunnableWithException r) throws Exception { + checkException(r, IllegalArgumentException.class); + } + + static void uoe(RunnableWithException r) throws Exception { + checkException(r, UnsupportedOperationException.class); + } + + static void npe(RunnableWithException r) throws Exception { + checkException(r, NullPointerException.class); + } + + interface RunnableWithException { + void run() throws Exception; + } + + static void checkException(RunnableWithException r, Class ex) + throws Exception { + try { + r.run(); + } catch (Exception e) { + if (ex.isAssignableFrom(e.getClass())) { + return; + } + throw e; + } + throw new Exception("No exception thrown"); + } + + static void equals(Object o, String s) throws Exception { + if (!o.toString().equals(s)) { + throw new Exception(o.toString() + " is not " + s); + } + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/sun/security/provider/SecureRandom/AutoReseed.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/provider/SecureRandom/AutoReseed.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.security.SecureRandom; +import java.security.Security; + +/** + * @test + * @bug 8051408 + * @summary make sure nextBytes etc can be called before setSeed + */ +public class AutoReseed { + + public static void main(String[] args) throws Exception { + SecureRandom sr; + String old = Security.getProperty("securerandom.drbg.config"); + try { + for (String mech : + new String[]{"Hash_DRBG", "HMAC_DRBG", "CTR_DRBG"}) { + System.out.println("Testing " + mech + "..."); + Security.setProperty("securerandom.drbg.config", mech); + + // Check auto reseed works + sr = SecureRandom.getInstance("DRBG"); + sr.nextInt(); + sr = SecureRandom.getInstance("DRBG"); + sr.reseed(); + sr = SecureRandom.getInstance("DRBG"); + sr.generateSeed(10); + } + } finally { + Security.setProperty("securerandom.drbg.config", old); + } + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/sun/security/provider/SecureRandom/CommonSeeder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/provider/SecureRandom/CommonSeeder.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import sun.security.provider.AbstractDrbg; +import sun.security.provider.EntropySource; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.DrbgParameters; +import java.security.SecureRandom; +import java.security.Security; + +/** + * @test + * @bug 8051408 + * @modules java.base/sun.security.provider + * @run main/othervm CommonSeeder + * @summary check entropy reading of DRBGs + */ +public class CommonSeeder { + + static class MyES implements EntropySource { + int count = 100; + int lastCount = 100; + + @Override + public byte[] getEntropy(int minEntropy, int minLength, + int maxLength, boolean pr) { + count--; + return new byte[minLength]; + } + + /** + * Confirms genEntropy() has been called {@code less} times + * since last check. + */ + public void checkUsage(int less) throws Exception { + if (lastCount != count + less) { + throw new Exception(String.format( + "lastCount = %d, count = %d, less = %d", + lastCount, count, less)); + } + lastCount = count; + } + } + + public static void main(String[] args) throws Exception { + + byte[] result = new byte[10]; + MyES es = new MyES(); + + // Set es as the default entropy source, overriding SeedGenerator. + setDefaultSeeder(es); + + // Nothing happened yet + es.checkUsage(0); + + SecureRandom sr; + sr = SecureRandom.getInstance("DRBG"); + + // No entropy reading if only getInstance + es.checkUsage(0); + + // Entropy is read at 1st nextBytes of the 1st DRBG + sr.nextInt(); + es.checkUsage(1); + + for (String mech : new String[]{"Hash_DRBG", "HMAC_DRBG", "CTR_DRBG"}) { + System.out.println("Testing " + mech + "..."); + + // DRBG with pr_false will never read entropy again no matter + // if nextBytes or reseed is called. + + Security.setProperty("securerandom.drbg.config", mech); + sr = SecureRandom.getInstance("DRBG"); + sr.nextInt(); + sr.reseed(); + es.checkUsage(0); + + // DRBG with pr_true always read from default entropy, and + // its nextBytes always reseed itself + + Security.setProperty("securerandom.drbg.config", + mech + ",pr_and_reseed"); + sr = SecureRandom.getInstance("DRBG"); + + sr.nextInt(); + es.checkUsage(2); // one instantiate, one reseed + sr.nextInt(); + es.checkUsage(1); // one reseed in nextBytes + sr.reseed(); + es.checkUsage(1); // one reseed + sr.nextBytes(result, DrbgParameters.nextBytes(-1, false, null)); + es.checkUsage(0); // pr_false for this call + sr.nextBytes(result, DrbgParameters.nextBytes(-1, true, null)); + es.checkUsage(1); // pr_true for this call + sr.reseed(DrbgParameters.reseed(true, null)); + es.checkUsage(1); // reseed from es + sr.reseed(DrbgParameters.reseed(false, null)); + es.checkUsage(0); // reseed from AbstractDrbg.SeederHolder.seeder + } + } + + static void setDefaultSeeder(EntropySource es) throws Exception { + Field f = AbstractDrbg.class.getDeclaredField("defaultES"); + f.setAccessible(true); // no more private + Field f2 = Field.class.getDeclaredField("modifiers"); + f2.setAccessible(true); + f2.setInt(f, f2.getInt(f) - Modifier.FINAL); // no more final + f.set(null, es); + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/sun/security/provider/SecureRandom/DRBGAlg.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/provider/SecureRandom/DRBGAlg.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import sun.security.provider.MoreDrbgParameters; + +import java.security.DrbgParameters; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.SecureRandomParameters; +import java.security.Security; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static java.security.DrbgParameters.Capability.*; + +/** + * @test + * @bug 8051408 + * @modules java.base/sun.security.provider + * @summary make sure DRBG alg can be defined and instantiated freely + */ +public class DRBGAlg { + + public static void main(String[] args) throws Exception { + + check(null, "Hash_DRBG", "SHA-256", "reseed_only", ",128"); + check("", "Hash_DRBG", "SHA-256", "reseed_only", ",128"); + check("sha-1", "Hash_DRBG", "SHA-1", "reseed_only", ",128"); + check("sha-256", "Hash_DRBG", "SHA-256", "reseed_only", ",128"); + check("SHA-3"); + check("hash_drbg", "Hash_DRBG", "SHA-256", "reseed_only", ",128"); + check("hmac_drbg", "HMAC_DRBG", "SHA-256", "reseed_only", ",128"); + check("ctr_drbg", "CTR_DRBG", "AES-", "reseed_only", ",128", "use_df"); + + // trying all permutations + checkPermutations( + Collections.emptyList(), + Arrays.asList("hash_drbg","sha-512","Pr_and_Reseed","192"), + "Hash_DRBG", "SHA-512", "pr_and_reseed", ",192"); + + check("Hash_DRBG,Hmac_DRBG"); + check("SHA-1,SHA-256"); + check("128,256"); + check("none,reseed_only"); + check("use_df,no_df"); + check("Hash_DRBG,,SHA-1"); + + check(null, DrbgParameters.instantiation(112, PR_AND_RESEED, null), + "Hash_DRBG", "SHA-256", "pr_and_reseed", ",112"); + check(null, DrbgParameters.instantiation(256, PR_AND_RESEED, null), + "Hash_DRBG", "SHA-256", "pr_and_reseed", ",256"); + check(null, DrbgParameters.instantiation(384, PR_AND_RESEED, null)); + check("sha-1", DrbgParameters.instantiation(112, PR_AND_RESEED, null), + "Hash_DRBG", "SHA-1", "pr_and_reseed", ",112"); + check("sha-1", DrbgParameters.instantiation(192, PR_AND_RESEED, null)); + check("hash_drbg,sha-512,Pr_and_Reseed,192", + DrbgParameters.instantiation(112, NONE, null), + "Hash_DRBG", "SHA-512", "reseed_only", ",112"); + check("hash_drbg,sha-512,Pr_and_Reseed,192", + DrbgParameters.instantiation(-1, NONE, null), + "Hash_DRBG", "SHA-512", "reseed_only", ",192"); + // getInstance params can be stronger than definition + check("hash_drbg,sha-256,None,112", + DrbgParameters.instantiation(192, PR_AND_RESEED, null), + "Hash_DRBG", "SHA-256", "pr_and_reseed", ",192"); + + check("hash_drbg,sha-1", new MoreDrbgParameters( + null, null, "sha-512", null, false, + DrbgParameters.instantiation(-1, NONE, null)), + "Hash_DRBG", "SHA-512"); + check("hash_drbg,sha-1", new MoreDrbgParameters( + null, null, null, null, false, + DrbgParameters.instantiation(-1, NONE, null)), + "Hash_DRBG", "SHA-1"); + check("hash_drbg", new MoreDrbgParameters( + null, "hmac_drbg", null, null, false, + DrbgParameters.instantiation(-1, NONE, null)), + "HMAC_DRBG", "SHA-256"); + + check("hash_drbg,sha-1", new MoreDrbgParameters( + null, null, "sha-3", null, false, + DrbgParameters.instantiation(-1, NONE, null))); + check("hash_drbg,sha-1", new MoreDrbgParameters( + null, "Unknown_DRBG", null, null, false, + DrbgParameters.instantiation(-1, NONE, null))); + } + + /** + * Checks all permutatins of a config. This is a recursive method and + * should be called with checkPermutations(empty,config,expected). + * + * @param current the current chosen aspects + * @param remains the remaining + * @param expected the expected effective config + * @throws Exception when check fails + */ + private static void checkPermutations(List current, + List remains, String... expected) throws Exception { + if (remains.isEmpty()) { + check(current.stream().collect(Collectors.joining(",")), expected); + } else { + for (String r : remains) { + List newCurrent = new ArrayList<>(current); + newCurrent.add(r); + List newRemains = new ArrayList<>(remains); + newRemains.remove(r); + checkPermutations(newCurrent, newRemains, expected); + } + } + } + + /** + * Checks DRBG definition for getInstance(alg, params). + * + * @param define DRBG + * @param params getInstance request (null if none) + * @param expected expected actual instantiate params, empty if should fail + */ + static void check(String define, SecureRandomParameters params, + String... expected) throws Exception { + System.out.println("Testing " + define + " with " + params + "..."); + String old = Security.getProperty("securerandom.drbg.config"); + if (define != null) { + Security.setProperty("securerandom.drbg.config", define); + } + try { + String result = params != null ? + SecureRandom.getInstance("DRBG", params).toString() : + SecureRandom.getInstance("DRBG").toString(); + System.out.println("Result " + result); + if (expected.length == 0) { + throw new Exception("should fail"); + } + for (String s : expected) { + if (!result.contains(s)) { + throw new Exception(result); + } + } + } catch (NoSuchAlgorithmException e) { + System.out.println("Result NSAE"); + if (expected.length > 0) { + throw e; + } + } finally { + Security.setProperty("securerandom.drbg.config", old); + } + } + + /** + * Checks DRBG definition for getInstance(alg). + * + * @param define DRBG + * @param expected expected actual instantiate params, empty if should fail + */ + static void check(String define, String... expected) throws Exception { + check(define, null, expected); + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/sun/security/provider/SecureRandom/DrbgCavp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/security/provider/SecureRandom/DrbgCavp.java Fri May 06 11:38:44 2016 +0800 @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import sun.security.provider.EntropySource; +import sun.security.provider.MoreDrbgParameters; + +import javax.crypto.Cipher; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.lang.*; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.DrbgParameters; +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Queue; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; + +import static java.security.DrbgParameters.Capability.*; + +/** + * The Known-output DRBG test. The test vector can be obtained from + * http://csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors.zip. + * + * Manually run this test with + * + * java DrbgCavp drbgtestvectors.zip + * + */ +public class DrbgCavp { + + // the current nonce + private static byte[] nonce; + + // A buffer to store test materials for the current call and + // can be printed out of an error occurs. + private static ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + // Save err for restoring + private static PrintStream err = System.err; + + private static final int AES_LIMIT; + + static { + try { + AES_LIMIT = Cipher.getMaxAllowedKeyLength("AES"); + } catch (Exception e) { + // should not happen + throw new AssertionError("Cannot detect AES"); + } + } + + public static void main(String[] args) throws Exception { + + if (args.length != 1) { + System.out.println("Usage: java DrbgCavp drbgtestvectors.zip"); + return; + } + File tv = new File(args[0]); + + EntropySource es = new TestEntropySource(); + System.setErr(new PrintStream(bout)); + + // The testsuite is a zip file containing more zip files for different + // working modes. Each internal zip file contains test materials for + // different mechanisms. + + try (ZipFile zf = new ZipFile(tv)) { + String[] modes = {"no_reseed", "pr_false", "pr_true"}; + for (String mode : modes) { + try (ZipInputStream zis = new ZipInputStream(zf.getInputStream( + zf.getEntry("drbgvectors_" + mode + ".zip")))) { + while (true) { + ZipEntry ze = zis.getNextEntry(); + if (ze == null) { + break; + } + String fname = ze.getName(); + if (fname.equals("Hash_DRBG.txt") + || fname.equals("HMAC_DRBG.txt") + || fname.equals("CTR_DRBG.txt")) { + String algorithm + = fname.substring(0, fname.length() - 4); + test(mode, algorithm, es, zis); + } + } + } + } + } finally { + System.setErr(err); + } + } + + /** + * A special entropy source you can set entropy input at will. + */ + private static class TestEntropySource implements EntropySource { + + private static Queue data = new ArrayDeque<>(); + + @Override + public byte[] getEntropy(int minEntropy, int minLength, + int maxLength, boolean pr) { + byte[] result = data.poll(); + if (result == null + || result.length < minLength + || result.length > maxLength) { + throw new RuntimeException("Invalid entropy: " + + "need [" + minLength + ", " + maxLength + "], " + + (result == null ? "none" : "has " + result.length)); + } + return result; + } + + private static void setEntropy(byte[] input) { + data.offer(input); + } + + private static void clearEntropy() { + data.clear(); + } + } + + /** + * The test. + * + * // Algorithm line, might contain usedf flag + * [AES-128 use df] + * // Ignored, use mode argument + * [PredictionResistance = True] + * // Ignored, just read EntropyInput + * [EntropyInputLen = 128] + * // Ignored, just read Nonce + * [NonceLen = 64] + * // Ignored, just read PersonalizationString + * [PersonalizationStringLen = 128] + * // Ignored, just read AdditionalInput + * [AdditionalInputLen = 128] + * // Used to allocate buffer for nextBytes() call + * [ReturnedBitsLen = 512] + * + * // A sign we can ignore old unused entropy input + * COUNT = 0 + * + * // Instantiate + * EntropyInput = 92898f... + * Nonce = c2a4d9... + * PersonalizationString = ea65ee... // Enough to call getInstance() + * + * // Reseed + * EntropyInputReseed = bfd503... + * AdditionalInputReseed = 009e0b... // Enough to call reseed() + * + * // Generation + * AdditionalInput = 1a40fa.... // Enough to call nextBytes() for PR off + * EntropyInputPR = 20728a... // Enough to call nextBytes() for PR on + * ReturnedBits = 5a3539... // Compare this to last nextBytes() output + * + * @param mode one of "no_reseed", "pr_false", "pr_true" + * @param mech one of "Hash_DRBG", "HMAC_DRBG", "CTR_DRBG" + * @param es our own entropy source + * @param is test material + */ + private static void test(String mode, String mech, EntropySource es, + InputStream is) throws Exception { + + SecureRandom hd = null; + + // Expected output length in bits as in [ReturnedBitsLen] + int outLen = 0; + + // DRBG algorithm as in the algorithm line + String algorithm = null; + + // When CTR_DRBG uses a derivation function as in the algorithm line + boolean usedf = false; + + // Additional input as in "AdditionalInput" + byte[] additional = null; + + // Random bits generated + byte[] output = null; + + // Prediction resistance flag, determined by mode + boolean isPr = false; + + StringBuilder sb = new StringBuilder(); + + int lineno = 0; + + System.out.println(mode + "/" + mech); + + try (Stream lines = + new BufferedReader(new InputStreamReader(is)).lines()) { + for (String s: (Iterable) lines::iterator) { + lineno++; + err.print(hd == null ? '-' : '*'); + Line l = new Line(s); + if (l.key.contains("no df") || l.key.contains("use df") || + l.key.startsWith("SHA-")) { + sb = new StringBuilder(); + bout.reset(); + } + sb.append(String.format( + "%9s %4s %5d %s\n", mode, mech, lineno, s)); + switch (l.key) { + case "3KeyTDEA no df": + case "AES-128 no df": + case "AES-192 no df": + case "AES-256 no df": + case "3KeyTDEA use df": + case "AES-128 use df": + case "AES-192 use df": + case "AES-256 use df": + algorithm = l.key.split(" ")[0]; + usedf = l.key.contains("use df"); + break; + case "ReturnedBitsLen": + outLen = l.vint(); + output = new byte[outLen / 8]; + break; + case "EntropyInput": + TestEntropySource.setEntropy(l.vdata()); + break; + case "Nonce": + nonce = l.vdata(); + break; + case "COUNT": + // Remove unused entropy (say, when AES-256 is skipped) + TestEntropySource.clearEntropy(); + break; + case "PersonalizationString": + try { + isPr = mode.equals("pr_true"); + byte[] ps = null; + if (l.vdata().length != 0) { + ps = l.vdata(); + } + + // MoreDrbgParameters must be used because we + // want to set entropy input and nonce. Since + // it can also set mechanism, algorithm and usedf, + // we don't need to touch securerandom.drbg.config. + hd = SecureRandom.getInstance("DRBG", + new MoreDrbgParameters(es, mech, algorithm, + nonce, usedf, + DrbgParameters.instantiation( + -1, + isPr ? PR_AND_RESEED + : RESEED_ONLY, + ps)), + "SUN"); + } catch (NoSuchAlgorithmException iae) { + // AES-256 might not be available. This is OK. + if ((algorithm.equals("AES-192") + || algorithm.equals("AES-256")) + && AES_LIMIT == 128) { + hd = null; + } else { + throw iae; + } + } + break; + case "EntropyInputReseed": + TestEntropySource.setEntropy(l.vdata()); + break; + case "AdditionalInputReseed": + if (l.vdata().length == 0) { + additional = null; + } else { + additional = l.vdata(); + } + if (hd != null) { + if (additional == null) { + hd.reseed(); + } else { + hd.reseed(DrbgParameters.reseed( + isPr, additional)); + } + } + break; + case "EntropyInputPR": + if (l.vdata().length != 0) { + TestEntropySource.setEntropy(l.vdata()); + } + if (mode.equals("pr_true")) { + if (hd != null) { + if (additional == null) { + hd.nextBytes(output); + } else { + hd.nextBytes(output, + DrbgParameters.nextBytes( + -1, isPr, additional)); + } + } + } + break; + case "AdditionalInput": + if (l.vdata().length == 0) { + additional = null; + } else { + additional = l.vdata(); + } + if (!mode.equals("pr_true")) { + if (hd != null) { + if (additional == null) { + hd.nextBytes(output); + } else { + hd.nextBytes(output, + DrbgParameters.nextBytes( + -1, isPr, additional)); + } + } + } + break; + case "ReturnedBits": + if (hd != null) { + if (!Arrays.equals(output, l.vdata())) { + throw new Exception("\nExpected: " + + l.value + "\n Actual: " + hex(output)); + } + } + break; + default: + // Algorithm line for Hash_DRBG and HMAC_DRBG + if (l.key.startsWith("SHA-")) { + algorithm = l.key; + } + } + } + err.println(); + } catch (Exception e) { + err.println(); + err.println(sb.toString()); + err.println(bout.toString()); + throw e; + } + } + + /** + * Parse a line from test material. + * + * Brackets are removed. Key and value separated. + */ + static class Line { + + final String key; + final String value; + + Line(String s) { + s = s.trim(); + if (s.length() >= 2) { + if (s.charAt(0) == '[') { + s = s.substring(1, s.length() - 1); + } + } + if (s.indexOf('=') < 0) { + key = s; + value = null; + } else { + key = s.substring(0, s.indexOf('=')).trim(); + value = s.substring(s.indexOf('=') + 1).trim(); + } + } + + int vint() { + return Integer.parseInt(value); + } + + byte[] vdata() { + return xeh(value); + } + } + + // Bytes to HEX + private static String hex(byte[] in) { + StringBuilder sb = new StringBuilder(); + for (byte b: in) { + sb.append(String.format("%02x", b&0xff)); + } + return sb.toString(); + } + + // HEX to bytes + private static byte[] xeh(String in) { + in = in.replaceAll(" ", ""); + int len = in.length() / 2; + byte[] out = new byte[len]; + for (int i = 0; i < len; i++) { + out[i] = (byte) Integer.parseInt( + in.substring(i * 2, i * 2 + 2), 16); + } + return out; + } +} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/sun/security/provider/SecureRandom/SelfSeed.java --- a/jdk/test/sun/security/provider/SecureRandom/SelfSeed.java Thu May 05 16:36:06 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/** - * @test - * @bug 4168409 - * @summary SecureRandom forces all instances to self-seed, even if a seed is - * provided - */ - -import java.security.SecureRandom; - -public class SelfSeed { - - private static final int NUM_BYTES = 5; - private static byte seed[] = { (byte)0xaa, (byte)0x11, (byte)0xa1 }; - - public static void main(String[] args) { - - try { - SecureRandom sr1 = SecureRandom.getInstance("SHA1PRNG"); - sr1.setSeed(seed); - byte randomBytes[] = new byte[NUM_BYTES]; - sr1.nextBytes(randomBytes); - - SecureRandom sr2 = new SecureRandom(seed); - if (sr2.getAlgorithm().equals("SHA1PRNG") == false) { - System.out.println("Default PRNG is not SHA1PRNG, skipping test"); - return; - } - byte otherRandomBytes[] = new byte[NUM_BYTES]; - sr2.nextBytes(otherRandomBytes); - - // make sure the random bytes generated are the same - for (int i = 0; i < NUM_BYTES; i++) { - if (randomBytes[i] != otherRandomBytes[i]) - throw new SecurityException("FAILURE: " + - "Returned bytes not equal"); - } - - // success - } catch (Exception e) { - throw new SecurityException("FAILURE: " + e.toString()); - } - } -} diff -r c5dc5ab60139 -r 256c45c4af5d jdk/test/sun/security/provider/SecureRandom/StrongSeedReader.java --- a/jdk/test/sun/security/provider/SecureRandom/StrongSeedReader.java Thu May 05 16:36:06 2016 -0700 +++ b/jdk/test/sun/security/provider/SecureRandom/StrongSeedReader.java Fri May 06 11:38:44 2016 +0800 @@ -59,6 +59,7 @@ System.setProperty("java.security.egd", file.toURI().toString()); testSeed("NativePRNG"); testSeed("SHA1PRNG"); + testSeed("DRBG"); } finally { if (file != null) { file.delete();