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

ChaCha7539Engine

Implementation of Daniel J. Bernstein's ChaCha stream cipher.
using Org.BouncyCastle.Crypto.Utilities; using System; using System.Runtime.CompilerServices; namespace Org.BouncyCastle.Crypto.Engines { public class ChaCha7539Engine : Salsa20Engine { public override string AlgorithmName => "ChaCha7539"; protected override int NonceSize => 12; protected override void AdvanceCounter() { if (++engineState[12] == 0) throw new InvalidOperationException("attempt to increase counter past 2^32."); } protected override void ResetCounter() { engineState[12] = 0; } protected override void SetKey(byte[] keyBytes, byte[] ivBytes) { if (keyBytes != null) { if (keyBytes.Length != 32) throw new ArgumentException(AlgorithmName + " requires 256 bit key"); Salsa20Engine.PackTauOrSigma(keyBytes.Length, engineState, 0); Pack.LE_To_UInt32(keyBytes, 0, engineState, 4, 8); } Pack.LE_To_UInt32(ivBytes, 0, engineState, 13, 3); } protected override void GenerateKeyStream(byte[] output) { ChaChaEngine.ChachaCore(rounds, engineState, output); } internal void DoFinal(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff) { if (!initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); if (index != 0) throw new InvalidOperationException(AlgorithmName + " not in block-aligned state"); Check.DataLength(inBuf, inOff, inLen, "input buffer too short"); Check.OutputLength(outBuf, outOff, inLen, "output buffer too short"); while (inLen >= 128) { ProcessBlocks2(inBuf, inOff, outBuf, outOff); inOff += 128; inLen -= 128; outOff += 128; } if (inLen >= 64) { ImplProcessBlock(inBuf, inOff, outBuf, outOff); inOff += 64; inLen -= 64; outOff += 64; } if (inLen > 0) { GenerateKeyStream(keyStream); AdvanceCounter(); for (int i = 0; i < inLen; i++) { outBuf[outOff + i] = (byte)(inBuf[i + inOff] ^ keyStream[i]); } } engineState[12] = 0; } internal void ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) { if (!initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); if (LimitExceeded(64)) throw new MaxBytesExceededException("2^38 byte limit per IV would be exceeded; Change IV"); ImplProcessBlock(inBytes, inOff, outBytes, outOff); } internal void ProcessBlocks2(byte[] inBytes, int inOff, byte[] outBytes, int outOff) { if (!initialised) throw new InvalidOperationException(AlgorithmName + " not initialised"); if (LimitExceeded(128)) throw new MaxBytesExceededException("2^38 byte limit per IV would be exceeded; Change IV"); ImplProcessBlock(inBytes, inOff, outBytes, outOff); ImplProcessBlock(inBytes, inOff + 64, outBytes, outOff + 64); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void ImplProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff) { ChaChaEngine.ChachaCore(rounds, engineState, keyStream); AdvanceCounter(); for (int i = 0; i < 64; i++) { outBuf[outOff + i] = (byte)(keyStream[i] ^ inBuf[inOff + i]); } } } }