@@ -27,8 +27,6 @@ public ZipAESStream(Stream stream, ZipAESTransform transform, CryptoStreamMode m
2727 _transform = transform ;
2828 _slideBuffer = new byte [ 1024 ] ;
2929
30- _blockAndAuth = CRYPTO_BLOCK_SIZE + AUTH_CODE_LENGTH ;
31-
3230 // mode:
3331 // CryptoStreamMode.Read means we read from "stream" and pass decrypted to our Read() method.
3432 // Write bypasses this stream and uses the Transform directly.
@@ -41,33 +39,72 @@ public ZipAESStream(Stream stream, ZipAESTransform transform, CryptoStreamMode m
4139 // The final n bytes of the AES stream contain the Auth Code.
4240 private const int AUTH_CODE_LENGTH = 10 ;
4341
42+ // Blocksize is always 16 here, even for AES-256 which has transform.InputBlockSize of 32.
43+ private const int CRYPTO_BLOCK_SIZE = 16 ;
44+
45+ // total length of block + auth code
46+ private const int BLOCK_AND_AUTH = CRYPTO_BLOCK_SIZE + AUTH_CODE_LENGTH ;
47+
4448 private Stream _stream ;
4549 private ZipAESTransform _transform ;
4650 private byte [ ] _slideBuffer ;
4751 private int _slideBufStartPos ;
4852 private int _slideBufFreePos ;
4953
50- // Blocksize is always 16 here, even for AES-256 which has transform.InputBlockSize of 32.
51- private const int CRYPTO_BLOCK_SIZE = 16 ;
54+ // Buffer block transforms to enable partial reads
55+ private byte [ ] _transformBuffer = null ; // new byte[CRYPTO_BLOCK_SIZE];
56+ private int _transformBufferFreePos ;
57+ private int _transformBufferStartPos ;
5258
53- private int _blockAndAuth ;
59+ // Do we have some buffered data available?
60+ private bool HasBufferedData => _transformBuffer != null && _transformBufferStartPos < _transformBufferFreePos ;
5461
5562 /// <summary>
5663 /// Reads a sequence of bytes from the current CryptoStream into buffer,
5764 /// and advances the position within the stream by the number of bytes read.
5865 /// </summary>
5966 public override int Read ( byte [ ] buffer , int offset , int count )
67+ {
68+ // Nothing to do
69+ if ( count == 0 )
70+ return 0 ;
71+
72+ // If we have buffered data, read that first
73+ int nBytes = 0 ;
74+ if ( HasBufferedData )
75+ {
76+ nBytes = ReadBufferedData ( buffer , offset , count ) ;
77+
78+ // Read all requested data from the buffer
79+ if ( nBytes == count )
80+ return nBytes ;
81+
82+ offset += nBytes ;
83+ count -= nBytes ;
84+ }
85+
86+ // Read more data from the input, if available
87+ if ( _slideBuffer != null )
88+ nBytes += ReadAndTransform ( buffer , offset , count ) ;
89+
90+ return nBytes ;
91+ }
92+
93+ // Read data from the underlying stream and decrypt it
94+ private int ReadAndTransform ( byte [ ] buffer , int offset , int count )
6095 {
6196 int nBytes = 0 ;
6297 while ( nBytes < count )
6398 {
99+ int bytesLeftToRead = count - nBytes ;
100+
64101 // Calculate buffer quantities vs read-ahead size, and check for sufficient free space
65102 int byteCount = _slideBufFreePos - _slideBufStartPos ;
66103
67104 // Need to handle final block and Auth Code specially, but don't know total data length.
68105 // Maintain a read-ahead equal to the length of (crypto block + Auth Code).
69106 // When that runs out we can detect these final sections.
70- int lengthToRead = _blockAndAuth - byteCount ;
107+ int lengthToRead = BLOCK_AND_AUTH - byteCount ;
71108 if ( _slideBuffer . Length - _slideBufFreePos < lengthToRead )
72109 {
73110 // Shift the data to the beginning of the buffer
@@ -84,17 +121,11 @@ public override int Read(byte[] buffer, int offset, int count)
84121
85122 // Recalculate how much data we now have
86123 byteCount = _slideBufFreePos - _slideBufStartPos ;
87- if ( byteCount >= _blockAndAuth )
124+ if ( byteCount >= BLOCK_AND_AUTH )
88125 {
89- // At least a 16 byte block and an auth code remains.
90- _transform . TransformBlock ( _slideBuffer ,
91- _slideBufStartPos ,
92- CRYPTO_BLOCK_SIZE ,
93- buffer ,
94- offset ) ;
95- nBytes += CRYPTO_BLOCK_SIZE ;
96- offset += CRYPTO_BLOCK_SIZE ;
97- _slideBufStartPos += CRYPTO_BLOCK_SIZE ;
126+ var read = TransformAndBufferBlock ( buffer , offset , bytesLeftToRead , CRYPTO_BLOCK_SIZE ) ;
127+ nBytes += read ;
128+ offset += read ;
98129 }
99130 else
100131 {
@@ -103,14 +134,7 @@ public override int Read(byte[] buffer, int offset, int count)
103134 {
104135 // At least one byte of data plus auth code
105136 int finalBlock = byteCount - AUTH_CODE_LENGTH ;
106- _transform . TransformBlock ( _slideBuffer ,
107- _slideBufStartPos ,
108- finalBlock ,
109- buffer ,
110- offset ) ;
111-
112- nBytes += finalBlock ;
113- _slideBufStartPos += finalBlock ;
137+ nBytes += TransformAndBufferBlock ( buffer , offset , bytesLeftToRead , finalBlock ) ;
114138 }
115139 else if ( byteCount < AUTH_CODE_LENGTH )
116140 throw new Exception ( "Internal error missed auth code" ) ; // Coding bug
@@ -125,12 +149,62 @@ public override int Read(byte[] buffer, int offset, int count)
125149 }
126150 }
127151
152+ // don't need this any more, so use it as a 'complete' flag
153+ _slideBuffer = null ;
154+
128155 break ; // Reached the auth code
129156 }
130157 }
131158 return nBytes ;
132159 }
133160
161+ // read some buffered data
162+ private int ReadBufferedData ( byte [ ] buffer , int offset , int count )
163+ {
164+ int copyCount = Math . Min ( count , _transformBufferFreePos - _transformBufferStartPos ) ;
165+
166+ Array . Copy ( _transformBuffer , _transformBufferStartPos , buffer , offset , count ) ;
167+ _transformBufferStartPos += copyCount ;
168+
169+ return copyCount ;
170+ }
171+
172+ // Perform the crypto transform, and buffer the data if less than one block has been requested.
173+ private int TransformAndBufferBlock ( byte [ ] buffer , int offset , int count , int blockSize )
174+ {
175+ // If the requested data is greater than one block, transform it directly into the output
176+ // If it's smaller, do it into a temporary buffer and copy the requested part
177+ bool bufferRequired = ( blockSize > count ) ;
178+
179+ if ( bufferRequired && _transformBuffer == null )
180+ _transformBuffer = new byte [ CRYPTO_BLOCK_SIZE ] ;
181+
182+ var targetBuffer = bufferRequired ? _transformBuffer : buffer ;
183+ var targetOffset = bufferRequired ? 0 : offset ;
184+
185+ // Transform the data
186+ _transform . TransformBlock ( _slideBuffer ,
187+ _slideBufStartPos ,
188+ blockSize ,
189+ targetBuffer ,
190+ targetOffset ) ;
191+
192+ _slideBufStartPos += blockSize ;
193+
194+ if ( ! bufferRequired )
195+ {
196+ return blockSize ;
197+ }
198+ else
199+ {
200+ Array . Copy ( _transformBuffer , 0 , buffer , offset , count ) ;
201+ _transformBufferStartPos = count ;
202+ _transformBufferFreePos = blockSize ;
203+
204+ return count ;
205+ }
206+ }
207+
134208 /// <summary>
135209 /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
136210 /// </summary>
0 commit comments