@@ -264,9 +264,20 @@ public ZipEntry GetNextEntry()
264264 size = entry . Size ;
265265 }
266266
267- if ( method == CompressionMethod . Stored && ( ! isCrypted && csize != size || ( isCrypted && csize - ZipConstants . CryptoHeaderSize != size ) ) )
267+ if ( method == CompressionMethod . Stored )
268+ {
269+ if ( ! isCrypted && csize != size || ( isCrypted && csize - ZipConstants . CryptoHeaderSize != size ) )
270+ {
271+ throw new ZipException ( "Stored, but compressed != uncompressed" ) ;
272+ }
273+ }
274+ else if ( method == CompressionMethod . WinZipAES && entry . CompressionMethod == CompressionMethod . Stored )
268275 {
269- throw new ZipException ( "Stored, but compressed != uncompressed" ) ;
276+ var sizeWithoutAesOverhead = csize - ( entry . AESSaltLen + ZipConstants . AESPasswordVerifyLength + ZipConstants . AESAuthCodeLength ) ;
277+ if ( sizeWithoutAesOverhead != size )
278+ {
279+ throw new ZipException ( "Stored, but compressed != uncompressed" ) ;
280+ }
270281 }
271282
272283 // Determine how to handle reading of data if this is attempted.
@@ -308,6 +319,40 @@ private void ReadDataDescriptor()
308319 entry . Size = size ;
309320 }
310321
322+ /// <summary>
323+ /// Complete any decryption processing and clear any cryptographic state.
324+ /// </summary>
325+ protected override void StopDecrypting ( )
326+ {
327+ base . StopDecrypting ( ) ;
328+
329+ if ( entry . AESKeySize != 0 )
330+ {
331+ byte [ ] authBytes = new byte [ ZipConstants . AESAuthCodeLength ] ;
332+ int authBytesRead = inputBuffer . ReadRawBuffer ( authBytes , 0 , authBytes . Length ) ;
333+
334+ if ( authBytesRead < ZipConstants . AESAuthCodeLength )
335+ {
336+ throw new Exception ( "Internal error missed auth code" ) ; // Coding bug
337+ // Final block done. Check Auth code.
338+ }
339+
340+ /*
341+ byte[] calcAuthCode = (this.cryptoTransform as ZipAESTransform).GetAuthCode();
342+ for (int i = 0; i < ZipConstants.AESAuthCodeLength; i++)
343+ {
344+ if (calcAuthCode[i] != authBytes[i])
345+ {
346+ // throw new Exception("AES Authentication Code does not match. This is a super-CRC check on the data in the file after compression and encryption. \r\n"
347+ // + "The file may be damaged.");
348+ }
349+ }
350+ */
351+
352+ // Dispose the transform?
353+ }
354+ }
355+
311356 /// <summary>
312357 /// Complete cleanup as the final part of closing.
313358 /// </summary>
@@ -499,27 +544,58 @@ private int InitialRead(byte[] destination, int offset, int count)
499544 throw new ZipException ( "No password set." ) ;
500545 }
501546
502- // Generate and set crypto transform...
503- var managed = new PkzipClassicManaged ( ) ;
504- byte [ ] key = PkzipClassic . GenerateKeys ( ZipStrings . ConvertToArray ( password ) ) ;
547+ if ( entry . AESKeySize == 0 )
548+ {
549+ // Generate and set crypto transform...
550+ var managed = new PkzipClassicManaged ( ) ;
551+ byte [ ] key = PkzipClassic . GenerateKeys ( ZipStrings . ConvertToArray ( password ) ) ;
505552
506- inputBuffer . CryptoTransform = managed . CreateDecryptor ( key , null ) ;
553+ inputBuffer . CryptoTransform = managed . CreateDecryptor ( key , null ) ;
507554
508- byte [ ] cryptbuffer = new byte [ ZipConstants . CryptoHeaderSize ] ;
509- inputBuffer . ReadClearTextBuffer ( cryptbuffer , 0 , ZipConstants . CryptoHeaderSize ) ;
555+ byte [ ] cryptbuffer = new byte [ ZipConstants . CryptoHeaderSize ] ;
556+ inputBuffer . ReadClearTextBuffer ( cryptbuffer , 0 , ZipConstants . CryptoHeaderSize ) ;
510557
511- if ( cryptbuffer [ ZipConstants . CryptoHeaderSize - 1 ] != entry . CryptoCheckValue )
512- {
513- throw new ZipException ( "Invalid password" ) ;
514- }
558+ if ( cryptbuffer [ ZipConstants . CryptoHeaderSize - 1 ] != entry . CryptoCheckValue )
559+ {
560+ throw new ZipException ( "Invalid password" ) ;
561+ }
515562
516- if ( csize >= ZipConstants . CryptoHeaderSize )
517- {
518- csize -= ZipConstants . CryptoHeaderSize ;
563+ if ( csize >= ZipConstants . CryptoHeaderSize )
564+ {
565+ csize -= ZipConstants . CryptoHeaderSize ;
566+ }
567+ else if ( ( entry . Flags & ( int ) GeneralBitFlags . Descriptor ) == 0 )
568+ {
569+ throw new ZipException ( string . Format ( "Entry compressed size {0} too small for encryption" , csize ) ) ;
570+ }
519571 }
520- else if ( ( entry . Flags & ( int ) GeneralBitFlags . Descriptor ) == 0 )
572+ else
521573 {
522- throw new ZipException ( string . Format ( "Entry compressed size {0} too small for encryption" , csize ) ) ;
574+ int saltLen = entry . AESSaltLen ;
575+ byte [ ] saltBytes = new byte [ saltLen ] ;
576+ int saltIn = inputBuffer . ReadRawBuffer ( saltBytes , 0 , saltLen ) ;
577+
578+ if ( saltIn != saltLen )
579+ throw new ZipException ( "AES Salt expected " + saltLen + " got " + saltIn ) ;
580+
581+ //
582+ byte [ ] pwdVerifyRead = new byte [ ZipConstants . AESPasswordVerifyLength ] ;
583+ int pwdBytesRead = inputBuffer . ReadRawBuffer ( pwdVerifyRead , 0 , pwdVerifyRead . Length ) ;
584+
585+ if ( pwdBytesRead != pwdVerifyRead . Length )
586+ throw new EndOfStreamException ( ) ;
587+
588+ int blockSize = entry . AESKeySize / 8 ; // bits to bytes
589+
590+ var decryptor = new ZipAESTransform ( password , saltBytes , blockSize , false ) ;
591+ byte [ ] pwdVerifyCalc = decryptor . PwdVerifier ;
592+ if ( pwdVerifyCalc [ 0 ] != pwdVerifyRead [ 0 ] || pwdVerifyCalc [ 1 ] != pwdVerifyRead [ 1 ] )
593+ throw new ZipException ( "Invalid password for AES" ) ;
594+
595+ // The AES data has saltLen+AESPasswordVerifyLength bytes as a header, and AESAuthCodeLength bytes
596+ // as a footer.
597+ csize -= ( saltLen + ZipConstants . AESPasswordVerifyLength + ZipConstants . AESAuthCodeLength ) ;
598+ inputBuffer . CryptoTransform = decryptor ;
523599 }
524600 }
525601 else
0 commit comments