(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / corlib / System.Security.Cryptography / CryptoStream.cs
blob986279dc5f39a246d1ad9bbace29a33ac4c50f5a
1 //
2 // System.Security.Cryptography CryptoStream.cs
3 //
4 // Authors:
5 // Thomas Neidhart (tome@sbox.tugraz.at)
6 // Sebastien Pouliot (sebastien@ximian.com)
7 //
8 // Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 // (C) 2004 Novell (http://www.novell.com)
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 //
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 //
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System;
36 using System.Globalization;
37 using System.IO;
38 using System.Runtime.InteropServices;
40 namespace System.Security.Cryptography {
42 public class CryptoStream : Stream {
43 private Stream _stream;
44 private ICryptoTransform _transform;
45 private CryptoStreamMode _mode;
46 private byte[] _currentBlock;
47 private bool _disposed;
48 private bool _flushedFinalBlock;
49 private int _partialCount;
50 private bool _endOfStream;
52 private byte[] _waitingBlock;
53 private int _waitingCount;
55 private byte[] _transformedBlock;
56 private int _transformedPos;
57 private int _transformedCount;
59 private byte[] _workingBlock;
60 private int _workingCount;
62 public CryptoStream (Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
64 if ((mode == CryptoStreamMode.Read) && (!stream.CanRead)) {
65 throw new ArgumentException (
66 Locale.GetText ("Can't read on stream"));
68 if ((mode == CryptoStreamMode.Write) && (!stream.CanWrite)) {
69 throw new ArgumentException (
70 Locale.GetText ("Can't write on stream"));
72 _stream = stream;
73 _transform = transform;
74 _mode = mode;
75 _disposed = false;
76 if (transform != null) {
77 _workingBlock = new byte [transform.InputBlockSize];
78 if (mode == CryptoStreamMode.Read)
79 _currentBlock = new byte [transform.InputBlockSize];
80 else if (mode == CryptoStreamMode.Write)
81 _currentBlock = new byte [transform.OutputBlockSize];
85 ~CryptoStream ()
87 Dispose (false);
90 public override bool CanRead {
91 get { return (_mode == CryptoStreamMode.Read); }
94 public override bool CanSeek {
95 get { return false; }
98 public override bool CanWrite {
99 get { return (_mode == CryptoStreamMode.Write); }
102 public override long Length {
103 get { throw new NotSupportedException ("Length"); }
106 public override long Position {
107 get { throw new NotSupportedException ("Position"); }
108 set { throw new NotSupportedException ("Position"); }
111 public void Clear ()
113 Dispose (true);
114 GC.SuppressFinalize (this); // not called in Stream.Dispose
117 // LAMESPEC: A CryptoStream can be close in read mode
118 public override void Close ()
120 // only flush in write mode (bugzilla 46143)
121 if ((!_flushedFinalBlock) && (_mode == CryptoStreamMode.Write))
122 FlushFinalBlock ();
124 if (_stream != null)
125 _stream.Close ();
128 public override int Read ([In,Out] byte[] buffer, int offset, int count)
130 if (_mode != CryptoStreamMode.Read) {
131 throw new NotSupportedException (
132 Locale.GetText ("not in Read mode"));
134 if (offset < 0) {
135 throw new ArgumentOutOfRangeException ("offset",
136 Locale.GetText ("negative"));
138 if (count < 0) {
139 throw new ArgumentOutOfRangeException ("count",
140 Locale.GetText ("negative"));
142 // yes - buffer.Length will throw a NullReferenceException if buffer is null
143 // but by doing so we match MS implementation
144 // re-ordered to avoid integer overflow
145 if (offset > buffer.Length - count) {
146 throw new ArgumentException ("(offset+count)",
147 Locale.GetText ("buffer overflow"));
149 // for some strange reason ObjectDisposedException isn't throw
150 // instead we get a ArgumentNullException (probably from an internal method)
151 if (_workingBlock == null) {
152 throw new ArgumentNullException (
153 Locale.GetText ("object _disposed"));
156 int result = 0;
157 if ((count == 0) || ((_transformedPos == _transformedCount) && (_endOfStream)))
158 return result;
160 if (_waitingBlock == null) {
161 _transformedBlock = new byte [_transform.OutputBlockSize << 2];
162 _transformedPos = 0;
163 _transformedCount = 0;
164 _waitingBlock = new byte [_transform.InputBlockSize];
165 _waitingCount = _stream.Read (_waitingBlock, 0, _waitingBlock.Length);
168 while (count > 0) {
169 // transformed but not yet returned
170 int length = (_transformedCount - _transformedPos);
172 // need more data - at least one full block must be available if we haven't reach the end of the stream
173 if (length < _transform.InputBlockSize) {
174 int transformed = 0;
176 // load a new block
177 _workingCount = _stream.Read (_workingBlock, 0, _workingBlock.Length);
178 _endOfStream = (_workingCount < _transform.InputBlockSize);
180 if (!_endOfStream) {
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 else {
189 if (_workingCount > 0) {
190 // transform the waiting block
191 transformed = _transform.TransformBlock (_waitingBlock, 0, _waitingBlock.Length, _transformedBlock, _transformedCount);
193 // transfer temporary to waiting
194 Buffer.BlockCopy (_workingBlock, 0, _waitingBlock, 0, _workingCount);
195 _waitingCount = _workingCount;
197 length += transformed;
198 _transformedCount += transformed;
200 byte[] input = _transform.TransformFinalBlock (_waitingBlock, 0, _waitingCount);
201 transformed = input.Length;
202 Buffer.BlockCopy (input, 0, _transformedBlock, _transformedCount, input.Length);
203 // zeroize this last block
204 Array.Clear (input, 0, input.Length);
207 length += transformed;
208 _transformedCount += transformed;
210 // compaction
211 if (_transformedPos > _transform.InputBlockSize) {
212 Buffer.BlockCopy (_transformedBlock, _transformedPos, _transformedBlock, 0, length);
213 _transformedCount -= _transformedPos;
214 _transformedPos = 0;
217 length = ((count < length) ? count : length);
218 if (length > 0) {
219 Buffer.BlockCopy (_transformedBlock, _transformedPos, buffer, offset, length);
220 _transformedPos += length;
222 result += length;
223 offset += length;
224 count -= length;
227 // there may not be enough data in the stream for a
228 // complete block
229 if (((length != _transform.InputBlockSize) && (_waitingCount != _transform.InputBlockSize)) || (_endOfStream)) {
230 count = 0; // no more data can be read
234 return result;
237 public override void Write (byte[] buffer, int offset, int count)
239 if (_mode != CryptoStreamMode.Write) {
240 throw new NotSupportedException (
241 Locale.GetText ("not in Write mode"));
243 if (offset < 0) {
244 throw new ArgumentOutOfRangeException ("offset",
245 Locale.GetText ("negative"));
247 if (count < 0) {
248 throw new ArgumentOutOfRangeException ("count",
249 Locale.GetText ("negative"));
251 // re-ordered to avoid integer overflow
252 if (offset > buffer.Length - count) {
253 throw new ArgumentException ("(offset+count)",
254 Locale.GetText ("buffer overflow"));
257 // partial block (in progress)
258 if ((_partialCount > 0) && (_partialCount != _transform.InputBlockSize)) {
259 int remainder = _transform.InputBlockSize - _partialCount;
260 remainder = ((count < remainder) ? count : remainder);
261 Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, remainder);
262 _partialCount += remainder;
263 offset += remainder;
264 count -= remainder;
267 int bufferPos = offset;
268 while (count > 0) {
269 if (_partialCount == _transform.InputBlockSize) {
270 // use partial block to avoid (re)allocation
271 int len = _transform.TransformBlock (_workingBlock, 0, _partialCount, _currentBlock, 0);
272 _stream.Write (_currentBlock, 0, len);
273 // reset
274 _partialCount = 0;
277 if (_transform.CanTransformMultipleBlocks) {
278 // transform all except the last block (which may be the last block
279 // of the stream and require TransformFinalBlock)
280 int numBlock = ((_partialCount + count) / _transform.InputBlockSize);
281 int multiSize = (numBlock * _transform.InputBlockSize);
282 if (numBlock > 0) {
283 byte[] multiBlocks = new byte [multiSize];
284 int len = _transform.TransformBlock (buffer, offset, multiSize, multiBlocks, 0);
285 _stream.Write (multiBlocks, 0, len);
286 // copy last block into _currentBlock
287 _partialCount = count - multiSize;
288 Buffer.BlockCopy (buffer, offset + multiSize, _workingBlock, 0, _partialCount);
290 else {
291 Buffer.BlockCopy (buffer, offset, _workingBlock, _partialCount, count);
292 _partialCount += count;
294 count = 0; // the last block, if any, is in _workingBlock
296 else {
297 int len = Math.Min (_transform.InputBlockSize - _partialCount, count);
298 Buffer.BlockCopy (buffer, bufferPos, _workingBlock, _partialCount, len);
299 bufferPos += len;
300 _partialCount += len;
301 count -= 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 ()
310 if (_stream != null)
311 _stream.Flush ();
314 public void FlushFinalBlock ()
316 if (_flushedFinalBlock) {
317 throw new NotSupportedException (
318 Locale.GetText ("This method cannot be called twice."));
320 if (_mode != CryptoStreamMode.Write) {
321 throw new NotSupportedException (
322 Locale.GetText ("cannot flush a non-writeable CryptoStream"));
325 _flushedFinalBlock = true;
326 byte[] finalBuffer = _transform.TransformFinalBlock (_workingBlock, 0, _partialCount);
327 if (_stream != null) {
328 _stream.Write (finalBuffer, 0, finalBuffer.Length);
329 if (_stream is CryptoStream) {
330 // for cascading crypto streams
331 (_stream as CryptoStream).FlushFinalBlock ();
333 _stream.Flush ();
335 // zeroize
336 Array.Clear (finalBuffer, 0, finalBuffer.Length);
339 public override long Seek (long offset, SeekOrigin origin)
341 throw new NotSupportedException ("Seek");
344 // LAMESPEC: Exception NotSupportedException not documented
345 public override void SetLength (long value)
347 throw new NotSupportedException ("SetLength");
350 protected virtual void Dispose (bool disposing)
352 if (!_disposed) {
353 _disposed = true;
354 // always cleared for security reason
355 if (_workingBlock != null)
356 Array.Clear (_workingBlock, 0, _workingBlock.Length);
357 if (_currentBlock != null)
358 Array.Clear (_currentBlock, 0, _currentBlock.Length);
359 if (disposing) {
360 _stream = null;
361 _workingBlock = null;
362 _currentBlock = null;