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[] _currentBlock;
24 private bool _disposed;
25 private bool _flushedFinalBlock;
26 private int _partialCount;
27 private bool _endOfStream;
29 private byte[] _waitingBlock;
30 private int _waitingCount;
32 private byte[] _transformedBlock;
33 private int _transformedPos;
34 private int _transformedCount;
36 private byte[] _workingBlock;
37 private int _workingCount;
39 public CryptoStream (Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
41 if ((mode == CryptoStreamMode.Read) && (!stream.CanRead)) {
42 throw new ArgumentException (
43 Locale.GetText ("Can't read on stream"));
45 if ((mode == CryptoStreamMode.Write) && (!stream.CanWrite)) {
46 throw new ArgumentException (
47 Locale.GetText ("Can't write on stream"));
50 _transform = transform;
53 if (transform != null) {
54 _workingBlock = new byte [transform.InputBlockSize];
55 if (mode == CryptoStreamMode.Read)
56 _currentBlock = new byte [transform.InputBlockSize];
57 else if (mode == CryptoStreamMode.Write)
58 _currentBlock = new byte [transform.OutputBlockSize];
67 public override bool CanRead {
68 get { return (_mode == CryptoStreamMode.Read); }
71 public override bool CanSeek {
75 public override bool CanWrite {
76 get { return (_mode == CryptoStreamMode.Write); }
79 public override long Length {
80 get { throw new NotSupportedException ("Length"); }
83 public override long Position {
84 get { throw new NotSupportedException ("Position"); }
85 set { throw new NotSupportedException ("Position"); }
91 GC.SuppressFinalize (this); // not called in Stream.Dispose
94 // LAMESPEC: A CryptoStream can be close in read mode
95 public override void Close ()
97 // only flush in write mode (bugzilla 46143)
98 if ((!_flushedFinalBlock) && (_mode == CryptoStreamMode.Write))
105 public override int Read ([In,Out] byte[] buffer, int offset, int count)
107 if (_mode != CryptoStreamMode.Read) {
108 throw new NotSupportedException (
109 Locale.GetText ("not in Read mode"));
112 throw new ArgumentOutOfRangeException ("offset",
113 Locale.GetText ("negative"));
116 throw new ArgumentOutOfRangeException ("count",
117 Locale.GetText ("negative"));
119 // yes - buffer.Length will throw a NullReferenceException if buffer is null
120 // but by doing so we match MS implementation
121 // re-ordered to avoid integer overflow
122 if (offset > buffer.Length - count) {
123 throw new ArgumentException ("(offset+count)",
124 Locale.GetText ("buffer overflow"));
126 // for some strange reason ObjectDisposedException isn't throw
127 // instead we get a ArgumentNullException (probably from an internal method)
128 if (_workingBlock == null) {
129 throw new ArgumentNullException (
130 Locale.GetText ("object _disposed"));
134 if ((count == 0) || ((_transformedPos == _transformedCount) && (_endOfStream)))
137 if (_waitingBlock == null) {
138 _transformedBlock = new byte [_transform.OutputBlockSize << 2];
140 _transformedCount = 0;
141 _waitingBlock = new byte [_transform.InputBlockSize];
142 _waitingCount = _stream.Read (_waitingBlock, 0, _waitingBlock.Length);
146 // transformed but not yet returned
147 int length = (_transformedCount - _transformedPos);
149 // need more data - at least one full block must be available if we haven't reach the end of the stream
150 if (length < _transform.InputBlockSize) {
154 _workingCount = _stream.Read (_workingBlock, 0, _workingBlock.Length);
155 _endOfStream = (_workingCount < _transform.InputBlockSize);
158 // transform the waiting block
159 transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount);
161 // transfer temporary to waiting
162 Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount);
163 _waitingCount = _workingCount;
166 if (_workingCount > 0) {
167 // transform the waiting block
168 transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount);
170 // transfer temporary to waiting
171 Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount);
172 _waitingCount = _workingCount;
174 length += transformed;
175 _transformedCount += transformed;
177 byte[] input = _transform.TransformFinalBlock (_waitingBlock, 0, _waitingCount);
178 transformed = input.Length;
179 Buffer.BlockCopy (input, 0, _transformedBlock, _transformedCount, input.Length);
180 // zeroize this last block
181 Array.Clear (input, 0, input.Length);
184 length += transformed;
185 _transformedCount += transformed;
188 if (_transformedPos > _transform.InputBlockSize) {
189 Buffer.BlockCopy (_transformedBlock, _transformedPos, _transformedBlock, 0, length);
190 _transformedCount -= _transformedPos;
194 length = ((count < length) ? count : length);
196 Buffer.BlockCopy (_transformedBlock, _transformedPos, buffer, offset, length);
197 _transformedPos += length;
204 // there may not be enough data in the stream for a
206 if (((length != _transform.InputBlockSize) && (_waitingCount != _transform.InputBlockSize)) || (_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 // re-ordered to avoid integer overflow
229 if (offset > buffer.Length - count) {
230 throw new ArgumentException ("(offset+count)",
231 Locale.GetText ("buffer overflow"));
234 // partial block (in progress)
235 if ((_partialCount > 0) && (_partialCount != _transform.InputBlockSize)) {
236 int remainder = _transform.InputBlockSize - _partialCount;
237 remainder = ((count < remainder) ? count : remainder);
238 Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, remainder);
239 _partialCount += remainder;
244 int bufferPos = offset;
246 if (_partialCount == _transform.InputBlockSize) {
247 // use partial block to avoid (re)allocation
248 int len = _transform.TransformBlock (_workingBlock, 0, _partialCount, _currentBlock, 0);
249 _stream.Write (_currentBlock, 0, len);
254 if (_transform.CanTransformMultipleBlocks) {
255 // transform all except the last block (which may be the last block
256 // of the stream and require TransformFinalBlock)
257 int numBlock = ((_partialCount + count) / _transform.InputBlockSize);
258 if (((_partialCount + count) % _transform.InputBlockSize) == 0) // partial block ?
259 numBlock--; // no then reduce
260 int multiSize = (numBlock * _transform.InputBlockSize);
262 byte[] multiBlocks = new byte [multiSize];
263 int len = _transform.TransformBlock (buffer, offset, multiSize, multiBlocks, 0);
264 _stream.Write (multiBlocks, 0, len);
265 // copy last block into _currentBlock
266 _partialCount = count - multiSize;
267 Buffer.BlockCopy (buffer, offset + multiSize, _workingBlock, 0, _partialCount);
270 Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, count);
271 _partialCount += count;
273 count = 0; // the last block, if any, is in _workingBlock
276 int len = Math.Min (_transform.InputBlockSize - _partialCount, count);
277 Buffer.BlockCopy (buffer, bufferPos, _workingBlock, _partialCount, len);
279 _partialCount += len;
281 // here block may be full, but we wont TransformBlock it until next iteration
282 // so that the last block will be called in FlushFinalBlock using TransformFinalBlock
287 public override void Flush ()
293 public void FlushFinalBlock ()
295 if (_flushedFinalBlock) {
296 throw new NotSupportedException (
297 Locale.GetText ("This method cannot be called twice."));
299 if (_mode != CryptoStreamMode.Write) {
300 throw new NotSupportedException (
301 Locale.GetText ("cannot flush a non-writeable CryptoStream"));
304 _flushedFinalBlock = true;
305 byte[] finalBuffer = _transform.TransformFinalBlock (_workingBlock, 0, _partialCount);
306 if (_stream != null) {
307 _stream.Write (finalBuffer, 0, finalBuffer.Length);
308 if (_stream is CryptoStream) {
309 // for cascading crypto streams
310 (_stream as CryptoStream).FlushFinalBlock ();
315 Array.Clear (finalBuffer, 0, finalBuffer.Length);
318 public override long Seek (long offset, SeekOrigin origin)
320 throw new NotSupportedException ("Seek");
323 // LAMESPEC: Exception NotSupportedException not documented
324 public override void SetLength (long value)
326 throw new NotSupportedException ("SetLength");
329 protected virtual void Dispose (bool disposing)
333 // always cleared for security reason
334 if (_workingBlock != null)
335 Array.Clear (_workingBlock, 0, _workingBlock.Length);
336 if (_currentBlock != null)
337 Array.Clear (_currentBlock, 0, _currentBlock.Length);
340 _workingBlock = null;
341 _currentBlock = null;