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

BcChaCha20Poly1305

public sealed class BcChaCha20Poly1305 : TlsAeadCipherImpl
using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Tls.Crypto.Impl.BC { public sealed class BcChaCha20Poly1305 : TlsAeadCipherImpl { private static readonly byte[] Zeroes = new byte[15]; private readonly ChaCha7539Engine m_cipher = new ChaCha7539Engine(); private readonly Poly1305 m_mac = new Poly1305(); private readonly bool m_isEncrypting; private int m_additionalDataLength; public BcChaCha20Poly1305(bool isEncrypting) { m_isEncrypting = isEncrypting; } public int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset) { if (m_isEncrypting) { m_cipher.DoFinal(input, inputOffset, inputLength, output, outputOffset); if (inputLength != inputLength) throw new InvalidOperationException(); UpdateMac(output, outputOffset, inputLength); byte[] array = new byte[16]; Pack.UInt64_To_LE((ulong)m_additionalDataLength, array, 0); Pack.UInt64_To_LE((ulong)inputLength, array, 8); m_mac.BlockUpdate(array, 0, 16); m_mac.DoFinal(output, outputOffset + inputLength); return inputLength + 16; } int num = inputLength - 16; UpdateMac(input, inputOffset, num); byte[] array2 = new byte[16]; Pack.UInt64_To_LE((ulong)m_additionalDataLength, array2, 0); Pack.UInt64_To_LE((ulong)num, array2, 8); m_mac.BlockUpdate(array2, 0, 16); m_mac.DoFinal(array2, 0); if (!TlsUtilities.ConstantTimeAreEqual(16, array2, 0, input, inputOffset + num)) throw new TlsFatalAlert(20); m_cipher.DoFinal(input, inputOffset, num, output, outputOffset); int num2 = num; if (num != num2) throw new InvalidOperationException(); return num; } public int DoFinal(byte[] additionalData, byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset) { if (!Arrays.IsNullOrEmpty(additionalData)) { if (m_additionalDataLength != 0) throw new InvalidOperationException(); m_additionalDataLength = additionalData.Length; UpdateMac(additionalData, 0, additionalData.Length); } return DoFinal(input, inputOffset, inputLength, output, outputOffset); } public int GetOutputSize(int inputLength) { if (!m_isEncrypting) return inputLength - 16; return inputLength + 16; } public void Init(byte[] nonce, int macSize, byte[] additionalData) { if (nonce == null || nonce.Length != 12 || macSize != 16) throw new TlsFatalAlert(80); m_cipher.Init(m_isEncrypting, new ParametersWithIV(null, nonce)); InitMac(); if (Arrays.IsNullOrEmpty(additionalData)) m_additionalDataLength = 0; else { m_additionalDataLength = additionalData.Length; UpdateMac(additionalData, 0, additionalData.Length); } } public void Reset() { } public void SetKey(byte[] key, int keyOff, int keyLen) { KeyParameter parameters = new KeyParameter(key, keyOff, keyLen); m_cipher.Init(m_isEncrypting, new ParametersWithIV(parameters, Zeroes, 0, 12)); } private void InitMac() { byte[] array = new byte[64]; m_cipher.ProcessBytes(array, 0, 64, array, 0); m_mac.Init(new KeyParameter(array, 0, 32)); Array.Clear(array, 0, array.Length); } private void UpdateMac(byte[] buf, int off, int len) { m_mac.BlockUpdate(buf, off, len); int num = len % 16; if (num != 0) m_mac.BlockUpdate(Zeroes, 0, 16 - num); } } }