diff --git a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs index 5500af8d..c485f621 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Crypto; namespace SharpCompress.Common.Rar; @@ -55,6 +57,81 @@ internal sealed class RarCryptoWrapper : Stream return count; } + public override Task ReadAsync( + byte[] buffer, + int offset, + int count, + CancellationToken cancellationToken + ) => ReadAndDecryptAsync(buffer, offset, count, cancellationToken); + + private async Task ReadAndDecryptAsync( + byte[] buffer, + int offset, + int count, + CancellationToken cancellationToken + ) + { + cancellationToken.ThrowIfCancellationRequested(); + + var queueSize = _data.Count; + var sizeToRead = count - queueSize; + + if (sizeToRead > 0) + { + var alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf); + byte[] cipherText = new byte[16]; + + for (var i = 0; i < alignedSize / 16; i++) + { + var bytesRead = await _actualStream + .ReadAsync(cipherText, 0, 16, cancellationToken) + .ConfigureAwait(false); + + if (bytesRead == 0) + { + break; + } + + var readBytes = _rijndael.ProcessBlock(cipherText.AsSpan(0, bytesRead)); + foreach (var readByte in readBytes) + { + _data.Enqueue(readByte); + } + } + } + + var bytesToReturn = Math.Min(count, _data.Count); + for (var i = 0; i < bytesToReturn; i++) + { + buffer[offset + i] = _data.Dequeue(); + } + + return bytesToReturn; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override async ValueTask ReadAsync( + Memory buffer, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + + var array = System.Buffers.ArrayPool.Shared.Rent(buffer.Length); + try + { + var bytesRead = await ReadAndDecryptAsync(array, 0, buffer.Length, cancellationToken) + .ConfigureAwait(false); + new ReadOnlySpan(array, 0, bytesRead).CopyTo(buffer.Span); + return bytesRead; + } + finally + { + System.Buffers.ArrayPool.Shared.Return(array); + } + } +#endif + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();