diff options
author | David Hook <dgh@cryptoworkshop.com> | 2013-05-11 07:35:31 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2013-05-11 07:35:31 +0400 |
commit | 90fc0fc0f372ec58fffb5a7a2c7f234040fdefc4 (patch) | |
tree | e73667bf1c42eb5b7dea0f258ea0b83ac23d9840 | |
parent | df64b5e5c271bf4cb322871f5b7a6c94f0eb0f5f (diff) |
final clean ups, added some exception testing.
16 files changed, 450 insertions, 199 deletions
diff --git a/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java b/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java index 203d78f9..66f05c5f 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java +++ b/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java @@ -28,7 +28,7 @@ public class SP800SecureRandomBuilder * predictionResistant set to false. * <p> * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if - * the passed in SecureRandom does. + * the default SecureRandom does for its generateSeed() call. * </p> */ public SP800SecureRandomBuilder() @@ -41,7 +41,7 @@ public class SP800SecureRandomBuilder * for prediction resistance. * <p> * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if - * the passed in SecureRandom does. + * the passed in SecureRandom does for its generateSeed() call. * </p> * @param entropySource * @param predictionResistant diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java index b33a7664..ae038e7b 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java @@ -6,10 +6,18 @@ import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; +/** + * A SP800-90A CTR DRBG. + */ public class CTRSP800DRBG implements SP80090DRBG { - private EntropySource _entropySource; + private static final long TDEA_RESEED_MAX = 1L << (32 - 1); + private static final long AES_RESEED_MAX = 1L << (48 - 1); + private static final int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1); + private static final int AES_MAX_BITS_REQUEST = 1 << (19 - 1); + + private EntropySource _entropySource; private BlockCipher _engine; private int _keySizeInBits; private int _seedLength; @@ -17,28 +25,48 @@ public class CTRSP800DRBG // internal state private byte[] _Key; private byte[] _V; - private int _reseedCounter = 0; - - public CTRSP800DRBG(BlockCipher engine, int keySizeInBits, int securityStrength, EntropySource entropySource, byte[] personalisationString, byte[] nonce) + private long _reseedCounter = 0; + private boolean _isTDEA = false; + + /** + * Construct a SP800-90A CTR DRBG. + * <p> + * Minimum entropy requirement is the security strength requested. + * </p> + * @param engine underlying block cipher to use to support DRBG + * @param keySizeInBits size of the key to use with the block cipher. + * @param securityStrength security strength required (in bits) + * @param entropySource source of entropy to use for seeding/reseeding. + * @param personalizationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public CTRSP800DRBG(BlockCipher engine, int keySizeInBits, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) { - _entropySource = entropySource; _engine = engine; _keySizeInBits = keySizeInBits; _seedLength = keySizeInBits + engine.getBlockSize() * 8; + _isTDEA = isTDEA(engine); - int entropyLengthInBytes = securityStrength; - if (securityStrength > 256) { - throw new IllegalStateException( - "Security strength is not supported by the derivation function"); + throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); } - + + if (getMaxSecurityStrength(engine, keySizeInBits) < securityStrength) + { + throw new IllegalArgumentException("Requested security strength is not supported by block cipher and key size"); + } + + if (entropySource.entropySize() < securityStrength) + { + throw new IllegalArgumentException("Not enough entropy for security strength required"); + } + byte[] entropy = entropySource.getEntropy(); // Get_entropy_input - CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalisationString); + CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString); } private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce, @@ -118,16 +146,6 @@ public class CTRSP800DRBG // -- Internal state migration --- private static final byte[] K_BITS = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); - - private byte[] getBytes(byte[] input) - { - byte[] K = new byte[_keySizeInBits / 8]; - System.arraycopy(K_BITS, 0, K, 0, K.length); - _engine.init(true, new KeyParameter(K)); - byte[] out = new byte[_engine.getBlockSize()]; - _engine.processBlock(input, 0, out, 0); - return out; - } // 1. If (number_of_bits_to_return > max_number_of_bits), then return an // ERROR_FLAG. @@ -250,7 +268,6 @@ public class CTRSP800DRBG return temp; } - /* * 1. chaining_value = 0^outlen * . Comment: Set the first chaining value to outlen zeros. @@ -292,8 +309,42 @@ public class CTRSP800DRBG buf[offSet + 3] = ((byte)(value)); } + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) { + if (_isTDEA) + { + if (_reseedCounter > TDEA_RESEED_MAX) + { + return -1; + } + + if (Utils.isTooLarge(output, TDEA_MAX_BITS_REQUEST / 8)) + { + throw new IllegalArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST); + } + } + else + { + if (_reseedCounter > AES_RESEED_MAX) + { + return -1; + } + + if (Utils.isTooLarge(output, AES_MAX_BITS_REQUEST / 8)) + { + throw new IllegalArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST); + } + } + if (predictionResistant) { CTR_DRBG_Reseed_algorithm(_entropySource, additionalInput); @@ -327,7 +378,6 @@ public class CTRSP800DRBG System.arraycopy(out, 0, output, i * out.length, bytesToCopy); } - CTR_DRBG_Update(additionalInput, _Key, _V); _reseedCounter++; @@ -335,8 +385,32 @@ public class CTRSP800DRBG return output.length * 8; } + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ public void reseed(byte[] additionalInput) { CTR_DRBG_Reseed_algorithm(_entropySource, additionalInput); } + + private boolean isTDEA(BlockCipher cipher) + { + return cipher.getAlgorithmName().equals("DESede") || cipher.getAlgorithmName().equals("TDEA"); + } + + private int getMaxSecurityStrength(BlockCipher cipher, int keySizeInBits) + { + if (isTDEA(cipher) && keySizeInBits == 168) + { + return 112; + } + if (cipher.getAlgorithmName().equals("AES")) + { + return keySizeInBits; + } + + return -1; + } } diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java index ef529a8e..3cee39cb 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java @@ -11,6 +11,9 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; +/** + * A SP800-90A Dual EC DRBG. + */ public class DualECSP800DRBG implements SP80090DRBG { @@ -32,11 +35,14 @@ public class DualECSP800DRBG private static final BigInteger p521_Qx = new BigInteger("1b9fa3e518d683c6b65763694ac8efbaec6fab44f2276171a42726507dd08add4c3b3f4c1ebc5b1222ddba077f722943b24c3edfa0f85fe24d0c8c01591f0be6f63", 16); private static final BigInteger p521_Qy = new BigInteger("1f3bdba585295d9a1110d1df1f9430ef8442c5018976ff3437ef91b81dc0b8132c8d5c39c32d0e004a3092b7d327c0e7a4d26d2c7b69b58f9066652911e457779de", 16); - private final static int RESEED_MAX = 100000; + private static final long RESEED_MAX = 1L << (32 - 1); + private static final int MAX_ADDITIONAL_INPUT = 1 << (13 - 1); + private static final int MAX_ENTROPY_LENGTH = 1 << (13 - 1); + private static final int MAX_PERSONALIZATION_STRING = 1 << (13 -1); private Digest _digest; - private int _reseedCounter; - private EntropySource _entropySource; + private long _reseedCounter; + private EntropySource _entropySource; private int _securityStrength; private int _seedlen; private int _outlen; @@ -44,11 +50,13 @@ public class DualECSP800DRBG private ECPoint _P; private ECPoint _Q; private byte[] _s; - private int _sLength; + private int _sLength; /** * Construct a SP800-90A Dual EC DRBG. - * + * <p> + * Minimum entropy requirement is the security strength requested. + * </p> * @param digest source digest to use with the DRB stream. * @param securityStrength security strength required (in bits) * @param entropySource source of entropy to use for seeding/reseeding. @@ -57,23 +65,29 @@ public class DualECSP800DRBG */ public DualECSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce) { - if (securityStrength > digest.getDigestSize() * 8) // TODO: this may, or may not be correct, but it's good enough for now - { - throw new IllegalStateException( - "Security strength is not supported by the derivation function"); - } - - // TODO: validate digest choice _digest = digest; _entropySource = entropySource; _securityStrength = securityStrength; - // TODO: validate entropy length + if (Utils.isTooLarge(personalizationString, MAX_PERSONALIZATION_STRING / 8)) + { + throw new IllegalArgumentException("Personalization string too large"); + } + + if (entropySource.entropySize() < securityStrength || entropySource.entropySize() > MAX_ENTROPY_LENGTH) + { + throw new IllegalArgumentException("EntropySource must provide between " + securityStrength + " and " + MAX_ENTROPY_LENGTH + " bits"); + } + byte[] entropy = entropySource.getEntropy(); byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); if (securityStrength <= 128) { + if (Utils.getMaxSecurityStrength(digest) < 128) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } _seedlen = 256; _outlen = 240 / 8; _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve(); @@ -82,6 +96,10 @@ public class DualECSP800DRBG } else if (securityStrength <= 192) { + if (Utils.getMaxSecurityStrength(digest) < 192) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } _seedlen = 384; _outlen = 368 / 8; _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve(); @@ -90,6 +108,10 @@ public class DualECSP800DRBG } else if (securityStrength <= 256) { + if (Utils.getMaxSecurityStrength(digest) < 256) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } _seedlen = 521; _outlen = 504 / 8; _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve(); @@ -101,19 +123,37 @@ public class DualECSP800DRBG throw new IllegalArgumentException("security strength cannot be greater than 256 bits"); } - _s = hash_df(seedMaterial, _seedlen); + _s = Utils.hash_df(_digest, seedMaterial, _seedlen); _sLength = _s.length; - //System.err.println(new String(Hex.encode(_s))); _reseedCounter = 0; - } + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param additionalInput additional input to be added to the DRBG in this step. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant) { int numberOfBits = output.length*8; - - if (predictionResistant || _reseedCounter > RESEED_MAX) + int m = output.length / _outlen; + + if (Utils.isTooLarge(additionalInput, MAX_ADDITIONAL_INPUT / 8)) + { + throw new IllegalArgumentException("Additional input too large"); + } + + if (_reseedCounter + m > RESEED_MAX) + { + return -1; + } + + if (predictionResistant) { reseed(additionalInput); additionalInput = null; @@ -122,11 +162,9 @@ public class DualECSP800DRBG if (additionalInput != null) { // Note: we ignore the use of pad8 on the additional input as we mandate byte arrays for it. - additionalInput = hash_df(additionalInput, _seedlen); + additionalInput = Utils.hash_df(_digest, additionalInput, _seedlen); } - int m = output.length / _outlen; - for (int i = 0; i < m; i++) { BigInteger t = new BigInteger(1, xor(_s, additionalInput)); @@ -148,6 +186,8 @@ public class DualECSP800DRBG //System.err.println("R: " + new String(Hex.encode(r))); additionalInput = null; + + _reseedCounter++; } if (m * _outlen < output.length) @@ -164,84 +204,29 @@ public class DualECSP800DRBG // Need to preserve length of S as unsigned int. _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(new BigInteger(1, _s)).getX().toBigInteger()); - _reseedCounter++; - return numberOfBits; } + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ public void reseed(byte[] additionalInput) { - if (additionalInput == null) + if (Utils.isTooLarge(additionalInput, MAX_ADDITIONAL_INPUT / 8)) { - additionalInput = new byte[0]; + throw new IllegalArgumentException("Additional input string too large"); } byte[] entropy = _entropySource.getEntropy(); byte[] seedMaterial = Arrays.concatenate(pad8(_s, _seedlen), entropy, additionalInput); - _s = hash_df(seedMaterial, _seedlen); + _s = Utils.hash_df(_digest, seedMaterial, _seedlen); _reseedCounter = 0; } - // 1. temp = the Null string. - // 2. . - // 3. counter = an 8-bit binary value representing the integer "1". - // 4. For i = 1 to len do - // Comment : In step 4.1, no_of_bits_to_return - // is used as a 32-bit string. - // 4.1 temp = temp || Hash (counter || no_of_bits_to_return || - // input_string). - // 4.2 counter = counter + 1. - // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp. - // 6. Return SUCCESS and requested_bits. - private byte[] hash_df(byte[] seedMaterial, int seedLength) - { - byte[] temp = new byte[(seedLength + 7) / 8]; - - int len = temp.length / _digest.getDigestSize(); - int counter = 1; - - byte[] dig = new byte[_digest.getDigestSize()]; - - for (int i = 0; i <= len; i++) - { - _digest.update((byte)counter); - - _digest.update((byte)(seedLength >> 24)); - _digest.update((byte)(seedLength >> 16)); - _digest.update((byte)(seedLength >> 8)); - _digest.update((byte)seedLength); - - _digest.update(seedMaterial, 0, seedMaterial.length); - - _digest.doFinal(dig, 0); - - int bytesToCopy = ((temp.length - i * dig.length) > dig.length) - ? dig.length - : (temp.length - i * dig.length); - System.arraycopy(dig, 0, temp, i * dig.length, bytesToCopy); - - counter++; - } - - // do a left shift to get rid of excess bits. - if (seedLength % 8 != 0) - { - int shift = 8 - (seedLength % 8); - int carry = 0; - - for (int i = 0; i != temp.length; i++) - { - int b = temp[i] & 0xff; - temp[i] = (byte)((b >>> shift) | (carry << (8 - shift))); - carry = b; - } - } - - return temp; - } - private byte[] xor(byte[] a, byte[] b) { if (b == null) diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java index 11bdf963..3ddeaac6 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java @@ -11,18 +11,20 @@ import org.bouncycastle.util.Arrays; public class HMacSP800DRBG implements SP80090DRBG { - private final static int RESEED_MAX = 1 << (48 - 1); + private final static long RESEED_MAX = 1L << (48 - 1); private final static int MAX_BITS_REQUEST = 1 << (19 - 1); private byte[] _K; private byte[] _V; - private int _reseedCounter; + private long _reseedCounter; private EntropySource _entropySource; private Mac _hMac; /** * Construct a SP800-90A Hash DRBG. - * + * <p> + * Minimum entropy requirement is the security strength requested. + * </p> * @param hMac Hash MAC to base the DRBG on. * @param securityStrength security strength required (in bits) * @param entropySource source of entropy to use for seeding/reseeding. @@ -33,7 +35,7 @@ public class HMacSP800DRBG { if (securityStrength > Utils.getMaxSecurityStrength(hMac)) { - throw new IllegalArgumentException("Security strength is not supported by the derivation function"); + throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); } if (entropySource.entropySize() < securityStrength) diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java index 9a318ed1..4ed57163 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java @@ -15,7 +15,7 @@ public class HashSP800DRBG { private final static byte[] ONE = { 0x01 }; - private final static int RESEED_MAX = 1 << (48 - 1); + private final static long RESEED_MAX = 1L << (48 - 1); private final static int MAX_BITS_REQUEST = 1 << (19 - 1); private final static Hashtable seedlens = new Hashtable(); @@ -34,14 +34,16 @@ public class HashSP800DRBG private Digest _digest; private byte[] _V; private byte[] _C; - private int _reseedCounter; + private long _reseedCounter; private EntropySource _entropySource; private int _securityStrength; private int _seedLength; /** * Construct a SP800-90A Hash DRBG. - * + * <p> + * Minimum entropy requirement is the security strength requested. + * </p> * @param digest source digest to use for DRB stream. * @param securityStrength security strength required (in bits) * @param entropySource source of entropy to use for seeding/reseeding. @@ -52,7 +54,7 @@ public class HashSP800DRBG { if (securityStrength > Utils.getMaxSecurityStrength(digest)) { - throw new IllegalArgumentException("Security strength is not supported by the derivation function"); + throw new IllegalArgumentException("Requested security strength is not supported by the derivation function"); } if (entropySource.entropySize() < securityStrength) @@ -75,12 +77,12 @@ public class HashSP800DRBG byte[] entropy = entropySource.getEntropy(); byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); - byte[] seed = hash_df(seedMaterial, _seedLength); + byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength); _V = seed; byte[] subV = new byte[_V.length + 1]; System.arraycopy(_V, 0, subV, 1, _V.length); - _C = hash_df(subV, _seedLength); + _C = Utils.hash_df(_digest, subV, _seedLength); _reseedCounter = 1; } @@ -211,80 +213,17 @@ public class HashSP800DRBG // Comment: Precede with a byte of all zeros. byte[] entropy = _entropySource.getEntropy(); byte[] seedMaterial = Arrays.concatenate(ONE, _V, entropy, additionalInput); - byte[] seed = hash_df(seedMaterial, _seedLength); + byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength); _V = seed; byte[] subV = new byte[_V.length + 1]; subV[0] = 0x00; System.arraycopy(_V, 0, subV, 1, _V.length); - _C = hash_df(subV, _seedLength); + _C = Utils.hash_df(_digest, subV, _seedLength); _reseedCounter = 1; } - - // ---- Internal manipulation --- - // ---- Migrating from the external HashDF class -- - - - // 1. temp = the Null string. - // 2. . - // 3. counter = an 8-bit binary value representing the integer "1". - // 4. For i = 1 to len do - // Comment : In step 4.1, no_of_bits_to_return - // is used as a 32-bit string. - // 4.1 temp = temp || Hash (counter || no_of_bits_to_return || - // input_string). - // 4.2 counter = counter + 1. - // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp. - // 6. Return SUCCESS and requested_bits. - private byte[] hash_df(byte[] seedMaterial, int seedLength) - { - byte[] temp = new byte[seedLength / 8]; - - int len = temp.length / _digest.getDigestSize(); - int counter = 1; - - byte[] dig = new byte[_digest.getDigestSize()]; - - for (int i = 0; i <= len; i++) - { - _digest.update((byte)counter); - - _digest.update((byte)(seedLength >> 24)); - _digest.update((byte)(seedLength >> 16)); - _digest.update((byte)(seedLength >> 8)); - _digest.update((byte)seedLength); - - _digest.update(seedMaterial, 0, seedMaterial.length); - - _digest.doFinal(dig, 0); - - int bytesToCopy = ((temp.length - i * dig.length) > dig.length) - ? dig.length - : (temp.length - i * dig.length); - System.arraycopy(dig, 0, temp, i * dig.length, bytesToCopy); - - counter++; - } - - // do a left shift to get rid of excess bits. - if (seedLength % 8 != 0) - { - int shift = 8 - (seedLength % 8); - int carry = 0; - - for (int i = 0; i != temp.length; i++) - { - int b = temp[i] & 0xff; - temp[i] = (byte)((b >>> shift) | (carry << (8 - shift))); - carry = b; - } - } - - return temp; - } - private byte[] hash(byte[] input) { _digest.update(input, 0, input.length); diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java index 26516dea..f7a41176 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java @@ -34,4 +34,70 @@ class Utils return ((Integer)maxSecurityStrengths.get(name.substring(0, name.indexOf("/")))).intValue(); } + + /** + * Used by both Dual EC and Hash. + */ + static byte[] hash_df(Digest digest, byte[] seedMaterial, int seedLength) + { + // 1. temp = the Null string. + // 2. . + // 3. counter = an 8-bit binary value representing the integer "1". + // 4. For i = 1 to len do + // Comment : In step 4.1, no_of_bits_to_return + // is used as a 32-bit string. + // 4.1 temp = temp || Hash (counter || no_of_bits_to_return || + // input_string). + // 4.2 counter = counter + 1. + // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp. + // 6. Return SUCCESS and requested_bits. + byte[] temp = new byte[(seedLength + 7) / 8]; + + int len = temp.length / digest.getDigestSize(); + int counter = 1; + + byte[] dig = new byte[digest.getDigestSize()]; + + for (int i = 0; i <= len; i++) + { + digest.update((byte)counter); + + digest.update((byte)(seedLength >> 24)); + digest.update((byte)(seedLength >> 16)); + digest.update((byte)(seedLength >> 8)); + digest.update((byte)seedLength); + + digest.update(seedMaterial, 0, seedMaterial.length); + + digest.doFinal(dig, 0); + + int bytesToCopy = ((temp.length - i * dig.length) > dig.length) + ? dig.length + : (temp.length - i * dig.length); + System.arraycopy(dig, 0, temp, i * dig.length, bytesToCopy); + + counter++; + } + + // do a left shift to get rid of excess bits. + if (seedLength % 8 != 0) + { + int shift = 8 - (seedLength % 8); + int carry = 0; + + for (int i = 0; i != temp.length; i++) + { + int b = temp[i] & 0xff; + temp[i] = (byte)((b >>> shift) | (carry << (8 - shift))); + carry = b; + } + } + + return temp; + } + + static boolean isTooLarge(byte[] bytes, int maxBytes) + { + return bytes != null && bytes.length > maxBytes; + } } diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html b/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html index b86c2064..c0061660 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html @@ -1,5 +1,5 @@ <html> <body bgcolor="#ffffff"> -SP800-90A deterministic random bit generators. +SP800-90A deterministic random bit generators, can be used stand alone or in conjunction with SP800SecureRandomBuilder class. </body> </html> diff --git a/src/test/java/org/bouncycastle/crypto/prng/test/AllTests.java b/src/test/java/org/bouncycastle/crypto/prng/test/AllTests.java new file mode 100644 index 00000000..4c0504f8 --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/prng/test/AllTests.java @@ -0,0 +1,39 @@ +package org.bouncycastle.crypto.prng.test; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.util.test.SimpleTestResult; + +public class AllTests + extends TestCase +{ + public void testCrypto() + { + org.bouncycastle.util.test.Test[] tests = RegressionTest.tests; + + for (int i = 0; i != tests.length; i++) + { + SimpleTestResult result = (SimpleTestResult)tests[i].perform(); + + if (!result.isSuccessful()) + { + fail(result.toString()); + } + } + } + + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("Lightweight Crypto PRNG Tests"); + + suite.addTestSuite(AllTests.class); + + return suite; + } +} diff --git a/src/test/java/org/bouncycastle/crypto/test/CTRDRBGTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/CTRDRBGTest.java index c5f2b9d2..45376ace 100644 --- a/src/test/java/org/bouncycastle/crypto/test/CTRDRBGTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/CTRDRBGTest.java @@ -1,9 +1,11 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; +import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.engines.DESedeEngine; import org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG; import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; +import org.bouncycastle.crypto.test.TestEntropySourceProvider; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -334,6 +336,60 @@ public class CTRDRBGTest fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output))); } } + + // Exception tests + SP80090DRBG d; + try + { + d = new CTRSP800DRBG(new AESEngine(), 256, 256, new Bit232EntropyProvider().get(128), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Not enough entropy for security strength required")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new CTRSP800DRBG(new DESedeEngine(), 256, 256, new Bit232EntropyProvider().get(232), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new CTRSP800DRBG(new DESedeEngine(), 168, 256, new Bit232EntropyProvider().get(232), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new CTRSP800DRBG(new AESEngine(), 192, 256, new Bit232EntropyProvider().get(232), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by block cipher and key size")) + { + fail("Wrong exception", e); + } + } } private class Bit232EntropyProvider diff --git a/src/test/java/org/bouncycastle/crypto/test/DRBGTestVector.java b/src/test/java/org/bouncycastle/crypto/prng/test/DRBGTestVector.java index 482db87b..dd6801c5 100644 --- a/src/test/java/org/bouncycastle/crypto/test/DRBGTestVector.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/DRBGTestVector.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/org/bouncycastle/crypto/test/DualECDRBGTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/DualECDRBGTest.java index 9ae53bfb..9e33cbaf 100644 --- a/src/test/java/org/bouncycastle/crypto/test/DualECDRBGTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/DualECDRBGTest.java @@ -1,15 +1,17 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; +import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA384Digest; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.prng.drbg.DualECSP800DRBG; import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; +import org.bouncycastle.crypto.test.TestEntropySourceProvider; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; /** - * Dual EC SP800-90 DRBG + * Dual EC SP800-90 DRBG test */ public class DualECDRBGTest extends SimpleTest @@ -295,6 +297,48 @@ public class DualECDRBGTest fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output))); } } + + // Exception tests + // + SP80090DRBG d; + try + { + d = new DualECSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(128), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("EntropySource must provide between 256 and 4096 bits")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new DualECSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(1 << (13 - 1) + 1), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("EntropySource must provide between 256 and 4096 bits")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new DualECSP800DRBG(new SHA1Digest(), 256, new SHA256EntropyProvider().get(256), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by digest")) + { + fail("Wrong exception", e); + } + } } private class SHA256EntropyProvider diff --git a/src/test/java/org/bouncycastle/crypto/test/HMacDRBGTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/HMacDRBGTest.java index 9a154fe5..697f7b15 100644 --- a/src/test/java/org/bouncycastle/crypto/test/HMacDRBGTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/HMacDRBGTest.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA256Digest; @@ -7,6 +7,7 @@ import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.prng.drbg.HMacSP800DRBG; import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; +import org.bouncycastle.crypto.test.TestEntropySourceProvider; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -411,6 +412,22 @@ public class HMacDRBGTest fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output))); } } + + // Exception tests + // + SP80090DRBG d; + try + { + d = new HMacSP800DRBG(new HMac(new SHA256Digest()), 256, new SHA256EntropyProvider().get(128), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Not enough entropy for security strength required")) + { + fail("Wrong exception", e); + } + } } private class SHA1EntropyProvider diff --git a/src/test/java/org/bouncycastle/crypto/test/HashDRBGTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/HashDRBGTest.java index db194044..beacc321 100644 --- a/src/test/java/org/bouncycastle/crypto/test/HashDRBGTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/HashDRBGTest.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.test; +package org.bouncycastle.crypto.prng.test; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA256Digest; @@ -6,6 +6,7 @@ import org.bouncycastle.crypto.digests.SHA384Digest; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG; import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; +import org.bouncycastle.crypto.test.TestEntropySourceProvider; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -371,6 +372,35 @@ public class HashDRBGTest fail("Test #" + (i + 1) + ".2 failed, expected " + new String(Hex.encode(tv.expectedValue(1))) + " got " + new String(Hex.encode(output))); } } + + // Exception tests + // + SP80090DRBG d; + try + { + d = new HashSP800DRBG(new SHA256Digest(), 256, new SHA256EntropyProvider().get(128), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Not enough entropy for security strength required")) + { + fail("Wrong exception", e); + } + } + + try + { + d = new HashSP800DRBG(new SHA1Digest(), 256, new SHA256EntropyProvider().get(256), null, null); + fail("no exception thrown"); + } + catch (IllegalArgumentException e) + { + if (!e.getMessage().equals("Requested security strength is not supported by the derivation function")) + { + fail("Wrong exception", e); + } + } } private class SHA1EntropyProvider diff --git a/src/test/java/org/bouncycastle/crypto/random/test/RegressionTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/RegressionTest.java index e2f1e79c..d1f6f435 100644 --- a/src/test/java/org/bouncycastle/crypto/random/test/RegressionTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/RegressionTest.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.random.test; +package org.bouncycastle.crypto.prng.test; import org.bouncycastle.util.test.Test; import org.bouncycastle.util.test.TestResult; @@ -6,6 +6,10 @@ import org.bouncycastle.util.test.TestResult; public class RegressionTest { public static Test[] tests = { + new CTRDRBGTest(), + new DualECDRBGTest(), + new HashDRBGTest(), + new HMacDRBGTest(), new SP800RandomTest() }; diff --git a/src/test/java/org/bouncycastle/crypto/random/test/SP800RandomTest.java b/src/test/java/org/bouncycastle/crypto/prng/test/SP800RandomTest.java index 5a7ad692..c0cbb276 100644 --- a/src/test/java/org/bouncycastle/crypto/random/test/SP800RandomTest.java +++ b/src/test/java/org/bouncycastle/crypto/prng/test/SP800RandomTest.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.random.test; +package org.bouncycastle.crypto.prng.test; import java.security.SecureRandom; @@ -7,7 +7,6 @@ import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.engines.DESedeEngine; import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder; -import org.bouncycastle.crypto.test.DRBGTestVector; import org.bouncycastle.crypto.test.TestEntropySourceProvider; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; diff --git a/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index aea3003d..90e63879 100644 --- a/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -112,11 +112,7 @@ public class RegressionTest new NullTest(), new DSTU4145Test(), new SipHashTest(), - new OCBTest(), - new HashDRBGTest(), - new HMacDRBGTest(), - new CTRDRBGTest(), - new DualECDRBGTest() + new OCBTest() }; public static void main( |