diff options
author | David Hook <dgh@cryptoworkshop.com> | 2013-05-20 05:24:54 +0400 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2013-05-20 05:24:54 +0400 |
commit | b1bbd3b14d495baee344b263020b338c07419980 (patch) | |
tree | 1c4cb243ee2148d2df6dd37f6f11b5e6e4851983 | |
parent | ecc849da20a41aee8b4c6451015eca26dc3f1fb8 (diff) |
BJA-451 first cut of GMAC.
15 files changed, 608 insertions, 17 deletions
diff --git a/src/main/java/org/bouncycastle/crypto/macs/GMac.java b/src/main/java/org/bouncycastle/crypto/macs/GMac.java new file mode 100644 index 00000000..91bfe13d --- /dev/null +++ b/src/main/java/org/bouncycastle/crypto/macs/GMac.java @@ -0,0 +1,108 @@ +package org.bouncycastle.crypto.macs; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication + * 800-38D. + * <p> + * GMac is an invocation of the GCM mode where no data is encrypted (i.e. all input data to the Mac + * is processed as additional authenticated data with the underlying GCM block cipher). + */ +public class GMac implements Mac +{ + private final GCMBlockCipher cipher; + private final int macSizeBits; + + /** + * Creates a GMAC based on the operation of a block cipher in GCM mode. + * <p/> + * This will produce an authentication code the length of the block size of the cipher. + * + * @param cipher + * the cipher to be used in GCM mode to generate the MAC. + */ + public GMac(final GCMBlockCipher cipher) + { + this(cipher, 128); + } + + /** + * Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode. + * + * @param macSizeBits + * the mac size to generate, in bits. Must be a multiple of 8 and >= 96 and <= 128. + * @param cipher + * the cipher to be used in GCM mode to generate the MAC. + */ + public GMac(final GCMBlockCipher cipher, final int macSizeBits) + { + this.cipher = cipher; + this.macSizeBits = macSizeBits; + } + + /** + * Initialises the GMAC - requires a {@link ParametersWithIV} providing a {@link KeyParameter} + * and a nonce. + */ + public void init(final CipherParameters params) throws IllegalArgumentException + { + if (params instanceof ParametersWithIV) + { + final ParametersWithIV param = (ParametersWithIV)params; + + final byte[] iv = param.getIV(); + final KeyParameter keyParam = (KeyParameter)param.getParameters(); + + // GCM is always operated in encrypt mode to calculate MAC + cipher.init(true, new AEADParameters(keyParam, macSizeBits, iv)); + } else + { + throw new IllegalArgumentException("GMAC requires ParametersWithIV"); + } + } + + public String getAlgorithmName() + { + return cipher.getUnderlyingCipher().getAlgorithmName() + "-GMAC"; + } + + public int getMacSize() + { + return macSizeBits / 8; + } + + public void update(byte in) throws IllegalStateException + { + cipher.processAADByte(in); + } + + public void update(byte[] in, int inOff, int len) throws DataLengthException, IllegalStateException + { + cipher.processAADBytes(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) throws DataLengthException, IllegalStateException + { + try + { + return cipher.doFinal(out, outOff); + } catch (InvalidCipherTextException e) + { + // Impossible in encrypt mode + throw new IllegalStateException(e); + } + } + + public void reset() + { + cipher.reset(); + } +} diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java index 07f47ead..7a6f7b04 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java @@ -16,8 +16,10 @@ import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.engines.AESWrapEngine; import org.bouncycastle.crypto.engines.RFC3211WrapEngine; import org.bouncycastle.crypto.macs.CMac; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.CFBBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.modes.OFBBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; @@ -28,7 +30,6 @@ import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class AES @@ -88,6 +89,15 @@ public final class AES } } + public static class AESGMAC + extends BaseMac + { + public AESGMAC() + { + super(new GMac(new GCMBlockCipher(new AESFastEngine()))); + } + } + static public class Wrap extends BaseWrapCipher { @@ -159,7 +169,6 @@ public final class AES super(256); } } - /** * PBEWithSHA1And128BitAES-BC @@ -317,7 +326,7 @@ public final class AES } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = AES.class.getName(); @@ -473,6 +482,8 @@ public final class AES provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.getId(), "PKCS12PBE"); provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.getId(), "PKCS12PBE"); provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PKCS12PBE"); + + addGMacAlgorithm(provider, "AES", PREFIX + "$AESGMAC", PREFIX + "$KeyGen128"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java index 9ac54e73..68605f45 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java @@ -2,10 +2,12 @@ package org.bouncycastle.jcajce.provider.symmetric; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.CAST6Engine; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; public final class CAST6 { @@ -31,8 +33,17 @@ public final class CAST6 } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new CAST6Engine()))); + } + } + public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = CAST6.class.getName(); @@ -44,6 +55,8 @@ public final class CAST6 { provider.addAlgorithm("Cipher.CAST6", PREFIX + "$ECB"); provider.addAlgorithm("KeyGenerator.CAST6", PREFIX + "$KeyGen"); + + addGMacAlgorithm(provider, "CAST6", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java index 5c98e961..38b5ca7b 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java @@ -13,15 +13,17 @@ import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.CamelliaEngine; import org.bouncycastle.crypto.engines.CamelliaWrapEngine; import org.bouncycastle.crypto.engines.RFC3211WrapEngine; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class Camellia @@ -72,6 +74,15 @@ public final class Camellia } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new CamelliaEngine()))); + } + } + public static class KeyGen extends BaseKeyGenerator { @@ -161,7 +172,7 @@ public final class Camellia } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = Camellia.class.getName(); @@ -201,6 +212,7 @@ public final class Camellia provider.addAlgorithm("KeyGenerator." + NTTObjectIdentifiers.id_camellia192_cbc, PREFIX + "$KeyGen192"); provider.addAlgorithm("KeyGenerator." + NTTObjectIdentifiers.id_camellia256_cbc, PREFIX + "$KeyGen256"); + addGMacAlgorithm(provider, "CAMELLIA", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java index d488ac2c..2d089cc7 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java @@ -9,12 +9,14 @@ import javax.crypto.spec.IvParameterSpec; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.NoekeonEngine; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class Noekeon @@ -41,6 +43,15 @@ public final class Noekeon } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new NoekeonEngine()))); + } + } + public static class AlgParamGen extends BaseAlgorithmParameterGenerator { @@ -89,7 +100,7 @@ public final class Noekeon } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = Noekeon.class.getName(); @@ -108,6 +119,7 @@ public final class Noekeon provider.addAlgorithm("KeyGenerator.NOEKEON", PREFIX + "$KeyGen"); + addGMacAlgorithm(provider, "NOEKEON", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java index 0861f13b..a29e717c 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java @@ -11,16 +11,18 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.RC6Engine; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.CFBBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.modes.OFBBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class RC6 @@ -71,6 +73,15 @@ public final class RC6 } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new RC6Engine()))); + } + } + public static class KeyGen extends BaseKeyGenerator { @@ -128,7 +139,7 @@ public final class RC6 } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = RC6.class.getName(); @@ -143,6 +154,7 @@ public final class RC6 provider.addAlgorithm("KeyGenerator.RC6", PREFIX + "$KeyGen"); provider.addAlgorithm("AlgorithmParameters.RC6", PREFIX + "$AlgParams"); + addGMacAlgorithm(provider, "RC6", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java index e4cffe30..2ad41bff 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java @@ -12,15 +12,17 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.SEEDEngine; import org.bouncycastle.crypto.engines.SEEDWrapEngine; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BaseWrapCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; public final class SEED @@ -71,6 +73,15 @@ public final class SEED } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new SEEDEngine()))); + } + } + public static class AlgParamGen extends BaseAlgorithmParameterGenerator { @@ -119,7 +130,7 @@ public final class SEED } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = SEED.class.getName(); @@ -146,6 +157,7 @@ public final class SEED provider.addAlgorithm("KeyGenerator." + KISAObjectIdentifiers.id_seedCBC, PREFIX + "$KeyGen"); provider.addAlgorithm("KeyGenerator." + KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, PREFIX + "$KeyGen"); + addGMacAlgorithm(provider, "SEED", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java index 02959ec4..578de32d 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java @@ -3,12 +3,14 @@ package org.bouncycastle.jcajce.provider.symmetric; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.SerpentEngine; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; public final class Serpent { @@ -40,6 +42,15 @@ public final class Serpent } } + public static class SerpentGMAC + extends BaseMac + { + public SerpentGMAC() + { + super(new GMac(new GCMBlockCipher(new SerpentEngine()))); + } + } + public static class AlgParams extends IvAlgorithmParameters { @@ -50,7 +61,7 @@ public final class Serpent } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = Serpent.class.getName(); @@ -65,6 +76,7 @@ public final class Serpent provider.addAlgorithm("KeyGenerator.Serpent", PREFIX + "$KeyGen"); provider.addAlgorithm("AlgorithmParameters.Serpent", PREFIX + "$AlgParams"); + addGMacAlgorithm(provider, "SERPENT", PREFIX + "$SerpentGMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java new file mode 100644 index 00000000..49656c27 --- /dev/null +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java @@ -0,0 +1,21 @@ +package org.bouncycastle.jcajce.provider.symmetric; + +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; + +abstract class SymmetricAlgorithmProvider + extends AlgorithmProvider +{ + protected void addGMacAlgorithm( + ConfigurableProvider provider, + String algorithm, + String algorithmClassName, + String keyGeneratorClassName) + { + provider.addAlgorithm("Mac." + algorithm + "-GMAC", algorithmClassName); + provider.addAlgorithm("Alg.Alias.Mac." + algorithm + "GMAC", algorithm + "-GMAC"); + + provider.addAlgorithm("KeyGenerator." + algorithm + "-GMAC", keyGeneratorClassName); + provider.addAlgorithm("Alg.Alias.KeyGenerator." + algorithm + "GMAC", algorithm + "-GMAC"); + } +} diff --git a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java index 604e7663..67b9f663 100644 --- a/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java +++ b/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java @@ -3,14 +3,16 @@ package org.bouncycastle.jcajce.provider.symmetric; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.TwofishEngine; +import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory; -import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; public final class Twofish { @@ -42,6 +44,14 @@ public final class Twofish } } + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new TwofishEngine()))); + } + } /** * PBEWithSHAAndTwofish-CBC @@ -77,7 +87,7 @@ public final class Twofish } public static class Mappings - extends AlgorithmProvider + extends SymmetricAlgorithmProvider { private static final String PREFIX = Twofish.class.getName(); @@ -95,6 +105,8 @@ public final class Twofish provider.addAlgorithm("Alg.Alias.AlgorithmParameters.PBEWITHSHAANDTWOFISH-CBC", "PKCS12PBE"); provider.addAlgorithm("Cipher.PBEWITHSHAANDTWOFISH-CBC", PREFIX + "$PBEWithSHA"); provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAANDTWOFISH-CBC", PREFIX + "$PBEWithSHAKeyFactory"); + + addGMacAlgorithm(provider, "Twofish", PREFIX + "$GMAC", PREFIX + "$KeyGen"); } } } diff --git a/src/test/java/org/bouncycastle/crypto/test/GMacTest.java b/src/test/java/org/bouncycastle/crypto/test/GMacTest.java new file mode 100644 index 00000000..d8196f96 --- /dev/null +++ b/src/test/java/org/bouncycastle/crypto/test/GMacTest.java @@ -0,0 +1,170 @@ +package org.bouncycastle.crypto.test; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.engines.AESFastEngine; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +/** + * Test vectors for AES-GMAC, extracted from <a + * href="http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip">NIST CAVP GCM test + * vectors</a>. + * + */ +public class GMacTest extends SimpleTest +{ + private static class TestCase + { + private byte[] key; + private byte[] iv; + private byte[] ad; + private byte[] tag; + private String name; + + private TestCase(final String name, final String key, final String iv, final String ad, final String tag) + { + this.name = name; + this.key = Hex.decode(key); + this.iv = Hex.decode(iv); + this.ad = Hex.decode(ad); + this.tag = Hex.decode(tag); + } + + public String getName() + { + return name; + } + + public byte[] getKey() + { + return key; + } + + public byte[] getIv() + { + return iv; + } + + public byte[] getAd() + { + return ad; + } + + public byte[] getTag() + { + return tag; + } + } + + private static TestCase[] TEST_VECTORS = new TestCase[] { + // Count = 0, from each of the PTlen = 0 test vector sequences + new TestCase("128/96/0/128", "11754cd72aec309bf52f7687212e8957", "3c819d9a9bed087615030b65", "", + "250327c674aaf477aef2675748cf6971"), + new TestCase("128/96/0/120", "272f16edb81a7abbea887357a58c1917", "794ec588176c703d3d2a7a07", "", + "b6e6f197168f5049aeda32dafbdaeb"), + new TestCase("128/96/0/112", "81b6844aab6a568c4556a2eb7eae752f", "ce600f59618315a6829bef4d", "", + "89b43e9dbc1b4f597dbbc7655bb5"), + new TestCase("128/96/0/104", "cde2f9a9b1a004165ef9dc981f18651b", "29512c29566c7322e1e33e8e", "", + "2e58ce7dabd107c82759c66a75"), + new TestCase("128/96/0/96", "b01e45cc3088aaba9fa43d81d481823f", "5a2c4a66468713456a4bd5e1", "", + "014280f944f53c681164b2ff"), + + new TestCase("128/96/128/128", "77be63708971c4e240d1cb79e8d77feb", "e0e00f19fed7ba0136a797f3", + "7a43ec1d9c0a5a78a0b16533a6213cab", "209fcc8d3675ed938e9c7166709dd946"), + new TestCase("128/96/128/96", "bea48ae4980d27f357611014d4486625", "32bddb5c3aa998a08556454c", + "8a50b0b8c7654bced884f7f3afda2ead", "8e0f6d8bf05ffebe6f500eb1"), + + new TestCase("128/96/384/128", "99e3e8793e686e571d8285c564f75e2b", "c2dd0ab868da6aa8ad9c0d23", + "b668e42d4e444ca8b23cfdd95a9fedd5178aa521144890b093733cf5cf22526c5917ee476541809ac6867a8c399309fc", + "3f4fba100eaf1f34b0baadaae9995d85"), + new TestCase("128/96/384/96", "c77acd1b0918e87053cb3e51651e7013", "39ff857a81745d10f718ac00", + "407992f82ea23b56875d9a3cb843ceb83fd27cb954f7c5534d58539fe96fb534502a1b38ea4fac134db0a42de4be1137", + "2a5dc173285375dc82835876"), + + new TestCase( + "128/1024/0/128", + "d0f1f4defa1e8c08b4b26d576392027c", + "42b4f01eb9f5a1ea5b1eb73b0fb0baed54f387ecaa0393c7d7dffc6af50146ecc021abf7eb9038d4303d91f8d741a11743166c0860208bcc02c6258fd9511a2fa626f96d60b72fcff773af4e88e7a923506e4916ecbd814651e9f445adef4ad6a6b6c7290cc13b956130eef5b837c939fcac0cbbcc9656cd75b13823ee5acdac", + "", "7ab49b57ddf5f62c427950111c5c4f0d"), + new TestCase( + "128/1024/384/96", + "3cce72d37933394a8cac8a82deada8f0", + "aa2f0d676d705d9733c434e481972d4888129cf7ea55c66511b9c0d25a92a174b1e28aa072f27d4de82302828955aadcb817c4907361869bd657b45ff4a6f323871987fcf9413b0702d46667380cd493ed24331a28b9ce5bbfa82d3a6e7679fcce81254ba64abcad14fd18b22c560a9d2c1cd1d3c42dac44c683edf92aced894", + "5686b458e9c176f4de8428d9ebd8e12f569d1c7595cf49a4b0654ab194409f86c0dd3fdb8eb18033bb4338c70f0b97d1", + "a3a9444b21f330c3df64c8b6"), }; + + public void performTest() + { + for (int i = 0; i < TEST_VECTORS.length; i++) + { + TestCase testCase = TEST_VECTORS[i]; + + Mac mac = new GMac(new GCMBlockCipher(new AESFastEngine()), testCase.getTag().length * 8); + CipherParameters key = new KeyParameter(testCase.getKey()); + mac.init(new ParametersWithIV(key, testCase.getIv())); + + testSingleByte(mac, testCase); + testMultibyte(mac, testCase); + } + + // Invalid mac size + testInvalidMacSize(97); + testInvalidMacSize(136); + testInvalidMacSize(88); + testInvalidMacSize(64); + } + + private void testInvalidMacSize(int size) + { + try + { + GMac mac = new GMac(new GCMBlockCipher(new AESFastEngine()), size); + mac.init(new ParametersWithIV(null, new byte[16])); + fail("Expected failure for illegal mac size " + size); + } catch (IllegalArgumentException e) + { + } + } + + private void testMultibyte(Mac mac, TestCase testCase) + { + mac.update(testCase.getAd(), 0, testCase.getAd().length); + checkMac(mac, testCase); + } + + private void testSingleByte(Mac mac, TestCase testCase) + { + final byte[] ad = testCase.getAd(); + for (int i = 0; i < ad.length; i++) + { + mac.update(ad[i]); + } + checkMac(mac, testCase); + } + + private void checkMac(Mac mac, TestCase testCase) + { + final byte[] generatedMac = new byte[mac.getMacSize()]; + mac.doFinal(generatedMac, 0); + if (!areEqual(testCase.getTag(), generatedMac)) + { + fail("Failed " + testCase.getName() + " - expected " + new String(Hex.encode(testCase.getTag())) + " got " + + new String(Hex.encode(generatedMac))); + } + } + + public String getName() + { + return "GMac"; + } + + public static void main(String[] args) + { + runTest(new GMacTest()); + } +} diff --git a/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index 3c3fa8bb..906eb910 100644 --- a/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -96,6 +96,7 @@ public class RegressionTest new CMacTest(), new EAXTest(), new GCMTest(), + new GMacTest(), new HCFamilyTest(), new HCFamilyVecTest(), new ISAACTest(), diff --git a/src/test/java/org/bouncycastle/jce/provider/test/GMacTest.java b/src/test/java/org/bouncycastle/jce/provider/test/GMacTest.java new file mode 100644 index 00000000..72a4888a --- /dev/null +++ b/src/test/java/org/bouncycastle/jce/provider/test/GMacTest.java @@ -0,0 +1,145 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; +import org.bouncycastle.util.test.TestFailedException; + +public class GMacTest + extends SimpleTest +{ + public String getName() + { + return "GMac"; + } + + public void performTest() + throws Exception + { + checkRegistrations(); + } + + private void checkRegistrations() + throws Exception + { + List<String> missingMacs = new ArrayList<String>(); + List<String> missingKeyGens = new ArrayList<String>(); + + String[] ciphers = new String[] { "AES", "NOEKEON", "Twofish", "CAST6", "SEED", "Serpent", "RC6", "CAMELLIA" }; + String[] macs = new String[] + { + "a52308801b32d4770c701ace9b826f12", + "cf11dacaf6024a78dba76b256e23caab", + "13db7c428e5a7128149b5ec782d07fac", + "d13a33e78e48b274bf7d64bf9aecdb82", + "d05d550054735c6e7e01b6981fc14b4e", + "4a34dfe4f5410afd7c40b1e110377a73", + "d9f597c96b41f641da6c83d4760f543b", + "371ad8cc920c6bda2a26d8f237bd446b" + }; + + for (int i = 0; i < ciphers.length; i++) + { + String cipherName = ciphers[i]; + Cipher cipher; + try + { + cipher = Cipher.getInstance(cipherName, "BC"); + } + catch (Exception e) + { + System.err.println(cipherName + ": " + e.getMessage()); + continue; + } + int blocksize; + try + { + blocksize = cipher.getBlockSize(); + } + catch (Exception e) + { + System.err.println(cipherName + ": " + e.getMessage()); + continue; + } + // GCM is defined over 128 bit block ciphers + if (blocksize == 16) + { + String macName = cipherName + "-GMAC"; + String macNameAlt = cipherName + "GMAC"; + + // Check we have a GMAC registered for each name + checkMac(macName, missingMacs, missingKeyGens, macs[i]); + checkMac(macNameAlt, missingMacs, missingKeyGens, macs[i]); + } + } + if (missingMacs.size() != 0) + { + fail("Did not find GMAC registrations for the following ciphers: " + missingMacs); + } + if (missingKeyGens.size() != 0) + { + Collections.sort(missingKeyGens); + fail("Did not find GMAC KeyGenerator registrations for the following macs: " + missingKeyGens); + } + } + + private void checkMac(String name, List<String> missingMacs, List<String> missingKeyGens, String macOutput) + { + try + { + Mac mac = Mac.getInstance(name); + + mac.init(new SecretKeySpec(new byte[mac.getMacLength()], mac.getAlgorithm()), new IvParameterSpec( + new byte[16])); + mac.update(new byte[128]); + byte[] bytes = mac.doFinal(); + + if (!Arrays.areEqual(bytes, Hex.decode(macOutput))) + { + fail("wrong mac value computed for " + name); + } + + try + { + KeyGenerator kg = KeyGenerator.getInstance(name); + kg.generateKey(); + } + catch (NoSuchAlgorithmException e) + { + missingKeyGens.add(name); + } + } + catch (NoSuchAlgorithmException e) + { + missingMacs.add(name); + } + catch (TestFailedException e) + { + throw e; + } + catch (Exception e) + { + fail("Unexpected error", e); + } + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new GMacTest()); + } +}
\ No newline at end of file diff --git a/src/test/java/org/bouncycastle/jce/provider/test/JceTestUtil.java b/src/test/java/org/bouncycastle/jce/provider/test/JceTestUtil.java new file mode 100644 index 00000000..9c0805a8 --- /dev/null +++ b/src/test/java/org/bouncycastle/jce/provider/test/JceTestUtil.java @@ -0,0 +1,49 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.Security; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +abstract class JceTestUtil +{ + private JceTestUtil() + { + } + + static String[] getRegisteredAlgorithms(String prefix, String[] exclusionPatterns) + { + final BouncyCastleProvider prov = (BouncyCastleProvider)Security.getProvider("BC"); + + List matches = new ArrayList(); + Enumeration algos = prov.keys(); + while (algos.hasMoreElements()) + { + String algo = (String)algos.nextElement(); + if (!algo.startsWith(prefix)) + { + continue; + } + String algoName = algo.substring(prefix.length()); + if (!isExcluded(algoName, exclusionPatterns)) + { + matches.add(algoName); + } + } + return (String[])matches.toArray(new String[matches.size()]); + } + + private static boolean isExcluded(String algoName, String[] exclusionPatterns) + { + for (int i = 0; i < exclusionPatterns.length; i++) + { + if (algoName.contains(exclusionPatterns[i])) + { + return true; + } + } + return false; + } +} diff --git a/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java b/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java index 52026bdb..85972a05 100644 --- a/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java +++ b/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java @@ -67,6 +67,7 @@ public class RegressionTest new SigNameTest(), new MQVTest(), new CMacTest(), + new GMacTest(), new DSTU4145Test(), new CRL5Test(), new SipHashTest(), |