<PackageReference Include="BouncyCastle.Cryptography" Version="2.7.0-beta.98" />

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; ReadOnlySpan<byte> bs; DefaultInterpolatedStringHandler defaultInterpolatedStringHandler; if (aeadParameters != null) { keyParameter = aeadParameters.Key; bs = aeadParameters.Nonce; m_initialAssociatedText = aeadParameters.GetAssociatedText(); int macSize = aeadParameters.MacSize; if (macSize != 128) { defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(28, 1); defaultInterpolatedStringHandler.AppendLiteral("Invalid value for MAC size: "); defaultInterpolatedStringHandler.AppendFormatted(macSize); throw new ArgumentException(defaultInterpolatedStringHandler.ToStringAndClear()); } } else { ParametersWithIV parametersWithIV = parameters as ParametersWithIV; if (parametersWithIV == null) throw new ArgumentException("invalid parameters passed to " + AlgorithmName); keyParameter = (parametersWithIV.Parameters as KeyParameter); bs = parametersWithIV.IV; m_initialAssociatedText = null; } if (keyParameter == null) throw new ArgumentException(AlgorithmName + " Init parameters must include a key"); if (bs.Length != 16) { defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(30, 2); defaultInterpolatedStringHandler.AppendFormatted(AlgorithmName); defaultInterpolatedStringHandler.AppendLiteral(" requires exactly "); defaultInterpolatedStringHandler.AppendFormatted(16); defaultInterpolatedStringHandler.AppendLiteral(" bytes of IV"); throw new ArgumentException(defaultInterpolatedStringHandler.ToStringAndClear()); } ReadOnlySpan<byte> key = keyParameter.Key; if (key.Length != 16) { defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(24, 2); defaultInterpolatedStringHandler.AppendFormatted(AlgorithmName); defaultInterpolatedStringHandler.AppendLiteral(" key must be "); defaultInterpolatedStringHandler.AppendFormatted(16); defaultInterpolatedStringHandler.AppendLiteral(" bytes long"); throw new ArgumentException(defaultInterpolatedStringHandler.ToStringAndClear()); } K0 = Pack.LE_To_UInt64(key, 0); K1 = Pack.LE_To_UInt64(key, 8); N0 = Pack.LE_To_UInt64(bs, 0); N1 = Pack.LE_To_UInt64(bs, 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); m_bufPos = 0; } } public void ProcessAadBytes(byte[] inBytes, int inOff, int len) { Check.DataLength(inBytes, inOff, len, "input buffer too short"); ProcessAadBytes(inBytes.AsSpan(inOff, len)); } public void ProcessAadBytes(ReadOnlySpan<byte> input) { if (!input.IsEmpty) { CheckAad(); if (m_bufPos > 0) { int num = 16 - m_bufPos; if (input.Length < num) { input.CopyTo(m_buf.AsSpan(m_bufPos)); m_bufPos += input.Length; return; } input.Slice(0, num).CopyTo(m_buf.AsSpan(m_bufPos)); int num2 = num; input = input.Slice(num2, input.Length - num2); ProcessBufferAad(m_buf); } while (input.Length >= 16) { ProcessBufferAad(input); input = input.Slice(16, input.Length - 16); } input.CopyTo(m_buf); m_bufPos = input.Length; } } public int ProcessByte(byte input, byte[] outBytes, int outOff) { return ProcessByte(input, Spans.FromNullable(outBytes, outOff)); } public unsafe int ProcessByte(byte input, Span<byte> output) { byte* intPtr = stackalloc byte[1]; *intPtr = input; Span<byte> span = new Span<byte>(intPtr, 1); return ProcessBytes(span, output); } public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) { Check.DataLength(inBytes, inOff, len, "input buffer too short"); return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff)); } public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output) { bool num = CheckData(); int num2 = 0; ReadOnlySpan<byte> readOnlySpan; if (num) { if (m_bufPos > 0) { int num3 = 16 - m_bufPos; if (input.Length < num3) { input.CopyTo(m_buf.AsSpan(m_bufPos)); m_bufPos += input.Length; return 0; } readOnlySpan = input.Slice(0, num3); readOnlySpan.CopyTo(m_buf.AsSpan(m_bufPos)); int num4 = num3; input = input.Slice(num4, input.Length - num4); ProcessBufferEncrypt(m_buf, output); num2 = 16; } while (input.Length >= 16) { ReadOnlySpan<byte> buffer = input; int num4 = num2; ProcessBufferEncrypt(buffer, output.Slice(num4, output.Length - num4)); input = input.Slice(16, input.Length - 16); num2 += 16; } } else { int num5 = 32 - m_bufPos; if (input.Length < num5) { input.CopyTo(m_buf.AsSpan(m_bufPos)); m_bufPos += input.Length; return 0; } int num4; if (m_bufPos >= 16) { ReadOnlySpan<byte> buffer2 = m_buf; num4 = num2; ProcessBufferDecrypt(buffer2, output.Slice(num4, output.Length - num4)); num2 += 16; m_bufPos -= 16; m_buf.AsSpan(0, m_bufPos).CopyFrom(m_buf.AsSpan(16)); num5 += 16; if (input.Length < num5) { input.CopyTo(m_buf.AsSpan(m_bufPos)); m_bufPos += input.Length; return num2; } } num5 = 16 - m_bufPos; readOnlySpan = input.Slice(0, num5); readOnlySpan.CopyTo(m_buf.AsSpan(m_bufPos)); num4 = num5; input = input.Slice(num4, input.Length - num4); ReadOnlySpan<byte> buffer3 = m_buf; num4 = num2; ProcessBufferDecrypt(buffer3, output.Slice(num4, output.Length - num4)); num2 += 16; while (input.Length >= 32) { ReadOnlySpan<byte> buffer4 = input; num4 = num2; ProcessBufferDecrypt(buffer4, output.Slice(num4, output.Length - num4)); input = input.Slice(16, input.Length - 16); num2 += 16; } } input.CopyTo(m_buf); m_bufPos = input.Length; return num2; } public int DoFinal(byte[] outBytes, int outOff) { return DoFinal(outBytes.AsSpan(outOff)); } public int DoFinal(Span<byte> output) { int num; if (CheckData()) { num = m_bufPos + 16; Check.OutputLength(output, num, "output buffer too short"); ProcessFinalEncrypt(m_buf.AsSpan(0, m_bufPos), output); m_mac = new byte[16]; Pack.UInt64_To_LE(S3, m_mac.AsSpan()); Pack.UInt64_To_LE(S4, m_mac.AsSpan(8)); byte[] mac = m_mac; int bufPos = m_bufPos; mac.CopyTo(output.Slice(bufPos, output.Length - bufPos)); Reset(false); } else { if (m_bufPos < 16) throw new InvalidCipherTextException("data too short"); m_bufPos -= 16; num = m_bufPos; Check.OutputLength(output, num, "output buffer too short"); ProcessFinalDecrypt(m_buf.AsSpan(0, m_bufPos), output); S3 ^= Pack.LE_To_UInt64(m_buf.AsSpan(m_bufPos)); S4 ^= Pack.LE_To_UInt64(m_buf.AsSpan(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(ReadOnlySpan<byte> buffer) { S0 ^= Pack.LE_To_UInt64(buffer); S1 ^= Pack.LE_To_UInt64(buffer.Slice(8, buffer.Length - 8)); P8(); } private void ProcessBufferDecrypt(ReadOnlySpan<byte> buffer, Span<byte> output) { Check.OutputLength(output, 16, "output buffer too short"); ulong num = Pack.LE_To_UInt64(buffer); Pack.UInt64_To_LE(S0 ^ num, output); S0 = num; ulong num2 = Pack.LE_To_UInt64(buffer.Slice(8, buffer.Length - 8)); Pack.UInt64_To_LE(S1 ^ num2, output.Slice(8, output.Length - 8)); S1 = num2; P8(); } private void ProcessBufferEncrypt(ReadOnlySpan<byte> buffer, Span<byte> output) { Check.OutputLength(output, 16, "output buffer too short"); S0 ^= Pack.LE_To_UInt64(buffer); Pack.UInt64_To_LE(S0, output); S1 ^= Pack.LE_To_UInt64(buffer.Slice(8, buffer.Length - 8)); Pack.UInt64_To_LE(S1, output.Slice(8, output.Length - 8)); P8(); } private void ProcessFinalDecrypt(ReadOnlySpan<byte> input, Span<byte> output) { if (input.Length >= 8) { ulong num = Pack.LE_To_UInt64(input); Pack.UInt64_To_LE(S0 ^ num, output); S0 = num; input = input.Slice(8, input.Length - 8); if (!input.IsEmpty) ProcessFinalDecrypt64(input, output.Slice(8, output.Length - 8), ref S1); S1 ^= Pad(input.Length); } else { if (!input.IsEmpty) ProcessFinalDecrypt64(input, output, ref S0); S0 ^= Pad(input.Length); } FinishData(State.DecFinal); } private static void ProcessFinalDecrypt64(ReadOnlySpan<byte> input, Span<byte> output, ref ulong s) { int length = input.Length; ulong num = Pack.LE_To_UInt64_Low(input); Pack.UInt64_To_LE_Low(s ^ num, output.Slice(0, length)); s &= (ulong)(-1 << (length << 3)); s ^= num; } private void ProcessFinalEncrypt(ReadOnlySpan<byte> input, Span<byte> output) { if (input.Length >= 8) { S0 ^= Pack.LE_To_UInt64(input); Pack.UInt64_To_LE(S0, output); input = input.Slice(8, input.Length - 8); if (!input.IsEmpty) ProcessFinalEncrypt64(input, output.Slice(8, output.Length - 8), ref S1); S1 ^= Pad(input.Length); } else { if (!input.IsEmpty) ProcessFinalEncrypt64(input, output, ref S0); S0 ^= Pad(input.Length); } FinishData(State.EncFinal); } private static void ProcessFinalEncrypt64(ReadOnlySpan<byte> input, Span<byte> output, ref ulong s) { int length = input.Length; s ^= Pack.LE_To_UInt64_Low(input); Pack.UInt64_To_LE_Low(s, output.Slice(0, length)); } 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); } [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)); } } }