DeflaterOutputStream.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. // DeflaterOutputStream.cs
  2. //
  3. // Copyright (C) 2001 Mike Krueger
  4. //
  5. // This file was translated from java, it was part of the GNU Classpath
  6. // Copyright (C) 2001 Free Software Foundation, Inc.
  7. //
  8. // This program is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU General Public License
  10. // as published by the Free Software Foundation; either version 2
  11. // of the License, or (at your option) any later version.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU General Public License
  19. // along with this program; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. //
  22. // Linking this library statically or dynamically with other modules is
  23. // making a combined work based on this library. Thus, the terms and
  24. // conditions of the GNU General Public License cover the whole
  25. // combination.
  26. //
  27. // As a special exception, the copyright holders of this library give you
  28. // permission to link this library with independent modules to produce an
  29. // executable, regardless of the license terms of these independent
  30. // modules, and to copy and distribute the resulting executable under
  31. // terms of your choice, provided that you also meet, for each linked
  32. // independent module, the terms and conditions of the license of that
  33. // module. An independent module is a module which is not derived from
  34. // or based on this library. If you modify this library, you may extend
  35. // this exception to your version of the library, but you are not
  36. // obligated to do so. If you do not wish to do so, delete this
  37. // exception statement from your version.
  38. // HISTORY
  39. // 22-12-2009 DavidPierson Added AES support
  40. using System;
  41. using System.IO;
  42. #if !NETCF_1_0
  43. using System.Security.Cryptography;
  44. using CommonMPQ.SharpZipLib.Encryption;
  45. #endif
  46. namespace CommonMPQ.SharpZipLib.Zip.Compression.Streams
  47. {
  48. /// <summary>
  49. /// A special stream deflating or compressing the bytes that are
  50. /// written to it. It uses a Deflater to perform actual deflating.<br/>
  51. /// Authors of the original java version : Tom Tromey, Jochen Hoenicke
  52. /// </summary>
  53. public class DeflaterOutputStream : Stream
  54. {
  55. #region Constructors
  56. /// <summary>
  57. /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.
  58. /// </summary>
  59. /// <param name="baseOutputStream">
  60. /// the output stream where deflated output should be written.
  61. /// </param>
  62. public DeflaterOutputStream(Stream baseOutputStream)
  63. : this(baseOutputStream, new Deflater(), 512)
  64. {
  65. }
  66. /// <summary>
  67. /// Creates a new DeflaterOutputStream with the given Deflater and
  68. /// default buffer size.
  69. /// </summary>
  70. /// <param name="baseOutputStream">
  71. /// the output stream where deflated output should be written.
  72. /// </param>
  73. /// <param name="deflater">
  74. /// the underlying deflater.
  75. /// </param>
  76. public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater)
  77. : this(baseOutputStream, deflater, 512)
  78. {
  79. }
  80. /// <summary>
  81. /// Creates a new DeflaterOutputStream with the given Deflater and
  82. /// buffer size.
  83. /// </summary>
  84. /// <param name="baseOutputStream">
  85. /// The output stream where deflated output is written.
  86. /// </param>
  87. /// <param name="deflater">
  88. /// The underlying deflater to use
  89. /// </param>
  90. /// <param name="bufferSize">
  91. /// The buffer size in bytes to use when deflating (minimum value 512)
  92. /// </param>
  93. /// <exception cref="ArgumentOutOfRangeException">
  94. /// bufsize is less than or equal to zero.
  95. /// </exception>
  96. /// <exception cref="ArgumentException">
  97. /// baseOutputStream does not support writing
  98. /// </exception>
  99. /// <exception cref="ArgumentNullException">
  100. /// deflater instance is null
  101. /// </exception>
  102. public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize)
  103. {
  104. if ( baseOutputStream == null ) {
  105. throw new ArgumentNullException("baseOutputStream");
  106. }
  107. if (baseOutputStream.CanWrite == false) {
  108. throw new ArgumentException("Must support writing", "baseOutputStream");
  109. }
  110. if (deflater == null) {
  111. throw new ArgumentNullException("deflater");
  112. }
  113. if (bufferSize < 512) {
  114. throw new ArgumentOutOfRangeException("bufferSize");
  115. }
  116. baseOutputStream_ = baseOutputStream;
  117. buffer_ = new byte[bufferSize];
  118. deflater_ = deflater;
  119. }
  120. #endregion
  121. #region Public API
  122. /// <summary>
  123. /// Finishes the stream by calling finish() on the deflater.
  124. /// </summary>
  125. /// <exception cref="SharpZipBaseException">
  126. /// Not all input is deflated
  127. /// </exception>
  128. public virtual void Finish()
  129. {
  130. deflater_.Finish();
  131. while (!deflater_.IsFinished) {
  132. int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
  133. if (len <= 0) {
  134. break;
  135. }
  136. #if NETCF_1_0
  137. if ( keys != null ) {
  138. #else
  139. if (cryptoTransform_ != null) {
  140. #endif
  141. EncryptBlock(buffer_, 0, len);
  142. }
  143. baseOutputStream_.Write(buffer_, 0, len);
  144. }
  145. if (!deflater_.IsFinished) {
  146. throw new SharpZipBaseException("Can't deflate all input?");
  147. }
  148. baseOutputStream_.Flush();
  149. #if NETCF_1_0
  150. if ( keys != null ) {
  151. keys = null;
  152. }
  153. #else
  154. if (cryptoTransform_ != null) {
  155. #if !NET_1_1 && !NETCF_2_0
  156. if (cryptoTransform_ is ZipAESTransform) {
  157. AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
  158. }
  159. #endif
  160. cryptoTransform_.Dispose();
  161. cryptoTransform_ = null;
  162. }
  163. #endif
  164. }
  165. /// <summary>
  166. /// Get/set flag indicating ownership of the underlying stream.
  167. /// When the flag is true <see cref="Close"></see> will close the underlying stream also.
  168. /// </summary>
  169. public bool IsStreamOwner
  170. {
  171. get { return isStreamOwner_; }
  172. set { isStreamOwner_ = value; }
  173. }
  174. /// <summary>
  175. /// Allows client to determine if an entry can be patched after its added
  176. /// </summary>
  177. public bool CanPatchEntries {
  178. get {
  179. return baseOutputStream_.CanSeek;
  180. }
  181. }
  182. #endregion
  183. #region Encryption
  184. string password;
  185. #if NETCF_1_0
  186. uint[] keys;
  187. #else
  188. ICryptoTransform cryptoTransform_;
  189. /// <summary>
  190. /// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream.
  191. /// </summary>
  192. protected byte[] AESAuthCode;
  193. #endif
  194. /// <summary>
  195. /// Get/set the password used for encryption.
  196. /// </summary>
  197. /// <remarks>When set to null or if the password is empty no encryption is performed</remarks>
  198. public string Password {
  199. get {
  200. return password;
  201. }
  202. set {
  203. if ( (value != null) && (value.Length == 0) ) {
  204. password = null;
  205. } else {
  206. password = value;
  207. }
  208. }
  209. }
  210. /// <summary>
  211. /// Encrypt a block of data
  212. /// </summary>
  213. /// <param name="buffer">
  214. /// Data to encrypt. NOTE the original contents of the buffer are lost
  215. /// </param>
  216. /// <param name="offset">
  217. /// Offset of first byte in buffer to encrypt
  218. /// </param>
  219. /// <param name="length">
  220. /// Number of bytes in buffer to encrypt
  221. /// </param>
  222. protected void EncryptBlock(byte[] buffer, int offset, int length)
  223. {
  224. #if NETCF_1_0
  225. for (int i = offset; i < offset + length; ++i) {
  226. byte oldbyte = buffer[i];
  227. buffer[i] ^= EncryptByte();
  228. UpdateKeys(oldbyte);
  229. }
  230. #else
  231. cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
  232. #endif
  233. }
  234. /// <summary>
  235. /// Initializes encryption keys based on given <paramref name="password"/>.
  236. /// </summary>
  237. /// <param name="password">The password.</param>
  238. protected void InitializePassword(string password)
  239. {
  240. #if NETCF_1_0
  241. keys = new uint[] {
  242. 0x12345678,
  243. 0x23456789,
  244. 0x34567890
  245. };
  246. byte[] rawPassword = ZipConstants.ConvertToArray(password);
  247. for (int i = 0; i < rawPassword.Length; ++i) {
  248. UpdateKeys((byte)rawPassword[i]);
  249. }
  250. #else
  251. PkzipClassicManaged pkManaged = new PkzipClassicManaged();
  252. byte[] key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password));
  253. cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
  254. #endif
  255. }
  256. #if !NET_1_1 && !NETCF_2_0
  257. /// <summary>
  258. /// Initializes encryption keys based on given password.
  259. /// </summary>
  260. protected void InitializeAESPassword(ZipEntry entry, string rawPassword,
  261. out byte[] salt, out byte[] pwdVerifier) {
  262. salt = new byte[entry.AESSaltLen];
  263. // Salt needs to be cryptographically random, and unique per file
  264. if (_aesRnd == null)
  265. _aesRnd = new RNGCryptoServiceProvider();
  266. _aesRnd.GetBytes(salt);
  267. int blockSize = entry.AESKeySize / 8; // bits to bytes
  268. cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true);
  269. pwdVerifier = ((ZipAESTransform)cryptoTransform_).PwdVerifier;
  270. }
  271. #endif
  272. #if NETCF_1_0
  273. /// <summary>
  274. /// Encrypt a single byte
  275. /// </summary>
  276. /// <returns>
  277. /// The encrypted value
  278. /// </returns>
  279. protected byte EncryptByte()
  280. {
  281. uint temp = ((keys[2] & 0xFFFF) | 2);
  282. return (byte)((temp * (temp ^ 1)) >> 8);
  283. }
  284. /// <summary>
  285. /// Update encryption keys
  286. /// </summary>
  287. protected void UpdateKeys(byte ch)
  288. {
  289. keys[0] = Crc32.ComputeCrc32(keys[0], ch);
  290. keys[1] = keys[1] + (byte)keys[0];
  291. keys[1] = keys[1] * 134775813 + 1;
  292. keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));
  293. }
  294. #endif
  295. #endregion
  296. #region Deflation Support
  297. /// <summary>
  298. /// Deflates everything in the input buffers. This will call
  299. /// <code>def.deflate()</code> until all bytes from the input buffers
  300. /// are processed.
  301. /// </summary>
  302. protected void Deflate()
  303. {
  304. while (!deflater_.IsNeedingInput)
  305. {
  306. int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
  307. if (deflateCount <= 0) {
  308. break;
  309. }
  310. #if NETCF_1_0
  311. if (keys != null)
  312. #else
  313. if (cryptoTransform_ != null)
  314. #endif
  315. {
  316. EncryptBlock(buffer_, 0, deflateCount);
  317. }
  318. baseOutputStream_.Write(buffer_, 0, deflateCount);
  319. }
  320. if (!deflater_.IsNeedingInput) {
  321. throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?");
  322. }
  323. }
  324. #endregion
  325. #region Stream Overrides
  326. /// <summary>
  327. /// Gets value indicating stream can be read from
  328. /// </summary>
  329. public override bool CanRead
  330. {
  331. get {
  332. return false;
  333. }
  334. }
  335. /// <summary>
  336. /// Gets a value indicating if seeking is supported for this stream
  337. /// This property always returns false
  338. /// </summary>
  339. public override bool CanSeek {
  340. get {
  341. return false;
  342. }
  343. }
  344. /// <summary>
  345. /// Get value indicating if this stream supports writing
  346. /// </summary>
  347. public override bool CanWrite {
  348. get {
  349. return baseOutputStream_.CanWrite;
  350. }
  351. }
  352. /// <summary>
  353. /// Get current length of stream
  354. /// </summary>
  355. public override long Length {
  356. get {
  357. return baseOutputStream_.Length;
  358. }
  359. }
  360. /// <summary>
  361. /// Gets the current position within the stream.
  362. /// </summary>
  363. /// <exception cref="NotSupportedException">Any attempt to set position</exception>
  364. public override long Position {
  365. get {
  366. return baseOutputStream_.Position;
  367. }
  368. set {
  369. throw new NotSupportedException("Position property not supported");
  370. }
  371. }
  372. /// <summary>
  373. /// Sets the current position of this stream to the given value. Not supported by this class!
  374. /// </summary>
  375. /// <param name="offset">The offset relative to the <paramref name="origin"/> to seek.</param>
  376. /// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
  377. /// <returns>The new position in the stream.</returns>
  378. /// <exception cref="NotSupportedException">Any access</exception>
  379. public override long Seek(long offset, SeekOrigin origin)
  380. {
  381. throw new NotSupportedException("DeflaterOutputStream Seek not supported");
  382. }
  383. /// <summary>
  384. /// Sets the length of this stream to the given value. Not supported by this class!
  385. /// </summary>
  386. /// <param name="value">The new stream length.</param>
  387. /// <exception cref="NotSupportedException">Any access</exception>
  388. public override void SetLength(long value)
  389. {
  390. throw new NotSupportedException("DeflaterOutputStream SetLength not supported");
  391. }
  392. /// <summary>
  393. /// Read a byte from stream advancing position by one
  394. /// </summary>
  395. /// <returns>The byte read cast to an int. THe value is -1 if at the end of the stream.</returns>
  396. /// <exception cref="NotSupportedException">Any access</exception>
  397. public override int ReadByte()
  398. {
  399. throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");
  400. }
  401. /// <summary>
  402. /// Read a block of bytes from stream
  403. /// </summary>
  404. /// <param name="buffer">The buffer to store read data in.</param>
  405. /// <param name="offset">The offset to start storing at.</param>
  406. /// <param name="count">The maximum number of bytes to read.</param>
  407. /// <returns>The actual number of bytes read. Zero if end of stream is detected.</returns>
  408. /// <exception cref="NotSupportedException">Any access</exception>
  409. public override int Read(byte[] buffer, int offset, int count)
  410. {
  411. throw new NotSupportedException("DeflaterOutputStream Read not supported");
  412. }
  413. /// <summary>
  414. /// Asynchronous reads are not supported a NotSupportedException is always thrown
  415. /// </summary>
  416. /// <param name="buffer">The buffer to read into.</param>
  417. /// <param name="offset">The offset to start storing data at.</param>
  418. /// <param name="count">The number of bytes to read</param>
  419. /// <param name="callback">The async callback to use.</param>
  420. /// <param name="state">The state to use.</param>
  421. /// <returns>Returns an <see cref="IAsyncResult"/></returns>
  422. /// <exception cref="NotSupportedException">Any access</exception>
  423. public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
  424. {
  425. throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported");
  426. }
  427. /// <summary>
  428. /// Asynchronous writes arent supported, a NotSupportedException is always thrown
  429. /// </summary>
  430. /// <param name="buffer">The buffer to write.</param>
  431. /// <param name="offset">The offset to begin writing at.</param>
  432. /// <param name="count">The number of bytes to write.</param>
  433. /// <param name="callback">The <see cref="AsyncCallback"/> to use.</param>
  434. /// <param name="state">The state object.</param>
  435. /// <returns>Returns an IAsyncResult.</returns>
  436. /// <exception cref="NotSupportedException">Any access</exception>
  437. public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
  438. {
  439. throw new NotSupportedException("BeginWrite is not supported");
  440. }
  441. /// <summary>
  442. /// Flushes the stream by calling <see cref="DeflaterOutputStream.Flush">Flush</see> on the deflater and then
  443. /// on the underlying stream. This ensures that all bytes are flushed.
  444. /// </summary>
  445. public override void Flush()
  446. {
  447. deflater_.Flush();
  448. Deflate();
  449. baseOutputStream_.Flush();
  450. }
  451. /// <summary>
  452. /// Calls <see cref="Finish"/> and closes the underlying
  453. /// stream when <see cref="IsStreamOwner"></see> is true.
  454. /// </summary>
  455. public override void Close()
  456. {
  457. if ( !isClosed_ ) {
  458. isClosed_ = true;
  459. try {
  460. Finish();
  461. #if NETCF_1_0
  462. keys=null;
  463. #else
  464. if ( cryptoTransform_ != null ) {
  465. GetAuthCodeIfAES();
  466. cryptoTransform_.Dispose();
  467. cryptoTransform_ = null;
  468. }
  469. #endif
  470. }
  471. finally {
  472. if( isStreamOwner_ ) {
  473. baseOutputStream_.Close();
  474. }
  475. }
  476. }
  477. }
  478. private void GetAuthCodeIfAES() {
  479. #if !NET_1_1 && !NETCF_2_0
  480. if (cryptoTransform_ is ZipAESTransform) {
  481. AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
  482. }
  483. #endif
  484. }
  485. /// <summary>
  486. /// Writes a single byte to the compressed output stream.
  487. /// </summary>
  488. /// <param name="value">
  489. /// The byte value.
  490. /// </param>
  491. public override void WriteByte(byte value)
  492. {
  493. byte[] b = new byte[1];
  494. b[0] = value;
  495. Write(b, 0, 1);
  496. }
  497. /// <summary>
  498. /// Writes bytes from an array to the compressed stream.
  499. /// </summary>
  500. /// <param name="buffer">
  501. /// The byte array
  502. /// </param>
  503. /// <param name="offset">
  504. /// The offset into the byte array where to start.
  505. /// </param>
  506. /// <param name="count">
  507. /// The number of bytes to write.
  508. /// </param>
  509. public override void Write(byte[] buffer, int offset, int count)
  510. {
  511. deflater_.SetInput(buffer, offset, count);
  512. Deflate();
  513. }
  514. #endregion
  515. #region Instance Fields
  516. /// <summary>
  517. /// This buffer is used temporarily to retrieve the bytes from the
  518. /// deflater and write them to the underlying output stream.
  519. /// </summary>
  520. byte[] buffer_;
  521. /// <summary>
  522. /// The deflater which is used to deflate the stream.
  523. /// </summary>
  524. protected Deflater deflater_;
  525. /// <summary>
  526. /// Base stream the deflater depends on.
  527. /// </summary>
  528. protected Stream baseOutputStream_;
  529. bool isClosed_;
  530. bool isStreamOwner_ = true;
  531. #endregion
  532. #region Static Fields
  533. #if !NET_1_1 && !NETCF_2_0
  534. // Static to help ensure that multiple files within a zip will get different random salt
  535. private static RNGCryptoServiceProvider _aesRnd;
  536. #endif
  537. #endregion
  538. }
  539. }