8157308: Make AbstractDrbg non-Serializable

Reviewed-by: mullan
This commit is contained in:
Weijun Wang 2016-06-07 10:10:59 +08:00
parent 5e8fb78dc1
commit c01b490933
10 changed files with 339 additions and 169 deletions

View File

@ -33,12 +33,13 @@ import java.util.Objects;
import static java.security.DrbgParameters.Capability.*;
/**
* The abstract base class for all DRBGs.
* The abstract base class for all DRBGs. It is used as {@link DRBG#impl}.
* <p>
* This class creates 5 new abstract methods. 3 are defined by the SP800-90A:
* This class has 5 abstract methods. 3 are defined by SP800-90A:
* <ol>
* <li>{@link #generateAlgorithm(byte[], byte[])}
* <li>{@link #reseedAlgorithm(byte[], byte[])} (might not be supported)
* <li>{@link #reseedAlgorithm(byte[], byte[])} (In fact this is not an
* abstract method, but any DRBG supporting reseeding must override it.)
* <li>{@link #instantiateAlgorithm(byte[])}
* </ol>
* and 2 for implementation purpose:
@ -46,18 +47,19 @@ import static java.security.DrbgParameters.Capability.*;
* <li>{@link #initEngine()}
* <li>{@link #chooseAlgorithmAndStrength}
* </ol>
* 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.
* Although this class is not a child class of {@link SecureRandomSpi}, it
* implements all abstract methods there as final.
* <p>
* The initialization process of a DRBG is divided into 2 phases:
* {@link #configure configuration} is eagerly called to set up parameters,
* and {@link #instantiateIfNecessary instantiation} is lazily called only
* when nextBytes or reseed is called.
* <p>
* SecureRandom methods like reseed and nextBytes are not thread-safe.
* An implementation is required to protect shared access to instantiate states
* (instantiated, nonce) and DRBG states (v, c, key, reseedCounter).
* (instantiated, nonce) and DRBG states (v, c, key, reseedCounter, etc).
*/
public abstract class AbstractDrbg extends SecureRandomSpi {
private static final long serialVersionUID = 9L;
public abstract class AbstractDrbg {
/**
* This field is not null if {@code -Djava.security.debug=securerandom} is
@ -69,7 +71,7 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
// Common working status
private transient boolean instantiated = false;
private boolean instantiated = false;
/**
* Reseed counter of a DRBG instance. A mechanism should increment it
@ -78,7 +80,7 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
*
* Volatile, will be used in a double checked locking.
*/
protected transient volatile int reseedCounter = 0;
protected volatile int reseedCounter = 0;
// Mech features. If not same as below, must be redefined in constructor.
@ -170,7 +172,7 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
/**
* 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}.
* {@link #toString()}.
*/
protected String algorithm;
@ -217,7 +219,7 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
* After instantiation, this field is not null. Do not modify it
* in a mechanism.
*/
protected transient byte[] nonce;
protected byte[] nonce;
/**
* Requested nonce in {@link MoreDrbgParameters}. If set to null,
@ -237,7 +239,7 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
* {@link #configure(SecureRandomParameters)}. This field
* can be null. {@link #getEntropyInput} will take care of null check.
*/
private transient EntropySource es;
private EntropySource es;
// Five abstract methods for SP 800-90A DRBG
@ -286,10 +288,7 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
/**
* 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.
* or {@code Cipher}). This method is called during instantiation.
*/
protected abstract void initEngine();
@ -331,13 +330,11 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
// 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) {
@ -402,7 +399,6 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
}
}
@Override
public final void engineReseed(SecureRandomParameters params) {
if (debug != null) {
debug.println(this, "reseed with params");
@ -454,7 +450,6 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
* @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);
@ -469,7 +464,6 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
*
* @param input the seed
*/
@Override
public final synchronized void engineSetSeed(byte[] input) {
if (debug != null) {
debug.println(this, "setSeed");
@ -598,7 +592,6 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
*
* @return the curent configuration
*/
@Override
protected SecureRandomParameters engineGetParameters() {
// Or read from variable.
return DrbgParameters.instantiation(
@ -631,7 +624,8 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
this.es = m.es;
this.requestedAlgorithm = m.algorithm;
this.usedf = m.usedf;
params = m.config;
params = DrbgParameters.instantiation(m.strength,
m.capability, m.personalizationString);
}
if (params != null) {
if (params instanceof DrbgParameters.Instantiation) {

View File

@ -32,8 +32,6 @@ import java.util.Locale;
public abstract class AbstractHashDrbg extends AbstractDrbg {
private static final long serialVersionUID = 9L;
protected int outLen;
protected int seedLen;

View File

@ -28,14 +28,12 @@ package sun.security.provider;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
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 {
@ -47,7 +45,7 @@ public class CtrDrbg extends AbstractDrbg {
}
}
private transient Cipher cipher;
private Cipher cipher;
private String cipherAlg;
private String keyAlg;
@ -57,8 +55,8 @@ public class CtrDrbg extends AbstractDrbg {
private int keyLen;
private int seedLen;
private transient byte[] v;
private transient byte[] k;
private byte[] v;
private byte[] k;
public CtrDrbg(SecureRandomParameters params) {
mechName = "CTR_DRBG";
@ -165,7 +163,7 @@ public class CtrDrbg extends AbstractDrbg {
protected void initEngine() {
try {
/*
* Use the local SUN implementation to avoid native
* Use the local SunJCE implementation to avoid native
* performance overhead.
*/
cipher = Cipher.getInstance(cipherAlg, "SunJCE");
@ -463,12 +461,6 @@ public class CtrDrbg extends AbstractDrbg {
// Step 8. Return
}
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject ();
initEngine();
}
@Override
public String toString() {
return super.toString() + ","

View File

@ -25,6 +25,7 @@
package sun.security.provider;
import java.io.IOException;
import java.security.AccessController;
import java.security.DrbgParameters;
import java.security.PrivilegedAction;
@ -61,11 +62,12 @@ public final class DRBG extends SecureRandomSpi {
private static final long serialVersionUID = 9L;
private final AbstractDrbg impl;
private transient AbstractDrbg impl;
private final String mechName;
private final String algorithmName;
/**
* @serial
*/
private final MoreDrbgParameters mdp;
public DRBG(SecureRandomParameters params) {
@ -91,7 +93,7 @@ public final class DRBG extends SecureRandomSpi {
// Can be configured with a security property
String config = AccessController.doPrivileged((PrivilegedAction<String>)
() -> Security.getProperty(PROP_NAME));
() -> Security.getProperty(PROP_NAME));
if (config != null && !config.isEmpty()) {
for (String part : config.split(",")) {
@ -151,8 +153,9 @@ public final class DRBG extends SecureRandomSpi {
if (params != null) {
// MoreDrbgParameters is used for testing.
if (params instanceof MoreDrbgParameters) {
MoreDrbgParameters m = (MoreDrbgParameters)params;
params = m.config;
MoreDrbgParameters m = (MoreDrbgParameters) params;
params = DrbgParameters.instantiation(m.strength,
m.capability, m.personalizationString);
// No need to check null for es and nonce, they are still null
es = m.es;
@ -197,26 +200,27 @@ public final class DRBG extends SecureRandomSpi {
usedf = true;
}
MoreDrbgParameters m = new MoreDrbgParameters(
mdp = new MoreDrbgParameters(
es, mech, algorithm, nonce, usedf,
DrbgParameters.instantiation(strength, cap, ps));
switch (mech.toLowerCase(Locale.ROOT)) {
createImpl();
}
private void createImpl() {
switch (mdp.mech.toLowerCase(Locale.ROOT)) {
case "hash_drbg":
impl = new HashDrbg(m);
impl = new HashDrbg(mdp);
break;
case "hmac_drbg":
impl = new HmacDrbg(m);
impl = new HmacDrbg(mdp);
break;
case "ctr_drbg":
impl = new CtrDrbg(m);
impl = new CtrDrbg(mdp);
break;
default:
throw new IllegalArgumentException("Unsupported mech: " + mech);
throw new IllegalArgumentException("Unsupported mech: " + mdp.mech);
}
mechName = mech;
algorithmName = impl.algorithm;
}
@Override
@ -268,4 +272,13 @@ public final class DRBG extends SecureRandomSpi {
+ " cannot be provided more than once in " + PROP_NAME);
}
}
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
if (mdp.mech == null) {
throw new IllegalArgumentException("Input data is corrupted");
}
createImpl();
}
}

View File

@ -25,7 +25,6 @@
package sun.security.provider;
import java.io.IOException;
import java.math.BigInteger;
import java.security.DigestException;
import java.security.MessageDigest;
@ -36,15 +35,13 @@ 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 MessageDigest digest;
private transient byte[] v;
private transient byte[] c;
private byte[] v;
private byte[] c;
public HashDrbg(SecureRandomParameters params) {
mechName = "Hash_DRBG";
@ -267,10 +264,4 @@ public class HashDrbg extends AbstractHashDrbg {
// Step 5: No need to truncate
// Step 6: Return
}
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject ();
initEngine();
}
}

View File

@ -27,7 +27,6 @@ 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;
@ -36,14 +35,12 @@ import java.util.Arrays;
public class HmacDrbg extends AbstractHashDrbg {
private static final long serialVersionUID = 9L;
private transient Mac mac;
private Mac mac;
private String macAlg;
private transient byte[] v;
private transient byte[] k;
private byte[] v;
private byte[] k;
public HmacDrbg(SecureRandomParameters params) {
mechName = "HMAC_DRBG";
@ -101,6 +98,10 @@ public class HmacDrbg extends AbstractHashDrbg {
protected void initEngine() {
macAlg = "HmacSHA" + algorithm.substring(4);
try {
/*
* Use the local SunJCE implementation to avoid native
* performance overhead.
*/
mac = Mac.getInstance(macAlg, "SunJCE");
} catch (NoSuchProviderException | NoSuchAlgorithmException e) {
// Fallback to any available.
@ -194,10 +195,4 @@ public class HmacDrbg extends AbstractHashDrbg {
// Step 8. Return
}
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject ();
initEngine();
}
}

View File

@ -25,20 +25,30 @@
package sun.security.provider;
import java.io.IOException;
import java.io.Serializable;
import java.security.DrbgParameters;
import java.security.SecureRandomParameters;
/**
* Extra non-standard parameters that can be used by DRBGs.
* Exported and non-exported parameters that can be used by DRBGs.
*/
public class MoreDrbgParameters implements SecureRandomParameters {
public class MoreDrbgParameters implements SecureRandomParameters, Serializable {
private static final long serialVersionUID = 9L;
final transient EntropySource es;
final String mech;
final String algorithm;
final EntropySource es;
final byte[] nonce;
final boolean usedf;
final DrbgParameters.Instantiation config;
final int strength;
final DrbgParameters.Capability capability;
// The following 2 fields will be reassigned in readObject and
// thus cannot be final
byte[] nonce;
byte[] personalizationString;
/**
* Creates a new {@code MoreDrbgParameters} object.
@ -61,13 +71,31 @@ public class MoreDrbgParameters implements SecureRandomParameters {
this.mech = mech;
this.algorithm = algorithm;
this.es = es;
this.nonce = nonce;
this.nonce = (nonce == null) ? null : nonce.clone();
this.usedf = usedf;
this.config = config;
this.strength = config.getStrength();
this.capability = config.getCapability();
this.personalizationString = config.getPersonalizationString();
}
@Override
public String toString() {
return mech + "," + algorithm + "," + usedf + "," + config;
return mech + "," + algorithm + "," + usedf + "," + strength
+ "," + capability + "," + personalizationString;
}
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
if (nonce != null) {
nonce = nonce.clone();
}
if (personalizationString != null) {
personalizationString = personalizationString.clone();
}
if (capability == null) {
throw new IllegalArgumentException("Input data is corrupted");
}
}
}

View File

@ -22,20 +22,25 @@
*/
/* @test
* @bug 8051408
* @bug 8051408 8157308
* @modules java.base/sun.security.provider
* @build java.base/sun.security.provider.S
* @run main SpecTest
* @summary check the AbstractDrbg API etc
*/
import java.security.*;
import sun.security.provider.AbstractDrbg;
import sun.security.provider.S;
import static java.security.DrbgParameters.Capability.*;
/**
* This test makes sure the AbstractDrbg API works as specified. It also
* checks the SecureRandom API.
*
* The implementations must be patched into java.base/sun.security.provider
* because AbstractDrbg is not a public interface.
*/
public class AbstractDrbgSpec {
public class SpecTest {
public static void main(String args[]) throws Exception {
@ -170,9 +175,9 @@ public class AbstractDrbgSpec {
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());
put("SecureRandom.S1", S.S1.class.getName());
put("SecureRandom.S2", S.S2.class.getName());
put("SecureRandom.S3", S.S3.class.getName());
}
}
@ -180,92 +185,21 @@ public class AbstractDrbgSpec {
public static class Legacy extends Provider {
protected Legacy(String name, double version, String info) {
super(name, version, info);
put("SecureRandom.S", S1.class.getName());
put("SecureRandom.S", 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());
put("SecureRandom.S", 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);
put("SecureRandom.S", S.S3.class.getName());
}
}

View File

@ -0,0 +1,155 @@
/*
* 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.
*/
package sun.security.provider;
import java.security.SecureRandomParameters;
import java.security.SecureRandomSpi;
/**
* Read ../../../../SpecTest.java for details.
*/
public class S extends SecureRandomSpi {
protected AbstractDrbg impl;
// 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 weak DRBG. maximum strength is 128 and does
// not support prediction resistance or reseed.
public static class S2 extends S {
public S2(SecureRandomParameters params) {
impl = new Impl2(params);
}
}
// This is a strong DRBG.
public static class S3 extends S {
public S3(SecureRandomParameters params) {
impl = new Impl3(params);
}
}
// AbstractDrbg Implementations
static class Impl3 extends AbstractDrbg {
public Impl3(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) {
}
}
static class Impl2 extends Impl3 {
public Impl2(SecureRandomParameters params) {
super(null);
mechName = "S2";
highestSupportedSecurityStrength = 128;
supportPredictionResistance = false;
supportReseeding = false;
configure(params);
}
}
// Overridden SecureRandomSpi methods
@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();
}
}

View File

@ -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 sun.security.provider.DRBG;
import sun.security.provider.MoreDrbgParameters;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
/**
* @test
* @bug 8157308
* @modules java.base/sun.security.provider
* @summary Make AbstractDrbg non-Serializable
* @run main DRBGS11n mech
* @run main DRBGS11n capability
*/
public class DRBGS11n {
public static void main(String[] args) throws Exception {
DRBG d = new DRBG(null);
Field f = DRBG.class.getDeclaredField("mdp");
f.setAccessible(true);
MoreDrbgParameters mdp = (MoreDrbgParameters)f.get(d);
// Corrupt the mech or capability fields inside DRBG#mdp.
f = MoreDrbgParameters.class.getDeclaredField(args[0]);
f.setAccessible(true);
f.set(mdp, null);
try {
revive(d);
} catch (IllegalArgumentException iae) {
// Expected
return;
}
throw new Exception("revive should fail");
}
static <T> T revive(T in) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
new ObjectOutputStream(bout).writeObject(in);
return (T) new ObjectInputStream(
new ByteArrayInputStream(bout.toByteArray())).readObject();
}
}