diff options
author | gefeili <gli@keyfactor.com> | 2023-01-31 15:59:37 +1030 |
---|---|---|
committer | gefeili <gli@keyfactor.com> | 2023-01-31 15:59:37 +1030 |
commit | 70473a06799b0f5273b61dcba2f9057b2a8bdf68 (patch) | |
tree | 297339cd77f39439ef67ab7d1d95d05fbad5639d | |
parent | Initial push of Elephant v2 (diff) | |
download | BouncyCastle.NET-ed25519-70473a06799b0f5273b61dcba2f9057b2a8bdf68.tar.xz |
Add test of Elephyant and decryption part
-rw-r--r-- | crypto/src/crypto/engines/ElephantEngine.cs | 181 | ||||
-rw-r--r-- | crypto/test/src/crypto/test/ElephantTest.cs | 360 |
2 files changed, 469 insertions, 72 deletions
diff --git a/crypto/src/crypto/engines/ElephantEngine.cs b/crypto/src/crypto/engines/ElephantEngine.cs index b0ec4733c..e3d9ca670 100644 --- a/crypto/src/crypto/engines/ElephantEngine.cs +++ b/crypto/src/crypto/engines/ElephantEngine.cs @@ -12,7 +12,7 @@ using Org.BouncyCastle.Utilities; */ namespace Org.BouncyCastle.Crypto.Engines { - public class ElephantEngine : IAeadCipher + public class ElephantEngine : IAeadBlockCipher { public enum ElephantParameters { @@ -20,23 +20,23 @@ namespace Org.BouncyCastle.Crypto.Engines elephant176, elephant200 } - + private bool forEncryption; + private readonly string algorithmName; private ElephantParameters parameters; private int BLOCK_SIZE; private int nBits; private int nSBox; private int nRounds; private byte lfsrIV; - private byte[] m; private byte[] npub; private byte[] expanded_key; - private byte[] tag_buffer; + private byte[] tag; private byte CRYPTO_KEYBYTES = 16; private byte CRYPTO_NPUBBYTES = 12; private byte CRYPTO_ABYTES; + private bool initialised; private MemoryStream aadData = new MemoryStream(); - private bool encrypted; - private bool aadFinished; + private MemoryStream message = new MemoryStream(); private readonly byte[] sBoxLayer = { (byte)0xee, (byte)0xed, (byte)0xeb, (byte)0xe0, (byte)0xe2, (byte)0xe1, (byte)0xe4, (byte)0xef, (byte)0xe7, (byte)0xea, (byte)0xe8, (byte)0xe5, (byte)0xe9, (byte)0xec, (byte)0xe3, (byte)0xe6, @@ -75,6 +75,7 @@ namespace Org.BouncyCastle.Crypto.Engines nRounds = 80; lfsrIV = 0x75; CRYPTO_ABYTES = 8; + algorithmName = "Elephant 160 AEAD"; break; case ElephantParameters.elephant176: BLOCK_SIZE = 22; @@ -83,15 +84,19 @@ namespace Org.BouncyCastle.Crypto.Engines nRounds = 90; lfsrIV = 0x45; CRYPTO_ABYTES = 8; + algorithmName = "Elephant 176 AEAD"; break; case ElephantParameters.elephant200: BLOCK_SIZE = 25; nRounds = 18; CRYPTO_ABYTES = 16; + algorithmName = "Elephant 200 AEAD"; break; + default: + throw new ArgumentException("Invalid parameter settings for Elephant"); } this.parameters = parameters; - tag_buffer = new byte[BLOCK_SIZE]; + initialised = false; reset(false); } @@ -157,7 +162,7 @@ namespace Org.BouncyCastle.Crypto.Engines return x + y * 5; } - void KeccakP200Round(byte[] state, int indexRound) + private void KeccakP200Round(byte[] state, int indexRound) { int x, y; byte[] tempA = new byte[25]; @@ -215,7 +220,7 @@ namespace Org.BouncyCastle.Crypto.Engines // State should be BLOCK_SIZE bytes long // Note: input may be equal to output - void lfsr_step(byte[] output, byte[] input) + private void lfsr_step(byte[] output, byte[] input) { switch (parameters) { @@ -233,7 +238,7 @@ namespace Org.BouncyCastle.Crypto.Engines Array.Copy(input, 1, output, 0, BLOCK_SIZE - 1); } - void xor_block(byte[] state, byte[] block, int bOff, int size) + private void xor_block(byte[] state, byte[] block, int bOff, int size) { for (int i = 0; i < size; ++i) { @@ -244,7 +249,7 @@ namespace Org.BouncyCastle.Crypto.Engines // Write the ith assocated data block to "output". // The nonce is prepended and padding is added as required. // adlen is the length of the associated data in bytes - void get_ad_block(byte[] output, byte[] ad, int adlen, byte[] npub, int i) + private void get_ad_block(byte[] output, byte[] ad, int adlen, byte[] npub, int i) { int len = 0; // First block contains nonce @@ -282,9 +287,9 @@ namespace Org.BouncyCastle.Crypto.Engines // Return the ith ciphertext block. // clen is the length of the ciphertext in bytes - void get_c_block(byte[] output, byte[] c, int cOff, int clen, int i) + private void get_c_block(byte[] output, byte[] c, int cOff, int clen, int i) { - int block_offset = cOff + i * BLOCK_SIZE; + int block_offset = i * BLOCK_SIZE; // If clen is divisible by BLOCK_SIZE, add an additional padding block if (block_offset == clen) { @@ -296,13 +301,13 @@ namespace Org.BouncyCastle.Crypto.Engines // Fill with ciphertext if available if (BLOCK_SIZE <= r_clen) { // enough ciphertext - Array.Copy(c, block_offset, output, 0, BLOCK_SIZE); + Array.Copy(c, cOff + block_offset, output, 0, BLOCK_SIZE); } else { // not enough ciphertext, need to pad if (r_clen > 0) // c might be nullptr { - Array.Copy(c, block_offset, output, 0, r_clen); + Array.Copy(c, cOff + block_offset, output, 0, r_clen); } Arrays.Fill(output, r_clen, BLOCK_SIZE, (byte)0); output[r_clen] = 0x01; @@ -313,10 +318,7 @@ namespace Org.BouncyCastle.Crypto.Engines public void Init(bool forEncryption, ICipherParameters param) { - /** - * Elephant encryption and decryption is completely symmetrical, so the - * 'forEncryption' is irrelevant. - */ + this.forEncryption = forEncryption; if (!(param is ParametersWithIV)) { throw new ArgumentException( @@ -350,10 +352,14 @@ namespace Org.BouncyCastle.Crypto.Engines expanded_key = new byte[BLOCK_SIZE]; Array.Copy(k, 0, expanded_key, 0, CRYPTO_KEYBYTES); permutation(expanded_key); + initialised = true; + reset(false); } - public string AlgorithmName => "Elephant AEAD"; + public string AlgorithmName => algorithmName; + + public IBlockCipher UnderlyingCipher => throw new NotImplementedException(); public void ProcessAadByte(byte input) { @@ -363,26 +369,48 @@ namespace Org.BouncyCastle.Crypto.Engines public void ProcessAadBytes(byte[] input, int inOff, int len) { + if (inOff + len > input.Length) + { + throw new DataLengthException("input buffer too short"); + } aadData.Write(input, inOff, len); } public int ProcessByte(byte input, byte[] output, int outOff) { - return ProcessBytes(new byte[] { input }, 0, 1, output, outOff); + message.Write(new byte[] { input }, 0, 1); + return 0; } public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - if (encrypted) + if (inOff + len > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + message.Write(input, inOff, len); + return 0; + } + + + public int DoFinal(byte[] output, int outOff) + { + if (!initialised) + { + throw new ArgumentException("Need call init function before encryption/decryption"); + } + int mlen = (int)message.Length - (forEncryption ? 0 : CRYPTO_ABYTES); + if ((forEncryption && mlen + outOff + CRYPTO_ABYTES > output.Length) || + (!forEncryption && mlen + outOff - CRYPTO_ABYTES > output.Length)) { - throw new ArgumentException("Encryption has been processed"); + throw new OutputLengthException("output buffer is too short"); } - m = new byte[len]; - Array.Copy(input, inOff, m, 0, len); + byte[] tag_buffer = new byte[BLOCK_SIZE]; + byte[] m = message.GetBuffer(); byte[] ad = aadData.GetBuffer(); - int mlen = len, adlen = (int)aadData.Length; + int adlen = (int)aadData.Length; int nblocks_c = 1 + mlen / BLOCK_SIZE; int nblocks_m = (mlen % BLOCK_SIZE) != 0 ? nblocks_c : nblocks_c - 1; int nblocks_ad = 1 + (CRYPTO_NPUBBYTES + adlen) / BLOCK_SIZE; @@ -415,11 +443,17 @@ namespace Org.BouncyCastle.Crypto.Engines xor_block(buffer, m, offset, r_size); Array.Copy(buffer, 0, output, offset + outOff, r_size); } - if (i > 0 && i <= nblocks_c) { // Compute tag for ciphertext block - get_c_block(buffer, output, outOff, mlen, i - 1);//encrypt != 0 ? c : m + if (forEncryption) + { + get_c_block(buffer, output, outOff, mlen, i - 1); + } + else + { + get_c_block(buffer, m, 0, mlen, i - 1); + } xor_block(buffer, previous_mask, 0, BLOCK_SIZE); xor_block(buffer, next_mask, 0, BLOCK_SIZE); permutation(buffer); @@ -444,30 +478,34 @@ namespace Org.BouncyCastle.Crypto.Engines next_mask = temp; offset += BLOCK_SIZE; } - encrypted = true; - return 0; - } - - - public int DoFinal(byte[] output, int outOff) - { - byte[] tag = GetMac(); - Array.Copy(tag, 0, output, outOff, tag.Length); + outOff += mlen; + tag = new byte[CRYPTO_ABYTES]; + xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE); + permutation(tag_buffer); + xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE); + if (forEncryption) + { + Array.Copy(tag_buffer, 0, tag, 0, CRYPTO_ABYTES); + Array.Copy(tag, 0, output, outOff, tag.Length); + mlen += CRYPTO_ABYTES; + } + else + { + for (int i = 0; i < CRYPTO_ABYTES; ++i) + { + if (tag_buffer[i] != m[mlen + i]) + { + throw new ArgumentException("Mac does not match"); + } + } + } reset(false); - return 0; + return mlen; } public byte[] GetMac() { - byte[] tag = new byte[CRYPTO_ABYTES]; - if (!aadFinished) - { - xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE); - permutation(tag_buffer); - xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE); - } - Array.Copy(tag_buffer, 0, tag, 0, CRYPTO_ABYTES); return tag; } @@ -493,11 +531,12 @@ namespace Org.BouncyCastle.Crypto.Engines { if (clearMac) { - aadData.SetLength(0); + tag = null; } - encrypted = false; - aadFinished = false; + aadData.SetLength(0); + message.SetLength(0); } + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public void ProcessAadBytes(ReadOnlySpan<byte> input) { @@ -506,28 +545,48 @@ namespace Org.BouncyCastle.Crypto.Engines 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; + message.Write(new byte[] { input }); + return 0; } 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; + message.Write(input.ToArray()); + return 0; } public int DoFinal(Span<byte> output) { - byte[] tag = GetMac(); - tag.AsSpan(0, tag.Length).CopyTo(output); - reset(false); - return tag.Length; + byte[] rv; + if (forEncryption) + { + rv = new byte[message.Length + CRYPTO_ABYTES]; + } + else + { + rv = new byte[message.Length - CRYPTO_ABYTES]; + } + int len = DoFinal(rv, 0); + rv.AsSpan(0, len).CopyTo(output); + return rv.Length; + } #endif + + public int GetKeyBytesSize() + { + return CRYPTO_KEYBYTES; + } + + public int GetIVBytesSize() + { + return CRYPTO_NPUBBYTES; + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } } } diff --git a/crypto/test/src/crypto/test/ElephantTest.cs b/crypto/test/src/crypto/test/ElephantTest.cs index ff350b338..8d12a20e0 100644 --- a/crypto/test/src/crypto/test/ElephantTest.cs +++ b/crypto/test/src/crypto/test/ElephantTest.cs @@ -8,7 +8,7 @@ using Org.BouncyCastle.Utilities.Test; using System.Collections.Generic; using System.IO; using Org.BouncyCastle.Crypto.Engines; - +using Org.BouncyCastle.Crypto.Modes; namespace BouncyCastle.Crypto.Tests { @@ -25,6 +25,15 @@ namespace BouncyCastle.Crypto.Tests testVectors(ElephantEngine.ElephantParameters.elephant160, "v160"); testVectors(ElephantEngine.ElephantParameters.elephant176, "v176"); testVectors(ElephantEngine.ElephantParameters.elephant200, "v200"); + ElephantEngine elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant160); + testExceptions(elephant, elephant.GetKeyBytesSize(), elephant.GetIVBytesSize(), elephant.GetBlockSize()); + testParameters(elephant, 16, 12, 8, 20); + elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant176); + testExceptions(elephant, elephant.GetKeyBytesSize(), elephant.GetIVBytesSize(), elephant.GetBlockSize()); + testParameters(elephant, 16, 12, 8, 22); + elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant200); + testExceptions(elephant, elephant.GetKeyBytesSize(), elephant.GetIVBytesSize(), elephant.GetBlockSize()); + testParameters(elephant, 16, 12, 16, 25); } private void testVectors(ElephantEngine.ElephantParameters pbp, String filename) @@ -49,28 +58,43 @@ namespace BouncyCastle.Crypto.Tests //{ // 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); Elephant.Init(true, param); - adByte = Hex.Decode(map["AD"]); - Elephant.ProcessAadBytes(adByte, 0, adByte.Length); - ptByte = Hex.Decode(map["PT"]); - rv = new byte[Elephant.GetOutputSize(ptByte.Length)]; - Elephant.ProcessBytes(ptByte, 0, ptByte.Length, rv, 0); - Elephant.DoFinal(rv, ptByte.Length); - //foreach (byte b in Hex.Decode(map["CT"])) + Elephant.ProcessAadBytes(ad, 0, ad.Length); + rv = new byte[Elephant.GetOutputSize(pt.Length)]; + int len = Elephant.ProcessBytes(pt, 0, pt.Length, rv, 0); + //byte[] mac = new byte[16]; + Elephant.DoFinal(rv, len); + //foreach(byte b in Hex.Decode(map["CT"])) //{ // Console.Write(b.ToString("X2")); //} //Console.WriteLine(); - //foreach (byte b in rv) + //foreach (byte b in Arrays.Concatenate(rv, mac)) //{ // Console.Write(b.ToString("X2")); //} //Console.WriteLine(); - Assert.True(Arrays.AreEqual(rv, Hex.Decode(map["CT"]))); + Assert.True(Arrays.AreEqual(rv, ct)); + Elephant.Reset(); + Elephant.Init(false, param); + //Decrypt + Elephant.ProcessAadBytes(ad, 0, ad.Length); + rv = new byte[pt.Length + 16]; + len = Elephant.ProcessBytes(ct, 0, ct.Length, rv, 0); + Elephant.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(); Elephant.Reset(); + } else { @@ -87,6 +111,320 @@ namespace BouncyCastle.Crypto.Tests } } } + + 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[blocksize], 0, blocksize, new byte[blocksize], 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[blocksize], 0, blocksize, new byte[blocksize], blocksize >> 1); + // 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 NET6_0_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(ElephantEngine Elephant, int keySize, int ivSize, int macSize, int blockSize) + { + if (Elephant.GetKeyBytesSize() != keySize) + { + Assert.Fail("key bytes of " + Elephant.AlgorithmName + " is not correct"); + } + if (Elephant.GetIVBytesSize() != ivSize) + { + Assert.Fail("iv bytes of " + Elephant.AlgorithmName + " is not correct"); + } + if (Elephant.GetOutputSize(0) != macSize) + { + Assert.Fail("mac bytes of " + Elephant.AlgorithmName + " is not correct"); + } + if (Elephant.GetBlockSize() != blockSize) + { + Assert.Fail("block size of " + Elephant.AlgorithmName + " is not correct"); + } + Console.WriteLine(Elephant.AlgorithmName + " test Parameters pass"); + } + } } |