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

CipherStream

public sealed class CipherStream : Stream
using Org.BouncyCastle.Utilities.IO; using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace Org.BouncyCastle.Crypto.IO { public sealed class CipherStream : Stream { private readonly Stream m_stream; private readonly IBufferedCipher m_readCipher; private readonly IBufferedCipher m_writeCipher; private byte[] m_readBuf; private int m_readBufPos; private bool m_readEnded; public IBufferedCipher ReadCipher => m_readCipher; public IBufferedCipher WriteCipher => m_writeCipher; public override bool CanRead => m_stream.CanRead; public override bool CanSeek => false; public override bool CanWrite => m_stream.CanWrite; public override long Length { get { throw new NotSupportedException(); } } public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } private Stream ReadSource { get { if (m_readCipher != null) return this; return m_stream; } } public CipherStream(Stream stream, IBufferedCipher readCipher, IBufferedCipher writeCipher) { m_stream = stream; if (readCipher != null) { m_readCipher = readCipher; m_readBuf = null; } if (writeCipher != null) m_writeCipher = writeCipher; } public override void CopyTo(Stream destination, int bufferSize) { Streams.CopyTo(ReadSource, destination, bufferSize); } public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { return Streams.CopyToAsync(ReadSource, destination, bufferSize, cancellationToken); } public override void Flush() { m_stream.Flush(); } public override int Read(byte[] buffer, int offset, int count) { if (m_readCipher == null) return m_stream.Read(buffer, offset, count); Streams.ValidateBufferArguments(buffer, offset, count); int i; int num; for (i = 0; i < count; i += num) { if ((m_readBuf == null || m_readBufPos >= m_readBuf.Length) && !FillInBuf()) break; num = System.Math.Min(count - i, m_readBuf.Length - m_readBufPos); Array.Copy(m_readBuf, m_readBufPos, buffer, offset + i, num); m_readBufPos += num; } return i; } public override int Read(Span<byte> buffer) { if (m_readCipher == null) return m_stream.Read(buffer); if (buffer.IsEmpty) return 0; int i; int num; for (i = 0; i < buffer.Length; i += num) { if ((m_readBuf == null || m_readBufPos >= m_readBuf.Length) && !FillInBuf()) break; num = System.Math.Min(buffer.Length - i, m_readBuf.Length - m_readBufPos); Span<byte> span = m_readBuf.AsSpan(m_readBufPos, num); int num2 = i; span.CopyTo(buffer.Slice(num2, buffer.Length - num2)); m_readBufPos += num; } return i; } public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default(CancellationToken)) { return Streams.ReadAsync(ReadSource, buffer, cancellationToken); } public override int ReadByte() { if (m_readCipher == null) return m_stream.ReadByte(); if ((m_readBuf == null || m_readBufPos >= m_readBuf.Length) && !FillInBuf()) return -1; return m_readBuf[m_readBufPos++]; } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long length) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { if (m_writeCipher == null) m_stream.Write(buffer, offset, count); else { Streams.ValidateBufferArguments(buffer, offset, count); if (count > 0) { byte[] array = new byte[m_writeCipher.GetUpdateOutputSize(count)]; int num = m_writeCipher.ProcessBytes(buffer, offset, count, array, 0); if (num > 0) try { m_stream.Write(array, 0, num); } finally { Array.Clear(array, 0, array.Length); } } } } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (m_writeCipher == null) return m_stream.WriteAsync(buffer, offset, count, cancellationToken); Streams.ValidateBufferArguments(buffer, offset, count); if (count > 0) { if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); byte[] array = new byte[m_writeCipher.GetUpdateOutputSize(count)]; int num = m_writeCipher.ProcessBytes(buffer, offset, count, array, 0); if (num > 0) return Streams.WriteAsyncCompletion(m_stream.WriteAsync(array, 0, num, cancellationToken), array); } return Task.CompletedTask; } public unsafe override void Write(ReadOnlySpan<byte> buffer) { if (m_writeCipher == null) m_stream.Write(buffer); else if (!buffer.IsEmpty) { int updateOutputSize = m_writeCipher.GetUpdateOutputSize(buffer.Length); Span<byte> span; if (updateOutputSize <= Streams.DefaultBufferSize) { int num = updateOutputSize; span = new Span<byte>(stackalloc byte[(int)(uint)num], num); } else span = new byte[updateOutputSize]; Span<byte> output = span; int num2 = m_writeCipher.ProcessBytes(buffer, output); if (num2 > 0) try { m_stream.Write(output.Slice(0, num2)); } finally { output.Fill(0); } } } public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default(CancellationToken)) { if (m_writeCipher == null) return m_stream.WriteAsync(buffer, cancellationToken); if (!buffer.IsEmpty) { if (cancellationToken.IsCancellationRequested) return ValueTask.FromCanceled(cancellationToken); byte[] array = new byte[m_writeCipher.GetUpdateOutputSize(buffer.Length)]; int num = m_writeCipher.ProcessBytes(buffer.Span, array.AsSpan()); if (num > 0) return Streams.WriteAsyncCompletion(m_stream.WriteAsync(array.AsMemory(0, num), cancellationToken), array); } return ValueTask.CompletedTask; } public override void WriteByte(byte value) { if (m_writeCipher == null) m_stream.WriteByte(value); else { byte[] array = m_writeCipher.ProcessByte(value); if (array != null) try { m_stream.Write(array, 0, array.Length); } finally { Array.Clear(array, 0, array.Length); } } } protected unsafe override void Dispose(bool disposing) { if (disposing) { if (m_writeCipher != null) { int outputSize = m_writeCipher.GetOutputSize(0); Span<byte> span; if (outputSize <= 256) { int num = outputSize; span = new Span<byte>(stackalloc byte[(int)(uint)num], num); } else span = new byte[outputSize]; Span<byte> output = span; int length = m_writeCipher.DoFinal(output); m_stream.Write(output.Slice(0, length)); output.Fill(0); } m_stream.Dispose(); } base.Dispose(disposing); } private bool FillInBuf() { if (m_readEnded) return false; m_readBufPos = 0; do { m_readBuf = ReadAndProcessBlock(); } while (!m_readEnded && m_readBuf == null); return m_readBuf != null; } private byte[] ReadAndProcessBlock() { int blockSize = m_readCipher.GetBlockSize(); byte[] array = new byte[(blockSize == 0) ? 256 : blockSize]; int num = 0; do { int num2 = m_stream.Read(array, num, array.Length - num); if (num2 < 1) { m_readEnded = true; break; } num += num2; } while (num < array.Length); byte[] array2 = m_readEnded ? m_readCipher.DoFinal(array, 0, num) : m_readCipher.ProcessBytes(array); if (array2 != null && array2.Length == 0) array2 = null; return array2; } } }