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 // 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:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
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.
36 using System
.Globalization
;
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"));
73 _transform
= transform
;
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
];
90 public override bool CanRead
{
91 get { return (_mode == CryptoStreamMode.Read); }
94 public override bool CanSeek
{
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"); }
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
))
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"));
135 throw new ArgumentOutOfRangeException ("offset",
136 Locale
.GetText ("negative"));
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"));
157 if ((count
== 0) || ((_transformedPos
== _transformedCount
) && (_endOfStream
)))
160 if (_waitingBlock
== null) {
161 _transformedBlock
= new byte [_transform
.OutputBlockSize
<< 2];
163 _transformedCount
= 0;
164 _waitingBlock
= new byte [_transform
.InputBlockSize
];
165 _waitingCount
= _stream
.Read (_waitingBlock
, 0, _waitingBlock
.Length
);
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
) {
177 _workingCount
= _stream
.Read (_workingBlock
, 0, _workingBlock
.Length
);
178 _endOfStream
= (_workingCount
< _transform
.InputBlockSize
);
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
;
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
;
211 if (_transformedPos
> _transform
.InputBlockSize
) {
212 Buffer
.BlockCopy (_transformedBlock
, _transformedPos
, _transformedBlock
, 0, length
);
213 _transformedCount
-= _transformedPos
;
217 length
= ((count
< length
) ? count
: length
);
219 Buffer
.BlockCopy (_transformedBlock
, _transformedPos
, buffer
, offset
, length
);
220 _transformedPos
+= length
;
227 // there may not be enough data in the stream for a
229 if (((length
!= _transform
.InputBlockSize
) && (_waitingCount
!= _transform
.InputBlockSize
)) || (_endOfStream
)) {
230 count
= 0; // no more data can be read
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"));
244 throw new ArgumentOutOfRangeException ("offset",
245 Locale
.GetText ("negative"));
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
;
267 int bufferPos
= offset
;
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
);
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
);
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
);
291 Buffer
.BlockCopy (buffer
, offset
, _workingBlock
, _partialCount
, count
);
292 _partialCount
+= count
;
294 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 (
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 ();
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
)
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
);
361 _workingBlock
= null;
362 _currentBlock
= null;