diff options
author | David Hook <dgh@cryptoworkshop.com> | 2013-05-11 04:39:58 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2013-05-11 04:39:58 +0400 |
commit | c5bb1a5fdc5cc68a650d97e86039823560525197 (patch) | |
tree | c8b5872b8f273d55f89d5c035b459b27eba7c663 | |
parent | f733f717fb13387a61dedc0a57d23487069d9b67 (diff) |
finishing touches for Hash/HMAC DRBG
11 files changed, 231 insertions, 78 deletions
diff --git a/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java b/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java index 38527107..9f1d0427 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java +++ b/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java @@ -43,6 +43,11 @@ public class BasicEntropySourceProvider { return _sr.generateSeed((bitsRequired + 7) / 8); } + + public int entropySize() + { + return bitsRequired; + } }; } } diff --git a/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java b/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java index 56a19711..53bc549d 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java +++ b/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java @@ -2,7 +2,24 @@ package org.bouncycastle.crypto.prng; public interface EntropySource { + /** + * Return whether or not this entropy source is regarded as prediction resistant. + * + * @return true if it is, false otherwise. + */ boolean isPredictionResistant(); + /** + * Return a byte array of entropy. + * + * @return entropy bytes. + */ byte[] getEntropy(); + + /** + * Return the number of bits of entropy this source can produce. + * + * @return size in bits of the return value of getEntropy. + */ + int entropySize(); } diff --git a/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java b/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java index c5c5827a..e517f4de 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java +++ b/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java @@ -174,7 +174,7 @@ public class SP800SecureRandomBuilder public SP80090DRBG get(EntropySource entropySource) { - return new HashSP800DRBG(digest, entropySource, nonce, personalizationString, securityStrength); + return new HashSP800DRBG(digest, securityStrength, entropySource, personalizationString, nonce); } } @@ -218,7 +218,7 @@ public class SP800SecureRandomBuilder public SP80090DRBG get(EntropySource entropySource) { - return new HMacSP800DRBG(hMac, entropySource, nonce, personalizationString, securityStrength); + return new HMacSP800DRBG(hMac, securityStrength, entropySource, personalizationString, nonce); } } 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 de448f7b..6d342309 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java @@ -5,24 +5,45 @@ import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.util.Arrays; +/** + * A SP800-90A HMAC DRBG. + */ public class HMacSP800DRBG implements SP80090DRBG { + private final static int RESEED_MAX = 1 << (48 - 1); + private final static int MAX_BITS_REQUEST = 1 << (19 - 1); + private byte[] _K; private byte[] _V; private int _reseedCounter; private EntropySource _entropySource; private Mac _hMac; - public HMacSP800DRBG(Mac hMac, EntropySource entropySource, byte[] nonce, - byte[] personalisationString, int securityStrength) + /** + * Construct a SP800-90A Hash DRBG. + * + * @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. + * @param personalisationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public HMacSP800DRBG(Mac hMac, int securityStrength, EntropySource entropySource, byte[] personalisationString, byte[] nonce) { - // TODO: validate security strength + if (securityStrength > Utils.getMaxSecurityStrength(hMac)) + { + throw new IllegalArgumentException("Security strength is not supported by the derivation function"); + } + + if (entropySource.entropySize() < securityStrength) + { + throw new IllegalArgumentException("Not enough entropy for security strength required"); + } _entropySource = entropySource; _hMac = hMac; - // TODO: validate entropy length byte[] entropy = entropySource.getEntropy(); byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalisationString); @@ -64,12 +85,29 @@ public class HMacSP800DRBG _hMac.doFinal(_V, 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) { - // TODO: check reseed counter - int numberOfBits = output.length * 8; + if (numberOfBits > MAX_BITS_REQUEST) + { + throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST); + } + + if (_reseedCounter > RESEED_MAX) + { + return -1; + } + if (predictionResistant) { reseed(additionalInput); @@ -114,6 +152,11 @@ public class HMacSP800DRBG return numberOfBits; } + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ public void reseed(byte[] additionalInput) { byte[] entropy = _entropySource.getEntropy(); 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 489054b0..f0c6ead7 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java @@ -7,10 +7,17 @@ import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; -public class HashSP800DRBG implements SP80090DRBG +/** + * A SP800-90A Hash DRBG. + */ +public class HashSP800DRBG + implements SP80090DRBG { private final static byte[] ONE = { 0x01 }; - private final static int RESEED_MAX = 100000; + + private final static int RESEED_MAX = 1 << (48 - 1); + private final static int MAX_BITS_REQUEST = 1 << (19 - 1); + private final static Hashtable seedlens = new Hashtable(); static @@ -24,23 +31,35 @@ public class HashSP800DRBG implements SP80090DRBG seedlens.put("SHA-512", Integers.valueOf(888)); } - private Digest _digest; - private byte[] _V; - private byte[] _C; - private int _reseedCounter; + private Digest _digest; + private byte[] _V; + private byte[] _C; + private int _reseedCounter; private EntropySource _entropySource; - private int _securityStrength; - private int _seedLength; - - public HashSP800DRBG(Digest digest, EntropySource entropySource, byte[] nonce, - byte[] personalisationString, int securityStrength) + private int _securityStrength; + private int _seedLength; + + /** + * Construct a SP800-90A Hash DRBG. + * + * @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. + * @param personalisationString personalization string to distinguish this DRBG (may be null). + * @param nonce nonce to further distinguish this DRBG (may be null). + */ + public HashSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalisationString, byte[] nonce) { - if (securityStrength > digest.getDigestSize() * 8) // TODO: this may, or may not be correct, but it's good enough for now + if (securityStrength > Utils.getMaxSecurityStrength(digest)) { - throw new IllegalStateException( - "Security strength is not supported by the derivation function"); + throw new IllegalArgumentException("Security strength is not supported by the derivation function"); } - + + if (entropySource.entropySize() < securityStrength) + { + throw new IllegalArgumentException("Not enough entropy for security strength required"); + } + _digest = digest; _entropySource = entropySource; _securityStrength = securityStrength; @@ -62,31 +81,47 @@ public class HashSP800DRBG implements SP80090DRBG byte[] subV = new byte[_V.length + 1]; System.arraycopy(_V, 0, subV, 1, _V.length); _C = hash_df(subV, _seedLength); - _reseedCounter = 1; - -// System.out.println("Constructor V: "+ new String(Hex.encode(_V))); -// System.out.println("Constructor C: "+ new String(Hex.encode(_C))); + _reseedCounter = 1; } - // 1. If reseed_counter > reseed_interval, then return an indication that a - // reseed is required. - // 2. If (additional_input != Null), then do - // 2.1 w = Hash (0x02 || V || additional_input). - // 2.2 V = (V + w) mod 2^seedlen - // . - // 3. (returned_bits) = Hashgen (requested_number_of_bits, V). - // 4. H = Hash (0x03 || V). - // 5. V = (V + H + C + reseed_counter) mod 2^seedlen - // . - // 6. reseed_counter = reseed_counter + 1. - // 7. Return SUCCESS, returned_bits, and the new values of V, C, and - // reseed_counter for the new_working_state. + /** + * 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) { + // 1. If reseed_counter > reseed_interval, then return an indication that a + // reseed is required. + // 2. If (additional_input != Null), then do + // 2.1 w = Hash (0x02 || V || additional_input). + // 2.2 V = (V + w) mod 2^seedlen + // . + // 3. (returned_bits) = Hashgen (requested_number_of_bits, V). + // 4. H = Hash (0x03 || V). + // 5. V = (V + H + C + reseed_counter) mod 2^seedlen + // . + // 6. reseed_counter = reseed_counter + 1. + // 7. Return SUCCESS, returned_bits, and the new values of V, C, and + // reseed_counter for the new_working_state. int numberOfBits = output.length*8; - - if (predictionResistant || _reseedCounter > RESEED_MAX) + + if (numberOfBits > MAX_BITS_REQUEST) + { + throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST); + } + + if (_reseedCounter > RESEED_MAX) + { + return -1; + } + + if (predictionResistant) { reseed(additionalInput); additionalInput = null; @@ -125,11 +160,10 @@ public class HashSP800DRBG implements SP80090DRBG c[3] = (byte)_reseedCounter; addTo(_V, c); + _reseedCounter++; System.arraycopy(rv, 0, output, 0, output.length); -// System.out.println("Generate V: "+ new String(Hex.encode(_V))); -// System.out.println("Generate C: "+ new String(Hex.encode(_C))); return numberOfBits; } @@ -155,47 +189,37 @@ public class HashSP800DRBG implements SP80090DRBG } } - // 1. seed_material = 0x01 || V || entropy_input || additional_input. - // - // 2. seed = Hash_df (seed_material, seedlen). - // - // 3. V = seed. - // - // 4. C = Hash_df ((0x00 || V), seedlen). - // - // 5. reseed_counter = 1. - // - // 6. Return V, C, and reseed_counter for the new_working_state. - // - // Comment: Precede with a byte of all zeros. + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ public void reseed(byte[] additionalInput) { - if (additionalInput == null) - { - additionalInput = new byte[0]; - } - + // 1. seed_material = 0x01 || V || entropy_input || additional_input. + // + // 2. seed = Hash_df (seed_material, seedlen). + // + // 3. V = seed. + // + // 4. C = Hash_df ((0x00 || V), seedlen). + // + // 5. reseed_counter = 1. + // + // 6. Return V, C, and reseed_counter for the new_working_state. + // + // Comment: Precede with a byte of all zeros. byte[] entropy = _entropySource.getEntropy(); - -// System.out.println("Reseed Entropy: "+ new String(Hex.encode(entropy))); - byte[] seedMaterial = Arrays.concatenate(ONE, _V, entropy, additionalInput); - -// System.out.println("Reseed SeedMaterial: "+ new String(Hex.encode(seedMaterial))); - byte[] seed = hash_df(seedMaterial, _seedLength); - -// System.out.println("Reseed Seed: "+ new String(Hex.encode(seed))); _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); + _reseedCounter = 1; - -// System.out.println("Reseed V: "+ new String(Hex.encode(_V))); -// System.out.println("Reseed C: "+ new String(Hex.encode(_C))); } @@ -303,4 +327,4 @@ public class HashSP800DRBG implements SP80090DRBG return W; } -} +}
\ No newline at end of file diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java index c93eb54c..8a132708 100644 --- a/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java @@ -2,8 +2,22 @@ package org.bouncycastle.crypto.prng.drbg; public interface SP80090DRBG { - + + /** + * 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. + */ int generate(byte[] output, byte[] additionalInput, boolean predictionResistant); + /** + * Reseed the DRBG. + * + * @param additionalInput additional input to be added to the DRBG in this step. + */ void reseed(byte[] additionalInput); } diff --git a/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java b/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java new file mode 100644 index 00000000..26516dea --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java @@ -0,0 +1,37 @@ +package org.bouncycastle.crypto.prng.drbg; + +import java.util.Hashtable; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.util.Integers; + +class Utils +{ + static final Hashtable maxSecurityStrengths = new Hashtable(); + + static + { + maxSecurityStrengths.put("SHA-1", Integers.valueOf(128)); + + maxSecurityStrengths.put("SHA-224", Integers.valueOf(192)); + maxSecurityStrengths.put("SHA-256", Integers.valueOf(256)); + maxSecurityStrengths.put("SHA-384", Integers.valueOf(256)); + maxSecurityStrengths.put("SHA-512", Integers.valueOf(256)); + + maxSecurityStrengths.put("SHA-512/224", Integers.valueOf(192)); + maxSecurityStrengths.put("SHA-512/256", Integers.valueOf(256)); + } + + static int getMaxSecurityStrength(Digest d) + { + return ((Integer)maxSecurityStrengths.get(d.getAlgorithmName())).intValue(); + } + + static int getMaxSecurityStrength(Mac m) + { + String name = m.getAlgorithmName(); + + return ((Integer)maxSecurityStrengths.get(name.substring(0, name.indexOf("/")))).intValue(); + } +} diff --git a/src/main/java/org/bouncycastle/util/Arrays.java b/src/main/java/org/bouncycastle/util/Arrays.java index bbabc8b8..6c825cd2 100644 --- a/src/main/java/org/bouncycastle/util/Arrays.java +++ b/src/main/java/org/bouncycastle/util/Arrays.java @@ -716,13 +716,21 @@ public final class Arrays return rv; } + else if (d == null) + { + return concatenate(a, b, c); + } + else if (c == null) + { + return concatenate(a, b, d); + } else if (b == null) { return concatenate(a, c, d); } else { - return concatenate(a, b, d); + return concatenate(b, c, d); } } } diff --git a/src/test/java/org/bouncycastle/crypto/test/HMacDRBGTest.java b/src/test/java/org/bouncycastle/crypto/test/HMacDRBGTest.java index 06f2b25b..9a154fe5 100644 --- a/src/test/java/org/bouncycastle/crypto/test/HMacDRBGTest.java +++ b/src/test/java/org/bouncycastle/crypto/test/HMacDRBGTest.java @@ -388,7 +388,7 @@ public class HMacDRBGTest byte[] nonce = tv.nonce(); byte[] personalisationString = tv.personalizationString(); - SP80090DRBG d = new HMacSP800DRBG(new HMac(tv.getDigest()), tv.entropySource(), nonce, personalisationString, tv.securityStrength()); + SP80090DRBG d = new HMacSP800DRBG(new HMac(tv.getDigest()), tv.securityStrength(), tv.entropySource(), personalisationString, nonce); byte[] output = new byte[tv.expectedValue(0).length]; diff --git a/src/test/java/org/bouncycastle/crypto/test/HashDRBGTest.java b/src/test/java/org/bouncycastle/crypto/test/HashDRBGTest.java index 93920d64..db194044 100644 --- a/src/test/java/org/bouncycastle/crypto/test/HashDRBGTest.java +++ b/src/test/java/org/bouncycastle/crypto/test/HashDRBGTest.java @@ -348,7 +348,7 @@ public class HashDRBGTest byte[] nonce = tv.nonce(); byte[] personalisationString = tv.personalizationString(); - SP80090DRBG d = new HashSP800DRBG(tv.getDigest(), tv.entropySource(), nonce, personalisationString, tv.securityStrength()); + SP80090DRBG d = new HashSP800DRBG(tv.getDigest(), tv.securityStrength(), tv.entropySource(), personalisationString, nonce); byte[] output = new byte[tv.expectedValue(0).length]; diff --git a/src/test/java/org/bouncycastle/crypto/test/TestEntropySourceProvider.java b/src/test/java/org/bouncycastle/crypto/test/TestEntropySourceProvider.java index 08576977..9aee5521 100644 --- a/src/test/java/org/bouncycastle/crypto/test/TestEntropySourceProvider.java +++ b/src/test/java/org/bouncycastle/crypto/test/TestEntropySourceProvider.java @@ -36,6 +36,11 @@ public class TestEntropySourceProvider return rv; } + + public int entropySize() + { + return bitsRequired; + } }; } } |