From 89b1a71ce769a26c6479c9ab73869967e25f9ac3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 14 Feb 2023 00:35:12 +0700 Subject: Rework AsconEngine --- crypto/src/crypto/engines/AsconEngine.cs | 404 ++++++++++++++++++------------- crypto/test/src/crypto/test/AsconTest.cs | 135 ++++++----- 2 files changed, 305 insertions(+), 234 deletions(-) diff --git a/crypto/src/crypto/engines/AsconEngine.cs b/crypto/src/crypto/engines/AsconEngine.cs index e8848c3d8..c7b203a8b 100644 --- a/crypto/src/crypto/engines/AsconEngine.cs +++ b/crypto/src/crypto/engines/AsconEngine.cs @@ -11,13 +11,11 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines { - /** - * ASCON AEAD v1.2, https://ascon.iaik.tugraz.at/ - * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf - *

- * ASCON AEAD v1.2 with reference to C Reference Impl from: https://github.com/ascon/ascon-c - *

- */ + /// ASCON v1.2 AEAD, https://ascon.iaik.tugraz.at/ . + /// + /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf
+ /// ASCON v1.2 AEAD with reference to C Reference Impl from: https://github.com/ascon/ascon-c . + ///
public sealed class AsconEngine : IAeadCipher { @@ -28,13 +26,23 @@ namespace Org.BouncyCastle.Crypto.Engines ascon128 } - private readonly AsconParameters asconParameters; + private enum State + { + Uninitialized = 0, + EncInit = 1, + EncAad = 2, + EncData = 3, + EncFinal = 4, + DecInit = 5, + DecAad = 6, + DecData = 7, + DecFinal = 8, + } + 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 AsconParameters asconParameters; private readonly int CRYPTO_KEYBYTES; private readonly int CRYPTO_ABYTES; private readonly int ASCON_AEAD_RATE; @@ -52,8 +60,7 @@ namespace Org.BouncyCastle.Crypto.Engines private ulong x3; private ulong x4; private string algorithmName; - - public string AlgorithmName => algorithmName; + private State m_state = State.Uninitialized; public AsconEngine(AsconParameters asconParameters) { @@ -85,7 +92,6 @@ namespace Org.BouncyCastle.Crypto.Engines throw new ArgumentException("invalid parameter setting for ASCON AEAD"); } nr = (ASCON_AEAD_RATE == 8) ? 6 : 8; - initialised = false; } public int GetKeyBytesSize() @@ -98,9 +104,10 @@ namespace Org.BouncyCastle.Crypto.Engines return CRYPTO_ABYTES; } + public string AlgorithmName => algorithmName; + public void Init(bool forEncryption, ICipherParameters parameters) { - this.forEncryption = forEncryption; if (!(parameters is ParametersWithIV withIV)) throw new ArgumentException("ASCON Init parameters must include an IV"); @@ -128,43 +135,36 @@ namespace Org.BouncyCastle.Crypto.Engines K1 = Pack.BE_To_UInt64(k, 4); K2 = Pack.BE_To_UInt64(k, 12); } - initialised = true; - /*Mask-Gen*/ + else + { + throw new InvalidOperationException(); + } + + m_state = forEncryption ? State.EncInit : State.DecInit; + 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")); - } + CheckAad(); aadData.WriteByte(input); } public void ProcessAadBytes(byte[] inBytes, 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")); - } - Check.DataLength(inBytes, inOff, len, "input buffer too short"); + CheckAad(); + aadData.Write(inBytes, inOff, len); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public void ProcessAadBytes(ReadOnlySpan 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")); - } + CheckAad(); aadData.Write(input); } @@ -195,26 +195,34 @@ namespace Org.BouncyCastle.Crypto.Engines #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff)); #else - if (!initialised) - throw new ArgumentException("Need to call Init function before encryption/decryption"); + CheckData(); message.Write(inBytes, inOff, len); - int rv = ProcessBytes(outBytes, outOff); - encrypted = true; - return rv; + + switch (m_state) + { + case State.DecData: return ProcessBytes(false, outBytes, outOff); + case State.EncData: return ProcessBytes(true, outBytes, outOff); + default: + throw new InvalidOperationException(); + } #endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public int ProcessBytes(ReadOnlySpan input, Span output) { - if (!initialised) - throw new ArgumentException("Need to call Init function before encryption/decryption"); + CheckData(); message.Write(input); - int rv = ProcessBytes(output); - encrypted = true; - return rv; + + switch (m_state) + { + case State.DecData: return ProcessBytes(false, output); + case State.EncData: return ProcessBytes(true, output); + default: + throw new InvalidOperationException(); + } } #endif @@ -223,104 +231,91 @@ namespace Org.BouncyCastle.Crypto.Engines #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER return DoFinal(outBytes.AsSpan(outOff)); #else - if (!initialised) - throw new ArgumentException("Need to call Init before encryption/decryption"); + CheckData(); - if (!aadFinished) - { - ProcessAad(); - } - if (!encrypted) - { - ProcessBytes(Array.Empty(), 0, 0, Array.Empty(), 0); - } byte[] input = message.GetBuffer(); int len = Convert.ToInt32(message.Length); - if (forEncryption) + + switch (m_state) { - Check.OutputLength(outBytes, outOff, len + CRYPTO_ABYTES, "output buffer too short"); - } - else + case State.DecData: { + // TODO Check for underflow i.e. total input < CRYPTO_ABYTES Check.OutputLength(outBytes, outOff, len - CRYPTO_ABYTES, "output buffer too short"); - } - if (forEncryption) - { - ascon_final(outBytes, outOff, input, 0, len); - /* set tag */ - mac = new byte[16]; - Pack.UInt64_To_BE(x3, mac, 0); - Pack.UInt64_To_BE(x4, mac, 8); - Array.Copy(mac, 0, outBytes, len + outOff, 16); - Reset(false); - return len + CRYPTO_ABYTES; - } - else - { len -= CRYPTO_ABYTES; - ascon_final(outBytes, outOff, input, 0, len); + ascon_final(false, outBytes, outOff, input, 0, len); x3 ^= Pack.BE_To_UInt64(input, len); x4 ^= Pack.BE_To_UInt64(input, len + 8); ulong result = x3 | x4; - Reset(true); if (result != 0UL) throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed"); + Reset(true); return len; } + case State.EncData: + { + Check.OutputLength(outBytes, outOff, len + CRYPTO_ABYTES, "output buffer too short"); + ascon_final(true, outBytes, outOff, input, 0, len); + mac = new byte[16]; + Pack.UInt64_To_BE(x3, mac, 0); + Pack.UInt64_To_BE(x4, mac, 8); + Array.Copy(mac, 0, outBytes, len + outOff, 16); + Reset(false); + return len + CRYPTO_ABYTES; + } + default: + throw new InvalidOperationException(); + } #endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public int DoFinal(Span output) { - if (!initialised) - throw new ArgumentException("Need to call Init before encryption/decryption"); + CheckData(); - if (!aadFinished) - { - ProcessAad(); - } - if (!encrypted) - { - ProcessBytes(Array.Empty(), 0, 0, Array.Empty(), 0); - } byte[] input = message.GetBuffer(); int len = Convert.ToInt32(message.Length); - if (forEncryption) + + switch (m_state) { - Check.OutputLength(output, len + CRYPTO_ABYTES, "output buffer too short"); - } - else + case State.DecData: { + // TODO Check for underflow i.e. total input < CRYPTO_ABYTES Check.OutputLength(output, len - CRYPTO_ABYTES, "output buffer too short"); - } - if (forEncryption) - { - ascon_final(output, input.AsSpan(0, len)); - /* set tag */ - mac = new byte[CRYPTO_ABYTES]; - Pack.UInt64_To_BE(x3, mac, 0); - Pack.UInt64_To_BE(x4, mac, 8); - mac.AsSpan(0, CRYPTO_ABYTES).CopyTo(output[len..]); - Reset(false); - return len + CRYPTO_ABYTES; - } - else - { len -= CRYPTO_ABYTES; - ascon_final(output, input.AsSpan(0, len)); + ascon_final(false, output, input.AsSpan(0, len)); x3 ^= Pack.BE_To_UInt64(input, len); x4 ^= Pack.BE_To_UInt64(input, len + 8); ulong result = x3 | x4; - Reset(true); + + FinishData(State.DecFinal); if (result != 0UL) throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed"); + Reset(true); return len; } + case State.EncData: + { + Check.OutputLength(output, len + CRYPTO_ABYTES, "output buffer too short"); + ascon_final(true, output, input.AsSpan(0, len)); + mac = new byte[CRYPTO_ABYTES]; + Pack.UInt64_To_BE(x3, mac, 0); + Pack.UInt64_To_BE(x4, mac, 8); + + FinishData(State.EncFinal); + + mac.AsSpan(0, CRYPTO_ABYTES).CopyTo(output[len..]); + Reset(false); + return len + CRYPTO_ABYTES; + } + default: + throw new InvalidOperationException(); + } } #endif @@ -331,25 +326,37 @@ namespace Org.BouncyCastle.Crypto.Engines public int GetUpdateOutputSize(int len) { - int totalData = Convert.ToInt32(message.Length + len); - if (!forEncryption) - { - if (totalData < CRYPTO_ABYTES) - return 0; + int total = Convert.ToInt32(message.Length + System.Math.Max(0, len)); - totalData -= CRYPTO_ABYTES; + switch (m_state) + { + case State.DecInit: + case State.DecAad: + case State.DecData: + case State.DecFinal: + total = System.Math.Max(0, total - CRYPTO_ABYTES); + break; + default: + break; } - return totalData - totalData % ASCON_AEAD_RATE; + + return total - total % ASCON_AEAD_RATE; } public int GetOutputSize(int len) { - int totalData = Convert.ToInt32(message.Length + len); + int total = Convert.ToInt32(message.Length + System.Math.Max(0, len)); - if (forEncryption) - return totalData + CRYPTO_ABYTES; - - return System.Math.Max(0, totalData - CRYPTO_ABYTES); + switch (m_state) + { + case State.DecInit: + case State.DecAad: + case State.DecData: + case State.DecFinal: + return System.Math.Max(0, total - CRYPTO_ABYTES); + default: + return total + CRYPTO_ABYTES; + } } public void Reset() @@ -357,6 +364,67 @@ namespace Org.BouncyCastle.Crypto.Engines Reset(true); } + private void CheckAad() + { + switch (m_state) + { + case State.DecInit: + m_state = State.DecAad; + break; + case State.EncInit: + m_state = State.EncAad; + break; + case State.DecAad: + case State.EncAad: + break; + case State.EncFinal: + throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption"); + default: + throw new InvalidOperationException(); + } + } + + private void CheckData() + { + switch (m_state) + { + case State.DecInit: + case State.DecAad: + FinishAad(State.DecData); + break; + case State.EncInit: + case State.EncAad: + FinishAad(State.EncData); + break; + case State.DecData: + case State.EncData: + break; + case State.EncFinal: + throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption"); + default: + throw new InvalidOperationException(); + } + } + + private void FinishAad(State nextState) + { + byte[] aad = aadData.GetBuffer(); + int aadLen = Convert.ToInt32(aadData.Length); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ascon_adata(aad.AsSpan(0, aadLen)); +#else + ascon_adata(aad, 0, aadLen); +#endif + + m_state = nextState; + } + + private void FinishData(State nextState) + { + m_state = nextState; + } + private void P(int nr) { if (nr >= 8) @@ -396,34 +464,17 @@ namespace Org.BouncyCastle.Crypto.Engines x4 = t4 ^ Longs.RotateRight(t4, 7) ^ Longs.RotateRight(t4, 41); } - private void ProcessAad() - { - if (!aadFinished) - { - byte[] ad = aadData.GetBuffer(); - int adlen = Convert.ToInt32(aadData.Length); - /* perform ascon computation */ -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - ascon_adata(ad.AsSpan(0, adlen)); -#else - ascon_adata(ad, 0, adlen); -#endif - aadFinished = true; - } - } - private void ascon_aeadinit() { - /* initialize */ - x0 ^= ASCON_IV; + x0 = ASCON_IV; if (CRYPTO_KEYBYTES == 20) { x0 ^= K0; } - x1 ^= K1; - x2 ^= K2; - x3 ^= N0; - x4 ^= N1; + x1 = K1; + x2 = K2; + x3 = N0; + x4 = N1; P(12); if (CRYPTO_KEYBYTES == 20) { @@ -434,7 +485,7 @@ namespace Org.BouncyCastle.Crypto.Engines } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - private int ProcessBytes(Span output) + private int ProcessBytes(bool forEncryption, Span output) { int msgLen = Convert.ToInt32(message.Length); int outLen = 0; @@ -442,7 +493,6 @@ namespace Org.BouncyCastle.Crypto.Engines { if (msgLen >= ASCON_AEAD_RATE) { - ProcessAad(); byte[] input = message.GetBuffer(); outLen = (msgLen / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; Check.OutputLength(output, outLen, "output buffer is too short"); @@ -455,7 +505,6 @@ namespace Org.BouncyCastle.Crypto.Engines { if (msgLen - CRYPTO_ABYTES >= ASCON_AEAD_RATE) { - ProcessAad(); byte[] input = message.GetBuffer(); outLen = ((msgLen - CRYPTO_ABYTES) / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; Check.OutputLength(output, outLen, "output buffer is too short"); @@ -547,7 +596,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - private void ascon_final(Span output, ReadOnlySpan input) + private void ascon_final(bool forEncryption, Span output, ReadOnlySpan input) { if (forEncryption) { @@ -631,7 +680,7 @@ namespace Org.BouncyCastle.Crypto.Engines x4 ^= K2; } #else - private int ProcessBytes(byte[] output, int outOff) + private int ProcessBytes(bool forEncryption, byte[] output, int outOff) { int msgLen = Convert.ToInt32(message.Length); int outLen = 0; @@ -639,7 +688,6 @@ namespace Org.BouncyCastle.Crypto.Engines { if (msgLen >= ASCON_AEAD_RATE) { - ProcessAad(); byte[] input = message.GetBuffer(); outLen = (msgLen / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; Check.OutputLength(output, outOff, outLen, "output buffer is too short"); @@ -652,7 +700,6 @@ namespace Org.BouncyCastle.Crypto.Engines { if (msgLen - CRYPTO_ABYTES >= ASCON_AEAD_RATE) { - ProcessAad(); byte[] input = message.GetBuffer(); outLen = ((msgLen - CRYPTO_ABYTES) / ASCON_AEAD_RATE) * ASCON_AEAD_RATE; Check.OutputLength(output, outOff, outLen, "output buffer is too short"); @@ -664,40 +711,40 @@ namespace Org.BouncyCastle.Crypto.Engines return outLen; } - private void ascon_adata(byte[] ad, int adOff, int adlen) + private void ascon_adata(byte[] aad, int aadOff, int aadLen) { - if (adlen != 0) + if (aadLen != 0) { /* full associated data blocks */ - while (adlen >= ASCON_AEAD_RATE) + while (aadLen >= ASCON_AEAD_RATE) { - x0 ^= Pack.BE_To_UInt64(ad, adOff); + x0 ^= Pack.BE_To_UInt64(aad, aadOff); if (ASCON_AEAD_RATE == 16) { - x1 ^= Pack.BE_To_UInt64(ad, adOff + 8); + x1 ^= Pack.BE_To_UInt64(aad, aadOff + 8); } P(nr); - adOff += ASCON_AEAD_RATE; - adlen -= ASCON_AEAD_RATE; + aadOff += ASCON_AEAD_RATE; + aadLen -= ASCON_AEAD_RATE; } /* final associated data block */ - if (ASCON_AEAD_RATE == 16 && adlen >= 8) + if (ASCON_AEAD_RATE == 16 && aadLen >= 8) { - x0 ^= Pack.BE_To_UInt64(ad, adOff); - adOff += 8; - adlen -= 8; - x1 ^= PAD(adlen); - if (adlen != 0) + x0 ^= Pack.BE_To_UInt64(aad, aadOff); + aadOff += 8; + aadLen -= 8; + x1 ^= PAD(aadLen); + if (aadLen != 0) { - x1 ^= Pack.BE_To_UInt64_High(ad, adOff, adlen); + x1 ^= Pack.BE_To_UInt64_High(aad, aadOff, aadLen); } } else { - x0 ^= PAD(adlen); - if (adlen != 0) + x0 ^= PAD(aadLen); + if (aadLen != 0) { - x0 ^= Pack.BE_To_UInt64_High(ad, adOff, adlen); + x0 ^= Pack.BE_To_UInt64_High(aad, aadOff, aadLen); } } P(nr); @@ -748,7 +795,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - private void ascon_final(byte[] c, int cOff, byte[] m, int mOff, int mlen) + private void ascon_final(bool forEncryption, byte[] c, int cOff, byte[] m, int mOff, int mlen) { if (forEncryption) { @@ -837,19 +884,34 @@ namespace Org.BouncyCastle.Crypto.Engines private void Reset(bool clearMac) { - if (!initialised) - throw new ArgumentException("Need to call Init function before encryption/decryption"); + if (clearMac) + { + mac = null; + } - x0 = x1 = x2 = x3 = x4 = 0; - ascon_aeadinit(); aadData.SetLength(0); message.SetLength(0); - encrypted = false; - aadFinished = false; - if (clearMac) + + switch (m_state) { - mac = null; + case State.DecInit: + case State.EncInit: + break; + case State.DecAad: + case State.DecData: + case State.DecFinal: + m_state = State.DecInit; + break; + case State.EncAad: + case State.EncData: + case State.EncFinal: + m_state = State.EncFinal; + return; + default: + throw new InvalidOperationException(); } + + ascon_aeadinit(); } private static ulong PAD(int i) diff --git a/crypto/test/src/crypto/test/AsconTest.cs b/crypto/test/src/crypto/test/AsconTest.cs index 665673a55..95dd9c210 100644 --- a/crypto/test/src/crypto/test/AsconTest.cs +++ b/crypto/test/src/crypto/test/AsconTest.cs @@ -129,17 +129,17 @@ namespace Org.BouncyCastle.Crypto.Tests asconEngine.ProcessBytes(m, 0, m.Length, null, 0); Assert.Fail(asconEngine.AlgorithmName + " needs to be initialized before ProcessBytes"); } - catch (ArgumentException) + catch (InvalidOperationException) { //expected } try { - asconEngine.ProcessByte((byte)0, null, 0); + asconEngine.ProcessByte(0x00, null, 0); Assert.Fail(asconEngine.AlgorithmName + " needs to be initialized before ProcessByte"); } - catch (ArgumentException) + catch (InvalidOperationException) { //expected } @@ -149,7 +149,7 @@ namespace Org.BouncyCastle.Crypto.Tests asconEngine.Reset(); Assert.Fail(asconEngine.AlgorithmName + " needs to be initialized before Reset"); } - catch (ArgumentException) + catch (InvalidOperationException) { //expected } @@ -159,7 +159,7 @@ namespace Org.BouncyCastle.Crypto.Tests asconEngine.DoFinal(null, m.Length); Assert.Fail(asconEngine.AlgorithmName + " needs to be initialized before DoFinal"); } - catch (ArgumentException) + catch (InvalidOperationException) { //expected } @@ -170,7 +170,7 @@ namespace Org.BouncyCastle.Crypto.Tests asconEngine.GetOutputSize(0); asconEngine.GetUpdateOutputSize(0); } - catch (ArgumentException) + catch (InvalidOperationException) { //expected Assert.Fail(asconEngine.AlgorithmName + " functions can be called before initialization"); @@ -220,38 +220,44 @@ namespace Org.BouncyCastle.Crypto.Tests { Assert.Fail("mac should be equal when calling DoFinal and GetMac"); } - asconEngine.ProcessAadByte((byte)0); + + // TODO Maybe use a different IV for this + asconEngine.Init(true, param); + asconEngine.ProcessAadByte(0x00); byte[] mac1 = new byte[asconEngine.GetOutputSize(0)]; asconEngine.DoFinal(mac1, 0); if (Arrays.AreEqual(mac1, mac2)) { Assert.Fail("mac should not match"); } - asconEngine.Reset(); - asconEngine.ProcessBytes(new byte[16], 0, 16, new byte[16], 0); + + // TODO Maybe use a different IV for this + asconEngine.Init(true, param); + asconEngine.ProcessByte(0, null, 0); try { - asconEngine.ProcessAadByte((byte)0); - Assert.Fail("ProcessAadByte(s) cannot be called after encryption/decryption"); + asconEngine.ProcessAadByte(0x00); + Assert.Fail("ProcessAadByte cannot be called after encryption/decryption"); } - catch (ArgumentException) + catch (InvalidOperationException) { //expected } try { - asconEngine.ProcessAadBytes(new byte[] { 0 }, 0, 1); - Assert.Fail("ProcessAadByte(s) cannot be called once only"); + asconEngine.ProcessAadBytes(new byte[1], 0, 1); + Assert.Fail("ProcessAadBytes cannot be called after encryption/decryption"); } - catch (ArgumentException) + catch (InvalidOperationException) { //expected } - asconEngine.Reset(); + // TODO Maybe use a different IV for this + asconEngine.Init(true, param); try { - asconEngine.ProcessAadBytes(new byte[] { 0 }, 1, 1); + asconEngine.ProcessAadBytes(new byte[1], 1, 1); Assert.Fail("input for ProcessAadBytes is too short"); } catch (DataLengthException) @@ -260,13 +266,14 @@ namespace Org.BouncyCastle.Crypto.Tests } try { - asconEngine.ProcessBytes(new byte[] { 0 }, 1, 1, c1, 0); + asconEngine.ProcessBytes(new byte[1], 1, 1, c1, 0); Assert.Fail("input for ProcessBytes is too short"); } catch (DataLengthException) { //expected } + try { int inputSize = rand.Next(32, 64); @@ -290,40 +297,50 @@ namespace Org.BouncyCastle.Crypto.Tests mac1 = new byte[asconEngine.GetOutputSize(0)]; mac2 = new byte[asconEngine.GetOutputSize(0)]; - asconEngine.Reset(); - asconEngine.ProcessAadBytes(new byte[] { 0, 0 }, 0, 2); + + // TODO Maybe use a different IV for this + asconEngine.Init(true, param); + asconEngine.ProcessAadBytes(new byte[2], 0, 2); asconEngine.DoFinal(mac1, 0); - asconEngine.Reset(); - asconEngine.ProcessAadByte((byte)0); - asconEngine.ProcessAadByte((byte)0); + + // TODO Maybe use a different IV for this + asconEngine.Init(true, param); + asconEngine.ProcessAadByte(0x00); + asconEngine.ProcessAadByte(0x00); asconEngine.DoFinal(mac2, 0); + if (!Arrays.AreEqual(mac1, mac2)) { - Assert.Fail("mac should match for the same AAD with different ways of inputing"); + Assert.Fail("mac should match for the same AAD with different ways of inputting"); } - byte[] c2 = new byte[asconEngine.GetOutputSize(10)]; - byte[] c3 = new byte[asconEngine.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]; - asconEngine.Reset(); + byte[] c2 = new byte[asconEngine.GetOutputSize(m2.Length)]; + byte[] c3 = new byte[asconEngine.GetOutputSize(m3.Length)]; + + // TODO Maybe use a different IV for this + asconEngine.Init(true, param); asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); offset = asconEngine.ProcessBytes(m2, 0, m2.Length, c2, 0); asconEngine.DoFinal(c2, offset); - asconEngine.Reset(); + + // TODO Maybe use a different IV for this + asconEngine.Init(true, param); asconEngine.ProcessAadBytes(aad3, 1, aad2.Length); offset = asconEngine.ProcessBytes(m3, 1, m2.Length, c3, 1); asconEngine.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"); } - asconEngine.Reset(); + asconEngine.Init(false, param); asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); offset = asconEngine.ProcessBytes(c2, 0, c2.Length, m4, 0); @@ -332,9 +349,9 @@ namespace Org.BouncyCastle.Crypto.Tests { Assert.Fail("The encryption and decryption does not recover the plaintext"); } + c2[c2.Length - 1] ^= 1; - asconEngine.Reset(); - asconEngine.Init(false, param); + asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); offset = asconEngine.ProcessBytes(c2, 0, c2.Length, m4, 0); try @@ -351,6 +368,7 @@ namespace Org.BouncyCastle.Crypto.Tests byte[] m7 = new byte[32 + rand.Next(32)]; rand.NextBytes(m7); + // TODO Maybe use a different IV for this asconEngine.Init(true, param); byte[] c7 = new byte[asconEngine.GetOutputSize(m7.Length)]; byte[] c8 = new byte[c7.Length]; @@ -358,12 +376,16 @@ namespace Org.BouncyCastle.Crypto.Tests asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); offset = asconEngine.ProcessBytes(m7, 0, m7.Length, c7, 0); offset += asconEngine.DoFinal(c7, offset); - asconEngine.Reset(); + + // TODO Maybe use a different IV for this + asconEngine.Init(true, param); asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); offset = asconEngine.ProcessBytes(m7, 0, m7.Length / 2, c8, 0); offset += asconEngine.ProcessBytes(m7, m7.Length / 2, m7.Length - m7.Length / 2, c8, offset); offset += asconEngine.DoFinal(c8, offset); - asconEngine.Reset(); + + // TODO Maybe use a different IV for this + asconEngine.Init(true, param); int split = rand.Next(1, m7.Length); asconEngine.ProcessAadBytes(aad2, 0, aad2.Length); offset = asconEngine.ProcessBytes(m7, 0, split, c9, 0); @@ -374,37 +396,27 @@ namespace Org.BouncyCastle.Crypto.Tests Assert.Fail("Splitting input of plaintext should output the same ciphertext"); } // NOTE: .NET Core 3.1 has Span, but is tested against our .NET Standard 2.0 assembly. -//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + //#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER #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); + // TODO Maybe use a different IV for this asconEngine.Init(true, param); - asconEngine.ProcessAadBytes(aad4); - offset = asconEngine.ProcessBytes(m5, c4_1); - asconEngine.DoFinal(c4_2); - byte[] c5 = new byte[c2.Length]; - c4_1[..offset].CopyTo(c5); - c4_2[..(c5.Length - offset)].CopyTo(c5.AsSpan(offset)); - if (!Arrays.AreEqual(c2, c5)) + Span c4 = new byte[asconEngine.GetOutputSize(m2.Length)]; + asconEngine.ProcessAadBytes(aad2); + offset = asconEngine.ProcessBytes(m2, c4); + offset += asconEngine.DoFinal(c4[offset..]); + if (!c4[..offset].SequenceEqual(c2)) { - Assert.Fail("mac should match for the same AAD and message with different offset for both input and output"); + Assert.Fail("Encryption should match for the same AAD and message with/without Span-based API"); } - asconEngine.Reset(); + asconEngine.Init(false, param); - Span m6_1 = new byte[m2.Length]; - Span m6_2 = new byte[m2.Length]; - ReadOnlySpan c6 = new ReadOnlySpan(c2); - asconEngine.ProcessAadBytes(aad4); - offset = asconEngine.ProcessBytes(c6, m6_1); - asconEngine.DoFinal(m6_2); - byte[] m6 = new byte[m2.Length]; - m6_1[..offset].CopyTo(m6); - m6_2[..(m6.Length - offset)].CopyTo(m6.AsSpan(offset)); - if (!Arrays.AreEqual(m2, m6)) + Span m6 = new byte[m2.Length]; + asconEngine.ProcessAadBytes(aad2); + offset = asconEngine.ProcessBytes(c2, m6); + offset += asconEngine.DoFinal(m6[offset..]); + if (!m6[..offset].SequenceEqual(m2)) { - Assert.Fail("mac should match for the same AAD and message with different offset for both input and output"); + Assert.Fail("Decryption should match for the same AAD and message with/without Span-based API"); } #endif } @@ -460,7 +472,6 @@ namespace Org.BouncyCastle.Crypto.Tests { map[data[0].Trim()] = ""; } - } } } @@ -468,10 +479,8 @@ namespace Org.BouncyCastle.Crypto.Tests private void ImplTestExceptions(AsconDigest asconDigest, int digestSize) { - if (asconDigest.GetDigestSize() != digestSize) - { - Assert.Fail(asconDigest.AlgorithmName + ": digest size is not correct"); - } + Assert.AreEqual(digestSize, asconDigest.GetDigestSize(), + asconDigest.AlgorithmName + ": digest size is not correct"); try { -- cgit 1.4.1