From ac9e78b9c9bea20a4419187be7f7ba35beadb1a1 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 28 Dec 2022 14:25:10 +1030 Subject: Initial push of Elephant v2 --- crypto/src/crypto/engines/ElephantEngine.cs | 533 ++++++++++++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 crypto/src/crypto/engines/ElephantEngine.cs (limited to 'crypto/src') diff --git a/crypto/src/crypto/engines/ElephantEngine.cs b/crypto/src/crypto/engines/ElephantEngine.cs new file mode 100644 index 000000000..b0ec4733c --- /dev/null +++ b/crypto/src/crypto/engines/ElephantEngine.cs @@ -0,0 +1,533 @@ +using System; +using System.IO; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + + +/** + * Elephant AEAD v2, based on the current round 3 submission, https://www.esat.kuleuven.be/cosic/elephant/ + * Reference C implementation: https://github.com/TimBeyne/Elephant + * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/elephant-spec-final.pdf + */ +namespace Org.BouncyCastle.Crypto.Engines +{ + public class ElephantEngine : IAeadCipher + { + public enum ElephantParameters + { + elephant160, + elephant176, + elephant200 + } + + 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 CRYPTO_KEYBYTES = 16; + private byte CRYPTO_NPUBBYTES = 12; + private byte CRYPTO_ABYTES; + private MemoryStream aadData = new MemoryStream(); + private bool encrypted; + private bool aadFinished; + + 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, + (byte)0xde, (byte)0xdd, (byte)0xdb, (byte)0xd0, (byte)0xd2, (byte)0xd1, (byte)0xd4, (byte)0xdf, (byte)0xd7, (byte)0xda, (byte)0xd8, (byte)0xd5, (byte)0xd9, (byte)0xdc, (byte)0xd3, (byte)0xd6, + (byte)0xbe, (byte)0xbd, (byte)0xbb, (byte)0xb0, (byte)0xb2, (byte)0xb1, (byte)0xb4, (byte)0xbf, (byte)0xb7, (byte)0xba, (byte)0xb8, (byte)0xb5, (byte)0xb9, (byte)0xbc, (byte)0xb3, (byte)0xb6, + (byte)0x0e, (byte)0x0d, (byte)0x0b, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x04, (byte)0x0f, (byte)0x07, (byte)0x0a, (byte)0x08, (byte)0x05, (byte)0x09, (byte)0x0c, (byte)0x03, (byte)0x06, + (byte)0x2e, (byte)0x2d, (byte)0x2b, (byte)0x20, (byte)0x22, (byte)0x21, (byte)0x24, (byte)0x2f, (byte)0x27, (byte)0x2a, (byte)0x28, (byte)0x25, (byte)0x29, (byte)0x2c, (byte)0x23, (byte)0x26, + (byte)0x1e, (byte)0x1d, (byte)0x1b, (byte)0x10, (byte)0x12, (byte)0x11, (byte)0x14, (byte)0x1f, (byte)0x17, (byte)0x1a, (byte)0x18, (byte)0x15, (byte)0x19, (byte)0x1c, (byte)0x13, (byte)0x16, + (byte)0x4e, (byte)0x4d, (byte)0x4b, (byte)0x40, (byte)0x42, (byte)0x41, (byte)0x44, (byte)0x4f, (byte)0x47, (byte)0x4a, (byte)0x48, (byte)0x45, (byte)0x49, (byte)0x4c, (byte)0x43, (byte)0x46, + (byte)0xfe, (byte)0xfd, (byte)0xfb, (byte)0xf0, (byte)0xf2, (byte)0xf1, (byte)0xf4, (byte)0xff, (byte)0xf7, (byte)0xfa, (byte)0xf8, (byte)0xf5, (byte)0xf9, (byte)0xfc, (byte)0xf3, (byte)0xf6, + (byte)0x7e, (byte)0x7d, (byte)0x7b, (byte)0x70, (byte)0x72, (byte)0x71, (byte)0x74, (byte)0x7f, (byte)0x77, (byte)0x7a, (byte)0x78, (byte)0x75, (byte)0x79, (byte)0x7c, (byte)0x73, (byte)0x76, + (byte)0xae, (byte)0xad, (byte)0xab, (byte)0xa0, (byte)0xa2, (byte)0xa1, (byte)0xa4, (byte)0xaf, (byte)0xa7, (byte)0xaa, (byte)0xa8, (byte)0xa5, (byte)0xa9, (byte)0xac, (byte)0xa3, (byte)0xa6, + (byte)0x8e, (byte)0x8d, (byte)0x8b, (byte)0x80, (byte)0x82, (byte)0x81, (byte)0x84, (byte)0x8f, (byte)0x87, (byte)0x8a, (byte)0x88, (byte)0x85, (byte)0x89, (byte)0x8c, (byte)0x83, (byte)0x86, + (byte)0x5e, (byte)0x5d, (byte)0x5b, (byte)0x50, (byte)0x52, (byte)0x51, (byte)0x54, (byte)0x5f, (byte)0x57, (byte)0x5a, (byte)0x58, (byte)0x55, (byte)0x59, (byte)0x5c, (byte)0x53, (byte)0x56, + (byte)0x9e, (byte)0x9d, (byte)0x9b, (byte)0x90, (byte)0x92, (byte)0x91, (byte)0x94, (byte)0x9f, (byte)0x97, (byte)0x9a, (byte)0x98, (byte)0x95, (byte)0x99, (byte)0x9c, (byte)0x93, (byte)0x96, + (byte)0xce, (byte)0xcd, (byte)0xcb, (byte)0xc0, (byte)0xc2, (byte)0xc1, (byte)0xc4, (byte)0xcf, (byte)0xc7, (byte)0xca, (byte)0xc8, (byte)0xc5, (byte)0xc9, (byte)0xcc, (byte)0xc3, (byte)0xc6, + (byte)0x3e, (byte)0x3d, (byte)0x3b, (byte)0x30, (byte)0x32, (byte)0x31, (byte)0x34, (byte)0x3f, (byte)0x37, (byte)0x3a, (byte)0x38, (byte)0x35, (byte)0x39, (byte)0x3c, (byte)0x33, (byte)0x36, + (byte)0x6e, (byte)0x6d, (byte)0x6b, (byte)0x60, (byte)0x62, (byte)0x61, (byte)0x64, (byte)0x6f, (byte)0x67, (byte)0x6a, (byte)0x68, (byte)0x65, (byte)0x69, (byte)0x6c, (byte)0x63, (byte)0x66 + }; + + private readonly byte[] KeccakRoundConstants = { + (byte)0x01, (byte)0x82, (byte)0x8a, (byte)0x00, (byte)0x8b, (byte)0x01, (byte)0x81, (byte)0x09, (byte)0x8a, + (byte)0x88, (byte)0x09, (byte)0x0a, (byte)0x8b, (byte)0x8b, (byte)0x89, (byte)0x03, (byte)0x02, (byte)0x80 + }; + + private readonly int[] KeccakRhoOffsets = { 0, 1, 6, 4, 3, 4, 4, 6, 7, 4, 3, 2, 3, 1, 7, 1, 5, 7, 5, 0, 2, 2, 5, 0, 6 }; + + public ElephantEngine(ElephantParameters parameters) + { + switch (parameters) + { + case ElephantParameters.elephant160: + BLOCK_SIZE = 20; + nBits = 160; + nSBox = 20; + nRounds = 80; + lfsrIV = 0x75; + CRYPTO_ABYTES = 8; + break; + case ElephantParameters.elephant176: + BLOCK_SIZE = 22; + nBits = 176; + nSBox = 22; + nRounds = 90; + lfsrIV = 0x45; + CRYPTO_ABYTES = 8; + break; + case ElephantParameters.elephant200: + BLOCK_SIZE = 25; + nRounds = 18; + CRYPTO_ABYTES = 16; + break; + } + this.parameters = parameters; + tag_buffer = new byte[BLOCK_SIZE]; + reset(false); + } + + private void permutation(byte[] state) + { + switch (parameters) + { + case ElephantParameters.elephant160: + case ElephantParameters.elephant176: + byte IV = lfsrIV; + byte[] tmp = new byte[nSBox]; + for (int i = 0; i < nRounds; i++) + { + /* Add counter values */ + state[0] ^= IV; + state[nSBox - 1] ^= (byte)(((IV & 0x01) << 7) | ((IV & 0x02) << 5) | ((IV & 0x04) << 3) | ((IV & 0x08) + << 1) | ((IV & 0x10) >> 1) | ((IV & 0x20) >> 3) | ((IV & 0x40) >> 5) | ((IV & 0x80) >> 7)); + IV = (byte)(((IV << 1) | (((0x40 & IV) >> 6) ^ ((0x20 & IV) >> 5))) & 0x7f); + /* sBoxLayer layer */ + for (int j = 0; j < nSBox; j++) + { + state[j] = sBoxLayer[(state[j] & 0xFF)]; + } + /* pLayer */ + int PermutedBitNo; + Arrays.Fill(tmp, (byte)0); + for (int j = 0; j < nSBox; j++) + { + for (int k = 0; k < 8; k++) + { + PermutedBitNo = (j << 3) + k; + if (PermutedBitNo != nBits - 1) + { + PermutedBitNo = ((PermutedBitNo * nBits) >> 2) % (nBits - 1); + } + tmp[PermutedBitNo >> 3] ^= (byte)((((state[j] & 0xFF) >> k) & 0x1) << (PermutedBitNo & 7)); + } + } + Array.Copy(tmp, 0, state, 0, nSBox); + } + break; + case ElephantParameters.elephant200: + for (int i = 0; i < nRounds; i++) + { + KeccakP200Round(state, i); + } + break; + } + } + + private byte rotl(byte b) + { + return (byte)(((b & 0xFF) << 1) | ((b & 0xFF) >> 7)); + } + + private byte ROL8(byte a, int offset) + { + return (byte)((offset != 0) ? (((a & 0xFF) << offset) ^ ((a & 0xFF) >> (8 - offset))) : a); + } + + private int index(int x, int y) + { + return x + y * 5; + } + + void KeccakP200Round(byte[] state, int indexRound) + { + int x, y; + byte[] tempA = new byte[25]; + //theta + for (x = 0; x < 5; x++) + { + for (y = 0; y < 5; y++) + { + tempA[x] ^= state[index(x, y)]; + } + } + for (x = 0; x < 5; x++) + { + tempA[x + 5] = (byte)(ROL8(tempA[(x + 1) % 5], 1) ^ tempA[(x + 4) % 5]); + } + for (x = 0; x < 5; x++) + { + for (y = 0; y < 5; y++) + { + state[index(x, y)] ^= tempA[x + 5]; + } + } + //rho + for (x = 0; x < 5; x++) + { + for (y = 0; y < 5; y++) + { + tempA[index(x, y)] = ROL8(state[index(x, y)], KeccakRhoOffsets[index(x, y)]); + } + } + //pi + for (x = 0; x < 5; x++) + { + for (y = 0; y < 5; y++) + { + state[index(y, (2 * x + 3 * y) % 5)] = tempA[index(x, y)]; + } + } + //chi + for (y = 0; y < 5; y++) + { + for (x = 0; x < 5; x++) + { + tempA[x] = (byte)(state[index(x, y)] ^ ((~state[index((x + 1) % 5, y)]) & state[index((x + 2) % 5, y)])); + } + for (x = 0; x < 5; x++) + { + state[index(x, y)] = tempA[x]; + } + } + //iota + state[index(0, 0)] ^= KeccakRoundConstants[indexRound]; + } + + + // State should be BLOCK_SIZE bytes long + // Note: input may be equal to output + void lfsr_step(byte[] output, byte[] input) + { + switch (parameters) + { + case ElephantParameters.elephant160: + output[BLOCK_SIZE - 1] = (byte)((((input[0] & 0xFF) << 3) | ((input[0] & 0xFF) >> 5)) ^ + ((input[3] & 0xFF) << 7) ^ ((input[13] & 0xFF) >> 7)); + break; + case ElephantParameters.elephant176: + output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ ((input[3] & 0xFF) << 7) ^ ((input[19] & 0xFF) >> 7)); + break; + case ElephantParameters.elephant200: + output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ rotl(input[2]) ^ (input[13] << 1)); + break; + } + Array.Copy(input, 1, output, 0, BLOCK_SIZE - 1); + } + + void xor_block(byte[] state, byte[] block, int bOff, int size) + { + for (int i = 0; i < size; ++i) + { + state[i] ^= block[i + bOff]; + } + } + + // 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) + { + int len = 0; + // First block contains nonce + // Remark: nonce may not be longer then BLOCK_SIZE + if (i == 0) + { + Array.Copy(npub, 0, output, 0, CRYPTO_NPUBBYTES); + len += CRYPTO_NPUBBYTES; + } + int block_offset = i * BLOCK_SIZE - ((i != 0) ? 1 : 0) * CRYPTO_NPUBBYTES; + // If adlen is divisible by BLOCK_SIZE, add an additional padding block + if (i != 0 && block_offset == adlen) + { + Arrays.Fill(output, 0, BLOCK_SIZE, (byte)0); + output[0] = 0x01; + return; + } + int r_outlen = BLOCK_SIZE - len; + int r_adlen = adlen - block_offset; + // Fill with associated data if available + if (r_outlen <= r_adlen) + { // enough AD + Array.Copy(ad, block_offset, output, len, r_outlen); + } + else + { // not enough AD, need to pad + if (r_adlen > 0) // ad might be nullptr + { + Array.Copy(ad, block_offset, output, len, r_adlen); + } + Arrays.Fill(output, len + r_adlen, len + r_outlen, (byte)0); + output[len + r_adlen] = 0x01; + } + } + + // 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) + { + int block_offset = cOff + i * BLOCK_SIZE; + // If clen is divisible by BLOCK_SIZE, add an additional padding block + if (block_offset == clen) + { + Arrays.Fill(output, 0, BLOCK_SIZE, (byte)0); + output[0] = 0x01; + return; + } + int r_clen = clen - block_offset; + // Fill with ciphertext if available + if (BLOCK_SIZE <= r_clen) + { // enough ciphertext + Array.Copy(c, 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); + } + Arrays.Fill(output, r_clen, BLOCK_SIZE, (byte)0); + output[r_clen] = 0x01; + } + } + + + + public void Init(bool forEncryption, ICipherParameters param) + { + /** + * Elephant encryption and decryption is completely symmetrical, so the + * 'forEncryption' is irrelevant. + */ + if (!(param is ParametersWithIV)) + { + throw new ArgumentException( + "Elephant init parameters must include an IV"); + } + + ParametersWithIV ivParams = (ParametersWithIV)param; + + npub = ivParams.GetIV(); + + if (npub == null || npub.Length != 12) + { + throw new ArgumentException( + "Elephant requires exactly 12 bytes of IV"); + } + + if (!(ivParams.Parameters is KeyParameter)) + { + throw new ArgumentException( + "Elephant init parameters must include a key"); + } + + KeyParameter key = (KeyParameter)ivParams.Parameters; + byte[] k = key.GetKey(); + if (k.Length != 16) + { + throw new ArgumentException( + "Elephant key must be 128 bits long"); + } + // Storage for the expanded key L + expanded_key = new byte[BLOCK_SIZE]; + Array.Copy(k, 0, expanded_key, 0, CRYPTO_KEYBYTES); + permutation(expanded_key); + } + + + public string AlgorithmName => "Elephant AEAD"; + + public void ProcessAadByte(byte input) + { + aadData.Write(new byte[] { input }, 0, 1); + } + + + public void ProcessAadBytes(byte[] input, int inOff, int len) + { + aadData.Write(input, inOff, len); + } + + + public int ProcessByte(byte input, byte[] output, int outOff) + { + return ProcessBytes(new byte[] { input }, 0, 1, output, outOff); + } + + + public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + if (encrypted) + { + throw new ArgumentException("Encryption has been processed"); + } + m = new byte[len]; + Array.Copy(input, inOff, m, 0, len); + byte[] ad = aadData.GetBuffer(); + int mlen = len, 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; + int nb_it = System.Math.Max(nblocks_c + 1, nblocks_ad - 1); + // Buffers for storing previous, current and next mask + byte[] previous_mask = new byte[BLOCK_SIZE]; + byte[] current_mask = new byte[BLOCK_SIZE]; + byte[] next_mask = new byte[BLOCK_SIZE]; + Array.Copy(expanded_key, 0, current_mask, 0, BLOCK_SIZE); + // Buffer to store current ciphertext/AD block + byte[] buffer = new byte[BLOCK_SIZE]; + // Tag buffer and initialization of tag to first AD block + get_ad_block(tag_buffer, ad, adlen, npub, 0); + int offset = 0; + for (int i = 0; i < nb_it; ++i) + { + // Compute mask for the next message + lfsr_step(next_mask, current_mask); + if (i < nblocks_m) + { + // Compute ciphertext block + Array.Copy(npub, 0, buffer, 0, CRYPTO_NPUBBYTES); + Arrays.Fill(buffer, CRYPTO_NPUBBYTES, BLOCK_SIZE, (byte)0); + xor_block(buffer, current_mask, 0, BLOCK_SIZE); + xor_block(buffer, next_mask, 0, BLOCK_SIZE); + permutation(buffer); + xor_block(buffer, current_mask, 0, BLOCK_SIZE); + xor_block(buffer, next_mask, 0, BLOCK_SIZE); + int r_size = (i == nblocks_m - 1) ? mlen - offset : BLOCK_SIZE; + 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 + xor_block(buffer, previous_mask, 0, BLOCK_SIZE); + xor_block(buffer, next_mask, 0, BLOCK_SIZE); + permutation(buffer); + xor_block(buffer, previous_mask, 0, BLOCK_SIZE); + xor_block(buffer, next_mask, 0, BLOCK_SIZE); + xor_block(tag_buffer, buffer, 0, BLOCK_SIZE); + } + // If there is any AD left, compute tag for AD block + if (i + 1 < nblocks_ad) + { + get_ad_block(buffer, ad, adlen, npub, i + 1); + xor_block(buffer, next_mask, 0, BLOCK_SIZE); + permutation(buffer); + xor_block(buffer, next_mask, 0, BLOCK_SIZE); + xor_block(tag_buffer, buffer, 0, BLOCK_SIZE); + } + // Cyclically shift the mask buffers + // Value of next_mask will be computed in the next iteration + byte[] temp = previous_mask; + previous_mask = current_mask; + current_mask = next_mask; + 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); + reset(false); + return 0; + } + + + 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; + } + + + public int GetUpdateOutputSize(int len) + { + return len; + } + + + public int GetOutputSize(int len) + { + return len + CRYPTO_ABYTES; + } + + + public void Reset() + { + reset(true); + } + + private void reset(bool clearMac) + { + if (clearMac) + { + aadData.SetLength(0); + } + encrypted = false; + aadFinished = false; + } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void ProcessAadBytes(ReadOnlySpan input) + { + aadData.Write(input); + } + + public int ProcessByte(byte input, Span output) + { + byte[] rv = new byte[1]; + ProcessBytes(new byte[] { input }, 0, 1, rv, 0); + rv.AsSpan(0, 1).CopyTo(output); + return 1; + } + + public int ProcessBytes(ReadOnlySpan input, Span 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; + } + + public int DoFinal(Span output) + { + byte[] tag = GetMac(); + tag.AsSpan(0, tag.Length).CopyTo(output); + reset(false); + return tag.Length; + } +#endif + } +} + -- cgit 1.4.1 From 70473a06799b0f5273b61dcba2f9057b2a8bdf68 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 31 Jan 2023 15:59:37 +1030 Subject: Add test of Elephyant and decryption part --- crypto/src/crypto/engines/ElephantEngine.cs | 181 +++++++++----- crypto/test/src/crypto/test/ElephantTest.cs | 360 +++++++++++++++++++++++++++- 2 files changed, 469 insertions(+), 72 deletions(-) (limited to 'crypto/src') 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 input) { @@ -506,28 +545,48 @@ namespace Org.BouncyCastle.Crypto.Engines public int ProcessByte(byte input, Span 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 input, Span 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 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 c4_1 = new byte[c2.Length]; + Span c4_2 = new byte[c2.Length]; + ReadOnlySpan m5 = new ReadOnlySpan(m2); + ReadOnlySpan aad4 = new ReadOnlySpan(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 m6_1 = new byte[m2.Length]; + Span m6_2 = new byte[m2.Length]; + ReadOnlySpan c6 = new ReadOnlySpan(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"); + } + } } -- cgit 1.4.1