internal void ProcessExtraData(bool localHeader) { ZipExtraData zipExtraData = new ZipExtraData(this.extra); if (zipExtraData.Find(1)) { if ((this.versionToExtract & 255) < 45) { throw new ZipException("Zip64 Extended information found but version is not valid"); } this.forceZip64_ = true; if (zipExtraData.ValueLength < 4) { throw new ZipException("Extra data extended Zip64 information length is invalid"); } if (localHeader || this.size == unchecked ((ulong)-1)) { this.size = (ulong)zipExtraData.ReadLong(); } if (localHeader || this.compressedSize == unchecked ((ulong)-1)) { this.compressedSize = (ulong)zipExtraData.ReadLong(); } } else if ((this.versionToExtract & 255) >= 45 && (this.size == unchecked ((ulong)-1) || this.compressedSize == unchecked ((ulong)-1))) { throw new ZipException("Zip64 Extended information required but is missing."); } if (zipExtraData.Find(21589)) { int valueLength = zipExtraData.ValueLength; int num = zipExtraData.ReadByte(); if ((num & 1) != 0 && valueLength >= 5) { int seconds = zipExtraData.ReadInt(); this.DateTime = (new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() + new TimeSpan(0, 0, 0, seconds, 0)).ToLocalTime(); } } }
/// <summary> /// Test a local header against that provided from the central directory /// </summary> /// <param name="entry"> /// The entry to test against /// </param> /// <param name="tests">The type of <see cref="HeaderTest">tests</see> to carry out.</param> /// <returns>The offset of the entries data in the file</returns> long TestLocalHeader(ZipEntry entry, HeaderTest tests) { lock(baseStream_) { bool testHeader = (tests & HeaderTest.Header) != 0; bool testData = (tests & HeaderTest.Extract) != 0; baseStream_.Seek(offsetOfFirstEntry + entry.Offset, SeekOrigin.Begin); if ((int)ReadLEUint() != ZipConstants.LocalHeaderSignature) { throw new ZipException(string.Format("Wrong local header signature @{0:X}", offsetOfFirstEntry + entry.Offset)); } short extractVersion = ( short )ReadLEUshort(); short localFlags = ( short )ReadLEUshort(); short compressionMethod = ( short )ReadLEUshort(); short fileTime = ( short )ReadLEUshort(); short fileDate = ( short )ReadLEUshort(); uint crcValue = ReadLEUint(); long size = ReadLEUint(); long compressedSize = ReadLEUint(); int storedNameLength = ReadLEUshort(); int extraDataLength = ReadLEUshort(); // eliminate system id from version (use version only) extractVersion &= 0x00FF; if ( testData ) { if ( entry.IsFile ) { if ( !entry.IsCompressionMethodSupported() ) { throw new ZipException("Compression method not supported"); } if ( (extractVersion > ZipConstants.VersionMadeBy) || ((extractVersion > 20) && (extractVersion < ZipConstants.VersionZip64)) ) { throw new ZipException(string.Format("Version required to extract this entry not supported ({0})", extractVersion)); } if ( (localFlags & ( int )(GeneralBitFlags.Patched | GeneralBitFlags.StrongEncryption | GeneralBitFlags.EnhancedCompress | GeneralBitFlags.HeaderMasked)) != 0 ) { throw new ZipException("The library does not support the zip version required to extract this entry"); } } } if ( testHeader ) { if ((extractVersion <= 63) && // Ignore later versions as we dont know about them.. (extractVersion != 10) && (extractVersion != 11) && (extractVersion != 20) && (extractVersion != 21) && (extractVersion != 25) && (extractVersion != 27) && (extractVersion != 45) && (extractVersion != 46) && (extractVersion != 50) && (extractVersion != 51) && (extractVersion != 52) && (extractVersion != 61) && (extractVersion != 62) && (extractVersion != 63) ) { throw new ZipException(string.Format("Version required to extract this entry is invalid ({0})", extractVersion)); } // Local entry flags dont have reserved bit set on. if ( (localFlags & ( int )(GeneralBitFlags.ReservedPKware4 | GeneralBitFlags.ReservedPkware14 | GeneralBitFlags.ReservedPkware15)) != 0 ) { throw new ZipException("Reserved bit flags cannot be set."); } // Encryption requires extract version >= 20 if ( ((localFlags & ( int )GeneralBitFlags.Encrypted) != 0) && (extractVersion < 20) ) { throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion)); } // Strong encryption requires encryption flag to be set and extract version >= 50. if ( (localFlags & (int)GeneralBitFlags.StrongEncryption) != 0 ) { if ( (localFlags & (int)GeneralBitFlags.Encrypted) == 0 ) { throw new ZipException("Strong encryption flag set but encryption flag is not set"); } if ( extractVersion < 50 ) { throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion)); } } // Patched entries require extract version >= 27 if ( ((localFlags & ( int )GeneralBitFlags.Patched) != 0) && (extractVersion < 27) ) { throw new ZipException(string.Format("Patched data requires higher version than ({0})", extractVersion)); } // Central header flags match local entry flags. if ( localFlags != entry.Flags ) { throw new ZipException("Central header/local header flags mismatch"); } // Central header compression method matches local entry if ( entry.CompressionMethod != ( CompressionMethod )compressionMethod ) { throw new ZipException("Central header/local header compression method mismatch"); } // Strong encryption and extract version match if ( (localFlags & ( int )GeneralBitFlags.StrongEncryption) != 0 ) { if ( extractVersion < 62 ) { throw new ZipException("Strong encryption flag set but version not high enough"); } } if ( (localFlags & ( int )GeneralBitFlags.HeaderMasked) != 0 ) { if ( (fileTime != 0) || (fileDate != 0) ) { throw new ZipException("Header masked set but date/time values non-zero"); } } if ( (localFlags & ( int )GeneralBitFlags.Descriptor) == 0 ) { if ( crcValue != (uint)entry.Crc ) { throw new ZipException("Central header/local header crc mismatch"); } } // Crc valid for empty entry. if ( (size == 0) && (compressedSize == 0) ) { if ( crcValue != 0 ) { throw new ZipException("Invalid CRC for empty entry"); } } // TODO: make test more correct... can't compare lengths as was done originally as this can fail for MBCS strings // Assuming a code page at this point is not valid? Best is to store the name length in the ZipEntry probably if ( entry.Name.Length > storedNameLength ) { throw new ZipException("File name length mismatch"); } byte[] nameData = new byte[storedNameLength]; StreamUtils.ReadFully(baseStream_, nameData); string localName = ZipConstants.ConvertToStringExt(localFlags, nameData); // Central directory and local entry name match if ( localName != entry.Name ) { throw new ZipException("Central header and local header file name mismatch"); } // Directories have zero size. if ( entry.IsDirectory ) { if ( (compressedSize != 0) || (size != 0) ) { throw new ZipException("Directory cannot have size"); } } if ( !ZipNameTransform.IsValidName(localName, true) ) { throw new ZipException("Name is invalid"); } byte[] data = new byte[extraDataLength]; StreamUtils.ReadFully(baseStream_, data); ZipExtraData ed = new ZipExtraData(data); // Extra data / zip64 checks if ( ed.Find(1) ) { // Zip64 extra data but 'extract version' is too low if ( extractVersion < ZipConstants.VersionZip64 ) { throw new ZipException( string.Format("Extra data contains Zip64 information but version {0}.{1} is not high enough", extractVersion / 10, extractVersion % 10)); } // Zip64 extra data but size fields dont indicate its required. if ( (( uint )size != uint.MaxValue) && (( uint )compressedSize != uint.MaxValue) ) { throw new ZipException("Entry sizes not correct for Zip64"); } size = ed.ReadLong(); compressedSize = ed.ReadLong(); } else { // No zip64 extra data but entry requires it. if ( (extractVersion >= ZipConstants.VersionZip64) && ((( uint )size == uint.MaxValue) || (( uint )compressedSize == uint.MaxValue)) ) { throw new ZipException("Required Zip64 extended information missing"); } } } int extraLength = storedNameLength + extraDataLength; return offsetOfFirstEntry + entry.Offset + ZipConstants.LocalHeaderBaseSize + extraLength; } }
/// <summary> /// Process extra data fields updating the entry based on the contents. /// </summary> /// <param name="localHeader">True if the extra data fields should be handled /// for a local header, rather than for a central header. /// </param> internal void ProcessExtraData(bool localHeader) { ZipExtraData extraData = new ZipExtraData(this.extra); if (extraData.Find(0x0001)) { if ((versionToExtract & 0xff) < ZipConstants.VersionZip64) { throw new ZipException("Zip64 Extended information found but version is not valid"); } // The recorded size will change but remember that this is zip64. forceZip64_ = true; if (extraData.ValueLength < 4) { throw new ZipException("Extra data extended Zip64 information length is invalid"); } if (localHeader || (size == uint.MaxValue)) { size = (ulong)extraData.ReadLong(); } if (localHeader || (compressedSize == uint.MaxValue)) { compressedSize = (ulong)extraData.ReadLong(); } } else { if ( ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) && ((size == uint.MaxValue) || (compressedSize == uint.MaxValue))) { throw new ZipException("Zip64 Extended information required but is missing."); } } /* TODO: Testing for handling of windows extra data * if ( extraData.Find(10) ) { * // No room for any tags. * if ( extraData.ValueLength < 8 ) { * throw new ZipException("NTFS Extra data invalid"); * } * * extraData.ReadInt(); // Reserved * * while ( extraData.UnreadCount >= 4 ) { * int ntfsTag = extraData.ReadShort(); * int ntfsLength = extraData.ReadShort(); * if ( ntfsTag == 1 ) { * if ( ntfsLength >= 24 ) { * long lastModification = extraData.ReadLong(); * long lastAccess = extraData.ReadLong(); * long createTime = extraData.ReadLong(); * * DateTime = System.DateTime.FromFileTime(lastModification); * } * break; * } * else { * // An unknown NTFS tag so simply skip it. * extraData.Skip(ntfsLength); * } * } * } * else */ if (extraData.Find(0x5455)) { int length = extraData.ValueLength; int flags = extraData.ReadByte(); // Can include other times but these are ignored. Length of data should // actually be 1 + 4 * no of bits in flags. if (((flags & 1) != 0) && (length >= 5)) { int iTime = extraData.ReadInt(); DateTime = (new System.DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() + new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime(); } } }
/// <summary> /// Process extra data fields updating the entry based on the contents. /// </summary> /// <param name="localHeader">True if the extra data fields should be handled /// for a local header, rather than for a central header. /// </param> internal void ProcessExtraData(bool localHeader) { ZipExtraData extraData = new ZipExtraData(this.extra); if ( extraData.Find(0x0001) ) { if ( (versionToExtract & 0xff) < ZipConstants.VersionZip64 ) { throw new ZipException("Zip64 Extended information found but version is not valid"); } // The recorded size will change but remember that this is zip64. forceZip64_ = true; if ( extraData.ValueLength < 4 ) { throw new ZipException("Extra data extended Zip64 information length is invalid"); } if ( localHeader || (size == uint.MaxValue) ) { size = (ulong)extraData.ReadLong(); } if ( localHeader || (compressedSize == uint.MaxValue) ) { compressedSize = (ulong)extraData.ReadLong(); } if ( !localHeader && (offset == uint.MaxValue) ) { offset = extraData.ReadLong(); } } else { if ( ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) && ((size == uint.MaxValue) || (compressedSize == uint.MaxValue)) ) { throw new ZipException("Zip64 Extended information required but is missing."); } } if ( extraData.Find(10) ) { // No room for any tags. if ( extraData.ValueLength < 8 ) { throw new ZipException("NTFS Extra data invalid"); } extraData.ReadInt(); // Reserved while ( extraData.UnreadCount >= 4 ) { int ntfsTag = extraData.ReadShort(); int ntfsLength = extraData.ReadShort(); if ( ntfsTag == 1 ) { if ( ntfsLength >= 24 ) { long lastModification = extraData.ReadLong(); long lastAccess = extraData.ReadLong(); long createTime = extraData.ReadLong(); DateTime = System.DateTime.FromFileTime(lastModification); } break; } else { // An unknown NTFS tag so simply skip it. extraData.Skip(ntfsLength); } } } else if ( extraData.Find(0x5455) ) { int length = extraData.ValueLength; int flags = extraData.ReadByte(); // Can include other times but these are ignored. Length of data should // actually be 1 + 4 * no of bits in flags. if ( ((flags & 1) != 0) && (length >= 5) ) { int iTime = extraData.ReadInt(); DateTime = (new System.DateTime ( 1970, 1, 1, 0, 0, 0 ).ToUniversalTime() + new TimeSpan ( 0, 0, 0, iTime, 0 )).ToLocalTime(); } } }
internal void ProcessExtraData(bool localHeader) { ZipExtraData extraData = new ZipExtraData(_extra); if (extraData.Find(1)) { _forceZip64 = true; if (extraData.ValueLength < 4) { throw new ZipException("Extra data extended Zip64 information length is invalid"); } if (localHeader || (_size == 0xffffffffL)) { _size = (ulong)extraData.ReadLong(); } if (localHeader || (_compressedSize == 0xffffffffL)) { _compressedSize = (ulong)extraData.ReadLong(); } if (!localHeader && (Offset == 0xffffffffL)) { Offset = extraData.ReadLong(); } } else if (((_versionToExtract & 0xff) >= 0x2d) && ((_size == 0xffffffffL) || (_compressedSize == 0xffffffffL))) { throw new ZipException("Zip64 Extended information required but is missing."); } if (extraData.Find(10)) { if (extraData.ValueLength < 4) { throw new ZipException("NTFS Extra data invalid"); } extraData.ReadInt(); while (extraData.UnreadCount >= 4) { int num = extraData.ReadShort(); int amount = extraData.ReadShort(); if (num == 1) { if (amount >= 0x18) { long fileTime = extraData.ReadLong(); extraData.ReadLong(); extraData.ReadLong(); DateTime = DateTime.FromFileTime(fileTime); } break; } extraData.Skip(amount); } } else if (extraData.Find(0x5455)) { int valueLength = extraData.ValueLength; if (((extraData.ReadByte() & 1) != 0) && (valueLength >= 5)) { int seconds = extraData.ReadInt(); DateTime time = new DateTime(0x7b2, 1, 1, 0, 0, 0); DateTime = (time.ToUniversalTime() + new TimeSpan(0, 0, 0, seconds, 0)).ToLocalTime(); } } if (_method == CompressionMethod.WinZipAes) { ProcessAesExtraData(extraData); } }
private long TestLocalHeader(ZipEntry entry, HeaderTest tests) { lock (_baseStream) { var flag = (tests & HeaderTest.Header) != 0; var flag2 = (tests & HeaderTest.Extract) != 0; _baseStream.Seek(_offsetOfFirstEntry + entry.Offset, SeekOrigin.Begin); if (ReadLeUint() != ZipConstants.LocalHeaderSignature) { throw new ZipException($"Wrong local header signature @{_offsetOfFirstEntry + entry.Offset:X}"); } var num = (short)ReadLeUshort(); var flags = (short)ReadLeUshort(); var num3 = (short)ReadLeUshort(); var num4 = (short)ReadLeUshort(); var num5 = (short)ReadLeUshort(); var num6 = ReadLeUint(); long num7 = ReadLeUint(); long num8 = ReadLeUint(); int num9 = ReadLeUshort(); int num10 = ReadLeUshort(); var buffer = new byte[num9]; StreamUtils.ReadFully(_baseStream, buffer); var buffer2 = new byte[num10]; StreamUtils.ReadFully(_baseStream, buffer2); var data = new ZipExtraData(buffer2); if (data.Find(1)) { num8 = data.ReadLong(); num7 = data.ReadLong(); if ((flags & 8) != 0) { if ((num8 != -1L) && (num8 != entry.Size)) { throw new ZipException("Size invalid for descriptor"); } if ((num7 != -1L) && (num7 != entry.CompressedSize)) { throw new ZipException("Compressed size invalid for descriptor"); } } } else if ((num >= 0x2d) && ((((uint)num8) == uint.MaxValue) || (((uint)num7) == uint.MaxValue))) { throw new ZipException("Required Zip64 extended information missing"); } if (flag2 && entry.IsFile) { if (!entry.IsCompressionMethodSupported()) { throw new ZipException("Compression method not supported"); } if ((num > 0x33) || ((num > 20) && (num < 0x2d))) { throw new ZipException($"Version required to extract this entry not supported ({num})"); } if ((flags & 0x3060) != 0) { throw new ZipException("The library does not support the zip version required to extract this entry"); } } if (flag) { if (((((num <= 0x3f) && (num != 10)) && ((num != 11) && (num != 20))) && (((num != 0x15) && (num != 0x19)) && ((num != 0x1b) && (num != 0x2d)))) && ((((num != ZipConstants.CentralHeaderBaseSize) && (num != 50)) && ((num != 0x33) && (num != 0x34))) && (((num != 0x3d) && (num != 0x3e)) && (num != 0x3f)))) { throw new ZipException($"Version required to extract this entry is invalid ({num})"); } if ((flags & 0xc010) != 0) { throw new ZipException("Reserved bit flags cannot be set."); } if (((flags & 1) != 0) && (num < 20)) { throw new ZipException( $"Version required to extract this entry is too low for encryption ({num})"); } if ((flags & 0x40) != 0) { if ((flags & 1) == 0) { throw new ZipException("Strong encryption flag set but encryption flag is not set"); } if (num < 50) { throw new ZipException( $"Version required to extract this entry is too low for encryption ({num})"); } } if (((flags & 0x20) != 0) && (num < 0x1b)) { throw new ZipException($"Patched data requires higher version than ({num})"); } if (flags != entry.Flags) { throw new ZipException("Central header/local header flags mismatch"); } if (entry.CompressionMethod != ((CompressionMethod)num3)) { throw new ZipException("Central header/local header compression method mismatch"); } if (entry.Version != num) { throw new ZipException("Extract version mismatch"); } if (((flags & 0x40) != 0) && (num < 0x3e)) { throw new ZipException("Strong encryption flag set but version not high enough"); } if (((flags & 0x2000) != 0) && ((num4 != 0) || (num5 != 0))) { throw new ZipException("Header masked set but date/time values non-zero"); } if (((flags & 8) == 0) && (num6 != ((uint)entry.Crc))) { throw new ZipException("Central header/local header crc mismatch"); } if (((num8 == 0L) && (num7 == 0L)) && (num6 != 0)) { throw new ZipException("Invalid CRC for empty entry"); } if (entry.Name.Length > num9) { throw new ZipException("File name length mismatch"); } var name = ZipConstants.ConvertToStringExt(flags, buffer); if (name != entry.Name) { throw new ZipException("Central header and local header file name mismatch"); } if (entry.IsDirectory) { if (num8 > 0L) { throw new ZipException("Directory cannot have size"); } if (entry.IsCrypted) { if (num7 > 14L) { throw new ZipException("Directory compressed size invalid"); } } else if (num7 > 2L) { throw new ZipException("Directory compressed size invalid"); } } if (!ZipNameTransform.IsValidName(name, true)) { throw new ZipException("Name is invalid"); } } if ((((flags & 8) == 0) || (num8 > 0L)) || (num7 > 0L)) { if (num8 != entry.Size) { throw new ZipException( $"Size mismatch between central header({entry.Size}) and local header({num8})"); } if (((num7 != entry.CompressedSize) && (num7 != 0xffffffffL)) && (num7 != -1L)) { throw new ZipException( $"Compressed size mismatch between central header({entry.CompressedSize}) and local header({num7})"); } } var num11 = num9 + num10; return (((_offsetOfFirstEntry + entry.Offset) + 30L) + num11); } }