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

CtsBlockCipher

using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Crypto.Modes { public class CtsBlockCipher : BufferedBlockCipher { private readonly int m_blockSize; public CtsBlockCipher(IBlockCipher cipher) : this(EcbBlockCipher.GetBlockCipherMode(cipher)) { } public CtsBlockCipher(IBlockCipherMode cipherMode) { if (!(cipherMode is CbcBlockCipher) && !(cipherMode is EcbBlockCipher)) throw new ArgumentException("CtsBlockCipher can only accept ECB, or CBC ciphers"); m_cipherMode = cipherMode; m_blockSize = cipherMode.GetBlockSize(); buf = new byte[m_blockSize * 2]; bufOff = 0; } public override int GetBlockSize() { return m_blockSize; } public override int GetOutputSize(int length) { return bufOff + length; } public override int GetUpdateOutputSize(int length) { return BufferedCipherBase.GetFullBlocksSize(bufOff + length - 1, buf.Length); } public override int ProcessByte(byte input, byte[] output, int outOff) { return ProcessByte(input, Spans.FromNullable(output, outOff)); } public override int ProcessByte(byte input, Span<byte> output) { int result = 0; if (bufOff == buf.Length) { Check.OutputLength(output, m_blockSize, "output buffer too short"); result = m_cipherMode.ProcessBlock(buf, output); Array.Copy(buf, m_blockSize, buf, 0, m_blockSize); bufOff = m_blockSize; } buf[bufOff++] = input; return result; } public override int ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff) { if (length < 1) { if (length < 0) throw new ArgumentException("Can't have a negative input length!"); return 0; } return ProcessBytes(input.AsSpan(inOff, length), Spans.FromNullable(output, outOff)); } public override int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output) { int num = 0; int num2 = buf.Length - bufOff; if (input.Length > num2) { int updateOutputSize = GetUpdateOutputSize(input.Length); Check.OutputLength(output, updateOutputSize, "output buffer too short"); ReadOnlySpan<byte> readOnlySpan = input.Slice(0, num2); readOnlySpan.CopyTo(buf.AsSpan(bufOff)); int num3 = num2; input = input.Slice(num3, input.Length - num3); if (output.Slice(0, m_blockSize).Overlaps(input)) { byte[] array = new byte[input.Length]; input.CopyTo(array); input = array; } num = m_cipherMode.ProcessBlock(buf, output); Array.Copy(buf, m_blockSize, buf, 0, m_blockSize); bufOff = m_blockSize; while (input.Length > m_blockSize) { int num4 = num; IBlockCipherMode cipherMode = m_cipherMode; ReadOnlySpan<byte> input2 = buf; num3 = num; num = num4 + cipherMode.ProcessBlock(input2, output.Slice(num3, output.Length - num3)); readOnlySpan = input.Slice(0, m_blockSize); readOnlySpan.CopyTo(buf); num3 = m_blockSize; input = input.Slice(num3, input.Length - num3); } } input.CopyTo(buf.AsSpan(bufOff)); bufOff += input.Length; return num; } public override int DoFinal(byte[] output, int outOff) { return DoFinal(Spans.FromNullable(output, outOff)); } public override int DoFinal(Span<byte> output) { try { Check.DataLength(bufOff < m_blockSize, "need at least one block of input for CTS"); Check.OutputLength(output, bufOff, "output buffer too short"); Span<byte> span; if (forEncryption) { m_cipherMode.ProcessBlock(buf, buf); for (int i = bufOff; i < buf.Length; i++) { buf[i] = buf[i - m_blockSize]; } for (int j = m_blockSize; j < bufOff; j++) { buf[j] ^= buf[j - m_blockSize]; } m_cipherMode.UnderlyingCipher.ProcessBlock(buf.AsSpan(m_blockSize), output); span = buf.AsSpan(0, bufOff - m_blockSize); int blockSize = m_blockSize; span.CopyTo(output.Slice(blockSize, output.Length - blockSize)); } else { m_cipherMode.UnderlyingCipher.ProcessBlock(buf, buf); for (int k = m_blockSize; k < bufOff; k++) { byte b = buf[k - m_blockSize]; buf[k - m_blockSize] = buf[k]; buf[k] ^= b; } m_cipherMode.ProcessBlock(buf, output); span = buf.AsSpan(m_blockSize, bufOff - m_blockSize); int blockSize = m_blockSize; span.CopyTo(output.Slice(blockSize, output.Length - blockSize)); } return bufOff; } finally { Reset(); } } } }