From b999a754ac553719d4953eb866c0d8e9b2633c33 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 21 Jul 2014 13:07:18 +0700 Subject: Port a few more GCM/GMac updates --- crypto/src/crypto/macs/GMac.cs | 179 ++++++++-------- crypto/src/crypto/modes/GCMBlockCipher.cs | 15 +- crypto/test/src/crypto/test/GCMTest.cs | 4 +- crypto/test/src/crypto/test/GMacTest.cs | 343 +++++++++++++++--------------- 4 files changed, 275 insertions(+), 266 deletions(-) diff --git a/crypto/src/crypto/macs/GMac.cs b/crypto/src/crypto/macs/GMac.cs index eb340ddbc..f2c3990c6 100644 --- a/crypto/src/crypto/macs/GMac.cs +++ b/crypto/src/crypto/macs/GMac.cs @@ -8,104 +8,105 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Macs { - /// - /// The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication - /// 800-38D. - /// - /// - /// 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 - : IMac - { - private readonly GcmBlockCipher cipher; - private readonly int macSizeBits; + /// + /// The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication + /// 800-38D. + /// + /// + /// 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 + : IMac + { + private readonly GcmBlockCipher cipher; + private readonly int macSizeBits; - /// - /// Creates a GMAC based on the operation of a block cipher in GCM mode. - /// - /// - /// This will produce an authentication code the length of the block size of the cipher. - /// - /// the cipher to be used in GCM mode to generate the MAC. - public GMac(GcmBlockCipher cipher) - : this(cipher, 128) - { - } + /// + /// Creates a GMAC based on the operation of a block cipher in GCM mode. + /// + /// + /// This will produce an authentication code the length of the block size of the cipher. + /// + /// the cipher to be used in GCM mode to generate the MAC. + public GMac(GcmBlockCipher cipher) + : this(cipher, 128) + { + } - /// - /// Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode. - /// - /// - /// This will produce an authentication code the length of the block size of the cipher. - /// - /// the cipher to be used in GCM mode to generate the MAC. - /// the mac size to generate, in bits. Must be a multiple of 8, between 96 and 128 (inclusive). - public GMac(GcmBlockCipher cipher, int macSizeBits) - { - this.cipher = cipher; - this.macSizeBits = macSizeBits; - } + /// + /// Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode. + /// + /// + /// This will produce an authentication code the length of the block size of the cipher. + /// + /// the cipher to be used in GCM mode to generate the MAC. + /// the mac size to generate, in bits. Must be a multiple of 8, between 32 and 128 (inclusive). + /// Sizes less than 96 are not recommended, but are supported for specialized applications. + public GMac(GcmBlockCipher cipher, int macSizeBits) + { + this.cipher = cipher; + this.macSizeBits = macSizeBits; + } - /// - /// Initialises the GMAC - requires a - /// providing a and a nonce. - /// - public void Init(ICipherParameters parameters) - { - if (parameters is ParametersWithIV) - { - ParametersWithIV param = (ParametersWithIV)parameters; + /// + /// Initialises the GMAC - requires a + /// providing a and a nonce. + /// + public void Init(ICipherParameters parameters) + { + if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV)parameters; - byte[] iv = param.GetIV(); - KeyParameter keyParam = (KeyParameter)param.Parameters; + byte[] iv = param.GetIV(); + KeyParameter keyParam = (KeyParameter)param.Parameters; - // GCM is always operated in encrypt mode to calculate MAC - cipher.Init(true, new AeadParameters(keyParam, macSizeBits, iv)); - } - else - { - throw new ArgumentException("GMAC requires ParametersWithIV"); - } - } + // GCM is always operated in encrypt mode to calculate MAC + cipher.Init(true, new AeadParameters(keyParam, macSizeBits, iv)); + } + else + { + throw new ArgumentException("GMAC requires ParametersWithIV"); + } + } - public string AlgorithmName - { - get { return cipher.GetUnderlyingCipher().AlgorithmName + "-GMAC"; } - } + public string AlgorithmName + { + get { return cipher.GetUnderlyingCipher().AlgorithmName + "-GMAC"; } + } - public int GetMacSize() - { - return macSizeBits / 8; - } + public int GetMacSize() + { + return macSizeBits / 8; + } - public void Update(byte input) - { - cipher.ProcessAadByte(input); - } + public void Update(byte input) + { + cipher.ProcessAadByte(input); + } - public void BlockUpdate(byte[] input, int inOff, int len) - { - cipher.ProcessAadBytes(input, inOff, len); - } + public void BlockUpdate(byte[] input, int inOff, int len) + { + cipher.ProcessAadBytes(input, inOff, len); + } - public int DoFinal(byte[] output, int outOff) - { - try - { - return cipher.DoFinal(output, outOff); - } - catch (InvalidCipherTextException e) - { - // Impossible in encrypt mode - throw new InvalidOperationException(e.ToString()); - } - } + public int DoFinal(byte[] output, int outOff) + { + try + { + return cipher.DoFinal(output, outOff); + } + catch (InvalidCipherTextException e) + { + // Impossible in encrypt mode + throw new InvalidOperationException(e.ToString()); + } + } - public void Reset() - { - cipher.Reset(); - } - } + public void Reset() + { + cipher.Reset(); + } + } } diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs index 91858f6f5..2e2ac2eca 100644 --- a/crypto/src/crypto/modes/GCMBlockCipher.cs +++ b/crypto/src/crypto/modes/GCMBlockCipher.cs @@ -79,6 +79,10 @@ namespace Org.BouncyCastle.Crypto.Modes return BlockSize; } + /// + /// MAC sizes from 32 bits to 128 bits (must be a multiple of 8) are supported. The default is 128 bits. + /// Sizes less than 96 are not recommended, but are supported for specialized applications. + /// public virtual void Init( bool forEncryption, ICipherParameters parameters) @@ -96,7 +100,7 @@ namespace Org.BouncyCastle.Crypto.Modes initialAssociatedText = param.GetAssociatedText(); int macSizeBits = param.MacSize; - if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0) + if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0) { throw new ArgumentException("Invalid value for MAC size: " + macSizeBits); } @@ -126,9 +130,7 @@ namespace Org.BouncyCastle.Crypto.Modes throw new ArgumentException("IV must be at least 1 byte"); } - // TODO This should be configurable by Init parameters - // (but must be 16 if nonce length not 12) (BlockSize?) -// this.tagLength = 16; + // TODO Restrict macSize to 16 if nonce length not 12? // Cipher always used in forward mode // if keyParam is null we're reusing the last key. @@ -143,6 +145,10 @@ namespace Org.BouncyCastle.Crypto.Modes multiplier.Init(H); exp = null; } + else if (this.H == null) + { + throw new ArgumentException("Key must be specified in initial init"); + } this.J0 = new byte[BlockSize]; @@ -381,7 +387,6 @@ namespace Org.BouncyCastle.Crypto.Modes gHASHBlock(S, X); - // TODO Fix this if tagLength becomes configurable // T = MSBt(GCTRk(J0,S)) byte[] tag = new byte[BlockSize]; cipher.ProcessBlock(J0, 0, tag, 0); diff --git a/crypto/test/src/crypto/test/GCMTest.cs b/crypto/test/src/crypto/test/GCMTest.cs index cea183454..943ffdad4 100644 --- a/crypto/test/src/crypto/test/GCMTest.cs +++ b/crypto/test/src/crypto/test/GCMTest.cs @@ -340,7 +340,7 @@ namespace Org.BouncyCastle.Crypto.Tests Fail("incorrect block size not picked up"); } - catch (ArgumentException e) + catch (ArgumentException) { // expected } @@ -351,7 +351,7 @@ namespace Org.BouncyCastle.Crypto.Tests Fail("illegal argument not picked up"); } - catch (ArgumentException e) + catch (ArgumentException) { // expected } diff --git a/crypto/test/src/crypto/test/GMacTest.cs b/crypto/test/src/crypto/test/GMacTest.cs index 0f0e84e2a..383ff96b7 100644 --- a/crypto/test/src/crypto/test/GMacTest.cs +++ b/crypto/test/src/crypto/test/GMacTest.cs @@ -11,174 +11,177 @@ using Org.BouncyCastle.Utilities.Test; namespace Org.BouncyCastle.Crypto.Tests { - /** - * Test vectors for AES-GMAC, extracted from NIST CAVP GCM test - * vectors. - * - */ - [TestFixture] - public class GMacTest - : SimpleTest - { - private class TestCase - { - private byte[] key; - private byte[] iv; - private byte[] ad; - private byte[] tag; - private string name; - - internal TestCase(string name, string key, string iv, string ad, 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 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 override void PerformTest() - { - for (int i = 0; i < TEST_VECTORS.Length; i++) - { - TestCase testCase = TEST_VECTORS[i]; - - IMac mac = new GMac(new GcmBlockCipher(new AesFastEngine()), testCase.getTag().Length * 8); - ICipherParameters 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 (ArgumentException) - { - } - } - - private void testMultibyte(IMac mac, TestCase testCase) - { - mac.BlockUpdate(testCase.getAd(), 0, testCase.getAd().Length); - checkMac(mac, testCase); - } - - private void testSingleByte(IMac mac, TestCase testCase) - { - byte[] ad = testCase.getAd(); - for (int i = 0; i < ad.Length; i++) - { - mac.Update(ad[i]); - } - checkMac(mac, testCase); - } - - private void checkMac(IMac mac, TestCase testCase) - { - byte[] generatedMac = new byte[mac.GetMacSize()]; - mac.DoFinal(generatedMac, 0); - if (!AreEqual(testCase.getTag(), generatedMac)) - { - Fail("Failed " + testCase.getName() + " - expected " + Hex.ToHexString(testCase.getTag()) + " got " - + Hex.ToHexString(generatedMac)); - } - } - - public override string Name - { - get { return "GMac"; } - } - - public static void Main( - string[] args) - { - RunTest(new GMacTest()); - } - - [Test] - public void TestFunction() - { - string resultText = Perform().ToString(); - - Assert.AreEqual(Name + ": Okay", resultText); - } - } + /** + * Test vectors for AES-GMAC, extracted from NIST CAVP GCM test + * vectors. + * + */ + [TestFixture] + public class GMacTest + : SimpleTest + { + private class TestCase + { + private byte[] key; + private byte[] iv; + private byte[] ad; + private byte[] tag; + private string name; + + internal TestCase(string name, string key, string iv, string ad, 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 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 override void PerformTest() + { + for (int i = 0; i < TEST_VECTORS.Length; i++) + { + TestCase testCase = TEST_VECTORS[i]; + + IMac mac = new GMac(new GcmBlockCipher(new AesFastEngine()), testCase.getTag().Length * 8); + ICipherParameters 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(24); + } + + 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 (ArgumentException e) + { + if (!e.Message.StartsWith("Invalid value for MAC size")) + { + Fail("Illegal mac size failed with unexpected message"); + } + } + } + + private void testMultibyte(IMac mac, TestCase testCase) + { + mac.BlockUpdate(testCase.getAd(), 0, testCase.getAd().Length); + checkMac(mac, testCase); + } + + private void testSingleByte(IMac mac, TestCase testCase) + { + byte[] ad = testCase.getAd(); + for (int i = 0; i < ad.Length; i++) + { + mac.Update(ad[i]); + } + checkMac(mac, testCase); + } + + private void checkMac(IMac mac, TestCase testCase) + { + byte[] generatedMac = new byte[mac.GetMacSize()]; + mac.DoFinal(generatedMac, 0); + if (!AreEqual(testCase.getTag(), generatedMac)) + { + Fail("Failed " + testCase.getName() + " - expected " + Hex.ToHexString(testCase.getTag()) + " got " + + Hex.ToHexString(generatedMac)); + } + } + + public override string Name + { + get { return "GMac"; } + } + + public static void Main( + string[] args) + { + RunTest(new GMacTest()); + } + + [Test] + public void TestFunction() + { + string resultText = Perform().ToString(); + + Assert.AreEqual(Name + ": Okay", resultText); + } + } } -- cgit 1.4.1