@@ -1084,6 +1084,7 @@ public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandl
10841084 [ Flags ]
10851085 private enum HeaderTest
10861086 {
1087+ None = 0x0 ,
10871088 Extract = 0x01 , // Check that this header represents an entry whose data can be extracted
10881089 Header = 0x02 , // Check that this header contents are valid
10891090 }
@@ -1110,13 +1111,12 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
11101111
11111112 if ( signature != ZipConstants . LocalHeaderSignature )
11121113 {
1113- throw new ZipException ( string . Format ( "Wrong local header signature at 0x{0:x}, expected 0x{1:x8}, actual 0x{2:x8}" ,
1114- entryAbsOffset , ZipConstants . LocalHeaderSignature , signature ) ) ;
1114+ throw new ZipException ( $ "Wrong local header signature at 0x{ entryAbsOffset : x} , expected 0x{ ZipConstants . LocalHeaderSignature : x8} , actual 0x{ signature : x8} ") ;
11151115 }
11161116
11171117 var extractVersion = ( short ) ( ReadLEUshort ( ) & 0x00ff ) ;
11181118 var localFlags = ( GeneralBitFlags ) ReadLEUshort ( ) ;
1119- var compressionMethod = ( short ) ReadLEUshort ( ) ;
1119+ var compressionMethod = ( CompressionMethod ) ReadLEUshort ( ) ;
11201120 var fileTime = ( short ) ReadLEUshort ( ) ;
11211121 var fileDate = ( short ) ReadLEUshort ( ) ;
11221122 uint crcValue = ReadLEUint ( ) ;
@@ -1134,7 +1134,7 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
11341134 var localExtraData = new ZipExtraData ( extraData ) ;
11351135
11361136 // Extra data / zip64 checks
1137- if ( localExtraData . Find ( 1 ) )
1137+ if ( localExtraData . Find ( headerID : 1 ) )
11381138 {
11391139 // 2010-03-04 Forum 10512: removed checks for version >= ZipConstants.VersionZip64
11401140 // and size or compressedSize = MaxValue, due to rogue creators.
@@ -1175,15 +1175,19 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
11751175 throw new ZipException ( "Compression method not supported" ) ;
11761176 }
11771177
1178- if ( ( extractVersion > ZipConstants . VersionMadeBy )
1179- || ( ( extractVersion > 20 ) && ( extractVersion < ZipConstants . VersionZip64 ) ) )
1178+ if ( extractVersion > ZipConstants . VersionMadeBy
1179+ || ( extractVersion > 20 && extractVersion < ZipConstants . VersionZip64 ) )
11801180 {
1181- throw new ZipException ( string . Format ( "Version required to extract this entry not supported ({0 })" , extractVersion ) ) ;
1181+ throw new ZipException ( $ "Version required to extract this entry not supported ({ extractVersion } )") ;
11821182 }
11831183
1184- if ( localFlags . HasAny ( GeneralBitFlags . Patched | GeneralBitFlags . StrongEncryption | GeneralBitFlags . EnhancedCompress | GeneralBitFlags . HeaderMasked ) )
1184+ const GeneralBitFlags notSupportedFlags = GeneralBitFlags . Patched
1185+ | GeneralBitFlags . StrongEncryption
1186+ | GeneralBitFlags . EnhancedCompress
1187+ | GeneralBitFlags . HeaderMasked ;
1188+ if ( localFlags . HasAny ( notSupportedFlags ) )
11851189 {
1186- throw new ZipException ( "The library does not support the zip version required to extract this entry" ) ;
1190+ throw new ZipException ( $ "The library does not support the zip features required to extract this entry ( { localFlags & notSupportedFlags : F } ) ") ;
11871191 }
11881192 }
11891193 }
@@ -1207,7 +1211,7 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
12071211 ( extractVersion != 63 )
12081212 )
12091213 {
1210- throw new ZipException ( string . Format ( "Version required to extract this entry is invalid ({0 })" , extractVersion ) ) ;
1214+ throw new ZipException ( $ "Version required to extract this entry is invalid ({ extractVersion } )") ;
12111215 }
12121216
12131217 var localEncoding = _stringCodec . ZipInputEncoding ( localFlags ) ;
@@ -1221,7 +1225,7 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
12211225 // Encryption requires extract version >= 20
12221226 if ( localFlags . HasAny ( GeneralBitFlags . Encrypted ) && extractVersion < 20 )
12231227 {
1224- throw new ZipException ( string . Format ( "Version required to extract this entry is too low for encryption ({0 })" , extractVersion ) ) ;
1228+ throw new ZipException ( $ "Version required to extract this entry is too low for encryption ({ extractVersion } )") ;
12251229 }
12261230
12271231 // Strong encryption requires encryption flag to be set and extract version >= 50.
@@ -1234,26 +1238,26 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
12341238
12351239 if ( extractVersion < 50 )
12361240 {
1237- throw new ZipException ( string . Format ( "Version required to extract this entry is too low for encryption ({0 })" , extractVersion ) ) ;
1241+ throw new ZipException ( $ "Version required to extract this entry is too low for encryption ({ extractVersion } )") ;
12381242 }
12391243 }
12401244
12411245 // Patched entries require extract version >= 27
12421246 if ( localFlags . HasAny ( GeneralBitFlags . Patched ) && extractVersion < 27 )
12431247 {
1244- throw new ZipException ( string . Format ( "Patched data requires higher version than ({0 })" , extractVersion ) ) ;
1248+ throw new ZipException ( $ "Patched data requires higher version than ({ extractVersion } )") ;
12451249 }
12461250
12471251 // Central header flags match local entry flags.
12481252 if ( ( int ) localFlags != entry . Flags )
12491253 {
1250- throw new ZipException ( "Central header/local header flags mismatch" ) ;
1254+ throw new ZipException ( $ "Central header/local header flags mismatch ( { ( GeneralBitFlags ) entry . Flags : F } vs { localFlags : F } ) ") ;
12511255 }
12521256
12531257 // Central header compression method matches local entry
1254- if ( entry . CompressionMethodForHeader != ( CompressionMethod ) compressionMethod )
1258+ if ( entry . CompressionMethodForHeader != compressionMethod )
12551259 {
1256- throw new ZipException ( "Central header/local header compression method mismatch" ) ;
1260+ throw new ZipException ( $ "Central header/local header compression method mismatch ( { entry . CompressionMethodForHeader : G } vs { compressionMethod : G } ) ") ;
12571261 }
12581262
12591263 if ( entry . Version != extractVersion )
@@ -1272,7 +1276,7 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
12721276
12731277 if ( localFlags . HasAny ( GeneralBitFlags . HeaderMasked ) )
12741278 {
1275- if ( ( fileTime != 0 ) || ( fileDate != 0 ) )
1279+ if ( fileTime != 0 || fileDate != 0 )
12761280 {
12771281 throw new ZipException ( "Header masked set but date/time values non-zero" ) ;
12781282 }
@@ -1287,8 +1291,8 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
12871291 }
12881292
12891293 // Crc valid for empty entry.
1290- // This will also apply to streamed entries where size isnt known and the header cant be patched
1291- if ( ( size == 0 ) && ( compressedSize == 0 ) )
1294+ // This will also apply to streamed entries where size isn't known and the header cant be patched
1295+ if ( size == 0 && compressedSize == 0 )
12921296 {
12931297 if ( crcValue != 0 )
12941298 {
@@ -1351,20 +1355,15 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
13511355 if ( ! localFlags . HasAny ( GeneralBitFlags . Descriptor ) ||
13521356 ( ( size > 0 || compressedSize > 0 ) && entry . Size > 0 ) )
13531357 {
1354- if ( ( size != 0 )
1355- && ( size != entry . Size ) )
1358+ if ( size != 0 && size != entry . Size )
13561359 {
1357- throw new ZipException (
1358- string . Format ( "Size mismatch between central header({0}) and local header({1})" ,
1359- entry . Size , size ) ) ;
1360+ throw new ZipException ( $ "Size mismatch between central header ({ entry . Size } ) and local header ({ size } )") ;
13601361 }
13611362
1362- if ( ( compressedSize != 0 )
1363+ if ( compressedSize != 0
13631364 && ( compressedSize != entry . CompressedSize && compressedSize != 0xFFFFFFFF && compressedSize != - 1 ) )
13641365 {
1365- throw new ZipException (
1366- string . Format ( "Compressed size mismatch between central header({0}) and local header({1})" ,
1367- entry . CompressedSize , compressedSize ) ) ;
1366+ throw new ZipException ( $ "Compressed size mismatch between central header({ entry . CompressedSize } ) and local header({ compressedSize } )") ;
13681367 }
13691368 }
13701369
@@ -3502,20 +3501,16 @@ private void ReadEntries()
35023501 }
35033502
35043503 bool isZip64 = false ;
3505- bool requireZip64 = false ;
3506-
3504+
35073505 // Check if zip64 header information is required.
3508- if ( ( thisDiskNumber == 0xffff ) ||
3509- ( startCentralDirDisk == 0xffff ) ||
3510- ( entriesForThisDisk == 0xffff ) ||
3511- ( entriesForWholeCentralDir == 0xffff ) ||
3512- ( centralDirSize == 0xffffffff ) ||
3513- ( offsetOfCentralDir == 0xffffffff ) )
3514- {
3515- requireZip64 = true ;
3516- }
3517-
3518- // #357 - always check for the existance of the Zip64 central directory.
3506+ bool requireZip64 = thisDiskNumber == 0xffff ||
3507+ startCentralDirDisk == 0xffff ||
3508+ entriesForThisDisk == 0xffff ||
3509+ entriesForWholeCentralDir == 0xffff ||
3510+ centralDirSize == 0xffffffff ||
3511+ offsetOfCentralDir == 0xffffffff ;
3512+
3513+ // #357 - always check for the existence of the Zip64 central directory.
35193514 // #403 - Take account of the fixed size of the locator when searching.
35203515 // Subtract from locatedEndOfCentralDir so that the endLocation is the location of EndOfCentralDirectorySignature,
35213516 // rather than the data following the signature.
@@ -3549,7 +3544,7 @@ private void ReadEntries()
35493544
35503545 if ( sig64 != ZipConstants . Zip64CentralFileHeaderSignature )
35513546 {
3552- throw new ZipException ( string . Format ( "Invalid Zip64 Central directory signature at {0 :X}" , offset64 ) ) ;
3547+ throw new ZipException ( $ "Invalid Zip64 Central directory signature at { offset64 : X} ") ;
35533548 }
35543549
35553550 // NOTE: Record size = SizeOfFixedFields + SizeOfVariableData - 12.
@@ -3604,8 +3599,11 @@ private void ReadEntries()
36043599 int extraLen = ReadLEUshort ( ) ;
36053600 int commentLen = ReadLEUshort ( ) ;
36063601
3607- int diskStartNo = ReadLEUshort ( ) ; // Not currently used
3608- int internalAttributes = ReadLEUshort ( ) ; // Not currently used
3602+
3603+ // ReSharper disable once UnusedVariable, Currently unused but needs to be read to offset the stream
3604+ int diskStartNo = ReadLEUshort ( ) ;
3605+ // ReSharper disable once UnusedVariable, Currently unused but needs to be read to offset the stream
3606+ int internalAttributes = ReadLEUshort ( ) ;
36093607
36103608 uint externalAttributes = ReadLEUint ( ) ;
36113609 long offset = ReadLEUint ( ) ;
@@ -3629,7 +3627,7 @@ private void ReadEntries()
36293627 ExternalFileAttributes = ( int ) externalAttributes
36303628 } ;
36313629
3632- if ( ( bitFlags & 8 ) == 0 )
3630+ if ( ! entry . HasFlag ( GeneralBitFlags . Descriptor ) )
36333631 {
36343632 entry . CryptoCheckValue = ( byte ) ( crc >> 24 ) ;
36353633 }
@@ -3672,9 +3670,15 @@ private void ReadEntries()
36723670 /// </exception>
36733671 private long LocateEntry ( ZipEntry entry )
36743672 {
3675- return TestLocalHeader ( entry , HeaderTest . Extract ) ;
3673+ return TestLocalHeader ( entry , SkipLocalEntryTestsOnLocate ? HeaderTest . None : HeaderTest . Extract ) ;
36763674 }
36773675
3676+ /// <summary>
3677+ /// Skip the verification of the local header when reading an archive entry. Set this to attempt to read the
3678+ /// entries even if the headers should indicate that doing so would fail or produce an unexpected output.
3679+ /// </summary>
3680+ public bool SkipLocalEntryTestsOnLocate { get ; set ; } = false ;
3681+
36783682 private Stream CreateAndInitDecryptionStream ( Stream baseStream , ZipEntry entry )
36793683 {
36803684 CryptoStream result = null ;
@@ -3691,15 +3695,15 @@ private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry)
36913695 }
36923696 int saltLen = entry . AESSaltLen ;
36933697 byte [ ] saltBytes = new byte [ saltLen ] ;
3694- int saltIn = StreamUtils . ReadRequestedBytes ( baseStream , saltBytes , 0 , saltLen ) ;
3695- if ( saltIn != saltLen )
3696- throw new ZipException ( "AES Salt expected " + saltLen + " got " + saltIn ) ;
3697- //
3698+ int saltIn = StreamUtils . ReadRequestedBytes ( baseStream , saltBytes , offset : 0 , saltLen ) ;
3699+
3700+ if ( saltIn != saltLen ) throw new ZipException ( $ "AES Salt expected { saltLen } git { saltIn } " ) ;
3701+
36983702 byte [ ] pwdVerifyRead = new byte [ 2 ] ;
36993703 StreamUtils . ReadFully ( baseStream , pwdVerifyRead ) ;
37003704 int blockSize = entry . AESKeySize / 8 ; // bits to bytes
37013705
3702- var decryptor = new ZipAESTransform ( rawPassword_ , saltBytes , blockSize , false ) ;
3706+ var decryptor = new ZipAESTransform ( rawPassword_ , saltBytes , blockSize , writeMode : false ) ;
37033707 byte [ ] pwdVerifyCalc = decryptor . PwdVerifier ;
37043708 if ( pwdVerifyCalc [ 0 ] != pwdVerifyRead [ 0 ] || pwdVerifyCalc [ 1 ] != pwdVerifyRead [ 1 ] )
37053709 throw new ZipException ( "Invalid password for AES" ) ;
@@ -3712,8 +3716,7 @@ private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry)
37123716 }
37133717 else
37143718 {
3715- if ( ( entry . Version < ZipConstants . VersionStrongEncryption )
3716- || ( entry . Flags & ( int ) GeneralBitFlags . StrongEncryption ) == 0 )
3719+ if ( entry . Version < ZipConstants . VersionStrongEncryption || ! entry . HasFlag ( GeneralBitFlags . StrongEncryption ) )
37173720 {
37183721 var classicManaged = new PkzipClassicManaged ( ) ;
37193722
@@ -3738,31 +3741,29 @@ private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry)
37383741
37393742 private Stream CreateAndInitEncryptionStream ( Stream baseStream , ZipEntry entry )
37403743 {
3741- CryptoStream result = null ;
3742- if ( ( entry . Version < ZipConstants . VersionStrongEncryption )
3743- || ( entry . Flags & ( int ) GeneralBitFlags . StrongEncryption ) == 0 )
3744- {
3745- var classicManaged = new PkzipClassicManaged ( ) ;
3744+ if ( entry . Version >= ZipConstants . VersionStrongEncryption &&
3745+ entry . HasFlag ( GeneralBitFlags . StrongEncryption ) ) return null ;
37463746
3747- OnKeysRequired ( entry . Name ) ;
3748- if ( HaveKeys == false )
3749- {
3750- throw new ZipException ( "No password available for encrypted stream" ) ;
3751- }
3747+ var classicManaged = new PkzipClassicManaged ( ) ;
37523748
3753- // Closing a CryptoStream will close the base stream as well so wrap it in an UncompressedStream
3754- // which doesnt do this.
3755- result = new CryptoStream ( new UncompressedStream ( baseStream ) ,
3756- classicManaged . CreateEncryptor ( key , null ) , CryptoStreamMode . Write ) ;
3749+ OnKeysRequired ( entry . Name ) ;
3750+ if ( HaveKeys == false )
3751+ {
3752+ throw new ZipException ( "No password available for encrypted stream" ) ;
3753+ }
37573754
3758- if ( ( entry . Crc < 0 ) || ( entry . Flags & 8 ) != 0 )
3759- {
3760- WriteEncryptionHeader ( result , entry . DosTime << 16 ) ;
3761- }
3762- else
3763- {
3764- WriteEncryptionHeader ( result , entry . Crc ) ;
3765- }
3755+ // Closing a CryptoStream will close the base stream as well so wrap it in an UncompressedStream
3756+ // which doesnt do this.
3757+ var result = new CryptoStream ( new UncompressedStream ( baseStream ) ,
3758+ classicManaged . CreateEncryptor ( key , null ) , CryptoStreamMode . Write ) ;
3759+
3760+ if ( entry . Crc < 0 || entry . HasFlag ( GeneralBitFlags . Descriptor ) )
3761+ {
3762+ WriteEncryptionHeader ( result , entry . DosTime << 16 ) ;
3763+ }
3764+ else
3765+ {
3766+ WriteEncryptionHeader ( result , entry . Crc ) ;
37663767 }
37673768 return result ;
37683769 }
@@ -3785,7 +3786,7 @@ private static void WriteEncryptionHeader(Stream stream, long crcValue)
37853786 rng . GetBytes ( cryptBuffer ) ;
37863787 }
37873788 cryptBuffer [ 11 ] = ( byte ) ( crcValue >> 24 ) ;
3788- stream . Write ( cryptBuffer , 0 , cryptBuffer . Length ) ;
3789+ stream . Write ( cryptBuffer , offset : 0 , cryptBuffer . Length ) ;
37893790 }
37903791
37913792 #endregion Internal routines
0 commit comments