summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorTim Whittington <bc@whittington.net.nz>2013-10-16 21:22:08 +1300
committerTim Whittington <bc@whittington.net.nz>2013-10-20 20:51:28 +1300
commitfc763ffe88a7f22f51a314e8ebec2af5b901658c (patch)
tree1e652a3ba1f12d4af2237dfc21068d3570fdd60d /crypto
parentfixed line endings (diff)
downloadBouncyCastle.NET-ed25519-fc763ffe88a7f22f51a314e8ebec2af5b901658c.tar.xz
Port GMac implementation and tests from bc-java.
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto.mdp2
-rw-r--r--crypto/src/crypto/macs/GMac.cs111
-rw-r--r--crypto/src/crypto/modes/EAXBlockCipher.cs5
-rw-r--r--crypto/src/crypto/modes/GCMBlockCipher.cs5
-rw-r--r--crypto/src/crypto/modes/IAeadBlockCipher.cs3
-rw-r--r--crypto/test/src/crypto/test/GMacTest.cs184
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs1
7 files changed, 311 insertions, 0 deletions
diff --git a/crypto/crypto.mdp b/crypto/crypto.mdp
index 8152a40a9..72c228fec 100644
--- a/crypto/crypto.mdp
+++ b/crypto/crypto.mdp
@@ -2297,6 +2297,8 @@
     <File subtype="Code" buildaction="Compile" name="src/bcpg/sig/RevocationReason.cs" />
     <File subtype="Code" buildaction="Compile" name="src/bcpg/sig/RevocationReasonTags.cs" />
     <File subtype="Code" buildaction="Compile" name="src/bcpg/sig/RevocationKeyTags.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/crypto/macs/GMac.cs" />
+    <File subtype="Code" buildaction="Compile" name="test/src/crypto/test/GMacTest.cs" />
   </Contents>
   <References>
     <ProjectReference type="Assembly" localcopy="True" refto="test/lib/nunit.core.dll" />
diff --git a/crypto/src/crypto/macs/GMac.cs b/crypto/src/crypto/macs/GMac.cs
new file mode 100644
index 000000000..12ca68f90
--- /dev/null
+++ b/crypto/src/crypto/macs/GMac.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+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>
+		/// 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 and >= 96 and <= 128.</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;
+
+	            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");
+	        }
+	    }
+
+	    public string AlgorithmName
+	    {
+			get { return cipher.GetUnderlyingCipher().AlgorithmName + "-GMAC"; }
+	    }
+
+		public int GetMacSize()
+	    {
+	        return macSizeBits / 8;
+	    }
+
+	    public void Update(byte input) 
+	    {
+			cipher.ProcessAadByte(input);
+	    }
+
+		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 void Reset()
+	    {
+	        cipher.Reset();
+	    }
+	}
+}
diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs
index bb027b597..5ccc69b66 100644
--- a/crypto/src/crypto/modes/EAXBlockCipher.cs
+++ b/crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -65,6 +65,11 @@ namespace Org.BouncyCastle.Crypto.Modes
 			get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; }
 		}
 
+		public IBlockCipher GetUnderlyingCipher()
+		{
+			return cipher;
+		}
+
 		public virtual int GetBlockSize()
 		{
 			return cipher.GetBlockSize();
diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index 95fe6f7ec..74b895e7b 100644
--- a/crypto/src/crypto/modes/GCMBlockCipher.cs
+++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -69,6 +69,11 @@ namespace Org.BouncyCastle.Crypto.Modes
 			get { return cipher.AlgorithmName + "/GCM"; }
 		}
 
+		public IBlockCipher GetUnderlyingCipher()
+		{
+			return cipher;
+		}
+
 		public virtual int GetBlockSize()
 		{
 			return BlockSize;
diff --git a/crypto/src/crypto/modes/IAeadBlockCipher.cs b/crypto/src/crypto/modes/IAeadBlockCipher.cs
index 06bc50488..52c4ff428 100644
--- a/crypto/src/crypto/modes/IAeadBlockCipher.cs
+++ b/crypto/src/crypto/modes/IAeadBlockCipher.cs
@@ -11,6 +11,9 @@ namespace Org.BouncyCastle.Crypto.Modes
 		/// <summary>The name of the algorithm this cipher implements.</summary>
 		string AlgorithmName { get; }
 
+		/// <summary>The block cipher underlying this algorithm.</summary>
+		IBlockCipher GetUnderlyingCipher();
+
 		/// <summary>Initialise the cipher.</summary>
 		/// <remarks>Parameter can either be an AeadParameters or a ParametersWithIV object.</remarks>
 		/// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
diff --git a/crypto/test/src/crypto/test/GMacTest.cs b/crypto/test/src/crypto/test/GMacTest.cs
new file mode 100644
index 000000000..0f0e84e2a
--- /dev/null
+++ b/crypto/test/src/crypto/test/GMacTest.cs
@@ -0,0 +1,184 @@
+using System;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities.Encoders;
+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);
+		}
+	}
+}
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index 687e9ee4f..4a2723b21 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -98,6 +98,7 @@ namespace Org.BouncyCastle.Crypto.Tests
             new CMacTest(),
             new EaxTest(),
             new GcmTest(),
+            new GMacTest(),
             new HCFamilyTest(),
             new HCFamilyVecTest(),
             new IsaacTest(),