X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem.Security.Cryptography%2FCryptoStream.cs;h=28511eec1b188f35b13439e594222453907814e5;hb=61fdcc0f234667d585e8d2dc7cee2d9e954af01b;hp=0f4df3c6cf3cbb2cf536960da74f82f0ca9ce5be;hpb=053b98da026216f1e62e177e019bfbb45b169fc6;p=mono.git diff --git a/mcs/class/corlib/System.Security.Cryptography/CryptoStream.cs b/mcs/class/corlib/System.Security.Cryptography/CryptoStream.cs old mode 100755 new mode 100644 index 0f4df3c6cf3..28511eec1b1 --- a/mcs/class/corlib/System.Security.Cryptography/CryptoStream.cs +++ b/mcs/class/corlib/System.Security.Cryptography/CryptoStream.cs @@ -1,137 +1,369 @@ // // System.Security.Cryptography CryptoStream.cs // -// Author: -// Thomas Neidhart (tome@sbox.tugraz.at) -// Sebastien Pouliot (spouliot@motus.com) +// Authors: +// Thomas Neidhart (tome@sbox.tugraz.at) +// Sebastien Pouliot (sebastien@ximian.com) // -// Portions (C) 2002 Motus Technologies Inc. (http://www.motus.com) +// Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) +// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; +using System.Globalization; using System.IO; +using System.Runtime.InteropServices; namespace System.Security.Cryptography { -public class CryptoStream : Stream { - private Stream _stream; - private ICryptoTransform _transform; - private CryptoStreamMode _mode; - - public CryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode) - { - _stream = stream; - _transform = transform; - _mode = mode; - } +#if NET_2_0 + [ComVisible (true)] +#endif + public class CryptoStream : Stream { + private Stream _stream; + private ICryptoTransform _transform; + private CryptoStreamMode _mode; + private byte[] _currentBlock; + private bool _disposed; + private bool _flushedFinalBlock; + private int _partialCount; + private bool _endOfStream; - ~CryptoStream () - { - Dispose (false); - } - - public override bool CanRead { - get { return (_mode == CryptoStreamMode.Read); } - } + private byte[] _waitingBlock; + private int _waitingCount; - public override bool CanSeek { - get { return false; } - } + private byte[] _transformedBlock; + private int _transformedPos; + private int _transformedCount; - public override bool CanWrite { - get { return (_mode == CryptoStreamMode.Write); } - } - - public override long Length { - get { - throw new NotSupportedException("Length property not supported by CryptoStream"); + private byte[] _workingBlock; + private int _workingCount; + + public CryptoStream (Stream stream, ICryptoTransform transform, CryptoStreamMode mode) + { + if ((mode == CryptoStreamMode.Read) && (!stream.CanRead)) { + throw new ArgumentException ( + Locale.GetText ("Can't read on stream")); + } + if ((mode == CryptoStreamMode.Write) && (!stream.CanWrite)) { + throw new ArgumentException ( + Locale.GetText ("Can't write on stream")); + } + _stream = stream; + _transform = transform; + _mode = mode; + _disposed = false; + if (transform != null) { + _workingBlock = new byte [transform.InputBlockSize]; + if (mode == CryptoStreamMode.Read) + _currentBlock = new byte [transform.InputBlockSize]; + else if (mode == CryptoStreamMode.Write) + _currentBlock = new byte [transform.OutputBlockSize]; + } } - } - public override long Position { - get { - throw new NotSupportedException("Position property not supported by CryptoStream"); + ~CryptoStream () + { + Dispose (false); } - set { - throw new NotSupportedException("Position property not supported by CryptoStream"); + + public override bool CanRead { + get { return (_mode == CryptoStreamMode.Read); } } - } - public void Clear () - { - Dispose (true); - } + public override bool CanSeek { + get { return false; } + } - [MonoTODO("Limited support for HMACSHA1")] - public override void Close () - { - if (_mode != CryptoStreamMode.Write) - throw new NotSupportedException (); - // TODO: limited implemention for HMACSHA1 - byte[] buffer = new byte [0]; - _transform.TransformFinalBlock (buffer, 0, 0); - if (_stream != null) - _stream.Close(); - } + public override bool CanWrite { + get { return (_mode == CryptoStreamMode.Write); } + } + + public override long Length { + get { throw new NotSupportedException ("Length"); } + } - [MonoTODO] - public override int Read (byte[] buffer, int offset, int count) - { - if (_mode != CryptoStreamMode.Read) - throw new NotSupportedException (); - if ((offset < 0) || (count < 0)) - throw new ArgumentOutOfRangeException (); - if (offset + count > buffer.Length) - throw new ArgumentException (); - // TODO: implement - return 0; - } + public override long Position { + get { throw new NotSupportedException ("Position"); } + set { throw new NotSupportedException ("Position"); } + } - [MonoTODO("Limited support for HMACSHA1")] - public override void Write (byte[] buffer, int offset, int count) - { - if (_mode != CryptoStreamMode.Write) - throw new NotSupportedException (); - if ((offset < 0) || (count < 0)) - throw new ArgumentOutOfRangeException (); - if (offset + count > buffer.Length) - throw new ArgumentException (); - // TODO: limited implemention for HMACSHA1 - byte[] output = new byte [count]; - _transform.TransformBlock (buffer, offset, count, output, 0); - } + public void Clear () + { + Dispose (true); + GC.SuppressFinalize (this); // not called in Stream.Dispose + } - [MonoTODO] - public override void Flush () - { - if (_mode != CryptoStreamMode.Write) - throw new NotSupportedException ("cannot flush a non-writeable CryptoStream"); - // TODO: implement - } + // LAMESPEC: A CryptoStream can be close in read mode + public override void Close () + { + // only flush in write mode (bugzilla 46143) + if ((!_flushedFinalBlock) && (_mode == CryptoStreamMode.Write)) + FlushFinalBlock (); - [MonoTODO] - public void FlushFinalBlock () - { - if (_mode != CryptoStreamMode.Write) - throw new NotSupportedException ("cannot flush a non-writeable CryptoStream"); - // TODO: implement - } + if (_stream != null) + _stream.Close (); + } - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException ("cannot Seek a CryptoStream"); - } - - // LAMESPEC: Exception NotSupportedException not documented - public override void SetLength (long value) - { - throw new NotSupportedException ("cannot SetLength a CryptoStream"); - } + public override int Read ([In,Out] byte[] buffer, int offset, int count) + { + if (_mode != CryptoStreamMode.Read) { + throw new NotSupportedException ( + Locale.GetText ("not in Read mode")); + } + if (offset < 0) { + throw new ArgumentOutOfRangeException ("offset", + Locale.GetText ("negative")); + } + if (count < 0) { + throw new ArgumentOutOfRangeException ("count", + Locale.GetText ("negative")); + } + // yes - buffer.Length will throw a NullReferenceException if buffer is null + // but by doing so we match MS implementation + // re-ordered to avoid integer overflow + if (offset > buffer.Length - count) { + throw new ArgumentException ("(offset+count)", + Locale.GetText ("buffer overflow")); + } + // for some strange reason ObjectDisposedException isn't throw + // instead we get a ArgumentNullException (probably from an internal method) + if (_workingBlock == null) { + throw new ArgumentNullException ( + Locale.GetText ("object _disposed")); + } + + int result = 0; + if ((count == 0) || ((_transformedPos == _transformedCount) && (_endOfStream))) + return result; + + if (_waitingBlock == null) { + _transformedBlock = new byte [_transform.OutputBlockSize << 2]; + _transformedPos = 0; + _transformedCount = 0; + _waitingBlock = new byte [_transform.InputBlockSize]; + _waitingCount = _stream.Read (_waitingBlock, 0, _waitingBlock.Length); + } + + while (count > 0) { + // transformed but not yet returned + int length = (_transformedCount - _transformedPos); + + // need more data - at least one full block must be available if we haven't reach the end of the stream + if (length < _transform.InputBlockSize) { + int transformed = 0; - protected virtual void Dispose (bool disposing) - { + // load a new block + _workingCount = _stream.Read (_workingBlock, 0, _workingBlock.Length); + _endOfStream = (_workingCount < _transform.InputBlockSize); + + if (!_endOfStream) { + // transform the waiting block + transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount); + + // transfer temporary to waiting + Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount); + _waitingCount = _workingCount; + } + else { + if (_workingCount > 0) { + // transform the waiting block + transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount); + + // transfer temporary to waiting + Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount); + _waitingCount = _workingCount; + + length += transformed; + _transformedCount += transformed; + } + byte[] input = _transform.TransformFinalBlock (_waitingBlock, 0, _waitingCount); + transformed = input.Length; + Buffer.BlockCopy (input, 0, _transformedBlock, _transformedCount, input.Length); + // zeroize this last block + Array.Clear (input, 0, input.Length); + } + + length += transformed; + _transformedCount += transformed; + } + // compaction + if (_transformedPos > _transform.InputBlockSize) { + Buffer.BlockCopy (_transformedBlock, _transformedPos, _transformedBlock, 0, length); + _transformedCount -= _transformedPos; + _transformedPos = 0; + } + + length = ((count < length) ? count : length); + if (length > 0) { + Buffer.BlockCopy (_transformedBlock, _transformedPos, buffer, offset, length); + _transformedPos += length; + + result += length; + offset += length; + count -= length; + } + + // there may not be enough data in the stream for a + // complete block + if (((length != _transform.InputBlockSize) && (_waitingCount != _transform.InputBlockSize)) || (_endOfStream)) { + count = 0; // no more data can be read + } + } + + return result; + } + + public override void Write (byte[] buffer, int offset, int count) + { + if (_mode != CryptoStreamMode.Write) { + throw new NotSupportedException ( + Locale.GetText ("not in Write mode")); + } + if (offset < 0) { + throw new ArgumentOutOfRangeException ("offset", + Locale.GetText ("negative")); + } + if (count < 0) { + throw new ArgumentOutOfRangeException ("count", + Locale.GetText ("negative")); + } + // re-ordered to avoid integer overflow + if (offset > buffer.Length - count) { + throw new ArgumentException ("(offset+count)", + Locale.GetText ("buffer overflow")); + } + + // partial block (in progress) + if ((_partialCount > 0) && (_partialCount != _transform.InputBlockSize)) { + int remainder = _transform.InputBlockSize - _partialCount; + remainder = ((count < remainder) ? count : remainder); + Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, remainder); + _partialCount += remainder; + offset += remainder; + count -= remainder; + } + + int bufferPos = offset; + while (count > 0) { + if (_partialCount == _transform.InputBlockSize) { + // use partial block to avoid (re)allocation + int len = _transform.TransformBlock (_workingBlock, 0, _partialCount, _currentBlock, 0); + _stream.Write (_currentBlock, 0, len); + // reset + _partialCount = 0; + } + + if (_transform.CanTransformMultipleBlocks) { + // transform all except the last block (which may be the last block + // of the stream and require TransformFinalBlock) + int numBlock = ((_partialCount + count) / _transform.InputBlockSize); + int multiSize = (numBlock * _transform.InputBlockSize); + if (numBlock > 0) { + byte[] multiBlocks = new byte [multiSize]; + int len = _transform.TransformBlock (buffer, offset, multiSize, multiBlocks, 0); + _stream.Write (multiBlocks, 0, len); + // copy last block into _currentBlock + _partialCount = count - multiSize; + Buffer.BlockCopy (buffer, offset + multiSize, _workingBlock, 0, _partialCount); + } + else { + Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, count); + _partialCount += count; + } + count = 0; // the last block, if any, is in _workingBlock + } + else { + int len = Math.Min (_transform.InputBlockSize - _partialCount, count); + Buffer.BlockCopy (buffer, bufferPos, _workingBlock, _partialCount, len); + bufferPos += len; + _partialCount += len; + count -= len; + // here block may be full, but we wont TransformBlock it until next iteration + // so that the last block will be called in FlushFinalBlock using TransformFinalBlock + } + } + } + + public override void Flush () + { + if (_stream != null) + _stream.Flush (); + } + + public void FlushFinalBlock () + { + if (_flushedFinalBlock) { + throw new NotSupportedException ( + Locale.GetText ("This method cannot be called twice.")); + } + if (_mode != CryptoStreamMode.Write) { + throw new NotSupportedException ( + Locale.GetText ("cannot flush a non-writeable CryptoStream")); + } + + _flushedFinalBlock = true; + byte[] finalBuffer = _transform.TransformFinalBlock (_workingBlock, 0, _partialCount); + if (_stream != null) { + _stream.Write (finalBuffer, 0, finalBuffer.Length); + if (_stream is CryptoStream) { + // for cascading crypto streams + (_stream as CryptoStream).FlushFinalBlock (); + } + _stream.Flush (); + } + // zeroize + Array.Clear (finalBuffer, 0, finalBuffer.Length); + } + + public override long Seek (long offset, SeekOrigin origin) + { + throw new NotSupportedException ("Seek"); + } + + // LAMESPEC: Exception NotSupportedException not documented + public override void SetLength (long value) + { + throw new NotSupportedException ("SetLength"); + } + +#if NET_2_0 + protected override void Dispose (bool disposing) +#else + protected virtual void Dispose (bool disposing) +#endif + { + if (!_disposed) { + _disposed = true; + // always cleared for security reason + if (_workingBlock != null) + Array.Clear (_workingBlock, 0, _workingBlock.Length); + if (_currentBlock != null) + Array.Clear (_currentBlock, 0, _currentBlock.Length); + if (disposing) { + _stream = null; + _workingBlock = null; + _currentBlock = null; + } + } + } } - -} // CryptoStream - -} // System.Security.Cryptography +}