X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem.Security.Cryptography%2FCryptoStream.cs;h=3c4890d610590c55ce5a801aee695e5135361b83;hb=8d2921233196ad498b69a443bf4c0f84c0c25d52;hp=5f57549b1f8784c49df1623bfdf09abc4da8abba;hpb=8950e1c0a2342fe645d858b61f83342f603673c4;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 5f57549b1f8..3c4890d6105 --- a/mcs/class/corlib/System.Security.Cryptography/CryptoStream.cs +++ b/mcs/class/corlib/System.Security.Cryptography/CryptoStream.cs @@ -6,62 +6,82 @@ // Sebastien Pouliot (sebastien@ximian.com) // // Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) -// (C) 2004 Novell (http://www.novell.com) +// Copyright (C) 2004-2005, 2007 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 { + [ComVisible (true)] public class CryptoStream : Stream { private Stream _stream; private ICryptoTransform _transform; private CryptoStreamMode _mode; - private byte[] _previousBlock; private byte[] _currentBlock; private bool _disposed; private bool _flushedFinalBlock; - private int _blockSize; private int _partialCount; private bool _endOfStream; - + private byte[] _waitingBlock; private int _waitingCount; - + private byte[] _transformedBlock; private int _transformedPos; private int _transformedCount; - + 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")); + if (mode == CryptoStreamMode.Read) { + if (!stream.CanRead) + throw new ArgumentException (Locale.GetText ("Can't read on stream")); + } else if (mode == CryptoStreamMode.Write) { + if (!stream.CanWrite) + throw new ArgumentException (Locale.GetText ("Can't write on stream")); + } else { + throw new ArgumentException ("mode"); } _stream = stream; _transform = transform; _mode = mode; _disposed = false; if (transform != null) { - if (mode == CryptoStreamMode.Read) - _blockSize = transform.InputBlockSize; - else if (mode == CryptoStreamMode.Write) - _blockSize = transform.OutputBlockSize; - _workingBlock = new byte [_blockSize]; - _currentBlock = new byte [_blockSize]; + if (mode == CryptoStreamMode.Read) { + _currentBlock = new byte [transform.InputBlockSize]; + _workingBlock = new byte [transform.InputBlockSize]; + } + else if (mode == CryptoStreamMode.Write) { + _currentBlock = new byte [transform.OutputBlockSize]; + _workingBlock = new byte [transform.OutputBlockSize]; + } } } - + ~CryptoStream () { Dispose (false); @@ -70,11 +90,11 @@ namespace System.Security.Cryptography { public override bool CanRead { get { return (_mode == CryptoStreamMode.Read); } } - + public override bool CanSeek { get { return false; } } - + public override bool CanWrite { get { return (_mode == CryptoStreamMode.Write); } } @@ -82,29 +102,23 @@ namespace System.Security.Cryptography { public override long Length { get { throw new NotSupportedException ("Length"); } } - + public override long Position { get { throw new NotSupportedException ("Position"); } set { throw new NotSupportedException ("Position"); } } - + public void Clear () { - Dispose (true); - GC.SuppressFinalize (this); // not called in Stream.Dispose + Close (); } - + // 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 (); - - if (_stream != null) - _stream.Close (); + base.Close (); } - + public override int Read ([In,Out] byte[] buffer, int offset, int count) { if (_mode != CryptoStreamMode.Read) { @@ -121,45 +135,44 @@ namespace System.Security.Cryptography { } // yes - buffer.Length will throw a NullReferenceException if buffer is null // but by doing so we match MS implementation - if (offset + count > buffer.Length) { + // re-ordered to avoid integer overflow + if (offset > buffer.Length - count) { throw new ArgumentException ("(offset+count)", Locale.GetText ("buffer overflow")); } - // for some strange reason Object_disposedException isn't throw - // instead we get a ArgumentNullException (probably from an internal method) + // for some strange reason ObjectDisposedException isn't throw if (_workingBlock == null) { - throw new ArgumentNullException ( - Locale.GetText ("object _disposed")); + return 0; } - + int result = 0; if ((count == 0) || ((_transformedPos == _transformedCount) && (_endOfStream))) return result; - + if (_waitingBlock == null) { - _transformedBlock = new byte [_blockSize << 2]; + _transformedBlock = new byte [_transform.OutputBlockSize << 2]; _transformedPos = 0; _transformedCount = 0; - _waitingBlock = new byte [_blockSize]; + _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 < _blockSize) { + if (length < _transform.InputBlockSize) { int transformed = 0; - + // load a new block - _workingCount = _stream.Read (_workingBlock, 0, _workingBlock.Length); - _endOfStream = (_workingCount < _blockSize); - + _workingCount = _stream.Read (_workingBlock, 0, _transform.InputBlockSize); + _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; @@ -168,49 +181,54 @@ namespace System.Security.Cryptography { 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; - Array.Copy (input, 0, _transformedBlock, _transformedCount, input.Length); - // zeroize this last block - Array.Clear (input, 0, input.Length); + if (!_flushedFinalBlock) { + 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); + _flushedFinalBlock = true; + } } - + length += transformed; _transformedCount += transformed; } // compaction - if (_transformedPos > _blockSize) { + if (_transformedPos > _transform.OutputBlockSize) { Buffer.BlockCopy (_transformedBlock, _transformedPos, _transformedBlock, 0, length); _transformedCount -= _transformedPos; _transformedPos = 0; } - + length = ((count < length) ? count : length); - Buffer.BlockCopy (_transformedBlock, _transformedPos, buffer, offset, length); - _transformedPos += length; - - result += length; - offset += 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 != _blockSize) || (_endOfStream)) { + 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) { @@ -225,54 +243,60 @@ namespace System.Security.Cryptography { throw new ArgumentOutOfRangeException ("count", Locale.GetText ("negative")); } - if (offset + count > buffer.Length) { + // re-ordered to avoid integer overflow + if (offset > buffer.Length - count) { throw new ArgumentException ("(offset+count)", Locale.GetText ("buffer overflow")); } - + + if (_stream == null) + throw new ArgumentNullException ("inner stream was diposed"); + + int buffer_length = count; + // partial block (in progress) - if ((_partialCount > 0) && (_partialCount != _blockSize)) { - int remainder = _blockSize - _partialCount; + 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 == _blockSize) { - _partialCount = 0; + if (_partialCount == _transform.InputBlockSize) { // use partial block to avoid (re)allocation - _transform.TransformBlock (_workingBlock, 0, _blockSize, _currentBlock, 0); - _stream.Write (_currentBlock, 0, _currentBlock.Length); + 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) / _blockSize); - if (((_partialCount + count) % _blockSize) == 0) // partial block ? - numBlock--; // no then reduce - int multiSize = (numBlock * _blockSize); - if (numBlock > 0) { - byte[] multiBlocks = new byte [multiSize]; - _transform.TransformBlock (buffer, offset, multiSize, multiBlocks, 0); - _stream.Write (multiBlocks, 0, multiSize); - // copy last block into _currentBlock - _partialCount = count - multiSize; - Array.Copy (buffer, offset + multiSize, _workingBlock, 0, _partialCount); + // get the biggest multiple of InputBlockSize in count (without mul or div) + int size = (count & ~(_transform.InputBlockSize - 1)); + int rem = (count & (_transform.InputBlockSize - 1)); + // avoid reallocating memory at each call (reuse same buffer whenever possible) + int sizeWorkingBlock = (1 + size / _transform.InputBlockSize) * _transform.OutputBlockSize; + if (_workingBlock.Length < sizeWorkingBlock) { + Array.Clear (_workingBlock, 0, _workingBlock.Length); + _workingBlock = new byte [sizeWorkingBlock]; } - else { - Array.Copy (buffer, offset, _workingBlock, _partialCount, count); - _partialCount += count; + + if (size > 0) { + int len = _transform.TransformBlock (buffer, offset, size, _workingBlock, 0); + _stream.Write (_workingBlock, 0, len); } + + if (rem > 0) + Buffer.BlockCopy (buffer, buffer_length - rem, _workingBlock, 0, rem); + _partialCount = rem; count = 0; // the last block, if any, is in _workingBlock - } - else { - int len = Math.Min (_blockSize - _partialCount, count); - Array.Copy (buffer, bufferPos, _workingBlock, _partialCount, len); + } else { + int len = Math.Min (_transform.InputBlockSize - _partialCount, count); + Buffer.BlockCopy (buffer, bufferPos, _workingBlock, _partialCount, len); bufferPos += len; _partialCount += len; count -= len; @@ -281,34 +305,33 @@ namespace System.Security.Cryptography { } } } - + 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")); - } - + if (_flushedFinalBlock) + throw new NotSupportedException (Locale.GetText ("This method cannot be called twice.")); + if (_disposed) + throw new NotSupportedException (Locale.GetText ("CryptoStream was disposed.")); + _flushedFinalBlock = true; byte[] finalBuffer = _transform.TransformFinalBlock (_workingBlock, 0, _partialCount); - if (_stream != null) { + if (_stream != null && _mode == CryptoStreamMode.Write) { _stream.Write (finalBuffer, 0, finalBuffer.Length); + } + if (_stream is CryptoStream) { + // for cascading crypto streams + (_stream as CryptoStream).FlushFinalBlock (); + } else { _stream.Flush (); } // zeroize Array.Clear (finalBuffer, 0, finalBuffer.Length); } - + public override long Seek (long offset, SeekOrigin origin) { throw new NotSupportedException ("Seek"); @@ -319,23 +342,28 @@ namespace System.Security.Cryptography { { throw new NotSupportedException ("SetLength"); } - - protected virtual void Dispose (bool disposing) + + protected override void Dispose (bool disposing) { if (!_disposed) { + if (disposing) { + if (!_flushedFinalBlock) { + FlushFinalBlock (); + } + + if (_stream != null) + _stream.Close (); + } _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 (_previousBlock != null) - Array.Clear (_previousBlock, 0, _previousBlock.Length); if (disposing) { _stream = null; _workingBlock = null; _currentBlock = null; - _previousBlock = null; } } }