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 // (C) 2004 Novell (http://www.novell.com)
13 using System.Globalization;
15 using System.Runtime.InteropServices;
17 namespace System.Security.Cryptography {
19 public class CryptoStream : Stream {
20 private Stream _stream;
21 private ICryptoTransform _transform;
22 private CryptoStreamMode _mode;
23 private byte[] _previousBlock;
24 private byte[] _currentBlock;
25 private bool _disposed;
26 private bool _flushedFinalBlock;
27 private int _blockSize;
28 private int _partialCount;
29 private bool _endOfStream;
31 private byte[] _waitingBlock;
32 private int _waitingCount;
34 private byte[] _transformedBlock;
35 private int _transformedPos;
36 private int _transformedCount;
38 private byte[] _workingBlock;
39 private int _workingCount;
41 public CryptoStream (Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
43 if ((mode == CryptoStreamMode.Read) && (!stream.CanRead)) {
44 throw new ArgumentException (
45 Locale.GetText ("Can't read on stream"));
47 if ((mode == CryptoStreamMode.Write) && (!stream.CanWrite)) {
48 throw new ArgumentException (
49 Locale.GetText ("Can't write on stream"));
52 _transform = transform;
55 if (transform != null) {
56 if (mode == CryptoStreamMode.Read)
57 _blockSize = transform.InputBlockSize;
58 else if (mode == CryptoStreamMode.Write)
59 _blockSize = transform.OutputBlockSize;
60 _workingBlock = new byte [_blockSize];
61 _currentBlock = new byte [_blockSize];
70 public override bool CanRead {
71 get { return (_mode == CryptoStreamMode.Read); }
74 public override bool CanSeek {
78 public override bool CanWrite {
79 get { return (_mode == CryptoStreamMode.Write); }
82 public override long Length {
83 get { throw new NotSupportedException ("Length"); }
86 public override long Position {
87 get { throw new NotSupportedException ("Position"); }
88 set { throw new NotSupportedException ("Position"); }
94 GC.SuppressFinalize (this); // not called in Stream.Dispose
97 // LAMESPEC: A CryptoStream can be close in read mode
98 public override void Close ()
100 // only flush in write mode (bugzilla 46143)
101 if ((!_flushedFinalBlock) && (_mode == CryptoStreamMode.Write))
108 public override int Read ([In,Out] byte[] buffer, int offset, int count)
110 if (_mode != CryptoStreamMode.Read) {
111 throw new NotSupportedException (
112 Locale.GetText ("not in Read mode"));
115 throw new ArgumentOutOfRangeException ("offset",
116 Locale.GetText ("negative"));
119 throw new ArgumentOutOfRangeException ("count",
120 Locale.GetText ("negative"));
122 // yes - buffer.Length will throw a NullReferenceException if buffer is null
123 // but by doing so we match MS implementation
124 if (offset + count > buffer.Length) {
125 throw new ArgumentException ("(offset+count)",
126 Locale.GetText ("buffer overflow"));
128 // for some strange reason Object_disposedException isn't throw
129 // instead we get a ArgumentNullException (probably from an internal method)
130 if (_workingBlock == null) {
131 throw new ArgumentNullException (
132 Locale.GetText ("object _disposed"));
136 if ((count == 0) || ((_transformedPos == _transformedCount) && (_endOfStream)))
139 if (_waitingBlock == null) {
140 _transformedBlock = new byte [_blockSize << 2];
142 _transformedCount = 0;
143 _waitingBlock = new byte [_blockSize];
144 _waitingCount = _stream.Read (_waitingBlock, 0, _waitingBlock.Length);
148 // transformed but not yet returned
149 int length = (_transformedCount - _transformedPos);
151 // need more data - at least one full block must be available if we haven't reach the end of the stream
152 if (length < _blockSize) {
156 _workingCount = _stream.Read (_workingBlock, 0, _workingBlock.Length);
157 _endOfStream = (_workingCount < _blockSize);
160 // transform the waiting block
161 transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount);
163 // transfer temporary to waiting
164 Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount);
165 _waitingCount = _workingCount;
168 if (_workingCount > 0) {
169 // transform the waiting block
170 transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount);
172 // transfer temporary to waiting
173 Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount);
174 _waitingCount = _workingCount;
176 length += transformed;
177 _transformedCount += transformed;
179 byte[] input = _transform.TransformFinalBlock (_waitingBlock, 0, _waitingCount);
180 transformed = input.Length;
181 Array.Copy (input, 0, _transformedBlock, _transformedCount, input.Length);
182 // zeroize this last block
183 Array.Clear (input, 0, input.Length);
186 length += transformed;
187 _transformedCount += transformed;
190 if (_transformedPos > _blockSize) {
191 Buffer.BlockCopy (_transformedBlock, _transformedPos, _transformedBlock, 0, length);
192 _transformedCount -= _transformedPos;
196 length = ((count < length) ? count : length);
197 Buffer.BlockCopy (_transformedBlock, _transformedPos, buffer, offset, length);
198 _transformedPos += length;
204 // there may not be enough data in the stream for a
206 if ((length != _blockSize) || (_endOfStream)) {
207 count = 0; // no more data can be read
214 public override void Write (byte[] buffer, int offset, int count)
216 if (_mode != CryptoStreamMode.Write) {
217 throw new NotSupportedException (
218 Locale.GetText ("not in Write mode"));
221 throw new ArgumentOutOfRangeException ("offset",
222 Locale.GetText ("negative"));
225 throw new ArgumentOutOfRangeException ("count",
226 Locale.GetText ("negative"));
228 if (offset + count > buffer.Length) {
229 throw new ArgumentException ("(offset+count)",
230 Locale.GetText ("buffer overflow"));
233 // partial block (in progress)
234 if ((_partialCount > 0) && (_partialCount != _blockSize)) {
235 int remainder = _blockSize - _partialCount;
236 remainder = ((count < remainder) ? count : remainder);
237 Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, remainder);
238 _partialCount += remainder;
243 int bufferPos = offset;
245 if (_partialCount == _blockSize) {
247 // use partial block to avoid (re)allocation
248 _transform.TransformBlock (_workingBlock, 0, _blockSize, _currentBlock, 0);
249 _stream.Write (_currentBlock, 0, _currentBlock.Length);
252 if (_transform.CanTransformMultipleBlocks) {
253 // transform all except the last block (which may be the last block
254 // of the stream and require TransformFinalBlock)
255 int numBlock = ((_partialCount + count) / _blockSize);
256 if (((_partialCount + count) % _blockSize) == 0) // partial block ?
257 numBlock--; // no then reduce
258 int multiSize = (numBlock * _blockSize);
260 byte[] multiBlocks = new byte [multiSize];
261 _transform.TransformBlock (buffer, offset, multiSize, multiBlocks, 0);
262 _stream.Write (multiBlocks, 0, multiSize);
263 // copy last block into _currentBlock
264 _partialCount = count - multiSize;
265 Array.Copy (buffer, offset + multiSize, _workingBlock, 0, _partialCount);
268 Array.Copy (buffer, offset, _workingBlock, _partialCount, count);
269 _partialCount += count;
271 count = 0; // the last block, if any, is in _workingBlock
274 int len = Math.Min (_blockSize - _partialCount, count);
275 Array.Copy (buffer, bufferPos, _workingBlock, _partialCount, len);
277 _partialCount += len;
279 // here block may be full, but we wont TransformBlock it until next iteration
280 // so that the last block will be called in FlushFinalBlock using TransformFinalBlock
285 public override void Flush ()
291 public void FlushFinalBlock ()
293 if (_flushedFinalBlock) {
294 throw new NotSupportedException (
295 Locale.GetText ("This method cannot be called twice."));
297 if (_mode != CryptoStreamMode.Write) {
298 throw new NotSupportedException (
299 Locale.GetText ("cannot flush a non-writeable CryptoStream"));
302 _flushedFinalBlock = true;
303 byte[] finalBuffer = _transform.TransformFinalBlock (_workingBlock, 0, _partialCount);
304 if (_stream != null) {
305 _stream.Write (finalBuffer, 0, finalBuffer.Length);
309 Array.Clear (finalBuffer, 0, finalBuffer.Length);
312 public override long Seek (long offset, SeekOrigin origin)
314 throw new NotSupportedException ("Seek");
317 // LAMESPEC: Exception NotSupportedException not documented
318 public override void SetLength (long value)
320 throw new NotSupportedException ("SetLength");
323 protected virtual void Dispose (bool disposing)
327 // always cleared for security reason
328 if (_workingBlock != null)
329 Array.Clear (_workingBlock, 0, _workingBlock.Length);
330 if (_currentBlock != null)
331 Array.Clear (_currentBlock, 0, _currentBlock.Length);
332 if (_previousBlock != null)
333 Array.Clear (_previousBlock, 0, _previousBlock.Length);
336 _workingBlock = null;
337 _currentBlock = null;
338 _previousBlock = null;