#region Copyright 2010-2011 by Roger Knapp, Licensed under the Apache License, Version 2.0
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Security.Cryptography;
using CSharpTest.Net.IO;
using System.IO;
namespace CSharpTest.Net.Crypto
{
/// Represents a writtable stream for computing the hash value without retaining the data
public sealed class HashStream : AggregateStream
{
private static readonly byte[] EmptyBytes = new byte[0];
private readonly HashAlgorithm _algo;
private CryptoStream _hashStream;
private bool _closed;
/// Represents a writtable stream for computing the hash value without retaining the data
public HashStream(HashAlgorithm algo)
: this(algo, Stream.Null)
{ }
/// Wraps an existing stream while computing a hash on all bytes read from/written to the stream
public HashStream(HashAlgorithm algo, Stream underlyingStream)
: base(underlyingStream)
{
_algo = algo;
_hashStream = new CryptoStream(Stream.Null, _algo, CryptoStreamMode.Write);
}
///
/// When overridden in a derived class, gets a value indicating whether the current stream supports writing.
///
public override bool CanWrite { get { return true; } }
///
/// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current
/// position within this stream by the number of bytes written.
///
public override void Write(byte[] buffer, int offset, int count)
{
_hashStream.Write(buffer, offset, count);
base.Write(buffer, offset, count);
}
///
/// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current
/// position within this stream by the number of bytes written.
///
public override void WriteByte(byte value)
{
_hashStream.WriteByte(value);
base.WriteByte(value);
}
///
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position
/// within the stream by the number of bytes read.
///
public override int Read(byte[] buffer, int offset, int count)
{
int amount = base.Read(buffer, offset, count);
_hashStream.Write(buffer, offset, amount);
return amount;
}
///
/// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
///
public override int ReadByte()
{
int value = base.ReadByte();
if(value >= 0)
_hashStream.WriteByte((byte)value);
return value;
}
///
/// Releases the unmanaged resources used by the and optionally releases the managed resources.
///
protected override void Dispose(bool disposing)
{
_closed = true;
base.Dispose(disposing);
}
///
/// Can be called once, and only once, to obtain the hash generated while reading/writing. After this is
/// called the stream will reset the hash and start computing a new hash value.
///
public Hash FinalizeHash()
{
try
{
_hashStream.FlushFinalBlock();
return Hash.FromBytes(_algo.Hash);
}
finally
{
_algo.Initialize();
_hashStream = new CryptoStream(Stream.Null, _algo, CryptoStreamMode.Write);
}
}
/// Represents a writtable stream for computing the hash value without retaining the data
/// The hash code computed by the series of Write(...) calls
public new Hash Close()
{
if (_closed)
throw new ObjectDisposedException(GetType().FullName);
try { return FinalizeHash(); }
finally
{
_closed = true;
base.Close();
}
}
///
/// Change the underlying stream that is being written to / read from without affecting the current hash
///
public void ChangeStream(Stream stream)
{
base.Stream = stream ?? Stream.Null;
}
}
}