// For AES the method in the entry is 99, and the real compression method is in the extradata // private void ProcessAESExtraData(ZipExtraData extraData) { if (extraData.Find(0x9901)) { // Set version for Zipfile.CreateAndInitDecryptionStream versionToExtract = ZipConstants.VERSION_AES; // Ver 5.1 = AES see "Version" getter // // Unpack AES extra data field see http://www.winzip.com/aes_info.htm int length = extraData.ValueLength; // Data size currently 7 if (length < 7) { throw new ZipException("AES Extra Data Length " + length + " invalid."); } int ver = extraData.ReadShort(); // Version number (1=AE-1 2=AE-2) int vendorId = extraData.ReadShort(); // 2-character vendor ID 0x4541 = "AE" int encrStrength = extraData.ReadByte(); // encryption strength 1 = 128 2 = 192 3 = 256 int actualCompress = extraData.ReadShort(); // The actual compression method used to compress the file _aesVer = ver; _aesEncryptionStrength = encrStrength; method = (CompressionMethod)actualCompress; } else { throw new ZipException("AES Extra Data missing"); } }
// Write the local file header // TODO: ZipHelperStream.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage private void WriteLocalHeader(ZipEntry entry, EntryPatchData patchData) { CompressionMethod method = entry.CompressionMethod; bool headerInfoAvailable = true; // How to get this? bool patchEntryHeader = false; WriteLEInt(ZipConstants.LocalHeaderSignature); WriteLEShort(entry.Version); WriteLEShort(entry.Flags); WriteLEShort((byte)method); WriteLEInt((int)entry.DosTime); if (headerInfoAvailable == true) { WriteLEInt((int)entry.Crc); if (entry.LocalHeaderRequiresZip64) { WriteLEInt(-1); WriteLEInt(-1); } else { WriteLEInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize : (int)entry.CompressedSize); WriteLEInt((int)entry.Size); } } else { if (patchData != null) { patchData.CrcPatchOffset = stream_.Position; } WriteLEInt(0); // Crc if (patchData != null) { patchData.SizePatchOffset = stream_.Position; } // For local header both sizes appear in Zip64 Extended Information if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) { WriteLEInt(-1); WriteLEInt(-1); } else { WriteLEInt(0); // Compressed size WriteLEInt(0); // Uncompressed size } } byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name); if (name.Length > 0xFFFF) { throw new ZipException("Entry name too long."); } var ed = new ZipExtraData(entry.ExtraData); if (entry.LocalHeaderRequiresZip64 && (headerInfoAvailable || patchEntryHeader)) { ed.StartNewEntry(); if (headerInfoAvailable) { ed.AddLeLong(entry.Size); ed.AddLeLong(entry.CompressedSize); } else { ed.AddLeLong(-1); ed.AddLeLong(-1); } ed.AddNewEntry(1); if (!ed.Find(1)) { throw new ZipException("Internal error cant find extra data"); } if (patchData != null) { patchData.SizePatchOffset = ed.CurrentReadIndex; } } else { ed.Delete(1); } byte[] extra = ed.GetEntryData(); WriteLEShort(name.Length); WriteLEShort(extra.Length); if (name.Length > 0) { stream_.Write(name, 0, name.Length); } if (entry.LocalHeaderRequiresZip64 && patchEntryHeader) { patchData.SizePatchOffset += stream_.Position; } if (extra.Length > 0) { stream_.Write(extra, 0, extra.Length); } }
/// <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) { var extraData = new ZipExtraData(this.extra); if (extraData.Find(0x0001)) { // Version required to extract is ignored here as some archivers dont set it correctly // in theory it should be version 45 or higher // 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"); } // (localHeader ||) was deleted, because actually there is no specific difference with reading sizes between local header & central directory // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT // ... // 4.4 Explanation of fields // ... // 4.4.8 compressed size: (4 bytes) // 4.4.9 uncompressed size: (4 bytes) // // The size of the file compressed (4.4.8) and uncompressed, // (4.4.9) respectively. When a decryption header is present it // will be placed in front of the file data and the value of the // compressed file size will include the bytes of the decryption // header. If bit 3 of the general purpose bit flag is set, // these fields are set to zero in the local header and the // correct values are put in the data descriptor and // in the central directory. If an archive is in ZIP64 format // and the value in this field is 0xFFFFFFFF, the size will be // in the corresponding 8 byte ZIP64 extended information // extra field. When encrypting the central directory, if the // local header is not in ZIP64 format and general purpose bit // flag 13 is set indicating masking, the value stored for the // uncompressed size in the Local Header will be zero. // // Othewise there is problem with minizip implementation if (size == uint.MaxValue) { size = (ulong)extraData.ReadLong(); } if (compressedSize == uint.MaxValue) { compressedSize = (ulong)extraData.ReadLong(); } if (!localHeader && (offset == uint.MaxValue)) { offset = extraData.ReadLong(); } // Disk number on which file starts is ignored } else { if ( ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) && ((size == uint.MaxValue) || (compressedSize == uint.MaxValue)) ) { throw new ZipException("Zip64 Extended information required but is missing."); } } DateTime = GetDateTime(extraData); if (method == CompressionMethod.WinZipAES) { ProcessAESExtraData(extraData); } }