Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crypto/crypto.mdp
Original file line numberDiff line numberDiff line change
Expand Up@@ -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" />
Expand Down
111 changes: 111 additions & 0 deletions crypto/src/crypto/macs/GMac.cs
Original file line numberDiff line numberDiff line change
@@ -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();
}
}
}
5 changes: 5 additions & 0 deletions crypto/src/crypto/modes/EAXBlockCipher.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,11 @@ public virtual string AlgorithmName
get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; }
}

public IBlockCipher GetUnderlyingCipher()
{
return cipher;
}

public virtual int GetBlockSize()
{
return cipher.GetBlockSize();
Expand Down
5 changes: 5 additions & 0 deletions crypto/src/crypto/modes/GCMBlockCipher.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,11 @@ public virtual string AlgorithmName
get { return cipher.AlgorithmName + "/GCM"; }
}

public IBlockCipher GetUnderlyingCipher()
{
return cipher;
}

public virtual int GetBlockSize()
{
return BlockSize;
Expand Down
3 changes: 3 additions & 0 deletions crypto/src/crypto/modes/IAeadBlockCipher.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,9 @@ public interface IAeadBlockCipher
/// <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>
Expand Down
184 changes: 184 additions & 0 deletions crypto/test/src/crypto/test/GMacTest.cs
Original file line numberDiff line numberDiff line change
@@ -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);
}
}
}
1 change: 1 addition & 0 deletions crypto/test/src/crypto/test/RegressionTest.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,6 +98,7 @@ public class RegressionTest
new CMacTest(),
new EaxTest(),
new GcmTest(),
new GMacTest(),
new HCFamilyTest(),
new HCFamilyVecTest(),
new IsaacTest(),
Expand Down