66
77use crate :: aes_ctr:: AesCipher ;
88use crate :: format:: aes:: AesMode ;
9- use crate :: result:: ZipResult ;
9+ use crate :: result:: { ZipResult , invalid } ;
1010use crate :: { aes_ctr, result:: ZipError } ;
1111use constant_time_eq:: constant_time_eq;
1212use hmac:: { KeyInit , Mac , SimpleHmacReset } ;
@@ -140,15 +140,18 @@ pub struct AesReader<R> {
140140}
141141
142142impl < R : Read > AesReader < R > {
143- pub const fn new ( reader : R , aes_mode : AesMode , compressed_size : u64 ) -> AesReader < R > {
144- let data_length = compressed_size
145- - ( PWD_VERIFY_LENGTH + AUTH_CODE_LENGTH + aes_mode. salt_length ( ) ) as u64 ;
146-
147- Self {
143+ pub fn new ( reader : R , aes_mode : AesMode , compressed_size : u64 ) -> ZipResult < AesReader < R > > {
144+ let metadata_len = ( PWD_VERIFY_LENGTH + AUTH_CODE_LENGTH + aes_mode. salt_length ( ) ) as u64 ;
145+ // we accept 0 size data
146+ if compressed_size < metadata_len {
147+ return Err ( invalid ! ( "Compressed size of AES data is invalid" ) ) ;
148+ }
149+ let data_length = compressed_size - metadata_len;
150+ Ok ( Self {
148151 reader,
149152 aes_mode,
150153 data_length,
151- }
154+ } )
152155 }
153156
154157 /// Read the AES header bytes and validate the password.
@@ -192,6 +195,7 @@ impl<R: Read> AesReader<R> {
192195
193196 Ok ( AesReaderValid {
194197 reader : self . reader ,
198+ data_length : self . data_length ,
195199 data_remaining : self . data_length ,
196200 cipher,
197201 hmac,
@@ -227,6 +231,7 @@ impl<R: Read> AesReader<R> {
227231#[ derive( Debug ) ]
228232pub struct AesReaderValid < R : Read > {
229233 reader : R ,
234+ data_length : u64 ,
230235 data_remaining : u64 ,
231236 cipher : Cipher ,
232237 hmac : SimpleHmacReset < Sha1 > ,
@@ -245,6 +250,10 @@ impl<R: Read> Read for AesReaderValid<R> {
245250 /// practically unusable, since its position after the error is not known.
246251 fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
247252 if self . data_remaining == 0 {
253+ // handle case were payload is empty
254+ if self . data_length == 0 {
255+ self . verify_hmac ( ) ?;
256+ }
248257 return Ok ( 0 ) ;
249258 }
250259
@@ -262,38 +271,48 @@ impl<R: Read> Read for AesReaderValid<R> {
262271
263272 // if there is no data left to read, check the integrity of the data
264273 if self . data_remaining == 0 {
265- debug_assert ! (
266- !self . finalized,
267- "Tried to use an already finalized HMAC. This is a bug!"
268- ) ;
269- if self . finalized {
270- return Err ( Error :: new (
271- ErrorKind :: InvalidData ,
272- "Tried to use an already finalized HMAC" ,
273- ) ) ;
274- }
275- self . finalized = true ;
276-
277- // Zip uses HMAC-Sha1-80, which only uses the first half of the hash
278- // see https://www.winzip.com/win/en/aes_info.html#auth-faq
279- let mut read_auth_code = [ 0 ; AUTH_CODE_LENGTH ] ;
280- self . reader . read_exact ( & mut read_auth_code) ?;
281- let computed_auth_code = & self . hmac . finalize_reset ( ) . into_bytes ( ) [ 0 ..AUTH_CODE_LENGTH ] ;
282-
283- // use constant time comparison to mitigate timing attacks
284- if !constant_time_eq ( computed_auth_code, & read_auth_code) {
285- return Err ( Error :: new (
286- ErrorKind :: InvalidData ,
287- "Invalid authentication code, this could be due to an invalid password or errors in the data" ,
288- ) ) ;
289- }
274+ self . verify_hmac ( ) ?;
275+ } else if read == 0 && self . data_remaining > 0 {
276+ return Err ( Error :: new (
277+ ErrorKind :: UnexpectedEof ,
278+ "Read 0 bytes of AES data but still contains remaining data" ,
279+ ) ) ;
290280 }
291281
292282 Ok ( read)
293283 }
294284}
295285
296286impl < R : Read > AesReaderValid < R > {
287+ fn verify_hmac ( & mut self ) -> io:: Result < ( ) > {
288+ debug_assert ! (
289+ !self . finalized,
290+ "Tried to use an already finalized HMAC. This is a bug!"
291+ ) ;
292+ if self . finalized {
293+ return Err ( Error :: new (
294+ ErrorKind :: InvalidData ,
295+ "Tried to use an already finalized HMAC" ,
296+ ) ) ;
297+ }
298+ self . finalized = true ;
299+
300+ // Zip uses HMAC-Sha1-80, which only uses the first half of the hash
301+ // see https://www.winzip.com/win/en/aes_info.html#auth-faq
302+ let mut read_auth_code = [ 0 ; AUTH_CODE_LENGTH ] ;
303+ self . reader . read_exact ( & mut read_auth_code) ?;
304+ let computed_auth_code = & self . hmac . finalize_reset ( ) . into_bytes ( ) [ 0 ..AUTH_CODE_LENGTH ] ;
305+
306+ // use constant time comparison to mitigate timing attacks
307+ if !constant_time_eq ( computed_auth_code, & read_auth_code) {
308+ return Err ( Error :: new (
309+ ErrorKind :: InvalidData ,
310+ "Invalid authentication code, this could be due to an invalid password or errors in the data" ,
311+ ) ) ;
312+ }
313+ Ok ( ( ) )
314+ }
315+
297316 /// Consumes this decoder, returning the underlying reader.
298317 pub fn into_inner ( self ) -> R {
299318 self . reader
@@ -452,7 +471,7 @@ mod tests {
452471 {
453472 let compressed_length = buf. get_ref ( ) . len ( ) as u64 ;
454473 let mut reader =
455- AesReader :: new ( & mut buf, aes_mode, compressed_length) . validate ( password) ?;
474+ AesReader :: new ( & mut buf, aes_mode, compressed_length) ? . validate ( password) ?;
456475 reader. read_to_end ( & mut read_buffer) ?;
457476 }
458477
0 commit comments