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

AsconEngine

public sealed class AsconEngine : IAeadCipher
ASCON v1.2 AEAD, https://ascon.iaik.tugraz.at/ .
using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; using System.Runtime.CompilerServices; namespace Org.BouncyCastle.Crypto.Engines { [Obsolete("This class is deprecated. For the latest Ascon version, use AsconAead128 instead.")] public sealed class AsconEngine : IAeadCipher { public enum AsconParameters { ascon80pq, ascon128a, ascon128 } private enum State { Uninitialized, EncInit, EncAad, EncData, EncFinal, DecInit, DecAad, DecData, DecFinal } private readonly AsconParameters asconParameters; private readonly int CRYPTO_KEYBYTES; private readonly int CRYPTO_ABYTES; private readonly int ASCON_AEAD_RATE; private readonly int nr; private byte[] mac; private ulong K0; private ulong K1; private ulong K2; private ulong N0; private ulong N1; private readonly ulong ASCON_IV; private ulong x0; private ulong x1; private ulong x2; private ulong x3; private ulong x4; private string algorithmName; private State m_state; private byte[] initialAssociatedText; private readonly int m_bufferSizeDecrypt; private readonly byte[] m_buf; private int m_bufPos; public string AlgorithmName => algorithmName; public AsconEngine(AsconParameters asconParameters) { this.asconParameters = asconParameters; switch (asconParameters) { case AsconParameters.ascon80pq: CRYPTO_KEYBYTES = 20; CRYPTO_ABYTES = 16; ASCON_AEAD_RATE = 8; ASCON_IV = 11547242664487288832; algorithmName = "Ascon-80pq AEAD"; break; case AsconParameters.ascon128a: CRYPTO_KEYBYTES = 16; CRYPTO_ABYTES = 16; ASCON_AEAD_RATE = 16; ASCON_IV = 9259414062373011456; algorithmName = "Ascon-128a AEAD"; break; case AsconParameters.ascon128: CRYPTO_KEYBYTES = 16; CRYPTO_ABYTES = 16; ASCON_AEAD_RATE = 8; ASCON_IV = 9241399655273594880; algorithmName = "Ascon-128 AEAD"; break; default: throw new ArgumentException("invalid parameter setting for ASCON AEAD"); } nr = ((ASCON_AEAD_RATE == 8) ? 6 : 8); m_bufferSizeDecrypt = ASCON_AEAD_RATE + CRYPTO_ABYTES; m_buf = new byte[m_bufferSizeDecrypt]; } public int GetKeyBytesSize() { return CRYPTO_KEYBYTES; } public int GetIVBytesSize() { return CRYPTO_ABYTES; } public void Init(bool forEncryption, ICipherParameters parameters) { AeadParameters aeadParameters = parameters as AeadParameters; KeyParameter keyParameter; ReadOnlySpan<byte> bs; if (aeadParameters != null) { keyParameter = aeadParameters.Key; bs = aeadParameters.Nonce; initialAssociatedText = aeadParameters.GetAssociatedText(); int macSize = aeadParameters.MacSize; if (macSize != CRYPTO_ABYTES * 8) throw new ArgumentException("Invalid value for MAC size: " + macSize.ToString()); } else { ParametersWithIV parametersWithIV = parameters as ParametersWithIV; if (parametersWithIV == null) throw new ArgumentException("invalid parameters passed to Ascon"); keyParameter = (parametersWithIV.Parameters as KeyParameter); bs = parametersWithIV.IV; initialAssociatedText = null; } if (keyParameter == null) throw new ArgumentException("Ascon Init parameters must include a key"); AsconParameters asconParameters; if (bs.Length != CRYPTO_ABYTES) { asconParameters = this.asconParameters; throw new ArgumentException(asconParameters.ToString() + " requires exactly " + CRYPTO_ABYTES.ToString() + " bytes of IV"); } ReadOnlySpan<byte> key = keyParameter.Key; if (key.Length != CRYPTO_KEYBYTES) { asconParameters = this.asconParameters; throw new ArgumentException(asconParameters.ToString() + " key must be " + CRYPTO_KEYBYTES.ToString() + " bytes long"); } N0 = Pack.BE_To_UInt64(bs, 0); N1 = Pack.BE_To_UInt64(bs, 8); if (CRYPTO_KEYBYTES == 16) { K1 = Pack.BE_To_UInt64(key, 0); K2 = Pack.BE_To_UInt64(key, 8); } else { if (CRYPTO_KEYBYTES != 20) throw new InvalidOperationException(); K0 = Pack.BE_To_UInt32(key, 0); K1 = Pack.BE_To_UInt64(key, 4); K2 = Pack.BE_To_UInt64(key, 12); } m_state = (forEncryption ? State.EncInit : State.DecInit); Reset(true); } public void ProcessAadByte(byte input) { CheckAad(); m_buf[m_bufPos] = input; if (++m_bufPos == ASCON_AEAD_RATE) { 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 = ASCON_AEAD_RATE - 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 >= ASCON_AEAD_RATE) { ProcessBufferAad(input); int num2 = ASCON_AEAD_RATE; input = input.Slice(num2, input.Length - num2); } 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 = ASCON_AEAD_RATE - 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 = ASCON_AEAD_RATE; } while (input.Length >= ASCON_AEAD_RATE) { ReadOnlySpan<byte> buffer = input; int num4 = num2; ProcessBufferEncrypt(buffer, output.Slice(num4, output.Length - num4)); num4 = ASCON_AEAD_RATE; input = input.Slice(num4, input.Length - num4); num2 += ASCON_AEAD_RATE; } } else { int num5 = m_bufferSizeDecrypt - m_bufPos; if (input.Length < num5) { input.CopyTo(m_buf.AsSpan(m_bufPos)); m_bufPos += input.Length; return 0; } int num4; while (m_bufPos >= ASCON_AEAD_RATE) { ReadOnlySpan<byte> buffer2 = m_buf; num4 = num2; ProcessBufferDecrypt(buffer2, output.Slice(num4, output.Length - num4)); m_bufPos -= ASCON_AEAD_RATE; m_buf.AsSpan(0, m_bufPos).CopyFrom(m_buf.AsSpan(ASCON_AEAD_RATE)); num2 += ASCON_AEAD_RATE; num5 += ASCON_AEAD_RATE; if (input.Length < num5) { input.CopyTo(m_buf.AsSpan(m_bufPos)); m_bufPos += input.Length; return num2; } } num5 = ASCON_AEAD_RATE - 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 += ASCON_AEAD_RATE; while (input.Length >= m_bufferSizeDecrypt) { ReadOnlySpan<byte> buffer4 = input; num4 = num2; ProcessBufferDecrypt(buffer4, output.Slice(num4, output.Length - num4)); num4 = ASCON_AEAD_RATE; input = input.Slice(num4, input.Length - num4); num2 += ASCON_AEAD_RATE; } } 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 + CRYPTO_ABYTES; Check.OutputLength(output, num, "output buffer too short"); ProcessFinalEncrypt(m_buf.AsSpan(0, m_bufPos), output); mac = new byte[CRYPTO_ABYTES]; Pack.UInt64_To_BE(x3, mac.AsSpan()); Pack.UInt64_To_BE(x4, mac.AsSpan(8)); byte[] source = mac; int bufPos = m_bufPos; source.CopyTo(output.Slice(bufPos, output.Length - bufPos)); Reset(false); } else { if (m_bufPos < CRYPTO_ABYTES) throw new InvalidCipherTextException("data too short"); m_bufPos -= CRYPTO_ABYTES; num = m_bufPos; Check.OutputLength(output, num, "output buffer too short"); ProcessFinalDecrypt(m_buf.AsSpan(0, m_bufPos), output); x3 ^= Pack.BE_To_UInt64(m_buf.AsSpan(m_bufPos)); x4 ^= Pack.BE_To_UInt64(m_buf.AsSpan(m_bufPos + 8)); if ((x3 | x4) != 0) throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed"); Reset(true); } return num; } public byte[] GetMac() { return 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 - CRYPTO_ABYTES); break; case State.DecData: case State.DecFinal: num = System.Math.Max(0, num + m_bufPos - CRYPTO_ABYTES); break; case State.EncData: case State.EncFinal: num += m_bufPos; break; } return num - num % ASCON_AEAD_RATE; } 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 - CRYPTO_ABYTES); case State.DecData: case State.DecFinal: return System.Math.Max(0, num + m_bufPos - CRYPTO_ABYTES); case State.EncData: case State.EncFinal: return num + m_bufPos + CRYPTO_ABYTES; default: return num + CRYPTO_ABYTES; } } 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] = 128; if (m_bufPos >= 8) { x0 ^= Pack.BE_To_UInt64(m_buf, 0); x1 ^= (ulong)((long)Pack.BE_To_UInt64(m_buf, 8) & (-1 << 56 - (m_bufPos - 8 << 3))); } else x0 ^= (ulong)((long)Pack.BE_To_UInt64(m_buf, 0) & (-1 << 56 - (m_bufPos << 3))); P(nr); } x4 ^= 1; m_bufPos = 0; m_state = nextState; } private void FinishData(State nextState) { switch (asconParameters) { case AsconParameters.ascon128: x1 ^= K1; x2 ^= K2; break; case AsconParameters.ascon128a: x2 ^= K1; x3 ^= K2; break; case AsconParameters.ascon80pq: x1 ^= ((K0 << 32) | (K1 >> 32)); x2 ^= ((K1 << 32) | (K2 >> 32)); x3 ^= K2 << 32; break; default: throw new InvalidOperationException(); } P(12); x3 ^= K1; x4 ^= K2; m_state = nextState; } private void P(int nr) { if (nr >= 8) { if (nr == 12) { ROUND(240); ROUND(225); ROUND(210); ROUND(195); } ROUND(180); ROUND(165); } ROUND(150); ROUND(135); ROUND(120); ROUND(105); ROUND(90); ROUND(75); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ROUND(ulong c) { ulong num = x0 ^ x1 ^ x2 ^ x3 ^ c ^ (x1 & (x0 ^ x2 ^ x4 ^ c)); ulong num2 = x0 ^ x2 ^ x3 ^ x4 ^ c ^ ((x1 ^ x2 ^ c) & (x1 ^ x3)); ulong num3 = x1 ^ x2 ^ x4 ^ c ^ (x3 & x4); ulong num4 = x0 ^ x1 ^ x2 ^ c ^ (~x0 & (x3 ^ x4)); ulong num5 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); x0 = (num ^ Longs.RotateRight(num, 19) ^ Longs.RotateRight(num, 28)); x1 = (num2 ^ Longs.RotateRight(num2, 39) ^ Longs.RotateRight(num2, 61)); x2 = ~(num3 ^ Longs.RotateRight(num3, 1) ^ Longs.RotateRight(num3, 6)); x3 = (num4 ^ Longs.RotateRight(num4, 10) ^ Longs.RotateRight(num4, 17)); x4 = (num5 ^ Longs.RotateRight(num5, 7) ^ Longs.RotateRight(num5, 41)); } private void ascon_aeadinit() { x0 = ASCON_IV; if (CRYPTO_KEYBYTES == 20) x0 ^= K0; x1 = K1; x2 = K2; x3 = N0; x4 = N1; P(12); if (CRYPTO_KEYBYTES == 20) x2 ^= K0; x3 ^= K1; x4 ^= K2; } private void ProcessBufferAad(ReadOnlySpan<byte> buffer) { x0 ^= Pack.BE_To_UInt64(buffer); if (ASCON_AEAD_RATE == 16) x1 ^= Pack.BE_To_UInt64(buffer.Slice(8, buffer.Length - 8)); P(nr); } private void ProcessBufferDecrypt(ReadOnlySpan<byte> buffer, Span<byte> output) { Check.OutputLength(output, ASCON_AEAD_RATE, "output buffer too short"); ulong num = Pack.BE_To_UInt64(buffer); Pack.UInt64_To_BE(x0 ^ num, output); x0 = num; if (ASCON_AEAD_RATE == 16) { ulong num2 = Pack.BE_To_UInt64(buffer.Slice(8, buffer.Length - 8)); Pack.UInt64_To_BE(x1 ^ num2, output.Slice(8, output.Length - 8)); x1 = num2; } P(nr); } private void ProcessBufferEncrypt(ReadOnlySpan<byte> buffer, Span<byte> output) { Check.OutputLength(output, ASCON_AEAD_RATE, "output buffer too short"); x0 ^= Pack.BE_To_UInt64(buffer); Pack.UInt64_To_BE(x0, output); if (ASCON_AEAD_RATE == 16) { x1 ^= Pack.BE_To_UInt64(buffer.Slice(8, buffer.Length - 8)); Pack.UInt64_To_BE(x1, output.Slice(8, output.Length - 8)); } P(nr); } private void ProcessFinalDecrypt(ReadOnlySpan<byte> input, Span<byte> output) { if (input.Length >= 8) { ulong num = Pack.BE_To_UInt64(input); x0 ^= num; Pack.UInt64_To_BE(x0, output); x0 = num; input = input.Slice(8, input.Length - 8); output = output.Slice(8, output.Length - 8); x1 ^= PAD(input.Length); if (!input.IsEmpty) { num = Pack.BE_To_UInt64_High(input); x1 ^= num; Pack.UInt64_To_BE_High(x1, output.Slice(0, input.Length)); x1 &= ulong.MaxValue >> (input.Length << 3); x1 ^= num; } } else { x0 ^= PAD(input.Length); if (!input.IsEmpty) { ulong num2 = Pack.BE_To_UInt64_High(input); x0 ^= num2; Pack.UInt64_To_BE_High(x0, output.Slice(0, input.Length)); x0 &= ulong.MaxValue >> (input.Length << 3); x0 ^= num2; } } FinishData(State.DecFinal); } private void ProcessFinalEncrypt(ReadOnlySpan<byte> input, Span<byte> output) { if (input.Length >= 8) { x0 ^= Pack.BE_To_UInt64(input); Pack.UInt64_To_BE(x0, output); input = input.Slice(8, input.Length - 8); output = output.Slice(8, output.Length - 8); x1 ^= PAD(input.Length); if (!input.IsEmpty) { x1 ^= Pack.BE_To_UInt64_High(input); Pack.UInt64_To_BE_High(x1, output.Slice(0, input.Length)); } } else { x0 ^= PAD(input.Length); if (!input.IsEmpty) { x0 ^= Pack.BE_To_UInt64_High(input); Pack.UInt64_To_BE_High(x0, output.Slice(0, input.Length)); } } FinishData(State.EncFinal); } private void Reset(bool clearMac) { if (clearMac) mac = null; Arrays.Clear(m_buf); 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; } ascon_aeadinit(); if (initialAssociatedText != null) ProcessAadBytes(initialAssociatedText); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong PAD(int i) { return 9223372036854775808 >> (i << 3); } } }