ZipEntry.Read.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. // ZipEntry.Read.cs
  2. // ------------------------------------------------------------------
  3. //
  4. // Copyright (c) 2009-2011 Dino Chiesa
  5. // All rights reserved.
  6. //
  7. // This code module is part of DotNetZip, a zipfile class library.
  8. //
  9. // ------------------------------------------------------------------
  10. //
  11. // This code is licensed under the Microsoft Public License.
  12. // See the file License.txt for the license details.
  13. // More info on: http://dotnetzip.codeplex.com
  14. //
  15. // ------------------------------------------------------------------
  16. //
  17. // last saved (in emacs):
  18. // Time-stamp: <2011-July-09 21:31:28>
  19. //
  20. // ------------------------------------------------------------------
  21. //
  22. // This module defines logic for Reading the ZipEntry from a
  23. // zip file.
  24. //
  25. // ------------------------------------------------------------------
  26. using System;
  27. using System.IO;
  28. namespace Ionic.Zip
  29. {
  30. public partial class ZipEntry
  31. {
  32. private int _readExtraDepth;
  33. private void ReadExtraField()
  34. {
  35. _readExtraDepth++;
  36. // workitem 8098: ok (restore)
  37. long posn = this.ArchiveStream.Position;
  38. this.ArchiveStream.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
  39. // workitem 10178
  40. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
  41. byte[] block = new byte[30];
  42. this.ArchiveStream.Read(block, 0, block.Length);
  43. int i = 26;
  44. Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
  45. Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
  46. // workitem 8098: ok (relative)
  47. this.ArchiveStream.Seek(filenameLength, SeekOrigin.Current);
  48. // workitem 10178
  49. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
  50. ProcessExtraField(this.ArchiveStream, extraFieldLength);
  51. // workitem 8098: ok (restore)
  52. this.ArchiveStream.Seek(posn, SeekOrigin.Begin);
  53. // workitem 10178
  54. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
  55. _readExtraDepth--;
  56. }
  57. private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding)
  58. {
  59. int bytesRead = 0;
  60. // change for workitem 8098
  61. ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position;
  62. int signature = Ionic.Zip.SharedUtilities.ReadEntrySignature(ze.ArchiveStream);
  63. bytesRead += 4;
  64. // Return false if this is not a local file header signature.
  65. if (ZipEntry.IsNotValidSig(signature))
  66. {
  67. // Getting "not a ZipEntry signature" is not always wrong or an error.
  68. // This will happen after the last entry in a zipfile. In that case, we
  69. // expect to read :
  70. // a ZipDirEntry signature (if a non-empty zip file) or
  71. // a ZipConstants.EndOfCentralDirectorySignature.
  72. //
  73. // Anything else is a surprise.
  74. ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature
  75. // workitem 10178
  76. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
  77. if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature))
  78. {
  79. throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position));
  80. }
  81. return false;
  82. }
  83. byte[] block = new byte[26];
  84. int n = ze.ArchiveStream.Read(block, 0, block.Length);
  85. if (n != block.Length) return false;
  86. bytesRead += n;
  87. int i = 0;
  88. ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256);
  89. ze._BitField = (Int16)(block[i++] + block[i++] * 256);
  90. ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256);
  91. ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
  92. // transform the time data into something usable (a DateTime)
  93. ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob);
  94. ze._timestamp |= ZipEntryTimestamp.DOS;
  95. if ((ze._BitField & 0x01) == 0x01)
  96. {
  97. ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field
  98. ze._sourceIsEncrypted = true;
  99. }
  100. // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and
  101. // CRC values are not true values; the true values will follow the entry data.
  102. // But, regardless of the status of bit 3 in the bitfield, the slots for
  103. // the three amigos may contain marker values for ZIP64. So we must read them.
  104. {
  105. ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
  106. ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
  107. ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
  108. if ((uint)ze._CompressedSize == 0xFFFFFFFF ||
  109. (uint)ze._UncompressedSize == 0xFFFFFFFF)
  110. ze._InputUsesZip64 = true;
  111. }
  112. Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
  113. Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
  114. block = new byte[filenameLength];
  115. n = ze.ArchiveStream.Read(block, 0, block.Length);
  116. bytesRead += n;
  117. // if the UTF8 bit is set for this entry, override the
  118. // encoding the application requested.
  119. if ((ze._BitField & 0x0800) == 0x0800)
  120. {
  121. // workitem 12744
  122. ze.AlternateEncoding = System.Text.Encoding.UTF8;
  123. ze.AlternateEncodingUsage = ZipOption.Always;
  124. }
  125. // need to use this form of GetString() for .NET CF
  126. ze._FileNameInArchive = ze.AlternateEncoding.GetString(block, 0, block.Length);
  127. // workitem 6898
  128. if (ze._FileNameInArchive.EndsWith("/")) ze.MarkAsDirectory();
  129. bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength);
  130. ze._LengthOfTrailer = 0;
  131. // workitem 6607 - don't read for directories
  132. // actually get the compressed size and CRC if necessary
  133. if (!ze._FileNameInArchive.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008)
  134. {
  135. // This descriptor exists only if bit 3 of the general
  136. // purpose bit flag is set (see below). It is byte aligned
  137. // and immediately follows the last byte of compressed data,
  138. // as well as any encryption trailer, as with AES.
  139. // This descriptor is used only when it was not possible to
  140. // seek in the output .ZIP file, e.g., when the output .ZIP file
  141. // was standard output or a non-seekable device. For ZIP64(tm) format
  142. // archives, the compressed and uncompressed sizes are 8 bytes each.
  143. // workitem 8098: ok (restore)
  144. long posn = ze.ArchiveStream.Position;
  145. // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and
  146. // a consistent data record after that. To be consistent, the data record must
  147. // indicate the length of the entry data.
  148. bool wantMore = true;
  149. long SizeOfDataRead = 0;
  150. int tries = 0;
  151. while (wantMore)
  152. {
  153. tries++;
  154. // We call the FindSignature shared routine to find the specified signature
  155. // in the already-opened zip archive, starting from the current cursor
  156. // position in that filestream. If we cannot find the signature, then the
  157. // routine returns -1, and the ReadHeader() method returns false,
  158. // indicating we cannot read a legal entry header. If we have found it,
  159. // then the FindSignature() method returns the number of bytes in the
  160. // stream we had to seek forward, to find the sig. We need this to
  161. // determine if the zip entry is valid, later.
  162. if (ze._container.ZipFile != null)
  163. ze._container.ZipFile.OnReadBytes(ze);
  164. long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature);
  165. if (d == -1) return false;
  166. // total size of data read (through all loops of this).
  167. SizeOfDataRead += d;
  168. if (ze._InputUsesZip64)
  169. {
  170. // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size)
  171. block = new byte[20];
  172. n = ze.ArchiveStream.Read(block, 0, block.Length);
  173. if (n != 20) return false;
  174. // do not increment bytesRead - it is for entry header only.
  175. // the data we have just read is a footer (falls after the file data)
  176. //bytesRead += n;
  177. i = 0;
  178. ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
  179. ze._CompressedSize = BitConverter.ToInt64(block, i);
  180. i += 8;
  181. ze._UncompressedSize = BitConverter.ToInt64(block, i);
  182. i += 8;
  183. ze._LengthOfTrailer += 24; // bytes including sig, CRC, Comp and Uncomp sizes
  184. }
  185. else
  186. {
  187. // read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size)
  188. block = new byte[12];
  189. n = ze.ArchiveStream.Read(block, 0, block.Length);
  190. if (n != 12) return false;
  191. // do not increment bytesRead - it is for entry header only.
  192. // the data we have just read is a footer (falls after the file data)
  193. //bytesRead += n;
  194. i = 0;
  195. ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
  196. ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
  197. ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
  198. ze._LengthOfTrailer += 16; // bytes including sig, CRC, Comp and Uncomp sizes
  199. }
  200. wantMore = (SizeOfDataRead != ze._CompressedSize);
  201. if (wantMore)
  202. {
  203. // Seek back to un-read the last 12 bytes - maybe THEY contain
  204. // the ZipEntryDataDescriptorSignature.
  205. // (12 bytes for the CRC, Comp and Uncomp size.)
  206. ze.ArchiveStream.Seek(-12, SeekOrigin.Current);
  207. // workitem 10178
  208. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
  209. // Adjust the size to account for the false signature read in
  210. // FindSignature().
  211. SizeOfDataRead += 4;
  212. }
  213. }
  214. // seek back to previous position, to prepare to read file data
  215. // workitem 8098: ok (restore)
  216. ze.ArchiveStream.Seek(posn, SeekOrigin.Begin);
  217. // workitem 10178
  218. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
  219. }
  220. ze._CompressedFileDataSize = ze._CompressedSize;
  221. // bit 0 set indicates that some kind of encryption is in use
  222. if ((ze._BitField & 0x01) == 0x01)
  223. {
  224. #if AESCRYPTO
  225. if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 ||
  226. ze.Encryption == EncryptionAlgorithm.WinZipAes256)
  227. {
  228. int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile);
  229. // read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128.
  230. ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream);
  231. bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes)
  232. // according to WinZip, the CompressedSize includes the AES Crypto framing data.
  233. ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata;
  234. ze._LengthOfTrailer += 10; // MAC
  235. }
  236. else
  237. #endif
  238. {
  239. // read in the header data for "weak" encryption
  240. ze._WeakEncryptionHeader = new byte[12];
  241. bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader);
  242. // decrease the filedata size by 12 bytes
  243. ze._CompressedFileDataSize -= 12;
  244. }
  245. }
  246. // Remember the size of the blob for this entry.
  247. // We also have the starting position in the stream for this entry.
  248. ze._LengthOfHeader = bytesRead;
  249. ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer;
  250. // We've read in the regular entry header, the extra field, and any
  251. // encryption header. The pointer in the file is now at the start of the
  252. // filedata, which is potentially compressed and encrypted. Just ahead in
  253. // the file, there are _CompressedFileDataSize bytes of data, followed by
  254. // potentially a non-zero length trailer, consisting of optionally, some
  255. // encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24
  256. // bytes).
  257. return true;
  258. }
  259. internal static int ReadWeakEncryptionHeader(Stream s, byte[] buffer)
  260. {
  261. // PKZIP encrypts the compressed data stream. Encrypted files must
  262. // be decrypted before they can be extracted.
  263. // Each PKZIP-encrypted file has an extra 12 bytes stored at the start of the data
  264. // area defining the encryption header for that file. The encryption header is
  265. // originally set to random values, and then itself encrypted, using three, 32-bit
  266. // keys. The key values are initialized using the supplied encryption password.
  267. // After each byte is encrypted, the keys are then updated using pseudo-random
  268. // number generation techniques in combination with the same CRC-32 algorithm used
  269. // in PKZIP and implemented in the CRC32.cs module in this project.
  270. // read the 12-byte encryption header
  271. int additionalBytesRead = s.Read(buffer, 0, 12);
  272. if (additionalBytesRead != 12)
  273. throw new ZipException(String.Format("Unexpected end of data at position 0x{0:X8}", s.Position));
  274. return additionalBytesRead;
  275. }
  276. private static bool IsNotValidSig(int signature)
  277. {
  278. return (signature != ZipConstants.ZipEntrySignature);
  279. }
  280. /// <summary>
  281. /// Reads one <c>ZipEntry</c> from the given stream. The content for
  282. /// the entry does not get decompressed or decrypted. This method
  283. /// basically reads metadata, and seeks.
  284. /// </summary>
  285. /// <param name="zc">the ZipContainer this entry belongs to.</param>
  286. /// <param name="first">
  287. /// true of this is the first entry being read from the stream.
  288. /// </param>
  289. /// <returns>the <c>ZipEntry</c> read from the stream.</returns>
  290. internal static ZipEntry ReadEntry(ZipContainer zc, bool first)
  291. {
  292. ZipFile zf = zc.ZipFile;
  293. Stream s = zc.ReadStream;
  294. System.Text.Encoding defaultEncoding = zc.AlternateEncoding;
  295. ZipEntry entry = new ZipEntry();
  296. entry._Source = ZipEntrySource.ZipFile;
  297. entry._container = zc;
  298. entry._archiveStream = s;
  299. if (zf != null)
  300. zf.OnReadEntry(true, null);
  301. if (first) HandlePK00Prefix(s);
  302. // Read entry header, including any encryption header
  303. if (!ReadHeader(entry, defaultEncoding)) return null;
  304. // Store the position in the stream for this entry
  305. // change for workitem 8098
  306. entry.__FileDataPosition = entry.ArchiveStream.Position;
  307. // seek past the data without reading it. We will read on Extract()
  308. s.Seek(entry._CompressedFileDataSize + entry._LengthOfTrailer, SeekOrigin.Current);
  309. // workitem 10178
  310. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
  311. // ReadHeader moves the file pointer to the end of the entry header,
  312. // as well as any encryption header.
  313. // CompressedFileDataSize includes:
  314. // the maybe compressed, maybe encrypted file data
  315. // the encryption trailer, if any
  316. // the bit 3 descriptor, if any
  317. // workitem 5306
  318. // http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306
  319. HandleUnexpectedDataDescriptor(entry);
  320. if (zf != null)
  321. {
  322. zf.OnReadBytes(entry);
  323. zf.OnReadEntry(false, entry);
  324. }
  325. return entry;
  326. }
  327. internal static void HandlePK00Prefix(Stream s)
  328. {
  329. // in some cases, the zip file begins with "PK00". This is a throwback and is rare,
  330. // but we handle it anyway. We do not change behavior based on it.
  331. uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
  332. if (datum != ZipConstants.PackedToRemovableMedia)
  333. {
  334. s.Seek(-4, SeekOrigin.Current); // unread the block
  335. // workitem 10178
  336. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
  337. }
  338. }
  339. private static void HandleUnexpectedDataDescriptor(ZipEntry entry)
  340. {
  341. Stream s = entry.ArchiveStream;
  342. // In some cases, the "data descriptor" is present, without a signature, even when
  343. // bit 3 of the BitField is NOT SET. This is the CRC, followed
  344. // by the compressed length and the uncompressed length (4 bytes for each
  345. // of those three elements). Need to check that here.
  346. //
  347. uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
  348. if (datum == entry._Crc32)
  349. {
  350. int sz = Ionic.Zip.SharedUtilities.ReadInt(s);
  351. if (sz == entry._CompressedSize)
  352. {
  353. sz = Ionic.Zip.SharedUtilities.ReadInt(s);
  354. if (sz == entry._UncompressedSize)
  355. {
  356. // ignore everything and discard it.
  357. }
  358. else
  359. {
  360. s.Seek(-12, SeekOrigin.Current); // unread the three blocks
  361. // workitem 10178
  362. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
  363. }
  364. }
  365. else
  366. {
  367. s.Seek(-8, SeekOrigin.Current); // unread the two blocks
  368. // workitem 10178
  369. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
  370. }
  371. }
  372. else
  373. {
  374. s.Seek(-4, SeekOrigin.Current); // unread the block
  375. // workitem 10178
  376. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
  377. }
  378. }
  379. /// <summary>
  380. /// Finds a particular segment in the given extra field.
  381. /// This is used when modifying a previously-generated
  382. /// extra field, in particular when removing the AES crypto
  383. /// segment in the extra field.
  384. /// </summary>
  385. static internal int FindExtraFieldSegment(byte[] extra, int offx, UInt16 targetHeaderId)
  386. {
  387. int j = offx;
  388. while (j + 3 < extra.Length)
  389. {
  390. UInt16 headerId = (UInt16)(extra[j++] + extra[j++] * 256);
  391. if (headerId == targetHeaderId) return j-2;
  392. // else advance to next segment
  393. Int16 dataSize = (short)(extra[j++] + extra[j++] * 256);
  394. j+= dataSize;
  395. }
  396. return -1;
  397. }
  398. /// <summary>
  399. /// At current cursor position in the stream, read the extra
  400. /// field, and set the properties on the ZipEntry instance
  401. /// appropriately. This can be called when processing the
  402. /// Extra field in the Central Directory, or in the local
  403. /// header.
  404. /// </summary>
  405. internal int ProcessExtraField(Stream s, Int16 extraFieldLength)
  406. {
  407. int additionalBytesRead = 0;
  408. if (extraFieldLength > 0)
  409. {
  410. byte[] buffer = this._Extra = new byte[extraFieldLength];
  411. additionalBytesRead = s.Read(buffer, 0, buffer.Length);
  412. long posn = s.Position - additionalBytesRead;
  413. int j = 0;
  414. while (j + 3 < buffer.Length)
  415. {
  416. int start = j;
  417. UInt16 headerId = (UInt16)(buffer[j++] + buffer[j++] * 256);
  418. UInt16 dataSize = (UInt16)(buffer[j++] + buffer[j++] * 256);
  419. switch (headerId)
  420. {
  421. case 0x000a: // NTFS ctime, atime, mtime
  422. j = ProcessExtraFieldWindowsTimes(buffer, j, dataSize, posn);
  423. break;
  424. case 0x5455: // Unix ctime, atime, mtime
  425. j = ProcessExtraFieldUnixTimes(buffer, j, dataSize, posn);
  426. break;
  427. case 0x5855: // Info-zip Extra field (outdated)
  428. // This is outdated, so the field is supported on
  429. // read only.
  430. j = ProcessExtraFieldInfoZipTimes(buffer, j, dataSize, posn);
  431. break;
  432. case 0x7855: // Unix uid/gid
  433. // ignored. DotNetZip does not handle this field.
  434. break;
  435. case 0x7875: // ??
  436. // ignored. I could not find documentation on this field,
  437. // though it appears in some zip files.
  438. break;
  439. case 0x0001: // ZIP64
  440. j = ProcessExtraFieldZip64(buffer, j, dataSize, posn);
  441. break;
  442. #if AESCRYPTO
  443. case 0x9901: // WinZip AES encryption is in use. (workitem 6834)
  444. // we will handle this extra field only if compressionmethod is 0x63
  445. j = ProcessExtraFieldWinZipAes(buffer, j, dataSize, posn);
  446. break;
  447. #endif
  448. case 0x0017: // workitem 7968: handle PKWare Strong encryption header
  449. j = ProcessExtraFieldPkwareStrongEncryption(buffer, j);
  450. break;
  451. }
  452. // move to the next Header in the extra field
  453. j = start + dataSize + 4;
  454. }
  455. }
  456. return additionalBytesRead;
  457. }
  458. private int ProcessExtraFieldPkwareStrongEncryption(byte[] Buffer, int j)
  459. {
  460. // Value Size Description
  461. // ----- ---- -----------
  462. // 0x0017 2 bytes Tag for this "extra" block type
  463. // TSize 2 bytes Size of data that follows
  464. // Format 2 bytes Format definition for this record
  465. // AlgID 2 bytes Encryption algorithm identifier
  466. // Bitlen 2 bytes Bit length of encryption key
  467. // Flags 2 bytes Processing flags
  468. // CertData TSize-8 Certificate decryption extra field data
  469. // (refer to the explanation for CertData
  470. // in the section describing the
  471. // Certificate Processing Method under
  472. // the Strong Encryption Specification)
  473. j += 2;
  474. _UnsupportedAlgorithmId = (UInt16)(Buffer[j++] + Buffer[j++] * 256);
  475. _Encryption_FromZipFile = _Encryption = EncryptionAlgorithm.Unsupported;
  476. // DotNetZip doesn't support this algorithm, but we don't need to throw
  477. // here. we might just be reading the archive, which is fine. We'll
  478. // need to throw if Extract() is called.
  479. return j;
  480. }
  481. #if AESCRYPTO
  482. private int ProcessExtraFieldWinZipAes(byte[] buffer, int j, UInt16 dataSize, long posn)
  483. {
  484. if (this._CompressionMethod == 0x0063)
  485. {
  486. if ((this._BitField & 0x01) != 0x01)
  487. throw new BadReadException(String.Format(" Inconsistent metadata at position 0x{0:X16}", posn));
  488. this._sourceIsEncrypted = true;
  489. //this._aesCrypto = new WinZipAesCrypto(this);
  490. // see spec at http://www.winzip.com/aes_info.htm
  491. if (dataSize != 7)
  492. throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) in WinZip AES field at position 0x{1:X16}", dataSize, posn));
  493. this._WinZipAesMethod = BitConverter.ToInt16(buffer, j);
  494. j += 2;
  495. if (this._WinZipAesMethod != 0x01 && this._WinZipAesMethod != 0x02)
  496. throw new BadReadException(String.Format(" Unexpected vendor version number (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}",
  497. this._WinZipAesMethod, posn));
  498. Int16 vendorId = BitConverter.ToInt16(buffer, j);
  499. j += 2;
  500. if (vendorId != 0x4541)
  501. throw new BadReadException(String.Format(" Unexpected vendor ID (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}", vendorId, posn));
  502. int keystrength = (buffer[j] == 1) ? 128 : (buffer[j] == 3) ? 256 : -1;
  503. if (keystrength < 0)
  504. throw new BadReadException(String.Format("Invalid key strength ({0})", keystrength));
  505. _Encryption_FromZipFile = this._Encryption = (keystrength == 128)
  506. ? EncryptionAlgorithm.WinZipAes128
  507. : EncryptionAlgorithm.WinZipAes256;
  508. j++;
  509. // set the actual compression method
  510. this._CompressionMethod_FromZipFile =
  511. this._CompressionMethod = BitConverter.ToInt16(buffer, j);
  512. j += 2; // for the next segment of the extra field
  513. }
  514. return j;
  515. }
  516. #endif
  517. private delegate T Func<T>();
  518. private int ProcessExtraFieldZip64(byte[] buffer, int j, UInt16 dataSize, long posn)
  519. {
  520. // The PKWare spec says that any of {UncompressedSize, CompressedSize,
  521. // RelativeOffset} exceeding 0xFFFFFFFF can lead to the ZIP64 header,
  522. // and the ZIP64 header may contain one or more of those. If the
  523. // values are present, they will be found in the prescribed order.
  524. // There may also be a 4-byte "disk start number."
  525. // This means that the DataSize must be 28 bytes or less.
  526. this._InputUsesZip64 = true;
  527. // workitem 7941: check datasize before reading.
  528. if (dataSize > 28)
  529. throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) for ZIP64 extra field at position 0x{1:X16}",
  530. dataSize, posn));
  531. int remainingData = dataSize;
  532. var slurp = new Func<Int64>( () => {
  533. if (remainingData < 8)
  534. throw new BadReadException(String.Format(" Missing data for ZIP64 extra field, position 0x{0:X16}", posn));
  535. var x = BitConverter.ToInt64(buffer, j);
  536. j+= 8;
  537. remainingData -= 8;
  538. return x;
  539. });
  540. if (this._UncompressedSize == 0xFFFFFFFF)
  541. this._UncompressedSize = slurp();
  542. if (this._CompressedSize == 0xFFFFFFFF)
  543. this._CompressedSize = slurp();
  544. if (this._RelativeOffsetOfLocalHeader == 0xFFFFFFFF)
  545. this._RelativeOffsetOfLocalHeader = slurp();
  546. // Ignore anything else. Potentially there are 4 more bytes for the
  547. // disk start number. DotNetZip currently doesn't handle multi-disk
  548. // archives.
  549. return j;
  550. }
  551. private int ProcessExtraFieldInfoZipTimes(byte[] buffer, int j, UInt16 dataSize, long posn)
  552. {
  553. if (dataSize != 12 && dataSize != 8)
  554. throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for InfoZip v1 extra field at position 0x{1:X16}", dataSize, posn));
  555. Int32 timet = BitConverter.ToInt32(buffer, j);
  556. this._Mtime = _unixEpoch.AddSeconds(timet);
  557. j += 4;
  558. timet = BitConverter.ToInt32(buffer, j);
  559. this._Atime = _unixEpoch.AddSeconds(timet);
  560. j += 4;
  561. this._Ctime = DateTime.UtcNow;
  562. _ntfsTimesAreSet = true;
  563. _timestamp |= ZipEntryTimestamp.InfoZip1; return j;
  564. }
  565. private int ProcessExtraFieldUnixTimes(byte[] buffer, int j, UInt16 dataSize, long posn)
  566. {
  567. // The Unix filetimes are 32-bit unsigned integers,
  568. // storing seconds since Unix epoch.
  569. if (dataSize != 13 && dataSize != 9 && dataSize != 5)
  570. throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for Extended Timestamp extra field at position 0x{1:X16}", dataSize, posn));
  571. int remainingData = dataSize;
  572. var slurp = new Func<DateTime>( () => {
  573. Int32 timet = BitConverter.ToInt32(buffer, j);
  574. j += 4;
  575. remainingData -= 4;
  576. return _unixEpoch.AddSeconds(timet);
  577. });
  578. if (dataSize == 13 || _readExtraDepth > 0)
  579. {
  580. byte flag = buffer[j++];
  581. remainingData--;
  582. if ((flag & 0x0001) != 0 && remainingData >= 4)
  583. this._Mtime = slurp();
  584. this._Atime = ((flag & 0x0002) != 0 && remainingData >= 4)
  585. ? slurp()
  586. : DateTime.UtcNow;
  587. this._Ctime = ((flag & 0x0004) != 0 && remainingData >= 4)
  588. ? slurp()
  589. :DateTime.UtcNow;
  590. _timestamp |= ZipEntryTimestamp.Unix;
  591. _ntfsTimesAreSet = true;
  592. _emitUnixTimes = true;
  593. }
  594. else
  595. ReadExtraField(); // will recurse
  596. return j;
  597. }
  598. private int ProcessExtraFieldWindowsTimes(byte[] buffer, int j, UInt16 dataSize, long posn)
  599. {
  600. // The NTFS filetimes are 64-bit unsigned integers, stored in Intel
  601. // (least significant byte first) byte order. They are expressed as the
  602. // number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch",
  603. // which is "01-Jan-1601 00:00:00 UTC".
  604. //
  605. // HeaderId 2 bytes 0x000a == NTFS stuff
  606. // Datasize 2 bytes ?? (usually 32)
  607. // reserved 4 bytes ??
  608. // timetag 2 bytes 0x0001 == time
  609. // size 2 bytes 24 == 8 bytes each for ctime, mtime, atime
  610. // mtime 8 bytes win32 ticks since win32epoch
  611. // atime 8 bytes win32 ticks since win32epoch
  612. // ctime 8 bytes win32 ticks since win32epoch
  613. if (dataSize != 32)
  614. throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for NTFS times extra field at position 0x{1:X16}", dataSize, posn));
  615. j += 4; // reserved
  616. Int16 timetag = (Int16)(buffer[j] + buffer[j + 1] * 256);
  617. Int16 addlsize = (Int16)(buffer[j + 2] + buffer[j + 3] * 256);
  618. j += 4; // tag and size
  619. if (timetag == 0x0001 && addlsize == 24)
  620. {
  621. Int64 z = BitConverter.ToInt64(buffer, j);
  622. this._Mtime = DateTime.FromFileTimeUtc(z);
  623. j += 8;
  624. // At this point the library *could* set the LastModified value
  625. // to coincide with the Mtime value. In theory, they refer to
  626. // the same property of the file, and should be the same anyway,
  627. // allowing for differences in precision. But they are
  628. // independent quantities in the zip archive, and this library
  629. // will keep them separate in the object model. There is no ill
  630. // effect from this, because as files are extracted, the
  631. // higher-precision value (Mtime) is used if it is present.
  632. // Apps may wish to compare the Mtime versus LastModified
  633. // values, but any difference when both are present is not
  634. // germaine to the correctness of the library. but note: when
  635. // explicitly setting either value, both are set. See the setter
  636. // for LastModified or the SetNtfsTimes() method.
  637. z = BitConverter.ToInt64(buffer, j);
  638. this._Atime = DateTime.FromFileTimeUtc(z);
  639. j += 8;
  640. z = BitConverter.ToInt64(buffer, j);
  641. this._Ctime = DateTime.FromFileTimeUtc(z);
  642. j += 8;
  643. _ntfsTimesAreSet = true;
  644. _timestamp |= ZipEntryTimestamp.Windows;
  645. _emitNtfsTimes = true;
  646. }
  647. return j;
  648. }
  649. }
  650. }