private void FillCryptoOverflowWithExcess(byte[] buffer, int start, int initialCount, int extendedCount)
        {
            //If the original count asked for is not the same as the blocksize adjusted count
            //we need to buffer the extra bytes
            if (initialCount != extendedCount)
            {
                int overflowSize = extendedCount - initialCount;
                int readIndex    = CryptoBlockOverflow.Length - overflowSize;

                //We should read the overflow into starting index OverflowLength - overflowSize index and not starting at 0
                //This allows us to know how many and what index to starting reading at all at the same time
                BufferUtil.QuickUnsafeCopy(buffer, start + (extendedCount - overflowSize), CryptoBlockOverflow, readIndex, overflowSize);
                CryptoBlockOverflowReadIndex = readIndex;
            }
        }
        public override async Task WriteAsync(byte[] bytes, int offset, int count)
        {
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            int blocksizeAdjustedCount = ConvertToBlocksizeCount(count);

            if (count == blocksizeAdjustedCount)
            {
                await DecoratedClient.WriteAsync(EncryptionServiceProvider.Crypt(bytes, offset, count), offset, count)
                .ConfigureAwait(false);
            }
            else
            {
                //Lock because we use the crypto buffer for this
                using (await WriteBuffer.BufferLock.LockAsync().ConfigureAwait(false))
                {
                    try
                    {
                        //We copy to the thread local buffer so we can use it as an extended buffer by "neededBytes" many more bytes.
                        //So the buffer is very large but we'll tell it to write bytes.length + neededBytes.
                        BufferUtil.QuickUnsafeCopy(bytes, offset, WriteBuffer.Buffer, 0, count);                         //dont copy full array, might only need less with count
                    }
                    catch (Exception e)
                    {
                        throw new InvalidOperationException($"Failed to copy bytes to crypto buffer. Bytes Length: {bytes.Length} Offset: {offset} Count: {count} BlocksizeAdjustedCount: {blocksizeAdjustedCount}", e);
                    }

                    EncryptionServiceProvider.Crypt(WriteBuffer.Buffer, 0, blocksizeAdjustedCount);

                    //recurr to write the bytes with the now properly sized buffer.
                    await DecoratedClient.WriteAsync(WriteBuffer.Buffer, 0, blocksizeAdjustedCount)
                    .ConfigureAwait(false);
                }
            }
        }
Пример #3
0
        /// <inheritdoc />
        public override async Task WriteAsync(byte[] bytes, int offset, int count)
        {
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            //If we have more bytes than we require buffering till
            //we should just write
            //We have to lock to prevent anything from touching the combined or buffer inbetween
            using (await BufferedData.BufferLock.LockAsync().ConfigureAwait(false))
                using (await CombinedBuffer.BufferLock.LockAsync().ConfigureAwait(false))
                {
                    if (count > BufferedData.Buffer.Length && CurrentIndex == -1)
                    {
                        await DecoratedClient.WriteAsync(bytes, offset, count)
                        .ConfigureAwait(false);
                    }
                    else if (count + CurrentIndex + 1 > BufferedData.Buffer.Length && CurrentIndex != -1)
                    {
                        //TODO: Do this somehow without copying
                        BufferUtil.QuickUnsafeCopy(BufferedData.Buffer, 0, CombinedBuffer.Buffer, 0, CurrentIndex + 1);
                        BufferUtil.QuickUnsafeCopy(bytes, offset, CombinedBuffer.Buffer, CurrentIndex + 1, count);

                        await DecoratedClient.WriteAsync(CombinedBuffer.Buffer, 0, count + CurrentIndex + 1)
                        .ConfigureAwait(false);

                        CurrentIndex = -1;
                    }
                    else
                    {
                        //At this point we know that the buffer isn't large enough to write so we need to buffer it
                        BufferUtil.QuickUnsafeCopy(bytes, offset, BufferedData.Buffer, CurrentIndex + 1, count);
                        CurrentIndex += count;
                    }
                }
        }
        //TODO: Refactor this
        /// <summary>
        /// Reads asyncronously <see cref="count"/> many bytes from the reader.
        /// </summary>
        /// <param name="buffer">The buffer to store the bytes into.</param>
        /// <param name="start">The start position in the buffer to start reading into.</param>
        /// <param name="count">How many bytes to read.</param>
        /// <param name="token">The cancel token to check during the async operation.</param>
        /// <returns>A future for the read bytes.</returns>
        public override async Task <int> ReadAsync(byte[] buffer, int start, int count, CancellationToken token)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }
            if (start < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(start));
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(count), $"Cannot read less than 0 bytes. Can't read: {count} many bytes");
            }

            //We read this outside of the lock to reduce locking time
            //If the above caller requested an invalid count of bytes to read
            //We should try to correct for it and read afew more bytes.
            int extendedCount = ConvertToBlocksizeCount(count);

            if (token.IsCancellationRequested)
            {
                return(0);
            }

            //TODO: Optimize for when the buffer is large enough. Right now it does needless BlockCopy even if there is room in the buffer
            //We should lock incase there are multiple calls
            using (await ReadBuffer.BufferLock.LockAsync(token).ConfigureAwait(false))
            {
                int cryptoOverflowSize = CryptoBlockOverflow.Length - CryptoBlockOverflowReadIndex;

                //If the overflow size is the exact size asked for then
                //we don't need to do anything but copy the buffer and return
                if (cryptoOverflowSize >= count)
                {
                    //Read from the crypto overflow and move the read index forward
                    BufferUtil.QuickUnsafeCopy(CryptoBlockOverflow, CryptoBlockOverflowReadIndex, buffer, start, count);
                    CryptoBlockOverflowReadIndex = CryptoBlockOverflowReadIndex + count;
                    return(count);
                }
                else if (cryptoOverflowSize == 0)
                {
                    //Since the buffer MAY be large enough we check to avoid a potential BlockCopy
                    if (buffer.Length > extendedCount)
                    {
                        //Since the provider may not give a buffer large enough we should use the crypto buffer
                        bool result = await ReadAndDecrypt(buffer, start, token, extendedCount)
                                      .ConfigureAwait(false);

                        //if the read/decrypt failed then return 0
                        if (!result)
                        {
                            return(0);
                        }

                        FillCryptoOverflowWithExcess(buffer, 0, count, extendedCount);
                    }
                    else
                    {
                        //Since the provider may not give a buffer large enough we should use the crypto buffer
                        bool result = await ReadAndDecrypt(ReadBuffer.Buffer, 0, token, extendedCount)
                                      .ConfigureAwait(false);

                        //if the read/decrypt failed then return 0
                        if (!result)
                        {
                            return(0);
                        }

                        //Now we must BlockCopy the requested amount into the buffer
                        BufferUtil.QuickUnsafeCopy(ReadBuffer.Buffer, 0, buffer, start, count);

                        FillCryptoOverflowWithExcess(ReadBuffer.Buffer, 0, count, extendedCount);
                    }
                }
                else if (cryptoOverflowSize < count)
                {
                    BufferUtil.QuickUnsafeCopy(CryptoBlockOverflow, CryptoBlockOverflowReadIndex, buffer, start, cryptoOverflowSize);
                    CryptoBlockOverflowReadIndex = CryptoBlockOverflow.Length;                     //set it to last index so we know we have no overflow anymore

                    //Recompute the extended count, since we read some of the cryptooverflow it will have changed potentially
                    int newCount = count - cryptoOverflowSize;
                    extendedCount = ConvertToBlocksizeCount(newCount);

                    //We can't directly read this into the buffer provided
                    //it may not be large enough to handle the blocksize shift
                    //So we must read with the cryptobuffer and then blockcopy the result
                    if (buffer.Length > extendedCount + start + cryptoOverflowSize)
                    {
                        //Read into buffer starting at the start requested + the overflow size
                        bool result = await ReadAndDecrypt(buffer, start + cryptoOverflowSize, token, extendedCount)
                                      .ConfigureAwait(false);

                        //if the read/decrypt failed then return 0
                        if (!result)
                        {
                            return(0);
                        }

                        FillCryptoOverflowWithExcess(buffer, start + cryptoOverflowSize, newCount, extendedCount);
                    }
                    else
                    {
                        //Read into buffer starting at the start requested + the overflow size
                        bool result = await ReadAndDecrypt(ReadBuffer.Buffer, 0, token, extendedCount)
                                      .ConfigureAwait(false);

                        //if the read/decrypt failed then return 0
                        if (!result)
                        {
                            return(0);
                        }

                        //Now we must BlockCopy the requested amount into the buffer
                        BufferUtil.QuickUnsafeCopy(ReadBuffer.Buffer, 0, buffer, start + cryptoOverflowSize, newCount);

                        FillCryptoOverflowWithExcess(ReadBuffer.Buffer, 0, newCount, extendedCount);
                    }
                }
            }

            //never return the extended count, callers above shouldn't have to deal
            //with more than they asked for
            return(count);
        }