diff --git a/crypto/src/crypto/engines/AsconEngine.cs b/crypto/src/crypto/engines/AsconEngine.cs
index 14d3ddf6d..c3091b9ef 100644
--- a/crypto/src/crypto/engines/AsconEngine.cs
+++ b/crypto/src/crypto/engines/AsconEngine.cs
@@ -4,6 +4,7 @@ using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
+using static Org.BouncyCastle.Tls.DtlsReliableHandshake;
/**
* ASCON AEAD v1.2, https://ascon.iaik.tugraz.at/
@@ -25,11 +26,16 @@ namespace Org.BouncyCastle.Crypto.Engines
private readonly AsconParameters asconParameters;
private readonly MemoryStream aadData = new MemoryStream();
+ private readonly MemoryStream message = new MemoryStream();
private bool encrypted;
+ private bool initialised;
+ private bool forEncryption;
+ private bool aadFinished;
private readonly int CRYPTO_KEYBYTES;
private readonly int CRYPTO_ABYTES;
private readonly int ASCON_AEAD_RATE;
private readonly int nr;
+ private byte[] mac;
private ulong K0;
private ulong K1;
private ulong K2;
@@ -41,10 +47,11 @@ namespace Org.BouncyCastle.Crypto.Engines
private ulong x2;
private ulong x3;
private ulong x4;
+ private String algorithmName;
public IBlockCipher UnderlyingCipher => throw new NotImplementedException();
- public string AlgorithmName => "ASCON AEAD";
+ public string AlgorithmName => algorithmName;
public AsconEngine(AsconParameters asconParameters)
{
@@ -56,35 +63,39 @@ namespace Org.BouncyCastle.Crypto.Engines
CRYPTO_ABYTES = 16;
ASCON_AEAD_RATE = 8;
ASCON_IV = 0xa0400c0600000000UL;
+ algorithmName = "Ascon-80pq AEAD";
break;
case AsconParameters.ascon128a:
CRYPTO_KEYBYTES = 16;
CRYPTO_ABYTES = 16;
ASCON_AEAD_RATE = 16;
ASCON_IV = 0x80800c0800000000UL;
+ algorithmName = "Ascon-128a AEAD";
break;
case AsconParameters.ascon128:
CRYPTO_KEYBYTES = 16;
CRYPTO_ABYTES = 16;
ASCON_AEAD_RATE = 8;
ASCON_IV = 0x80400c0600000000UL;
+ algorithmName = "Ascon-128 AEAD";
break;
default:
throw new ArgumentException("invalid parameter setting for ASCON AEAD");
}
nr = (ASCON_AEAD_RATE == 8) ? 6 : 8;
+ initialised = false;
}
private ulong U64BIG(ulong x)
{
- return (((0x00000000000000FFUL & (x)) << 56) |
- ((0x000000000000FF00UL & (x)) << 40) |
- ((0x0000000000FF0000UL & (x)) << 24) |
- ((0x00000000FF000000UL & (x)) << 8) |
- ((0x000000FF00000000UL & (x)) >> 8) |
- ((0x0000FF0000000000UL & (x)) >> 24) |
- ((0x00FF000000000000UL & (x)) >> 40) |
- ((0xFF00000000000000UL & (x)) >> 56));
+ return (((0x00000000000000FFUL & x) << 56) |
+ ((0x000000000000FF00UL & x) << 40) |
+ ((0x0000000000FF0000UL & x) << 24) |
+ ((0x00000000FF000000UL & x) << 8) |
+ ((0x000000FF00000000UL & x) >> 8) |
+ ((0x0000FF0000000000UL & x) >> 24) |
+ ((0x00FF000000000000UL & x) >> 40) |
+ ((0xFF00000000000000UL & x) >> 56));
}
private ulong ROR(ulong x, int n)
@@ -121,10 +132,17 @@ namespace Org.BouncyCastle.Crypto.Engines
private void STORE(byte[] bytes, int inOff, ulong w, int n)
{
- ulong x = Pack.LE_To_UInt64(bytes, inOff);
+ ulong x = 0;
+ for (int i = 0; i < n; ++i)
+ {
+ x |= (bytes[i + inOff] & 0xFFUL) << (i << 3);
+ }
x &= ~MASK(n);
x |= U64BIG(w);
- Pack.UInt64_To_LE(x, bytes, inOff);
+ for (int i = 0; i < n; ++i)
+ {
+ bytes[i + inOff] = (byte)(x >> (i << 3));
+ }
}
private ulong LOADBYTES(byte[] bytes, int inOff, int n)
@@ -261,34 +279,102 @@ namespace Org.BouncyCastle.Crypto.Engines
cOff += ASCON_AEAD_RATE;
mlen -= ASCON_AEAD_RATE;
}
- /* readonly plaintext block */
- if (ASCON_AEAD_RATE == 16 && mlen >= 8)
+ }
+
+ private void ascon_decrypt(byte[] m, int mOff, byte[] c, int cOff, int clen)
+ {
+ /* full ciphertext blocks */
+ while (clen >= ASCON_AEAD_RATE)
{
- x0 ^= LOAD(m, mOff, 8);
- STORE(c, cOff, x0, 8);
- mOff += 8;
- cOff += 8;
- mlen -= 8;
- x1 ^= PAD(mlen);
- if (mlen != 0)
+ ulong cx = LOAD(c, cOff, 8);
+ x0 ^= cx;
+ STORE(m, mOff, x0, 8);
+ x0 = cx;
+ if (ASCON_AEAD_RATE == 16)
+ {
+ cx = LOAD(c, cOff + 8, 8);
+ x1 ^= cx;
+ STORE(m, mOff + 8, x1, 8);
+ x1 = cx;
+ }
+ P(nr);
+ mOff += ASCON_AEAD_RATE;
+ cOff += ASCON_AEAD_RATE;
+ clen -= ASCON_AEAD_RATE;
+ }
+ }
+
+ private ulong CLEAR(ulong w, int n)
+ {
+ /* undefined for n == 0 */
+ ulong mask = 0x00ffffffffffffffUL >> (n * 8 - 8);
+ return w & mask;
+ }
+
+ private void ascon_final(byte[] c, int cOff, byte[] m, int mOff, int mlen)
+ {
+ if (forEncryption)
+ {
+ /* final plaintext block */
+ if (ASCON_AEAD_RATE == 16 && mlen >= 8)
+ {
+ x0 ^= LOAD(m, mOff, 8);
+ STORE(c, cOff, x0, 8);
+ mOff += 8;
+ cOff += 8;
+ mlen -= 8;
+ x1 ^= PAD(mlen);
+ if (mlen != 0)
+ {
+ x1 ^= LOAD(m, mOff, mlen);
+ STORE(c, cOff, x1, mlen);
+ }
+ }
+ else
{
- x1 ^= LOAD(m, mOff, mlen);
- STORE(c, cOff, x1, mlen);
+ x0 ^= PAD(mlen);
+ if (mlen != 0)
+ {
+ x0 ^= LOAD(m, mOff, mlen);
+ STORE(c, cOff, x0, mlen);
+ }
}
}
else
{
- x0 ^= PAD(mlen);
- if (mlen != 0)
+ /* final ciphertext block */
+ if (ASCON_AEAD_RATE == 16 && mlen >= 8)
{
- x0 ^= LOAD(m, mOff, mlen);
- STORE(c, cOff, x0, mlen);
+ ulong cx = LOAD(m, mOff, 8);
+ x0 ^= cx;
+ STORE(c, cOff, x0, 8);
+ x0 = cx;
+ mOff += 8;
+ cOff += 8;
+ mlen -= 8;
+ x1 ^= PAD(mlen);
+ if (mlen != 0)
+ {
+ cx = LOAD(m, mOff, mlen);
+ x1 ^= cx;
+ STORE(c, cOff, x1, mlen);
+ x1 = CLEAR(x1, mlen);
+ x1 ^= cx;
+ }
+ }
+ else
+ {
+ x0 ^= PAD(mlen);
+ if (mlen != 0)
+ {
+ ulong cx = LOAD(m, mOff, mlen);
+ x0 ^= cx;
+ STORE(c, cOff, x0, mlen);
+ x0 = CLEAR(x0, mlen);
+ x0 ^= cx;
+ }
}
}
- }
-
- private void ascon_final()
- {
/* finalize */
switch (asconParameters)
{
@@ -313,10 +399,7 @@ namespace Org.BouncyCastle.Crypto.Engines
public void Init(bool forEncryption, ICipherParameters param)
{
- /**
- * ASCON encryption and decryption is completely symmetrical, so the
- * 'forEncryption' is irrelevant.
-*/
+ this.forEncryption = forEncryption;
if (!(param is ParametersWithIV))
{
throw new ArgumentException(
@@ -352,18 +435,33 @@ namespace Org.BouncyCastle.Crypto.Engines
K1 = LOADBYTES(k, 4, 8);
K2 = LOADBYTES(k, 12, 8);
}
+ initialised = true;
/*Mask-Gen*/
- Reset();
+ reset(false);
}
public void ProcessAadByte(byte input)
{
+ if (aadFinished)
+ {
+ throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE +
+ " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
+ }
aadData.Write(new byte[] { input }, 0, 1);
}
public void ProcessAadBytes(byte[] input, int inOff, int len)
{
+ if (aadFinished)
+ {
+ throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE +
+ " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
+ }
+ if ((inOff + len) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
aadData.Write(input, inOff, len);
}
@@ -376,46 +474,127 @@ namespace Org.BouncyCastle.Crypto.Engines
public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
{
- if (encrypted)
+ if (!initialised)
{
- throw new ArgumentException("ProcessBytes for ASCONcan be called once only");
+ throw new ArgumentException("Need call init function before encryption/decryption");
}
- byte[] ad = aadData.GetBuffer();
- int adlen = (int)aadData.Length;
- /* perform ascon computation */
- //ascon_aeadinit();
- ascon_adata(ad, 0, adlen);
- ascon_encrypt(output, outOff, input, inOff, len);
- ascon_final();
+ if ((inOff + len) > input.Length)
+ {
+ throw new DataLengthException("input buffer too short");
+ }
+ message.Write(input, inOff, len);
+ int rv = processBytes(output, outOff);
encrypted = true;
- return len;
+ return rv;
+ }
+
+ private void processAAD()
+ {
+ if (!aadFinished)
+ {
+ byte[] ad = aadData.GetBuffer();
+ int adlen = (int)aadData.Length;
+ /* perform ascon computation */
+ ascon_adata(ad, 0, adlen);
+ aadFinished = true;
+ }
}
+ private int processBytes(byte[] output, int outOff)
+ {
+ int len = 0;
+ if (forEncryption)
+ {
+ if ((int)message.Length >= ASCON_AEAD_RATE)
+ {
+ processAAD();
+ byte[] input = message.GetBuffer();
+ len = ((int)message.Length / ASCON_AEAD_RATE) * ASCON_AEAD_RATE;
+ if (len + outOff > output.Length)
+ {
+ throw new OutputLengthException("output buffer is too short");
+ }
+ ascon_encrypt(output, outOff, input, 0, len);
+ int len_orig = (int)message.Length;
+ message.SetLength(0);
+ message.Write(input, len, len_orig - len);
+ }
+ }
+ else
+ {
+ if ((int)message.Length - CRYPTO_ABYTES >= ASCON_AEAD_RATE)
+ {
+ processAAD();
+ byte[] input = message.GetBuffer();
+ len = (((int)message.Length - CRYPTO_ABYTES) / ASCON_AEAD_RATE) * ASCON_AEAD_RATE;
+ if (len + outOff > output.Length)
+ {
+ throw new OutputLengthException("output buffer is too short");
+ }
+ ascon_decrypt(output, outOff, input, 0, len);
+ int len_orig = (int)message.Length;
+ message.SetLength(0);
+ message.Write(input, len, len_orig - len);
+ }
+ }
+ return len;
+ }
public int DoFinal(byte[] output, int outOff)
{
+ if (!initialised)
+ {
+ throw new ArgumentException("Need call init function before encryption/decryption");
+ }
+ if (!aadFinished)
+ {
+ processAAD();
+ }
if (!encrypted)
{
ProcessBytes(new byte[] { }, 0, 0, new byte[] { }, 0);
}
- /* set tag */
- STOREBYTES(output, outOff, x3, 8);
- STOREBYTES(output, outOff + 8, x4, 8);
- Reset();
- return CRYPTO_ABYTES;
+ byte[] input = message.GetBuffer();
+ int len = (int)message.Length;
+ if ((forEncryption && outOff + len + CRYPTO_ABYTES > output.Length) ||
+ (!forEncryption && outOff + len - CRYPTO_ABYTES > output.Length))
+ {
+ throw new OutputLengthException("output buffer too short");
+ }
+ if (forEncryption)
+ {
+ ascon_final(output, outOff, input, 0, len);
+ /* set tag */
+ mac = new byte[16];
+ STOREBYTES(mac, 0, x3, 8);
+ STOREBYTES(mac, 8, x4, 8);
+ Array.Copy(mac, 0, output, len + outOff, 16);
+ reset(false);
+ return len + CRYPTO_ABYTES;
+ }
+ else
+ {
+ len -= CRYPTO_ABYTES;
+ ascon_final(output, outOff, input, 0, len);
+ x3 ^= LOADBYTES(input, len, 8);
+ x4 ^= LOADBYTES(input, len + 8, 8);
+ ulong result = x3 | x4;
+ result |= result >> 32;
+ result |= result >> 16;
+ result |= result >> 8;
+ reset(true);
+ if ((((((int)(result & 0xffUL) - 1) >> 8) & 1) - 1) != 0)
+ {
+ throw new ArgumentException("Mac does not match");
+ }
+ return len;
+ }
}
public byte[] GetMac()
{
- if (!encrypted)
- {
- ProcessBytes(new byte[] { }, 0, 0, new byte[] { }, 0);
- }
- byte[] output = new byte[CRYPTO_ABYTES];
- STOREBYTES(output, 0, x3, 8);
- STOREBYTES(output, 8, x4, 8);
- return output;
+ return mac;
}
@@ -429,49 +608,86 @@ namespace Org.BouncyCastle.Crypto.Engines
return len + CRYPTO_ABYTES;
}
-
public void Reset()
{
+ reset(true);
+ }
+
+ private void reset(bool clearMac)
+ {
+ if (!initialised)
+ {
+ throw new ArgumentException("Need call init function before encryption/decryption");
+ }
x0 = x1 = x2 = x3 = x4 = 0;
ascon_aeadinit();
aadData.SetLength(0);
+ message.SetLength(0);
encrypted = false;
+ aadFinished = false;
+ if (clearMac)
+ {
+ mac = null;
+ }
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
public void ProcessAadBytes(ReadOnlySpan<byte> input)
{
+ if (aadFinished)
+ {
+ throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE +
+ " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
+ }
aadData.Write(input);
}
public int ProcessByte(byte input, Span<byte> output)
{
byte[] rv = new byte[1];
- ProcessBytes(new byte[] { input }, 0, 1, rv, 0);
- rv.AsSpan(0, 1).CopyTo(output);
- return 1;
+ int len = ProcessBytes(new byte[] { input }, 0, 1, rv, 0);
+ rv.AsSpan(0, len).CopyTo(output);
+ return len;
}
public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
byte[] rv = new byte[input.Length];
- ProcessBytes(input.ToArray(), 0, rv.Length, rv, 0);
- rv.AsSpan(0, rv.Length).CopyTo(output);
- return rv.Length;
+ int len = ProcessBytes(input.ToArray(), 0, rv.Length, rv, 0);
+ rv.AsSpan(0, len).CopyTo(output);
+ return len;
}
public int DoFinal(Span<byte> output)
{
- byte[] tag = GetMac();
- tag.AsSpan(0, tag.Length).CopyTo(output);
- Reset();
- return tag.Length;
+ byte[] rv;
+ if (forEncryption)
+ {
+ rv = new byte[message.Length + 16];
+ }
+ else
+ {
+ rv = new byte[message.Length];
+ }
+ int len = DoFinal(rv, 0);
+ rv.AsSpan(0, len).CopyTo(output);
+ return rv.Length;
}
#endif
public int GetBlockSize()
{
return ASCON_AEAD_RATE;
}
+
+ public int GetKeyBytesSize()
+ {
+ return CRYPTO_KEYBYTES;
+ }
+
+ public int GetIVBytesSize()
+ {
+ return CRYPTO_ABYTES;
+ }
}
}
diff --git a/crypto/test/src/crypto/test/AsconTest.cs b/crypto/test/src/crypto/test/AsconTest.cs
index f1da9133f..cc8450a19 100644
--- a/crypto/test/src/crypto/test/AsconTest.cs
+++ b/crypto/test/src/crypto/test/AsconTest.cs
@@ -8,6 +8,8 @@ using Org.BouncyCastle.Utilities.Test;
using System.Collections.Generic;
using System.IO;
using Org.BouncyCastle.Crypto.Engines;
+using System.Security.Cryptography;
+using Org.BouncyCastle.Crypto.Modes;
namespace BouncyCastle.Crypto.Tests
{
@@ -22,6 +24,15 @@ namespace BouncyCastle.Crypto.Tests
[Test]
public override void PerformTest()
{
+ AsconEngine Ascon = new AsconEngine(AsconEngine.AsconParameters.ascon80pq);
+ testExceptions(Ascon, Ascon.GetKeyBytesSize(), Ascon.GetIVBytesSize(), Ascon.GetBlockSize());
+ testParameters(Ascon, 20, 16, 16, 8);
+ Ascon = new AsconEngine(AsconEngine.AsconParameters.ascon128a);
+ testExceptions(Ascon, Ascon.GetKeyBytesSize(), Ascon.GetIVBytesSize(), Ascon.GetBlockSize());
+ testParameters(Ascon, 16, 16, 16, 16);
+ Ascon = new AsconEngine(AsconEngine.AsconParameters.ascon128);
+ testExceptions(Ascon, Ascon.GetKeyBytesSize(), Ascon.GetIVBytesSize(), Ascon.GetBlockSize());
+ testParameters(Ascon, 16, 16, 16, 8);
testVectors(AsconEngine.AsconParameters.ascon80pq, "160_128");
testVectors(AsconEngine.AsconParameters.ascon128a, "128_128_a");
testVectors(AsconEngine.AsconParameters.ascon128, "128_128");
@@ -37,7 +48,6 @@ namespace BouncyCastle.Crypto.Tests
{
string line;
string[] data;
- byte[] ptByte, adByte;
byte[] rv;
Dictionary<string, string> map = new Dictionary<string, string>();
while ((line = src.ReadLine()) != null)
@@ -45,19 +55,22 @@ namespace BouncyCastle.Crypto.Tests
data = line.Split(' ');
if (data.Length == 1)
{
- //if (!map["Count"].Equals("2"))
+ //if (!map["Count"].Equals("265"))
//{
// continue;
//}
- param = new ParametersWithIV(new KeyParameter(Hex.Decode(map["Key"])), Hex.Decode(map["Nonce"]));
+ byte[] key = Hex.Decode(map["Key"]);
+ byte[] nonce = Hex.Decode(map["Nonce"]);
+ byte[] ad = Hex.Decode(map["AD"]);
+ byte[] pt = Hex.Decode(map["PT"]);
+ byte[] ct = Hex.Decode(map["CT"]);
+ param = new ParametersWithIV(new KeyParameter(key), nonce);
Ascon.Init(true, param);
- adByte = Hex.Decode(map["AD"]);
- Ascon.ProcessAadBytes(adByte, 0, adByte.Length);
- ptByte = Hex.Decode(map["PT"]);
- rv = new byte[Ascon.GetOutputSize(ptByte.Length)];
- Ascon.ProcessBytes(ptByte, 0, ptByte.Length, rv, 0);
+ Ascon.ProcessAadBytes(ad, 0, ad.Length);
+ rv = new byte[Ascon.GetOutputSize(pt.Length)];
+ int len = Ascon.ProcessBytes(pt, 0, pt.Length, rv, 0);
//byte[] mac = new byte[16];
- Ascon.DoFinal(rv, ptByte.Length);
+ Ascon.DoFinal(rv, len);
//foreach(byte b in Hex.Decode(map["CT"]))
//{
// Console.Write(b.ToString("X2"));
@@ -68,10 +81,20 @@ namespace BouncyCastle.Crypto.Tests
// Console.Write(b.ToString("X2"));
//}
//Console.WriteLine();
- Assert.True(Arrays.AreEqual(rv, Hex.Decode(map["CT"])));
+ Assert.True(Arrays.AreEqual(rv, ct));
+ Ascon.Reset();
+ Ascon.Init(false, param);
+ //Decrypt
+ Ascon.ProcessAadBytes(ad, 0, ad.Length);
+ rv = new byte[pt.Length + 16];
+ len = Ascon.ProcessBytes(ct, 0, ct.Length, rv, 0);
+ Ascon.DoFinal(rv, len);
+ byte[] pt_recovered = new byte[pt.Length];
+ Array.Copy(rv, 0, pt_recovered, 0, pt.Length);
+ Assert.True(Arrays.AreEqual(pt, pt_recovered));
//Console.WriteLine(map["Count"] + " pass");
map.Clear();
- Ascon.Reset();
+
}
else
{
@@ -89,5 +112,320 @@ namespace BouncyCastle.Crypto.Tests
}
Console.WriteLine("Ascon AEAD pass");
}
+
+
+ private void testExceptions(IAeadBlockCipher aeadBlockCipher, int keysize, int ivsize, int blocksize)
+
+ {
+ ICipherParameters param;
+ byte[] k = new byte[keysize];
+ byte[] iv = new byte[ivsize];
+ byte[] m = new byte[0];
+ byte[] c1 = new byte[aeadBlockCipher.GetOutputSize(m.Length)];
+ param = new ParametersWithIV(new KeyParameter(k), iv);
+ try
+ {
+ aeadBlockCipher.ProcessBytes(m, 0, m.Length, c1, 0);
+ Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before ProcessBytes");
+ }
+ catch (ArgumentException e)
+ {
+ //expected
+ }
+
+ try
+ {
+ aeadBlockCipher.ProcessByte((byte)0, c1, 0);
+ Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before ProcessByte");
+ }
+ catch (ArgumentException e)
+ {
+ //expected
+ }
+
+ try
+ {
+ aeadBlockCipher.Reset();
+ Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before reset");
+ }
+ catch (ArgumentException e)
+ {
+ //expected
+ }
+
+ try
+ {
+ aeadBlockCipher.DoFinal(c1, m.Length);
+ Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before dofinal");
+ }
+ catch (ArgumentException e)
+ {
+ //expected
+ }
+
+ try
+ {
+ aeadBlockCipher.GetMac();
+ aeadBlockCipher.GetOutputSize(0);
+ aeadBlockCipher.GetUpdateOutputSize(0);
+ }
+ catch (ArgumentException e)
+ {
+ //expected
+ Assert.Fail(aeadBlockCipher.AlgorithmName + " functions can be called before initialisation");
+ }
+ Random rand = new Random();
+ int randomNum;
+ while ((randomNum = rand.Next(100)) == keysize) ;
+ byte[] k1 = new byte[randomNum];
+ while ((randomNum = rand.Next(100)) == ivsize) ;
+ byte[] iv1 = new byte[randomNum];
+ try
+ {
+ aeadBlockCipher.Init(true, new ParametersWithIV(new KeyParameter(k1), iv));
+ Assert.Fail(aeadBlockCipher.AlgorithmName + " k size does not match");
+ }
+ catch (ArgumentException e)
+ {
+ //expected
+ }
+ try
+ {
+ aeadBlockCipher.Init(true, new ParametersWithIV(new KeyParameter(k), iv1));
+ Assert.Fail(aeadBlockCipher.AlgorithmName + "iv size does not match");
+ }
+ catch (ArgumentException e)
+ {
+ //expected
+ }
+
+
+ aeadBlockCipher.Init(true, param);
+ try
+ {
+ aeadBlockCipher.DoFinal(c1, m.Length);
+ }
+ catch (Exception e)
+ {
+ Assert.Fail(aeadBlockCipher.AlgorithmName + " allows no input for AAD and plaintext");
+ }
+ byte[] mac2 = aeadBlockCipher.GetMac();
+ if (mac2 == null)
+ {
+ Assert.Fail("mac should not be empty after dofinal");
+ }
+ if (!Arrays.AreEqual(mac2, c1))
+ {
+ Assert.Fail("mac should be equal when calling dofinal and getMac");
+ }
+ aeadBlockCipher.ProcessAadByte((byte)0);
+ byte[] mac1 = new byte[aeadBlockCipher.GetOutputSize(0)];
+ aeadBlockCipher.DoFinal(mac1, 0);
+ if (Arrays.AreEqual(mac1, mac2))
+ {
+ Assert.Fail("mac should not match");
+ }
+ aeadBlockCipher.Reset();
+ aeadBlockCipher.ProcessBytes(new byte[16], 0, 16, new byte[16], 0);
+ try
+ {
+ aeadBlockCipher.ProcessAadByte((byte)0);
+ Assert.Fail("ProcessAadByte(s) cannot be called after encryption/decryption");
+ }
+ catch (ArgumentException e)
+ {
+ //expected
+ }
+ try
+ {
+ aeadBlockCipher.ProcessAadBytes(new byte[] { 0 }, 0, 1);
+ Assert.Fail("ProcessAadByte(s) cannot be called once only");
+ }
+ catch (ArgumentException e)
+ {
+ //expected
+ }
+
+ aeadBlockCipher.Reset();
+ try
+ {
+ aeadBlockCipher.ProcessAadBytes(new byte[] { 0 }, 1, 1);
+ Assert.Fail("input for ProcessAadBytes is too short");
+ }
+ catch (DataLengthException e)
+ {
+ //expected
+ }
+ try
+ {
+ aeadBlockCipher.ProcessBytes(new byte[] { 0 }, 1, 1, c1, 0);
+ Assert.Fail("input for ProcessBytes is too short");
+ }
+ catch (DataLengthException e)
+ {
+ //expected
+ }
+ try
+ {
+ aeadBlockCipher.ProcessBytes(new byte[16], 0, 16, new byte[16], 8);
+ Assert.Fail("output for ProcessBytes is too short");
+ }
+ catch (OutputLengthException e)
+ {
+ //expected
+ }
+ try
+ {
+ aeadBlockCipher.DoFinal(new byte[2], 2);
+ Assert.Fail("output for dofinal is too short");
+ }
+ catch (DataLengthException e)
+ {
+ //expected
+ }
+
+ mac1 = new byte[aeadBlockCipher.GetOutputSize(0)];
+ mac2 = new byte[aeadBlockCipher.GetOutputSize(0)];
+ aeadBlockCipher.Reset();
+ aeadBlockCipher.ProcessAadBytes(new byte[] { 0, 0 }, 0, 2);
+ aeadBlockCipher.DoFinal(mac1, 0);
+ aeadBlockCipher.Reset();
+ aeadBlockCipher.ProcessAadByte((byte)0);
+ aeadBlockCipher.ProcessAadByte((byte)0);
+ aeadBlockCipher.DoFinal(mac2, 0);
+ if (!Arrays.AreEqual(mac1, mac2))
+ {
+ Assert.Fail("mac should match for the same AAD with different ways of inputing");
+ }
+
+ byte[] c2 = new byte[aeadBlockCipher.GetOutputSize(10)];
+ byte[] c3 = new byte[aeadBlockCipher.GetOutputSize(10) + 2];
+ byte[] aad2 = { 0, 1, 2, 3, 4 };
+ byte[] aad3 = { 0, 0, 1, 2, 3, 4, 5 };
+ byte[] m2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ byte[] m3 = { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ byte[] m4 = new byte[m2.Length];
+ aeadBlockCipher.Reset();
+ aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
+ int offset = aeadBlockCipher.ProcessBytes(m2, 0, m2.Length, c2, 0);
+ aeadBlockCipher.DoFinal(c2, offset);
+ aeadBlockCipher.Reset();
+ aeadBlockCipher.ProcessAadBytes(aad3, 1, aad2.Length);
+ offset = aeadBlockCipher.ProcessBytes(m3, 1, m2.Length, c3, 1);
+ aeadBlockCipher.DoFinal(c3, offset + 1);
+ byte[] c3_partial = new byte[c2.Length];
+ Array.Copy(c3, 1, c3_partial, 0, c2.Length);
+ if (!Arrays.AreEqual(c2, c3_partial))
+ {
+ Assert.Fail("mac should match for the same AAD and message with different offset for both input and output");
+ }
+ aeadBlockCipher.Reset();
+ aeadBlockCipher.Init(false, param);
+ aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = aeadBlockCipher.ProcessBytes(c2, 0, c2.Length, m4, 0);
+ aeadBlockCipher.DoFinal(m4, offset);
+ if (!Arrays.AreEqual(m2, m4))
+ {
+ Assert.Fail("The encryption and decryption does not recover the plaintext");
+ }
+ Console.WriteLine(aeadBlockCipher.AlgorithmName + " test Exceptions pass");
+ c2[c2.Length - 1] ^= 1;
+ aeadBlockCipher.Reset();
+ aeadBlockCipher.Init(false, param);
+ aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = aeadBlockCipher.ProcessBytes(c2, 0, c2.Length, m4, 0);
+ try
+ {
+ aeadBlockCipher.DoFinal(m4, offset);
+ Assert.Fail("The decryption should fail");
+ }
+ catch (ArgumentException e)
+ {
+ //expected;
+ }
+ c2[c2.Length - 1] ^= 1;
+
+ byte[] m7 = new byte[blocksize * 2];
+ for (int i = 0; i < m7.Length; ++i)
+ {
+ m7[i] = (byte)rand.Next();
+ }
+ byte[] c7 = new byte[aeadBlockCipher.GetOutputSize(m7.Length)];
+ byte[] c8 = new byte[c7.Length];
+ byte[] c9 = new byte[c7.Length];
+ aeadBlockCipher.Init(true, param);
+ aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = aeadBlockCipher.ProcessBytes(m7, 0, m7.Length, c7, 0);
+ aeadBlockCipher.DoFinal(c7, offset);
+ aeadBlockCipher.Reset();
+ aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = aeadBlockCipher.ProcessBytes(m7, 0, blocksize, c8, 0);
+ offset += aeadBlockCipher.ProcessBytes(m7, blocksize, m7.Length - blocksize, c8, offset);
+ aeadBlockCipher.DoFinal(c8, offset);
+ aeadBlockCipher.Reset();
+ int split = rand.Next(blocksize * 2);
+ aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = aeadBlockCipher.ProcessBytes(m7, 0, split, c9, 0);
+ offset += aeadBlockCipher.ProcessBytes(m7, split, m7.Length - split, c9, offset);
+ aeadBlockCipher.DoFinal(c9, offset);
+ if (!Arrays.AreEqual(c7, c8) || !Arrays.AreEqual(c7, c9))
+ {
+ Assert.Fail("Splitting input of plaintext should output the same ciphertext");
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Span<byte> c4_1 = new byte[c2.Length];
+ Span<byte> c4_2 = new byte[c2.Length];
+ ReadOnlySpan<byte> m5 = new ReadOnlySpan<byte>(m2);
+ ReadOnlySpan<byte> aad4 = new ReadOnlySpan<byte>(aad2);
+ aeadBlockCipher.Init(true, param);
+ aeadBlockCipher.ProcessAadBytes(aad4);
+ offset = aeadBlockCipher.ProcessBytes(m5, c4_1);
+ aeadBlockCipher.DoFinal(c4_2);
+ byte[] c5 = new byte[c2.Length];
+ Array.Copy(c4_1.ToArray(), 0, c5, 0, offset);
+ Array.Copy(c4_2.ToArray(), 0, c5, offset, c5.Length - offset);
+ if (!Arrays.AreEqual(c2, c5))
+ {
+ Assert.Fail("mac should match for the same AAD and message with different offset for both input and output");
+ }
+ aeadBlockCipher.Reset();
+ aeadBlockCipher.Init(false, param);
+ Span<byte> m6_1 = new byte[m2.Length];
+ Span<byte> m6_2 = new byte[m2.Length];
+ ReadOnlySpan<byte> c6 = new ReadOnlySpan<byte>(c2);
+ aeadBlockCipher.ProcessAadBytes(aad4);
+ offset = aeadBlockCipher.ProcessBytes(c6, m6_1);
+ aeadBlockCipher.DoFinal(m6_2);
+ byte[] m6 = new byte[m2.Length];
+ Array.Copy(m6_1.ToArray(), 0, m6, 0, offset);
+ Array.Copy(m6_2.ToArray(), 0, m6, offset, m6.Length - offset);
+ if (!Arrays.AreEqual(m2, m6))
+ {
+ Assert.Fail("mac should match for the same AAD and message with different offset for both input and output");
+ }
+#endif
+ }
+
+ private void testParameters(AsconEngine ascon, int keySize, int ivSize, int macSize, int blockSize)
+ {
+ if (ascon.GetKeyBytesSize() != keySize)
+ {
+ Assert.Fail("key bytes of " + ascon.AlgorithmName + " is not correct");
+ }
+ if (ascon.GetIVBytesSize() != ivSize)
+ {
+ Assert.Fail("iv bytes of " + ascon.AlgorithmName + " is not correct");
+ }
+ if (ascon.GetOutputSize(0) != macSize)
+ {
+ Assert.Fail("mac bytes of " + ascon.AlgorithmName + " is not correct");
+ }
+ if (ascon.GetBlockSize() != blockSize)
+ {
+ Assert.Fail("block size of " + ascon.AlgorithmName + " is not correct");
+ }
+ Console.WriteLine(ascon.AlgorithmName + " test Parameters pass");
+ }
}
-}
+}
\ No newline at end of file
|