// // Helpers // internal static OutputSpanStream Stream(int size, int pad = 0) { var s = new OutputSpanStream(new byte[size + pad]); for (var i = 0; i < pad; i++) { s.WriteByte(0xCC); } return(s); }
public override int Read(byte[] buffer, int offset, int count) { // Start new block if (_blockMac == null) { _blockMac = _baseStream.ReadExact(32); // To avoid allocations we read into the cache buffer since it's unused at this point and parse from it _baseStream.ReadExact(_buffer, 0, 4); _blockSize = BitConverter.ToInt32(_buffer, 0); // End of stream if (_blockSize == 0) { return(0); } _blockReadPointer = 0; _bufferActualSize = 0; _bufferReadPointer = 0; // Start the MAC calculation for the new block _hmac = new HMACSHA256(Util.ComputeBlockHmacKey(_hmacKey, _blockIndex)); // To avoid allocation we write into the cache buffer and then use it for hashing var forHashing = new OutputSpanStream(_buffer); forHashing.WriteUInt64(_blockIndex); forHashing.WriteInt32(_blockSize); _hmac.TransformBlock(_buffer, 0, forHashing.Position, null, 0); } // Read next piece into the buffer if (_bufferReadPointer >= _bufferActualSize) { var toRead = Math.Min(_buffer.Length, _blockSize - _blockReadPointer); _bufferActualSize = _baseStream.Read(_buffer, 0, toRead); _bufferReadPointer = 0; // Hash the read portion _hmac.TransformBlock(_buffer, 0, _bufferActualSize, null, 0); } var toCopy = Math.Min(count, _bufferActualSize - _bufferReadPointer); Array.Copy(_buffer, _bufferReadPointer, buffer, offset, toCopy); _bufferReadPointer += toCopy; _blockReadPointer += toCopy; // End of block if (_blockReadPointer >= _blockSize) { // Finalize MAC _hmac.TransformFinalBlock(Array.Empty <byte>(), 0, 0); var storedMac = _blockMac; var computedMac = _hmac.Hash; _blockIndex++; _blockMac = null; _hmac.Dispose(); _hmac = null; if (!Crypto.AreEqual(storedMac, computedMac)) { throw new InternalErrorException("Corrupted, block MAC doesn't match"); } } return(toCopy); }