@@ -264,9 +264,20 @@ public ZipEntry GetNextEntry()
264
264
size = entry . Size ;
265
265
}
266
266
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 )
268
275
{
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
+ }
270
281
}
271
282
272
283
// Determine how to handle reading of data if this is attempted.
@@ -308,6 +319,40 @@ private void ReadDataDescriptor()
308
319
entry . Size = size ;
309
320
}
310
321
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
+
311
356
/// <summary>
312
357
/// Complete cleanup as the final part of closing.
313
358
/// </summary>
@@ -499,27 +544,58 @@ private int InitialRead(byte[] destination, int offset, int count)
499
544
throw new ZipException ( "No password set." ) ;
500
545
}
501
546
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 ) ) ;
505
552
506
- inputBuffer . CryptoTransform = managed . CreateDecryptor ( key , null ) ;
553
+ inputBuffer . CryptoTransform = managed . CreateDecryptor ( key , null ) ;
507
554
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 ) ;
510
557
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
+ }
515
562
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
+ }
519
571
}
520
- else if ( ( entry . Flags & ( int ) GeneralBitFlags . Descriptor ) == 0 )
572
+ else
521
573
{
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 ;
523
599
}
524
600
}
525
601
else
0 commit comments