diff options
author | David Hook <dgh@cryptoworkshop.com> | 2013-05-17 13:56:55 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2013-05-17 13:56:55 +0400 |
commit | 297de902c71c338522bc37401206d609ee0e8fa9 (patch) | |
tree | 910556a1970483ce79163fee0df85fc28dd91040 | |
parent | fea5a1cd30d318739c7b703a0c1d33f953683186 (diff) |
initial KEM check in
6 files changed, 639 insertions, 1 deletions
diff --git a/src/main/java/org/bouncycastle/crypto/KeyEncapsulation.java b/src/main/java/org/bouncycastle/crypto/KeyEncapsulation.java new file mode 100755 index 00000000..16744573 --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/KeyEncapsulation.java @@ -0,0 +1,22 @@ +package org.bouncycastle.crypto; + +/** + * The basic interface for key encapsulation mechanisms. + */ +public interface KeyEncapsulation +{ + /** + * Initialise the key encapsulation mechanism. + */ + public void init(CipherParameters param); + + /** + * Encapsulate a randomly generated session key. + */ + public CipherParameters encrypt(byte[] out, int outOff, int keyLen); + + /** + * Decapsulate an encapsulated session key. + */ + public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen); +} diff --git a/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java b/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java new file mode 100755 index 00000000..7332f264 --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java @@ -0,0 +1,254 @@ +package org.bouncycastle.crypto.kems; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.KeyEncapsulation; +import org.bouncycastle.crypto.params.ECKeyParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.params.KDFParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.BigIntegers; + +/** + * The ECIES Key Encapsulation Mechanism (ECIES-KEM) from ISO 18033-2. + */ +public class ECIESKeyEncapsulation + implements KeyEncapsulation +{ + private DerivationFunction kdf; + private SecureRandom rnd; + private ECKeyParameters key; + private boolean CofactorMode; + private boolean OldCofactorMode; + private boolean SingleHashMode; + + /** + * Set up the ECIES-KEM. + * + * @param kdf the key derivation function to be used. + * @param rnd the random source for the session key. + */ + public ECIESKeyEncapsulation( + DerivationFunction kdf, + SecureRandom rnd) + { + this.kdf = kdf; + this.rnd = rnd; + this.CofactorMode = false; + this.OldCofactorMode = false; + this.SingleHashMode = false; + } + + /** + * Set up the ECIES-KEM. + * + * @param kdf the key derivation function to be used. + * @param rnd the random source for the session key. + * @param cofactorMode true to use the new cofactor ECDH. + * @param oldCofactorMode true to use the old cofactor ECDH. + * @param singleHashMode true to use single hash mode. + */ + public ECIESKeyEncapsulation( + DerivationFunction kdf, + SecureRandom rnd, + boolean cofactorMode, + boolean oldCofactorMode, + boolean singleHashMode) + { + this.kdf = kdf; + this.rnd = rnd; + + // If both cofactorMode and oldCofactorMode are set to true + // then the implementation will use the new cofactor ECDH + this.CofactorMode = cofactorMode; + this.OldCofactorMode = oldCofactorMode; + this.SingleHashMode = singleHashMode; + } + + /** + * Initialise the ECIES-KEM. + * + * @param key the recipient's public (for encryption) or private (for decryption) key. + */ + public void init(CipherParameters key) + throws IllegalArgumentException + { + if (!(key instanceof ECKeyParameters)) + { + throw new IllegalArgumentException("EC key required"); + } + else + { + this.key = (ECKeyParameters)key; + } + } + + /** + * Generate and encapsulate a random session key. + * + * @param out the output buffer for the encapsulated key. + * @param outOff the offset for the output buffer. + * @param keyLen the length of the session key. + * @return the random session key. + */ + public CipherParameters encrypt(byte[] out, int outOff, int keyLen) + throws IllegalArgumentException + { + if (!(key instanceof ECPublicKeyParameters)) + { + throw new IllegalArgumentException("Public key required for encryption"); + } + + BigInteger n = key.getParameters().getN(); + BigInteger h = key.getParameters().getH(); + + // Generate the ephemeral key pair + BigInteger r = BigIntegers.createRandomInRange(BigInteger.ONE, n, rnd); + ECPoint gTilde = key.getParameters().getG().multiply(r); + + // Encode the ephemeral public key + byte[] C = gTilde.getEncoded(); + System.arraycopy(C, 0, out, outOff, C.length); + + // Compute the static-ephemeral key agreement + BigInteger rPrime; + if (CofactorMode) + { + rPrime = r.multiply(h).mod(n); + } + else + { + rPrime = r; + } + + ECPoint hTilde = ((ECPublicKeyParameters) key).getQ().multiply(rPrime); + + // Encode the shared secret value + int PEHlen = (key.getParameters().getCurve().getFieldSize()+7)/8; + byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger()); + + // Initialise the KDF + byte[] kdfInput; + if (SingleHashMode) + { + kdfInput = new byte[C.length + PEH.length]; + System.arraycopy(C, 0, kdfInput, 0, C.length); + System.arraycopy(PEH, 0, kdfInput, C.length, PEH.length); + } + else + { + kdfInput = PEH; + } + + kdf.init(new KDFParameters(kdfInput, null)); + + // Generate the secret key + byte[] K = new byte[keyLen]; + kdf.generateBytes(K, 0, K.length); + + // Return the ciphertext + return new KeyParameter(K); + } + + /** + * Generate and encapsulate a random session key. + * + * @param out the output buffer for the encapsulated key. + * @param keyLen the length of the session key. + * @return the random session key. + */ + public CipherParameters encrypt(byte[] out, int keyLen) + { + return encrypt(out, 0, keyLen); + } + + /** + * Decrypt an encapsulated session key. + * + * @param in the input buffer for the encapsulated key. + * @param inOff the offset for the input buffer. + * @param inLen the length of the encapsulated key. + * @param keyLen the length of the session key. + * @return the session key. + */ + public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen) + throws IllegalArgumentException + { + if (!(key instanceof ECPrivateKeyParameters)) + { + throw new IllegalArgumentException("Private key required for encryption"); + } + + BigInteger n = key.getParameters().getN(); + BigInteger h = key.getParameters().getH(); + + // Decode the ephemeral public key + byte[] C = new byte[inLen]; + System.arraycopy(in, inOff, C, 0, inLen); + ECPoint gTilde = key.getParameters().getCurve().decodePoint(C); + + // Compute the static-ephemeral key agreement + ECPoint gHat; + if ((CofactorMode) || (OldCofactorMode)) + { + gHat = gTilde.multiply(h); + } + else + { + gHat = gTilde; + } + + BigInteger xHat; + if(CofactorMode) + { + xHat = ((ECPrivateKeyParameters) key).getD().multiply(h.modInverse(n)).mod(n); + } + else + { + xHat = ((ECPrivateKeyParameters) key).getD(); + } + + ECPoint hTilde = gHat.multiply(xHat); + + // Encode the shared secret value + int PEHlen = (key.getParameters().getCurve().getFieldSize()+7)/8; + byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger()); + + // Initialise the KDF + byte[] kdfInput; + if (SingleHashMode) + { + kdfInput = new byte[C.length + PEH.length]; + System.arraycopy(C, 0, kdfInput, 0, C.length); + System.arraycopy(PEH, 0, kdfInput, C.length, PEH.length); + } + else + { + kdfInput = PEH; + } + kdf.init(new KDFParameters(kdfInput, null)); + + // Generate the secret key + byte[] K = new byte[keyLen]; + kdf.generateBytes(K, 0, K.length); + + return new KeyParameter(K); + } + + /** + * Decrypt an encapsulated session key. + * + * @param in the input buffer for the encapsulated key. + * @param keyLen the length of the session key. + * @return the session key. + */ + public CipherParameters decrypt(byte[] in, int keyLen) + { + return decrypt(in, 0, in.length, keyLen); + } +} diff --git a/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java b/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java new file mode 100755 index 00000000..5ffd356a --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/kems/RSAKeyEncapsulation.java @@ -0,0 +1,161 @@ +package org.bouncycastle.crypto.kems; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.KeyEncapsulation; +import org.bouncycastle.crypto.params.KDFParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.RSAKeyParameters; +import org.bouncycastle.util.BigIntegers; + +/** + * The RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2. + */ +public class RSAKeyEncapsulation + implements KeyEncapsulation +{ + private DerivationFunction kdf; + private SecureRandom rnd; + private RSAKeyParameters key; + + /** + * Set up the RSA-KEM. + * + * @param kdf the key derivation function to be used. + * @param rnd the random source for the session key. + */ + public RSAKeyEncapsulation( + DerivationFunction kdf, + SecureRandom rnd) + { + this.kdf = kdf; + this.rnd = rnd; + } + + + /** + * Initialise the RSA-KEM. + * + * @param key the recipient's public (for encryption) or private (for decryption) key. + */ + public void init(CipherParameters key) + throws IllegalArgumentException + { + if (!(key instanceof RSAKeyParameters)) + { + throw new IllegalArgumentException("RSA key required"); + } + else + { + this.key = (RSAKeyParameters)key; + } + } + + + /** + * Generate and encapsulate a random session key. + * + * @param out the output buffer for the encapsulated key. + * @param outOff the offset for the output buffer. + * @param keyLen the length of the random session key. + * @return the random session key. + */ + public CipherParameters encrypt(byte[] out, int outOff, int keyLen) + throws IllegalArgumentException + { + if (key.isPrivate()) + { + throw new IllegalArgumentException("Public key required for encryption"); + } + + BigInteger n = key.getModulus(); + BigInteger e = key.getExponent(); + + // Generate the ephemeral random and encode it + BigInteger r = BigIntegers.createRandomInRange(BigInteger.ZERO, n.subtract(BigInteger.ONE), rnd); + byte[] R = BigIntegers.asUnsignedByteArray((n.bitLength()+7)/8, r); + + // Encrypt the random and encode it + BigInteger c = r.modPow(e, n); + byte[] C = BigIntegers.asUnsignedByteArray((n.bitLength()+7)/8, c); + System.arraycopy(C, 0, out, outOff, C.length); + + + // Initialise the KDF + kdf.init(new KDFParameters(R, null)); + + // Generate the secret key + byte[] K = new byte[keyLen]; + kdf.generateBytes(K, 0, K.length); + + return new KeyParameter(K); + } + + + /** + * Generate and encapsulate a random session key. + * + * @param out the output buffer for the encapsulated key. + * @param keyLen the length of the random session key. + * @return the random session key. + */ + public CipherParameters encrypt(byte[] out, int keyLen) + { + return encrypt(out, 0, keyLen); + } + + + /** + * Decrypt an encapsulated session key. + * + * @param in the input buffer for the encapsulated key. + * @param inOff the offset for the input buffer. + * @param inLen the length of the encapsulated key. + * @param keyLen the length of the session key. + * @return the session key. + */ + public CipherParameters decrypt(byte[] in, int inOff, int inLen, int keyLen) + throws IllegalArgumentException + { + if (!key.isPrivate()) + { + throw new IllegalArgumentException("Private key required for decryption"); + } + + BigInteger n = key.getModulus(); + BigInteger d = key.getExponent(); + + // Decode the input + byte[] C = new byte[inLen]; + System.arraycopy(in, inOff, C, 0, C.length); + BigInteger c = new BigInteger(1, C); + + // Decrypt the ephemeral random and encode it + BigInteger r = c.modPow(d, n); + byte[] R = BigIntegers.asUnsignedByteArray((n.bitLength()+7)/8, r); + + // Initialise the KDF + kdf.init(new KDFParameters(R, null)); + + // Generate the secret key + byte[] K = new byte[keyLen]; + kdf.generateBytes(K, 0, K.length); + + return new KeyParameter(K); + } + + /** + * Decrypt an encapsulated session key. + * + * @param in the input buffer for the encapsulated key. + * @param keyLen the length of the session key. + * @return the session key. + */ + public CipherParameters decrypt(byte[] in, int keyLen) + { + return decrypt(in, 0, in.length, keyLen); + } +} diff --git a/src/test/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java b/src/test/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java new file mode 100755 index 00000000..5d0b76ca --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/test/ECIESKeyEncapsulationTest.java @@ -0,0 +1,138 @@ +package org.bouncycastle.crypto.test; + +import java.security.SecureRandom; + +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.generators.KDF2BytesGenerator; +import org.bouncycastle.crypto.kems.ECIESKeyEncapsulation; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.test.SimpleTest; + +/** + * Tests for the ECIES Key Encapsulation Mechanism + */ +public class ECIESKeyEncapsulationTest + extends SimpleTest +{ + public String getName() + { + return "ECIESKeyEncapsulation"; + } + + public void performTest() + throws Exception + { + + // Set EC domain parameters and generate key pair + X9ECParameters spec = SECNamedCurves.getByName("secp224r1"); + ECDomainParameters ecDomain = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN()); + ECKeyPairGenerator ecGen = new ECKeyPairGenerator(); + + ecGen.init(new ECKeyGenerationParameters(ecDomain, new SecureRandom())); + + AsymmetricCipherKeyPair keys = ecGen.generateKeyPair(); + + // Set ECIES-KEM parameters + ECIESKeyEncapsulation kem; + KDF2BytesGenerator kdf = new KDF2BytesGenerator(new SHA1Digest()); + SecureRandom rnd = new SecureRandom(); + byte[] out = new byte[57]; + KeyParameter key1, key2; + + // Test basic ECIES-KEM + kem = new ECIESKeyEncapsulation(kdf, rnd); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed basic test"); + } + + // Test ECIES-KEM using new cofactor mode + kem = new ECIESKeyEncapsulation(kdf, rnd, true, false, false); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed cofactor test"); + } + + // Test ECIES-KEM using old cofactor mode + kem = new ECIESKeyEncapsulation(kdf, rnd, false, true, false); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed old cofactor test"); + } + + // Test ECIES-KEM using single hash mode + kem = new ECIESKeyEncapsulation(kdf, rnd, false, false, true); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed single hash test"); + } + + // Test ECIES-KEM using new cofactor mode and single hash mode + kem = new ECIESKeyEncapsulation(kdf, rnd, true, false, true); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed cofactor and single hash test"); + } + + // Test ECIES-KEM using old cofactor mode and single hash mode + kem = new ECIESKeyEncapsulation(kdf, rnd, false, true, true); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed old cofactor and single hash test"); + } + } + + public static void main( + String[] args) + { + runTest(new ECIESKeyEncapsulationTest()); + } +} diff --git a/src/test/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java b/src/test/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java new file mode 100755 index 00000000..50e751b7 --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/test/RSAKeyEncapsulationTest.java @@ -0,0 +1,61 @@ +package org.bouncycastle.crypto.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.generators.KDF2BytesGenerator; +import org.bouncycastle.crypto.generators.RSAKeyPairGenerator; +import org.bouncycastle.crypto.kems.RSAKeyEncapsulation; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.RSAKeyGenerationParameters; +import org.bouncycastle.util.test.SimpleTest; + +/** + * Tests for the RSA Key Encapsulation Mechanism + */ +public class RSAKeyEncapsulationTest + extends SimpleTest +{ + public String getName() + { + return "RSAKeyEncapsulation"; + } + + public void performTest() + throws Exception + { + // Generate RSA key pair + RSAKeyPairGenerator rsaGen = new RSAKeyPairGenerator(); + rsaGen.init(new RSAKeyGenerationParameters(BigInteger.valueOf(65537), new SecureRandom(), 1024, 5)); + AsymmetricCipherKeyPair keys = rsaGen.generateKeyPair(); + + // Set RSA-KEM parameters + RSAKeyEncapsulation kem; + KDF2BytesGenerator kdf = new KDF2BytesGenerator(new SHA1Digest()); + SecureRandom rnd = new SecureRandom(); + byte[] out = new byte[128]; + KeyParameter key1, key2; + + // Test RSA-KEM + kem = new RSAKeyEncapsulation(kdf, rnd); + + kem.init(keys.getPublic()); + key1 = (KeyParameter)kem.encrypt(out, 128); + + kem.init(keys.getPrivate()); + key2 = (KeyParameter)kem.decrypt(out, 128); + + if (!areEqual(key1.getKey(), key2.getKey())) + { + fail("failed test"); + } + } + + public static void main( + String[] args) + { + runTest(new RSAKeyEncapsulationTest()); + } +} diff --git a/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index 429780e6..3c3fa8bb 100644 --- a/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -113,7 +113,9 @@ public class RegressionTest new DSTU4145Test(), new SipHashTest(), new OCBTest(), - new NonMemoableDigestTest() + new NonMemoableDigestTest(), + new RSAKeyEncapsulationTest(), + new ECIESKeyEncapsulationTest() }; public static void main( |