in src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs [1099:1374]
private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
{
lock (baseStream_)
{
bool testHeader = (tests & HeaderTest.Header) != 0;
bool testData = (tests & HeaderTest.Extract) != 0;
var entryAbsOffset = offsetOfFirstEntry + entry.Offset;
baseStream_.Seek(entryAbsOffset, SeekOrigin.Begin);
var signature = (int)ReadLEUint();
if (signature != ZipConstants.LocalHeaderSignature)
{
throw new ZipException(string.Format("Wrong local header signature at 0x{0:x}, expected 0x{1:x8}, actual 0x{2:x8}",
entryAbsOffset, ZipConstants.LocalHeaderSignature, signature));
}
var extractVersion = (short)(ReadLEUshort() & 0x00ff);
var localFlags = (short)ReadLEUshort();
var compressionMethod = (short)ReadLEUshort();
var fileTime = (short)ReadLEUshort();
var fileDate = (short)ReadLEUshort();
uint crcValue = ReadLEUint();
long compressedSize = ReadLEUint();
long size = ReadLEUint();
int storedNameLength = ReadLEUshort();
int extraDataLength = ReadLEUshort();
byte[] nameData = new byte[storedNameLength];
StreamUtils.ReadFully(baseStream_, nameData);
byte[] extraData = new byte[extraDataLength];
StreamUtils.ReadFully(baseStream_, extraData);
var localExtraData = new ZipExtraData(extraData);
// Extra data / zip64 checks
if (localExtraData.Find(1))
{
// 2010-03-04 Forum 10512: removed checks for version >= ZipConstants.VersionZip64
// and size or compressedSize = MaxValue, due to rogue creators.
size = localExtraData.ReadLong();
compressedSize = localExtraData.ReadLong();
if ((localFlags & (int)GeneralBitFlags.Descriptor) != 0)
{
// These may be valid if patched later
if ((size != -1) && (size != entry.Size))
{
throw new ZipException("Size invalid for descriptor");
}
if ((compressedSize != -1) && (compressedSize != entry.CompressedSize))
{
throw new ZipException("Compressed size invalid for descriptor");
}
}
}
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");
}
}
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));
}
var localEncoding = _stringCodec.ZipInputEncoding(localFlags);
// 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.CompressionMethodForHeader != (CompressionMethod)compressionMethod)
{
throw new ZipException("Central header/local header compression method mismatch");
}
if (entry.Version != extractVersion)
{
throw new ZipException("Extract version 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.
// This will also apply to streamed entries where size isnt known and the header cant be patched
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");
}
// Name data has already been read convert it and compare.
string localName = localEncoding.GetString(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 actual size but can have compressed size
if (entry.IsDirectory)
{
if (size > 0)
{
throw new ZipException("Directory cannot have size");
}
// There may be other cases where the compressed size can be greater than this?
// If so until details are known we will be strict.
if (entry.IsCrypted)
{
if (compressedSize > entry.EncryptionOverheadSize + 2)
{
throw new ZipException("Directory compressed size invalid");
}
}
else if (compressedSize > 2)
{
// When not compressed the directory size can validly be 2 bytes
// if the true size wasn't known when data was originally being written.
// NOTE: Versions of the library 0.85.4 and earlier always added 2 bytes
throw new ZipException("Directory compressed size invalid");
}
}
if (!ZipNameTransform.IsValidName(localName, true))
{
throw new ZipException("Name is invalid");
}
}
// Tests that apply to both data and header.
// Size can be verified only if it is known in the local header.
// it will always be known in the central header.
if (((localFlags & (int)GeneralBitFlags.Descriptor) == 0) ||
((size > 0 || compressedSize > 0) && entry.Size > 0))
{
if ((size != 0)
&& (size != entry.Size))
{
throw new ZipException(
string.Format("Size mismatch between central header({0}) and local header({1})",
entry.Size, size));
}
if ((compressedSize != 0)
&& (compressedSize != entry.CompressedSize && compressedSize != 0xFFFFFFFF && compressedSize != -1))
{
throw new ZipException(
string.Format("Compressed size mismatch between central header({0}) and local header({1})",
entry.CompressedSize, compressedSize));
}
}
int extraLength = storedNameLength + extraDataLength;
return offsetOfFirstEntry + entry.Offset + ZipConstants.LocalHeaderBaseSize + extraLength;
}
}