ZipCrypto.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. // ZipCrypto.cs
  2. // ------------------------------------------------------------------
  3. //
  4. // Copyright (c) 2008, 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-28 06:30:59>
  19. //
  20. // ------------------------------------------------------------------
  21. //
  22. // This module provides the implementation for "traditional" Zip encryption.
  23. //
  24. // Created Tue Apr 15 17:39:56 2008
  25. //
  26. // ------------------------------------------------------------------
  27. using System;
  28. namespace Ionic.Zip
  29. {
  30. /// <summary>
  31. /// This class implements the "traditional" or "classic" PKZip encryption,
  32. /// which today is considered to be weak. On the other hand it is
  33. /// ubiquitous. This class is intended for use only by the DotNetZip
  34. /// library.
  35. /// </summary>
  36. ///
  37. /// <remarks>
  38. /// Most uses of the DotNetZip library will not involve direct calls into
  39. /// the ZipCrypto class. Instead, the ZipCrypto class is instantiated and
  40. /// used by the ZipEntry() class when encryption or decryption on an entry
  41. /// is employed. If for some reason you really wanted to use a weak
  42. /// encryption algorithm in some other application, you might use this
  43. /// library. But you would be much better off using one of the built-in
  44. /// strong encryption libraries in the .NET Framework, like the AES
  45. /// algorithm or SHA.
  46. /// </remarks>
  47. internal class ZipCrypto
  48. {
  49. /// <summary>
  50. /// The default constructor for ZipCrypto.
  51. /// </summary>
  52. ///
  53. /// <remarks>
  54. /// This class is intended for internal use by the library only. It's
  55. /// probably not useful to you. Seriously. Stop reading this
  56. /// documentation. It's a waste of your time. Go do something else.
  57. /// Check the football scores. Go get an ice cream with a friend.
  58. /// Seriously.
  59. /// </remarks>
  60. ///
  61. private ZipCrypto() { }
  62. public static ZipCrypto ForWrite(string password)
  63. {
  64. ZipCrypto z = new ZipCrypto();
  65. if (password == null)
  66. throw new BadPasswordException("This entry requires a password.");
  67. z.InitCipher(password);
  68. return z;
  69. }
  70. public static ZipCrypto ForRead(string password, ZipEntry e)
  71. {
  72. System.IO.Stream s = e._archiveStream;
  73. e._WeakEncryptionHeader = new byte[12];
  74. byte[] eh = e._WeakEncryptionHeader;
  75. ZipCrypto z = new ZipCrypto();
  76. if (password == null)
  77. throw new BadPasswordException("This entry requires a password.");
  78. z.InitCipher(password);
  79. ZipEntry.ReadWeakEncryptionHeader(s, eh);
  80. // Decrypt the header. This has a side effect of "further initializing the
  81. // encryption keys" in the traditional zip encryption.
  82. byte[] DecryptedHeader = z.DecryptMessage(eh, eh.Length);
  83. // CRC check
  84. // According to the pkzip spec, the final byte in the decrypted header
  85. // is the highest-order byte in the CRC. We check it here.
  86. if (DecryptedHeader[11] != (byte)((e._Crc32 >> 24) & 0xff))
  87. {
  88. // In the case that bit 3 of the general purpose bit flag is set to
  89. // indicate the presence of an 'Extended File Header' or a 'data
  90. // descriptor' (signature 0x08074b50), the last byte of the decrypted
  91. // header is sometimes compared with the high-order byte of the
  92. // lastmodified time, rather than the high-order byte of the CRC, to
  93. // verify the password.
  94. //
  95. // This is not documented in the PKWare Appnote.txt. It was
  96. // discovered this by analysis of the Crypt.c source file in the
  97. // InfoZip library http://www.info-zip.org/pub/infozip/
  98. //
  99. // The reason for this is that the CRC for a file cannot be known
  100. // until the entire contents of the file have been streamed. This
  101. // means a tool would have to read the file content TWICE in its
  102. // entirety in order to perform PKZIP encryption - once to compute
  103. // the CRC, and again to actually encrypt.
  104. //
  105. // This is so important for performance that using the timeblob as
  106. // the verification should be the standard practice for DotNetZip
  107. // when using PKZIP encryption. This implies that bit 3 must be
  108. // set. The downside is that some tools still cannot cope with ZIP
  109. // files that use bit 3. Therefore, DotNetZip DOES NOT force bit 3
  110. // when PKZIP encryption is in use, and instead, reads the stream
  111. // twice.
  112. //
  113. if ((e._BitField & 0x0008) != 0x0008)
  114. {
  115. throw new BadPasswordException("The password did not match.");
  116. }
  117. else if (DecryptedHeader[11] != (byte)((e._TimeBlob >> 8) & 0xff))
  118. {
  119. throw new BadPasswordException("The password did not match.");
  120. }
  121. // We have a good password.
  122. }
  123. else
  124. {
  125. // A-OK
  126. }
  127. return z;
  128. }
  129. /// <summary>
  130. /// From AppNote.txt:
  131. /// unsigned char decrypt_byte()
  132. /// local unsigned short temp
  133. /// temp :=- Key(2) | 2
  134. /// decrypt_byte := (temp * (temp ^ 1)) bitshift-right 8
  135. /// end decrypt_byte
  136. /// </summary>
  137. private byte MagicByte
  138. {
  139. get
  140. {
  141. UInt16 t = (UInt16)((UInt16)(_Keys[2] & 0xFFFF) | 2);
  142. return (byte)((t * (t ^ 1)) >> 8);
  143. }
  144. }
  145. // Decrypting:
  146. // From AppNote.txt:
  147. // loop for i from 0 to 11
  148. // C := buffer(i) ^ decrypt_byte()
  149. // update_keys(C)
  150. // buffer(i) := C
  151. // end loop
  152. /// <summary>
  153. /// Call this method on a cipher text to render the plaintext. You must
  154. /// first initialize the cipher with a call to InitCipher.
  155. /// </summary>
  156. ///
  157. /// <example>
  158. /// <code>
  159. /// var cipher = new ZipCrypto();
  160. /// cipher.InitCipher(Password);
  161. /// // Decrypt the header. This has a side effect of "further initializing the
  162. /// // encryption keys" in the traditional zip encryption.
  163. /// byte[] DecryptedMessage = cipher.DecryptMessage(EncryptedMessage);
  164. /// </code>
  165. /// </example>
  166. ///
  167. /// <param name="cipherText">The encrypted buffer.</param>
  168. /// <param name="length">
  169. /// The number of bytes to encrypt.
  170. /// Should be less than or equal to CipherText.Length.
  171. /// </param>
  172. ///
  173. /// <returns>The plaintext.</returns>
  174. public byte[] DecryptMessage(byte[] cipherText, int length)
  175. {
  176. if (cipherText == null)
  177. throw new ArgumentNullException("cipherText");
  178. if (length > cipherText.Length)
  179. throw new ArgumentOutOfRangeException("length",
  180. "Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array.");
  181. byte[] plainText = new byte[length];
  182. for (int i = 0; i < length; i++)
  183. {
  184. byte C = (byte)(cipherText[i] ^ MagicByte);
  185. UpdateKeys(C);
  186. plainText[i] = C;
  187. }
  188. return plainText;
  189. }
  190. /// <summary>
  191. /// This is the converse of DecryptMessage. It encrypts the plaintext
  192. /// and produces a ciphertext.
  193. /// </summary>
  194. ///
  195. /// <param name="plainText">The plain text buffer.</param>
  196. ///
  197. /// <param name="length">
  198. /// The number of bytes to encrypt.
  199. /// Should be less than or equal to plainText.Length.
  200. /// </param>
  201. ///
  202. /// <returns>The ciphertext.</returns>
  203. public byte[] EncryptMessage(byte[] plainText, int length)
  204. {
  205. if (plainText == null)
  206. throw new ArgumentNullException("plaintext");
  207. if (length > plainText.Length)
  208. throw new ArgumentOutOfRangeException("length",
  209. "Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array.");
  210. byte[] cipherText = new byte[length];
  211. for (int i = 0; i < length; i++)
  212. {
  213. byte C = plainText[i];
  214. cipherText[i] = (byte)(plainText[i] ^ MagicByte);
  215. UpdateKeys(C);
  216. }
  217. return cipherText;
  218. }
  219. /// <summary>
  220. /// This initializes the cipher with the given password.
  221. /// See AppNote.txt for details.
  222. /// </summary>
  223. ///
  224. /// <param name="passphrase">
  225. /// The passphrase for encrypting or decrypting with this cipher.
  226. /// </param>
  227. ///
  228. /// <remarks>
  229. /// <code>
  230. /// Step 1 - Initializing the encryption keys
  231. /// -----------------------------------------
  232. /// Start with these keys:
  233. /// Key(0) := 305419896 (0x12345678)
  234. /// Key(1) := 591751049 (0x23456789)
  235. /// Key(2) := 878082192 (0x34567890)
  236. ///
  237. /// Then, initialize the keys with a password:
  238. ///
  239. /// loop for i from 0 to length(password)-1
  240. /// update_keys(password(i))
  241. /// end loop
  242. ///
  243. /// Where update_keys() is defined as:
  244. ///
  245. /// update_keys(char):
  246. /// Key(0) := crc32(key(0),char)
  247. /// Key(1) := Key(1) + (Key(0) bitwiseAND 000000ffH)
  248. /// Key(1) := Key(1) * 134775813 + 1
  249. /// Key(2) := crc32(key(2),key(1) rightshift 24)
  250. /// end update_keys
  251. ///
  252. /// Where crc32(old_crc,char) is a routine that given a CRC value and a
  253. /// character, returns an updated CRC value after applying the CRC-32
  254. /// algorithm described elsewhere in this document.
  255. ///
  256. /// </code>
  257. ///
  258. /// <para>
  259. /// After the keys are initialized, then you can use the cipher to
  260. /// encrypt the plaintext.
  261. /// </para>
  262. ///
  263. /// <para>
  264. /// Essentially we encrypt the password with the keys, then discard the
  265. /// ciphertext for the password. This initializes the keys for later use.
  266. /// </para>
  267. ///
  268. /// </remarks>
  269. public void InitCipher(string passphrase)
  270. {
  271. byte[] p = SharedUtilities.StringToByteArray(passphrase);
  272. for (int i = 0; i < passphrase.Length; i++)
  273. UpdateKeys(p[i]);
  274. }
  275. private void UpdateKeys(byte byteValue)
  276. {
  277. _Keys[0] = (UInt32)crc32.ComputeCrc32((int)_Keys[0], byteValue);
  278. _Keys[1] = _Keys[1] + (byte)_Keys[0];
  279. _Keys[1] = _Keys[1] * 0x08088405 + 1;
  280. _Keys[2] = (UInt32)crc32.ComputeCrc32((int)_Keys[2], (byte)(_Keys[1] >> 24));
  281. }
  282. ///// <summary>
  283. ///// The byte array representing the seed keys used.
  284. ///// Get this after calling InitCipher. The 12 bytes represents
  285. ///// what the zip spec calls the "EncryptionHeader".
  286. ///// </summary>
  287. //public byte[] KeyHeader
  288. //{
  289. // get
  290. // {
  291. // byte[] result = new byte[12];
  292. // result[0] = (byte)(_Keys[0] & 0xff);
  293. // result[1] = (byte)((_Keys[0] >> 8) & 0xff);
  294. // result[2] = (byte)((_Keys[0] >> 16) & 0xff);
  295. // result[3] = (byte)((_Keys[0] >> 24) & 0xff);
  296. // result[4] = (byte)(_Keys[1] & 0xff);
  297. // result[5] = (byte)((_Keys[1] >> 8) & 0xff);
  298. // result[6] = (byte)((_Keys[1] >> 16) & 0xff);
  299. // result[7] = (byte)((_Keys[1] >> 24) & 0xff);
  300. // result[8] = (byte)(_Keys[2] & 0xff);
  301. // result[9] = (byte)((_Keys[2] >> 8) & 0xff);
  302. // result[10] = (byte)((_Keys[2] >> 16) & 0xff);
  303. // result[11] = (byte)((_Keys[2] >> 24) & 0xff);
  304. // return result;
  305. // }
  306. //}
  307. // private fields for the crypto stuff:
  308. private UInt32[] _Keys = { 0x12345678, 0x23456789, 0x34567890 };
  309. private Ionic.Crc.CRC32 crc32 = new Ionic.Crc.CRC32();
  310. }
  311. internal enum CryptoMode
  312. {
  313. Encrypt,
  314. Decrypt
  315. }
  316. /// <summary>
  317. /// A Stream for reading and concurrently decrypting data from a zip file,
  318. /// or for writing and concurrently encrypting data to a zip file.
  319. /// </summary>
  320. internal class ZipCipherStream : System.IO.Stream
  321. {
  322. private ZipCrypto _cipher;
  323. private System.IO.Stream _s;
  324. private CryptoMode _mode;
  325. /// <summary> The constructor. </summary>
  326. /// <param name="s">The underlying stream</param>
  327. /// <param name="mode">To either encrypt or decrypt.</param>
  328. /// <param name="cipher">The pre-initialized ZipCrypto object.</param>
  329. public ZipCipherStream(System.IO.Stream s, ZipCrypto cipher, CryptoMode mode)
  330. {
  331. if (s == null) throw new ArgumentNullException("s");
  332. _cipher = cipher;
  333. _s = s;
  334. _mode = mode;
  335. }
  336. public override int Read(byte[] buffer, int offset, int count)
  337. {
  338. if (_mode == CryptoMode.Encrypt)
  339. throw new NotSupportedException("This stream does not encrypt via Read()");
  340. if (buffer == null)
  341. throw new ArgumentNullException("buffer");
  342. byte[] db = new byte[count];
  343. int n = _s.Read(db, 0, count);
  344. byte[] decrypted = _cipher.DecryptMessage(db, n);
  345. for (int i = 0; i < n; i++)
  346. {
  347. buffer[offset + i] = decrypted[i];
  348. }
  349. return n;
  350. }
  351. public override void Write(byte[] buffer, int offset, int count)
  352. {
  353. if (_mode == CryptoMode.Decrypt)
  354. throw new NotSupportedException("This stream does not Decrypt via Write()");
  355. if (buffer == null)
  356. throw new ArgumentNullException("buffer");
  357. // workitem 7696
  358. if (count == 0) return;
  359. byte[] plaintext = null;
  360. if (offset != 0)
  361. {
  362. plaintext = new byte[count];
  363. for (int i = 0; i < count; i++)
  364. {
  365. plaintext[i] = buffer[offset + i];
  366. }
  367. }
  368. else plaintext = buffer;
  369. byte[] encrypted = _cipher.EncryptMessage(plaintext, count);
  370. _s.Write(encrypted, 0, encrypted.Length);
  371. }
  372. public override bool CanRead
  373. {
  374. get { return (_mode == CryptoMode.Decrypt); }
  375. }
  376. public override bool CanSeek
  377. {
  378. get { return false; }
  379. }
  380. public override bool CanWrite
  381. {
  382. get { return (_mode == CryptoMode.Encrypt); }
  383. }
  384. public override void Flush()
  385. {
  386. //throw new NotSupportedException();
  387. }
  388. public override long Length
  389. {
  390. get { throw new NotSupportedException(); }
  391. }
  392. public override long Position
  393. {
  394. get { throw new NotSupportedException(); }
  395. set { throw new NotSupportedException(); }
  396. }
  397. public override long Seek(long offset, System.IO.SeekOrigin origin)
  398. {
  399. throw new NotSupportedException();
  400. }
  401. public override void SetLength(long value)
  402. {
  403. throw new NotSupportedException();
  404. }
  405. }
  406. }