2 // System.Security.Cryptography CryptoStream.cs
5 // Thomas Neidhart (tome@sbox.tugraz.at)
6 // Sebastien Pouliot (sebastien@ximian.com)
8 // Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Globalization;
33 using System.Runtime.InteropServices;
35 namespace System.Security.Cryptography {
40 public class CryptoStream : Stream {
41 private Stream _stream;
42 private ICryptoTransform _transform;
43 private CryptoStreamMode _mode;
44 private byte[] _currentBlock;
45 private bool _disposed;
46 private bool _flushedFinalBlock;
47 private int _partialCount;
48 private bool _endOfStream;
50 private byte[] _waitingBlock;
51 private int _waitingCount;
53 private byte[] _transformedBlock;
54 private int _transformedPos;
55 private int _transformedCount;
57 private byte[] _workingBlock;
58 private int _workingCount;
60 public CryptoStream (Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
62 if ((mode == CryptoStreamMode.Read) && (!stream.CanRead)) {
63 throw new ArgumentException (
64 Locale.GetText ("Can't read on stream"));
66 if ((mode == CryptoStreamMode.Write) && (!stream.CanWrite)) {
67 throw new ArgumentException (
68 Locale.GetText ("Can't write on stream"));
71 _transform = transform;
74 if (transform != null) {
75 _workingBlock = new byte [transform.InputBlockSize];
76 if (mode == CryptoStreamMode.Read)
77 _currentBlock = new byte [transform.InputBlockSize];
78 else if (mode == CryptoStreamMode.Write)
79 _currentBlock = new byte [transform.OutputBlockSize];
88 public override bool CanRead {
89 get { return (_mode == CryptoStreamMode.Read); }
92 public override bool CanSeek {
96 public override bool CanWrite {
97 get { return (_mode == CryptoStreamMode.Write); }
100 public override long Length {
101 get { throw new NotSupportedException ("Length"); }
104 public override long Position {
105 get { throw new NotSupportedException ("Position"); }
106 set { throw new NotSupportedException ("Position"); }
112 GC.SuppressFinalize (this); // not called in Stream.Dispose
115 // LAMESPEC: A CryptoStream can be close in read mode
116 public override void Close ()
118 // only flush in write mode (bugzilla 46143)
119 if ((!_flushedFinalBlock) && (_mode == CryptoStreamMode.Write))
126 public override int Read ([In,Out] byte[] buffer, int offset, int count)
128 if (_mode != CryptoStreamMode.Read) {
129 throw new NotSupportedException (
130 Locale.GetText ("not in Read mode"));
133 throw new ArgumentOutOfRangeException ("offset",
134 Locale.GetText ("negative"));
137 throw new ArgumentOutOfRangeException ("count",
138 Locale.GetText ("negative"));
140 // yes - buffer.Length will throw a NullReferenceException if buffer is null
141 // but by doing so we match MS implementation
142 // re-ordered to avoid integer overflow
143 if (offset > buffer.Length - count) {
144 throw new ArgumentException ("(offset+count)",
145 Locale.GetText ("buffer overflow"));
147 // for some strange reason ObjectDisposedException isn't throw
148 // instead we get a ArgumentNullException (probably from an internal method)
149 if (_workingBlock == null) {
150 throw new ArgumentNullException (
151 Locale.GetText ("object _disposed"));
155 if ((count == 0) || ((_transformedPos == _transformedCount) && (_endOfStream)))
158 if (_waitingBlock == null) {
159 _transformedBlock = new byte [_transform.OutputBlockSize << 2];
161 _transformedCount = 0;
162 _waitingBlock = new byte [_transform.InputBlockSize];
163 _waitingCount = _stream.Read (_waitingBlock, 0, _waitingBlock.Length);
167 // transformed but not yet returned
168 int length = (_transformedCount - _transformedPos);
170 // need more data - at least one full block must be available if we haven't reach the end of the stream
171 if (length < _transform.InputBlockSize) {
175 _workingCount = _stream.Read (_workingBlock, 0, _workingBlock.Length);
176 _endOfStream = (_workingCount < _transform.InputBlockSize);
179 // transform the waiting block
180 transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount);
182 // transfer temporary to waiting
183 Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount);
184 _waitingCount = _workingCount;
187 if (_workingCount > 0) {
188 // transform the waiting block
189 transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount);
191 // transfer temporary to waiting
192 Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount);
193 _waitingCount = _workingCount;
195 length += transformed;
196 _transformedCount += transformed;
198 byte[] input = _transform.TransformFinalBlock (_waitingBlock, 0, _waitingCount);
199 transformed = input.Length;
200 Buffer.BlockCopy (input, 0, _transformedBlock, _transformedCount, input.Length);
201 // zeroize this last block
202 Array.Clear (input, 0, input.Length);
205 length += transformed;
206 _transformedCount += transformed;
209 if (_transformedPos > _transform.InputBlockSize) {
210 Buffer.BlockCopy (_transformedBlock, _transformedPos, _transformedBlock, 0, length);
211 _transformedCount -= _transformedPos;
215 length = ((count < length) ? count : length);
217 Buffer.BlockCopy (_transformedBlock, _transformedPos, buffer, offset, length);
218 _transformedPos += length;
225 // there may not be enough data in the stream for a
227 if (((length != _transform.InputBlockSize) && (_waitingCount != _transform.InputBlockSize)) || (_endOfStream)) {
228 count = 0; // no more data can be read
235 public override void Write (byte[] buffer, int offset, int count)
237 if (_mode != CryptoStreamMode.Write) {
238 throw new NotSupportedException (
239 Locale.GetText ("not in Write mode"));
242 throw new ArgumentOutOfRangeException ("offset",
243 Locale.GetText ("negative"));
246 throw new ArgumentOutOfRangeException ("count",
247 Locale.GetText ("negative"));
249 // re-ordered to avoid integer overflow
250 if (offset > buffer.Length - count) {
251 throw new ArgumentException ("(offset+count)",
252 Locale.GetText ("buffer overflow"));
255 // partial block (in progress)
256 if ((_partialCount > 0) && (_partialCount != _transform.InputBlockSize)) {
257 int remainder = _transform.InputBlockSize - _partialCount;
258 remainder = ((count < remainder) ? count : remainder);
259 Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, remainder);
260 _partialCount += remainder;
265 int bufferPos = offset;
267 if (_partialCount == _transform.InputBlockSize) {
268 // use partial block to avoid (re)allocation
269 int len = _transform.TransformBlock (_workingBlock, 0, _partialCount, _currentBlock, 0);
270 _stream.Write (_currentBlock, 0, len);
275 if (_transform.CanTransformMultipleBlocks) {
276 // transform all except the last block (which may be the last block
277 // of the stream and require TransformFinalBlock)
278 int numBlock = ((_partialCount + count) / _transform.InputBlockSize);
279 int multiSize = (numBlock * _transform.InputBlockSize);
281 byte[] multiBlocks = new byte [multiSize];
282 int len = _transform.TransformBlock (buffer, offset, multiSize, multiBlocks, 0);
283 _stream.Write (multiBlocks, 0, len);
284 // copy last block into _currentBlock
285 _partialCount = count - multiSize;
286 Buffer.BlockCopy (buffer, offset + multiSize, _workingBlock, 0, _partialCount);
289 Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, count);
290 _partialCount += count;
292 count = 0; // the last block, if any, is in _workingBlock
295 int len = Math.Min (_transform.InputBlockSize - _partialCount, count);
296 Buffer.BlockCopy (buffer, bufferPos, _workingBlock, _partialCount, len);
298 _partialCount += len;
300 // here block may be full, but we wont TransformBlock it until next iteration
301 // so that the last block will be called in FlushFinalBlock using TransformFinalBlock
306 public override void Flush ()
312 public void FlushFinalBlock ()
314 if (_flushedFinalBlock) {
315 throw new NotSupportedException (
316 Locale.GetText ("This method cannot be called twice."));
318 if (_mode != CryptoStreamMode.Write) {
319 throw new NotSupportedException (
320 Locale.GetText ("cannot flush a non-writeable CryptoStream"));
323 _flushedFinalBlock = true;
324 byte[] finalBuffer = _transform.TransformFinalBlock (_workingBlock, 0, _partialCount);
325 if (_stream != null) {
326 _stream.Write (finalBuffer, 0, finalBuffer.Length);
327 if (_stream is CryptoStream) {
328 // for cascading crypto streams
329 (_stream as CryptoStream).FlushFinalBlock ();
334 Array.Clear (finalBuffer, 0, finalBuffer.Length);
337 public override long Seek (long offset, SeekOrigin origin)
339 throw new NotSupportedException ("Seek");
342 // LAMESPEC: Exception NotSupportedException not documented
343 public override void SetLength (long value)
345 throw new NotSupportedException ("SetLength");
349 protected override void Dispose (bool disposing)
351 protected virtual void Dispose (bool disposing)
356 // always cleared for security reason
357 if (_workingBlock != null)
358 Array.Clear (_workingBlock, 0, _workingBlock.Length);
359 if (_currentBlock != null)
360 Array.Clear (_currentBlock, 0, _currentBlock.Length);
363 _workingBlock = null;
364 _currentBlock = null;