<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; byte[] array; if (aeadParameters != null) { keyParameter = aeadParameters.Key; array = aeadParameters.GetNonce(); 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); array = parametersWithIV.GetIV(); initialAssociatedText = null; } if (keyParameter == null) throw new ArgumentException("Ascon Init parameters must include a key"); AsconParameters asconParameters; int num; if (array.Length != CRYPTO_ABYTES) { asconParameters = this.asconParameters; string str = asconParameters.ToString(); num = CRYPTO_ABYTES; throw new ArgumentException(str + " requires exactly " + num.ToString() + " bytes of IV"); } byte[] key = keyParameter.GetKey(); if (key.Length != CRYPTO_KEYBYTES) { asconParameters = this.asconParameters; string str2 = asconParameters.ToString(); num = CRYPTO_KEYBYTES; throw new ArgumentException(str2 + " key must be " + num.ToString() + " bytes long"); } N0 = Pack.BE_To_UInt64(array, 0); N1 = Pack.BE_To_UInt64(array, 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, 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 = ASCON_AEAD_RATE - 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 >= ASCON_AEAD_RATE) { ProcessBufferAad(inBytes, inOff); inOff += ASCON_AEAD_RATE; len -= ASCON_AEAD_RATE; } 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 = ASCON_AEAD_RATE - 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 = ASCON_AEAD_RATE; } while (len >= ASCON_AEAD_RATE) { ProcessBufferEncrypt(inBytes, inOff, outBytes, outOff + num2); inOff += ASCON_AEAD_RATE; len -= ASCON_AEAD_RATE; num2 += ASCON_AEAD_RATE; } } else { int num4 = m_bufferSizeDecrypt - m_bufPos; if (len < num4) { Array.Copy(inBytes, inOff, m_buf, m_bufPos, len); m_bufPos += len; return 0; } while (m_bufPos >= ASCON_AEAD_RATE) { ProcessBufferDecrypt(m_buf, 0, outBytes, outOff + num2); m_bufPos -= ASCON_AEAD_RATE; Array.Copy(m_buf, ASCON_AEAD_RATE, m_buf, 0, m_bufPos); num2 += ASCON_AEAD_RATE; num4 += ASCON_AEAD_RATE; if (len < num4) { Array.Copy(inBytes, inOff, m_buf, m_bufPos, len); m_bufPos += len; return num2; } } num4 = ASCON_AEAD_RATE - m_bufPos; Array.Copy(inBytes, inOff, m_buf, m_bufPos, num4); inOff += num4; len -= num4; ProcessBufferDecrypt(m_buf, 0, outBytes, outOff + num2); num2 += ASCON_AEAD_RATE; while (len >= m_bufferSizeDecrypt) { ProcessBufferDecrypt(inBytes, inOff, outBytes, outOff + num2); inOff += ASCON_AEAD_RATE; len -= ASCON_AEAD_RATE; num2 += ASCON_AEAD_RATE; } } 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 + CRYPTO_ABYTES; Check.OutputLength(outBytes, outOff, num, "output buffer too short"); ProcessFinalEncrypt(m_buf, 0, m_bufPos, outBytes, outOff); mac = new byte[CRYPTO_ABYTES]; Pack.UInt64_To_BE(x3, mac, 0); Pack.UInt64_To_BE(x4, mac, 8); Array.Copy(mac, 0, outBytes, outOff + m_bufPos, CRYPTO_ABYTES); Reset(false); } else { if (m_bufPos < CRYPTO_ABYTES) throw new InvalidCipherTextException("data too short"); m_bufPos -= CRYPTO_ABYTES; num = m_bufPos; Check.OutputLength(outBytes, outOff, num, "output buffer too short"); ProcessFinalDecrypt(m_buf, 0, m_bufPos, outBytes, outOff); x3 ^= Pack.BE_To_UInt64(m_buf, m_bufPos); x4 ^= Pack.BE_To_UInt64(m_buf, 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(byte[] buffer, int bufOff) { x0 ^= Pack.BE_To_UInt64(buffer, bufOff); if (ASCON_AEAD_RATE == 16) x1 ^= Pack.BE_To_UInt64(buffer, bufOff + 8); P(nr); } private void ProcessBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { Check.OutputLength(output, outOff, ASCON_AEAD_RATE, "output buffer too short"); ulong num = Pack.BE_To_UInt64(buffer, bufOff); Pack.UInt64_To_BE(x0 ^ num, output, outOff); x0 = num; if (ASCON_AEAD_RATE == 16) { ulong num2 = Pack.BE_To_UInt64(buffer, bufOff + 8); Pack.UInt64_To_BE(x1 ^ num2, output, outOff + 8); x1 = num2; } P(nr); } private void ProcessBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { Check.OutputLength(output, outOff, ASCON_AEAD_RATE, "output buffer too short"); x0 ^= Pack.BE_To_UInt64(buffer, bufOff); Pack.UInt64_To_BE(x0, output, outOff); if (ASCON_AEAD_RATE == 16) { x1 ^= Pack.BE_To_UInt64(buffer, bufOff + 8); Pack.UInt64_To_BE(x1, output, outOff + 8); } P(nr); } private void ProcessFinalDecrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff) { if (inLen >= 8) { ulong num = Pack.BE_To_UInt64(input, inOff); x0 ^= num; Pack.UInt64_To_BE(x0, output, outOff); x0 = num; inOff += 8; outOff += 8; inLen -= 8; x1 ^= PAD(inLen); if (inLen != 0) { ulong num2 = Pack.BE_To_UInt64_High(input, inOff, inLen); x1 ^= num2; Pack.UInt64_To_BE_High(x1, output, outOff, inLen); x1 &= ulong.MaxValue >> (inLen << 3); x1 ^= num2; } } else { x0 ^= PAD(inLen); if (inLen != 0) { ulong num3 = Pack.BE_To_UInt64_High(input, inOff, inLen); x0 ^= num3; Pack.UInt64_To_BE_High(x0, output, outOff, inLen); x0 &= ulong.MaxValue >> (inLen << 3); x0 ^= num3; } } FinishData(State.DecFinal); } private void ProcessFinalEncrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff) { if (inLen >= 8) { x0 ^= Pack.BE_To_UInt64(input, inOff); Pack.UInt64_To_BE(x0, output, outOff); inOff += 8; outOff += 8; inLen -= 8; x1 ^= PAD(inLen); if (inLen != 0) { x1 ^= Pack.BE_To_UInt64_High(input, inOff, inLen); Pack.UInt64_To_BE_High(x1, output, outOff, inLen); } } else { x0 ^= PAD(inLen); if (inLen != 0) { x0 ^= Pack.BE_To_UInt64_High(input, inOff, inLen); Pack.UInt64_To_BE_High(x0, output, outOff, inLen); } } 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, 0, initialAssociatedText.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong PAD(int i) { return 9223372036854775808 >> (i << 3); } } }