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, 2007 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 {
38 public class CryptoStream : Stream {
39 private Stream _stream;
40 private ICryptoTransform _transform;
41 private CryptoStreamMode _mode;
42 private byte[] _currentBlock;
43 private bool _disposed;
44 private bool _flushedFinalBlock;
45 private int _partialCount;
46 private bool _endOfStream;
48 private byte[] _waitingBlock;
49 private int _waitingCount;
51 private byte[] _transformedBlock;
52 private int _transformedPos;
53 private int _transformedCount;
55 private byte[] _workingBlock;
56 private int _workingCount;
58 public CryptoStream (Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
60 if ((mode == CryptoStreamMode.Read) && (!stream.CanRead)) {
61 throw new ArgumentException (
62 Locale.GetText ("Can't read on stream"));
64 if ((mode == CryptoStreamMode.Write) && (!stream.CanWrite)) {
65 throw new ArgumentException (
66 Locale.GetText ("Can't write on stream"));
69 _transform = transform;
72 if (transform != null) {
73 if (mode == CryptoStreamMode.Read) {
74 _currentBlock = new byte [transform.InputBlockSize];
75 _workingBlock = new byte [transform.InputBlockSize];
77 else if (mode == CryptoStreamMode.Write) {
78 _currentBlock = new byte [transform.OutputBlockSize];
79 _workingBlock = new byte [transform.OutputBlockSize];
89 public override bool CanRead {
90 get { return (_mode == CryptoStreamMode.Read); }
93 public override bool CanSeek {
97 public override bool CanWrite {
98 get { return (_mode == CryptoStreamMode.Write); }
101 public override long Length {
102 get { throw new NotSupportedException ("Length"); }
105 public override long Position {
106 get { throw new NotSupportedException ("Position"); }
107 set { throw new NotSupportedException ("Position"); }
115 // LAMESPEC: A CryptoStream can be close in read mode
116 public override void Close ()
121 public override int Read ([In,Out] byte[] buffer, int offset, int count)
123 if (_mode != CryptoStreamMode.Read) {
124 throw new NotSupportedException (
125 Locale.GetText ("not in Read mode"));
128 throw new ArgumentOutOfRangeException ("offset",
129 Locale.GetText ("negative"));
132 throw new ArgumentOutOfRangeException ("count",
133 Locale.GetText ("negative"));
135 // yes - buffer.Length will throw a NullReferenceException if buffer is null
136 // but by doing so we match MS implementation
137 // re-ordered to avoid integer overflow
138 if (offset > buffer.Length - count) {
139 throw new ArgumentException ("(offset+count)",
140 Locale.GetText ("buffer overflow"));
142 // for some strange reason ObjectDisposedException isn't throw
143 if (_workingBlock == null) {
148 if ((count == 0) || ((_transformedPos == _transformedCount) && (_endOfStream)))
151 if (_waitingBlock == null) {
152 _transformedBlock = new byte [_transform.OutputBlockSize << 2];
154 _transformedCount = 0;
155 _waitingBlock = new byte [_transform.InputBlockSize];
156 _waitingCount = _stream.Read (_waitingBlock, 0, _waitingBlock.Length);
160 // transformed but not yet returned
161 int length = (_transformedCount - _transformedPos);
163 // need more data - at least one full block must be available if we haven't reach the end of the stream
164 if (length < _transform.InputBlockSize) {
168 _workingCount = _stream.Read (_workingBlock, 0, _transform.InputBlockSize);
169 _endOfStream = (_workingCount < _transform.InputBlockSize);
172 // transform the waiting block
173 transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount);
175 // transfer temporary to waiting
176 Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount);
177 _waitingCount = _workingCount;
180 if (_workingCount > 0) {
181 // transform the waiting block
182 transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount);
184 // transfer temporary to waiting
185 Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount);
186 _waitingCount = _workingCount;
188 length += transformed;
189 _transformedCount += transformed;
191 if (!_flushedFinalBlock) {
192 byte[] input = _transform.TransformFinalBlock (_waitingBlock, 0, _waitingCount);
193 transformed = input.Length;
194 Buffer.BlockCopy (input, 0, _transformedBlock, _transformedCount, input.Length);
195 // zeroize this last block
196 Array.Clear (input, 0, input.Length);
197 _flushedFinalBlock = true;
201 length += transformed;
202 _transformedCount += transformed;
205 if (_transformedPos > _transform.OutputBlockSize) {
206 Buffer.BlockCopy (_transformedBlock, _transformedPos, _transformedBlock, 0, length);
207 _transformedCount -= _transformedPos;
211 length = ((count < length) ? count : length);
213 Buffer.BlockCopy (_transformedBlock, _transformedPos, buffer, offset, length);
214 _transformedPos += length;
221 // there may not be enough data in the stream for a
223 if (((length != _transform.InputBlockSize) && (_waitingCount != _transform.InputBlockSize)) || (_endOfStream)) {
224 count = 0; // no more data can be read
231 public override void Write (byte[] buffer, int offset, int count)
233 if (_mode != CryptoStreamMode.Write) {
234 throw new NotSupportedException (
235 Locale.GetText ("not in Write mode"));
238 throw new ArgumentOutOfRangeException ("offset",
239 Locale.GetText ("negative"));
242 throw new ArgumentOutOfRangeException ("count",
243 Locale.GetText ("negative"));
245 // re-ordered to avoid integer overflow
246 if (offset > buffer.Length - count) {
247 throw new ArgumentException ("(offset+count)",
248 Locale.GetText ("buffer overflow"));
252 throw new ArgumentNullException ("inner stream was diposed");
254 int buffer_length = count;
256 // partial block (in progress)
257 if ((_partialCount > 0) && (_partialCount != _transform.InputBlockSize)) {
258 int remainder = _transform.InputBlockSize - _partialCount;
259 remainder = ((count < remainder) ? count : remainder);
260 Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, remainder);
261 _partialCount += remainder;
266 int bufferPos = offset;
268 if (_partialCount == _transform.InputBlockSize) {
269 // use partial block to avoid (re)allocation
270 int len = _transform.TransformBlock (_workingBlock, 0, _partialCount, _currentBlock, 0);
271 _stream.Write (_currentBlock, 0, len);
276 if (_transform.CanTransformMultipleBlocks) {
277 // get the biggest multiple of InputBlockSize in count (without mul or div)
278 int size = (count & ~(_transform.InputBlockSize - 1));
279 int rem = (count & (_transform.InputBlockSize - 1));
280 // avoid reallocating memory at each call (reuse same buffer whenever possible)
281 int sizeWorkingBlock = (1 + size / _transform.InputBlockSize) * _transform.OutputBlockSize;
282 if (_workingBlock.Length < sizeWorkingBlock) {
283 Array.Clear (_workingBlock, 0, _workingBlock.Length);
284 _workingBlock = new byte [sizeWorkingBlock];
288 int len = _transform.TransformBlock (buffer, offset, size, _workingBlock, 0);
289 _stream.Write (_workingBlock, 0, len);
293 Buffer.BlockCopy (buffer, buffer_length - rem, _workingBlock, 0, rem);
295 count = 0; // the last block, if any, is in _workingBlock
297 int len = Math.Min (_transform.InputBlockSize - _partialCount, count);
298 Buffer.BlockCopy (buffer, bufferPos, _workingBlock, _partialCount, len);
300 _partialCount += len;
302 // here block may be full, but we wont TransformBlock it until next iteration
303 // so that the last block will be called in FlushFinalBlock using TransformFinalBlock
308 public override void Flush ()
314 public void FlushFinalBlock ()
316 if (_flushedFinalBlock)
317 throw new NotSupportedException (Locale.GetText ("This method cannot be called twice."));
319 throw new NotSupportedException (Locale.GetText ("CryptoStream was disposed."));
320 if (_mode != CryptoStreamMode.Write)
322 _flushedFinalBlock = true;
323 byte[] finalBuffer = _transform.TransformFinalBlock (_workingBlock, 0, _partialCount);
324 if (_stream != null) {
325 _stream.Write (finalBuffer, 0, finalBuffer.Length);
326 if (_stream is CryptoStream) {
327 // for cascading crypto streams
328 (_stream as CryptoStream).FlushFinalBlock ();
333 Array.Clear (finalBuffer, 0, finalBuffer.Length);
336 public override long Seek (long offset, SeekOrigin origin)
338 throw new NotSupportedException ("Seek");
341 // LAMESPEC: Exception NotSupportedException not documented
342 public override void SetLength (long value)
344 throw new NotSupportedException ("SetLength");
347 protected override void Dispose (bool disposing)
351 // only flush in write mode (bugzilla 46143)
352 if (!_flushedFinalBlock) {
353 if (_mode == CryptoStreamMode.Write) {
357 _transform.TransformFinalBlock (new byte [0], 0, 0);
359 _flushedFinalBlock = true;
366 // always cleared for security reason
367 if (_workingBlock != null)
368 Array.Clear (_workingBlock, 0, _workingBlock.Length);
369 if (_currentBlock != null)
370 Array.Clear (_currentBlock, 0, _currentBlock.Length);
373 _workingBlock = null;
374 _currentBlock = null;