summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crypto/src/crypto/macs/GMac.cs179
-rw-r--r--crypto/src/crypto/modes/GCMBlockCipher.cs15
-rw-r--r--crypto/test/src/crypto/test/GCMTest.cs4
-rw-r--r--crypto/test/src/crypto/test/GMacTest.cs343
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
 {
-	/// <summary>
-	/// The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication
-	/// 800-38D.
-	/// </summary>
-	/// <remarks>
-	/// 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).
-	/// </remarks>
-	public class GMac 
-		: IMac
-	{
-	    private readonly GcmBlockCipher cipher;
-		private readonly int macSizeBits;
+    /// <summary>
+    /// The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication
+    /// 800-38D.
+    /// </summary>
+    /// <remarks>
+    /// 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).
+    /// </remarks>
+    public class GMac 
+        : IMac
+    {
+        private readonly GcmBlockCipher cipher;
+        private readonly int macSizeBits;
 
-		/// <summary>
-		/// Creates a GMAC based on the operation of a block cipher in GCM mode.
-		/// </summary>
-		/// <remarks>
-		/// This will produce an authentication code the length of the block size of the cipher.
-		/// </remarks>
-		/// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param>
-		public GMac(GcmBlockCipher cipher)
-			: this(cipher, 128)
-	    {
-	    }
+        /// <summary>
+        /// Creates a GMAC based on the operation of a block cipher in GCM mode.
+        /// </summary>
+        /// <remarks>
+        /// This will produce an authentication code the length of the block size of the cipher.
+        /// </remarks>
+        /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param>
+        public GMac(GcmBlockCipher cipher)
+            : this(cipher, 128)
+        {
+        }
 
-		/// <summary>
-		/// Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode.
-		/// </summary>
-		/// <remarks>
-		/// This will produce an authentication code the length of the block size of the cipher.
-		/// </remarks>
-		/// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param>
-		/// <param name="macSizeBits">the mac size to generate, in bits. Must be a multiple of 8, between 96 and 128 (inclusive).</param>
-		public GMac(GcmBlockCipher cipher, int macSizeBits)
-	    {
-	        this.cipher = cipher;
-	        this.macSizeBits = macSizeBits;
-	    }
+        /// <summary>
+        /// Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode.
+        /// </summary>
+        /// <remarks>
+        /// This will produce an authentication code the length of the block size of the cipher.
+        /// </remarks>
+        /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param>
+        /// <param name="macSizeBits">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.</param>
+        public GMac(GcmBlockCipher cipher, int macSizeBits)
+        {
+            this.cipher = cipher;
+            this.macSizeBits = macSizeBits;
+        }
 
-		/// <summary>
-		/// Initialises the GMAC - requires a <see cref="Org.BouncyCastle.Crypto.Parameters.ParametersWithIV"/> 
-		/// providing a <see cref="Org.BouncyCastle.Crypto.Parameters.KeyParameter"/> and a nonce.
-		/// </summary>
-	    public void Init(ICipherParameters parameters)
-	    {
-			if (parameters is ParametersWithIV)
-	        {
-				ParametersWithIV param = (ParametersWithIV)parameters;
+        /// <summary>
+        /// Initialises the GMAC - requires a <see cref="Org.BouncyCastle.Crypto.Parameters.ParametersWithIV"/> 
+        /// providing a <see cref="Org.BouncyCastle.Crypto.Parameters.KeyParameter"/> and a nonce.
+        /// </summary>
+        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;
         }
 
+        /// <remarks>
+        /// 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.
+        /// </remarks>
         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 <a
-	 * href="http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip">NIST CAVP GCM test
-	 * vectors</a>.
-	 * 
-	 */
-	[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 <a
+     * href="http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip">NIST CAVP GCM test
+     * vectors</a>.
+     * 
+     */
+    [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);
+        }
+    }
 }