diff options
author | gefeili <gli@keyfactor.com> | 2023-01-23 13:17:29 +1030 |
---|---|---|
committer | gefeili <gli@keyfactor.com> | 2023-01-23 13:17:29 +1030 |
commit | 50f605fdfafd4bcf2fff5f75b1ade5490e7710ab (patch) | |
tree | 83b07ad4b011dd84f78a32564ef3e73170c66f04 | |
parent | Complete tests for Ascon AEAD (diff) | |
download | BouncyCastle.NET-ed25519-50f605fdfafd4bcf2fff5f75b1ade5490e7710ab.tar.xz |
Complete tests for Ascon AEAD
-rw-r--r-- | crypto/src/crypto/engines/AsconEngine.cs | 354 | ||||
-rw-r--r-- | crypto/test/src/crypto/test/AsconTest.cs | 362 |
2 files changed, 635 insertions, 81 deletions
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 |