<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />

AsconAead128

public sealed class AsconAead128 : IAeadCipher
Ascon-AEAD128 was introduced as part of the NIST Lightweight Cryptography competition and descriLEd in the NIST Special Publication SP 800-232 (Initial Public Draft).
using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; using System.Runtime.CompilerServices; namespace Org.BouncyCastle.Crypto.Modes { public sealed class AsconAead128 : IAeadCipher { private enum State { Uninitialized, EncInit, EncAad, EncData, EncFinal, DecInit, DecAad, DecData, DecFinal } private const ulong AsconIV = 17594342703105; private const int BufSizeDecrypt = 32; private const int CryptoABytes = 16; private const int CryptoKeyBytes = 16; private const int Rate = 16; private readonly byte[] m_buf; private byte[] m_initialAssociatedText; private byte[] m_mac; private ulong K0; private ulong K1; private ulong N0; private ulong N1; private ulong S0; private ulong S1; private ulong S2; private ulong S3; private ulong S4; private State m_state; private int m_bufPos; public string AlgorithmName => "Ascon-AEAD128"; public AsconAead128() { m_buf = new byte[32]; } public int GetKeyBytesSize() { return 16; } public int GetIVBytesSize() { return 16; } public void Init(bool forEncryption, ICipherParameters parameters) { AeadParameters aeadParameters = parameters as AeadParameters; KeyParameter keyParameter; byte[] array; if (aeadParameters != null) { keyParameter = aeadParameters.Key; array = aeadParameters.GetNonce(); m_initialAssociatedText = aeadParameters.GetAssociatedText(); int macSize = aeadParameters.MacSize; if (macSize != 128) throw new ArgumentException($"""{macSize}"); } else { ParametersWithIV parametersWithIV = parameters as ParametersWithIV; if (parametersWithIV == null) throw new ArgumentException("invalid parameters passed to " + AlgorithmName); keyParameter = (parametersWithIV.Parameters as KeyParameter); array = parametersWithIV.GetIV(); m_initialAssociatedText = null; } if (keyParameter == null) throw new ArgumentException(AlgorithmName + " Init parameters must include a key"); if (array.Length != 16) throw new ArgumentException($"{AlgorithmName}""{16}"""); byte[] key = keyParameter.GetKey(); if (key.Length != 16) throw new ArgumentException($"{AlgorithmName}""{16}"""); K0 = Pack.LE_To_UInt64(key, 0); K1 = Pack.LE_To_UInt64(key, 8); N0 = Pack.LE_To_UInt64(array, 0); N1 = Pack.LE_To_UInt64(array, 8); m_state = (forEncryption ? State.EncInit : State.DecInit); Reset(true); } public void ProcessAadByte(byte input) { CheckAad(); m_buf[m_bufPos] = input; if (++m_bufPos == 16) { ProcessBufferAad(m_buf, 0); m_bufPos = 0; } } public void ProcessAadBytes(byte[] inBytes, int inOff, int len) { Check.DataLength(inBytes, inOff, len, "input buffer too short"); if (len > 0) { CheckAad(); if (m_bufPos > 0) { int num = 16 - m_bufPos; if (len < num) { Array.Copy(inBytes, inOff, m_buf, m_bufPos, len); m_bufPos += len; return; } Array.Copy(inBytes, inOff, m_buf, m_bufPos, num); inOff += num; len -= num; ProcessBufferAad(m_buf, 0); } while (len >= 16) { ProcessBufferAad(inBytes, inOff); inOff += 16; len -= 16; } Array.Copy(inBytes, inOff, m_buf, 0, len); m_bufPos = len; } } public int ProcessByte(byte input, byte[] outBytes, int outOff) { return ProcessBytes(new byte[1] { input }, 0, 1, outBytes, outOff); } public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) { Check.DataLength(inBytes, inOff, len, "input buffer too short"); bool num = CheckData(); int num2 = 0; if (num) { if (m_bufPos > 0) { int num3 = 16 - m_bufPos; if (len < num3) { Array.Copy(inBytes, inOff, m_buf, m_bufPos, len); m_bufPos += len; return 0; } Array.Copy(inBytes, inOff, m_buf, m_bufPos, num3); inOff += num3; len -= num3; ProcessBufferEncrypt(m_buf, 0, outBytes, outOff); num2 = 16; } while (len >= 16) { ProcessBufferEncrypt(inBytes, inOff, outBytes, outOff + num2); inOff += 16; len -= 16; num2 += 16; } } else { int num4 = 32 - m_bufPos; if (len < num4) { Array.Copy(inBytes, inOff, m_buf, m_bufPos, len); m_bufPos += len; return 0; } if (m_bufPos >= 16) { ProcessBufferDecrypt(m_buf, 0, outBytes, outOff + num2); m_bufPos -= 16; Array.Copy(m_buf, 16, m_buf, 0, m_bufPos); num2 += 16; num4 += 16; if (len < num4) { Array.Copy(inBytes, inOff, m_buf, m_bufPos, len); m_bufPos += len; return num2; } } num4 = 16 - m_bufPos; Array.Copy(inBytes, inOff, m_buf, m_bufPos, num4); inOff += num4; len -= num4; ProcessBufferDecrypt(m_buf, 0, outBytes, outOff + num2); num2 += 16; while (len >= 32) { ProcessBufferDecrypt(inBytes, inOff, outBytes, outOff + num2); inOff += 16; len -= 16; num2 += 16; } } Array.Copy(inBytes, inOff, m_buf, 0, len); m_bufPos = len; return num2; } public int DoFinal(byte[] outBytes, int outOff) { int num; if (CheckData()) { num = m_bufPos + 16; Check.OutputLength(outBytes, outOff, num, "output buffer too short"); ProcessFinalEncrypt(m_buf, 0, m_bufPos, outBytes, outOff); m_mac = new byte[16]; Pack.UInt64_To_LE(S3, m_mac, 0); Pack.UInt64_To_LE(S4, m_mac, 8); Array.Copy(m_mac, 0, outBytes, outOff + m_bufPos, 16); Reset(false); } else { if (m_bufPos < 16) throw new InvalidCipherTextException("data too short"); m_bufPos -= 16; num = m_bufPos; Check.OutputLength(outBytes, outOff, num, "output buffer too short"); ProcessFinalDecrypt(m_buf, 0, m_bufPos, outBytes, outOff); S3 ^= Pack.LE_To_UInt64(m_buf, m_bufPos); S4 ^= Pack.LE_To_UInt64(m_buf, m_bufPos + 8); if ((S3 | S4) != 0) throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed"); Reset(true); } return num; } public byte[] GetMac() { return m_mac; } public int GetUpdateOutputSize(int len) { int num = System.Math.Max(0, len); switch (m_state) { case State.DecInit: case State.DecAad: num = System.Math.Max(0, num - 16); break; case State.DecData: case State.DecFinal: num = System.Math.Max(0, num + m_bufPos - 16); break; case State.EncData: case State.EncFinal: num += m_bufPos; break; } return num - num % 16; } public int GetOutputSize(int len) { int num = System.Math.Max(0, len); switch (m_state) { case State.DecInit: case State.DecAad: return System.Math.Max(0, num - 16); case State.DecData: case State.DecFinal: return System.Math.Max(0, num + m_bufPos - 16); case State.EncData: case State.EncFinal: return num + m_bufPos + 16; default: return num + 16; } } public void Reset() { Reset(true); } private void CheckAad() { switch (m_state) { case State.EncAad: case State.DecAad: break; case State.DecInit: m_state = State.DecAad; break; case State.EncInit: m_state = State.EncAad; break; case State.EncFinal: throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption"); default: throw new InvalidOperationException(AlgorithmName + " needs to be initialized"); } } private bool CheckData() { switch (m_state) { case State.DecInit: case State.DecAad: FinishAad(State.DecData); return false; case State.EncInit: case State.EncAad: FinishAad(State.EncData); return true; case State.DecData: return false; case State.EncData: return true; case State.EncFinal: throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption"); default: throw new InvalidOperationException(AlgorithmName + " needs to be initialized"); } } private void FinishAad(State nextState) { State state = m_state; if (state == State.EncAad || state == State.DecAad) { m_buf[m_bufPos] = 1; if (m_bufPos >= 8) { S0 ^= Pack.LE_To_UInt64(m_buf, 0); S1 ^= (Pack.LE_To_UInt64(m_buf, 8) & (ulong.MaxValue >> 56 - (m_bufPos - 8 << 3))); } else S0 ^= (Pack.LE_To_UInt64(m_buf, 0) & (ulong.MaxValue >> 56 - (m_bufPos << 3))); P8(); } S4 ^= 9223372036854775808; m_bufPos = 0; m_state = nextState; } private void FinishData(State nextState) { S2 ^= K0; S3 ^= K1; P12(); S3 ^= K0; S4 ^= K1; m_state = nextState; } private void InitState() { S0 = 17594342703105; S1 = K0; S2 = K1; S3 = N0; S4 = N1; P12(); S3 ^= K0; S4 ^= K1; } private void P8() { Round(180); Round(165); Round(150); Round(135); Round(120); Round(105); Round(90); Round(75); } private void P12() { Round(240); Round(225); Round(210); Round(195); Round(180); Round(165); Round(150); Round(135); Round(120); Round(105); Round(90); Round(75); } private void ProcessBufferAad(byte[] buffer, int bufOff) { S0 ^= Pack.LE_To_UInt64(buffer, bufOff); S1 ^= Pack.LE_To_UInt64(buffer, bufOff + 8); P8(); } private void ProcessBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { Check.OutputLength(output, outOff, 16, "output buffer too short"); ulong num = Pack.LE_To_UInt64(buffer, bufOff); Pack.UInt64_To_LE(S0 ^ num, output, outOff); S0 = num; ulong num2 = Pack.LE_To_UInt64(buffer, bufOff + 8); Pack.UInt64_To_LE(S1 ^ num2, output, outOff + 8); S1 = num2; P8(); } private void ProcessBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { Check.OutputLength(output, outOff, 16, "output buffer too short"); S0 ^= Pack.LE_To_UInt64(buffer, bufOff); Pack.UInt64_To_LE(S0, output, outOff); S1 ^= Pack.LE_To_UInt64(buffer, bufOff + 8); Pack.UInt64_To_LE(S1, output, outOff + 8); P8(); } private void ProcessFinalDecrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff) { if (inLen >= 8) { ulong num = Pack.LE_To_UInt64(input, inOff); Pack.UInt64_To_LE(S0 ^ num, output, outOff); S0 = num; inLen -= 8; if (inLen > 0) ProcessFinalDecrypt64(input, inOff + 8, inLen, output, outOff + 8, ref S1); S1 ^= Pad(inLen); } else { if (inLen > 0) ProcessFinalDecrypt64(input, inOff, inLen, output, outOff, ref S0); S0 ^= Pad(inLen); } FinishData(State.DecFinal); } private static void ProcessFinalDecrypt64(byte[] input, int inOff, int inLen, byte[] output, int outOff, ref ulong s) { ulong num = Pack.LE_To_UInt64_Low(input, inOff, inLen); Pack.UInt64_To_LE_Low(s ^ num, output, outOff, inLen); s &= (ulong)(-1 << (inLen << 3)); s ^= num; } private void ProcessFinalEncrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff) { if (inLen >= 8) { S0 ^= Pack.LE_To_UInt64(input, inOff); Pack.UInt64_To_LE(S0, output, outOff); inLen -= 8; if (inLen > 0) ProcessFinalEncrypt64(input, inOff + 8, inLen, output, outOff + 8, ref S1); S1 ^= Pad(inLen); } else { if (inLen > 0) ProcessFinalEncrypt64(input, inOff, inLen, output, outOff, ref S0); S0 ^= Pad(inLen); } FinishData(State.EncFinal); } private static void ProcessFinalEncrypt64(byte[] input, int inOff, int inLen, byte[] output, int outOff, ref ulong s) { s ^= Pack.LE_To_UInt64_Low(input, inOff, inLen); Pack.UInt64_To_LE_Low(s, output, outOff, inLen); } private void Reset(bool clearMac) { if (clearMac) m_mac = null; Array.Clear(m_buf, 0, m_buf.Length); m_bufPos = 0; switch (m_state) { 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(AlgorithmName + " needs to be initialized"); case State.EncInit: case State.DecInit: break; } InitState(); if (m_initialAssociatedText != null) ProcessAadBytes(m_initialAssociatedText, 0, m_initialAssociatedText.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Round(ulong c) { ulong num = S2 ^ c; ulong num2 = S0 ^ S1 ^ num ^ S3 ^ (S1 & (S0 ^ num ^ S4)); ulong num3 = S0 ^ num ^ S3 ^ S4 ^ ((S1 ^ num) & (S1 ^ S3)); ulong num4 = S1 ^ num ^ S4 ^ (S3 & S4); ulong num5 = S0 ^ S1 ^ num ^ (~S0 & (S3 ^ S4)); ulong num6 = S1 ^ S3 ^ S4 ^ ((S0 ^ S4) & S1); S0 = (num2 ^ Longs.RotateRight(num2, 19) ^ Longs.RotateRight(num2, 28)); S1 = (num3 ^ Longs.RotateRight(num3, 39) ^ Longs.RotateRight(num3, 61)); S2 = ~(num4 ^ Longs.RotateRight(num4, 1) ^ Longs.RotateRight(num4, 6)); S3 = (num5 ^ Longs.RotateRight(num5, 10) ^ Longs.RotateRight(num5, 17)); S4 = (num6 ^ Longs.RotateRight(num6, 7) ^ Longs.RotateRight(num6, 41)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Pad(int i) { return (ulong)(1 << (i << 3)); } } }