// DeflaterOutputStream.cs // // Copyright (C) 2001 Mike Krueger // // This file was translated from java, it was part of the GNU Classpath // Copyright (C) 2001 Free Software Foundation, Inc. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // Linking this library statically or dynamically with other modules is // making a combined work based on this library. Thus, the terms and // conditions of the GNU General Public License cover the whole // combination. // // As a special exception, the copyright holders of this library give you // permission to link this library with independent modules to produce an // executable, regardless of the license terms of these independent // modules, and to copy and distribute the resulting executable under // terms of your choice, provided that you also meet, for each linked // independent module, the terms and conditions of the license of that // module. An independent module is a module which is not derived from // or based on this library. If you modify this library, you may extend // this exception to your version of the library, but you are not // obligated to do so. If you do not wish to do so, delete this // exception statement from your version. // HISTORY // 22-12-2009 DavidPierson Added AES support using System; using System.IO; using PdfSharp.SharpZipLib.Checksums; // ReSharper disable RedundantThisQualifier //#if !NETCF_1_0 //using System.Security.Cryptography; //using PdfSharp.SharpZipLib.Encryption; //#endif namespace PdfSharp.SharpZipLib.Zip.Compression.Streams { /// /// A special stream deflating or compressing the bytes that are /// written to it. It uses a Deflater to perform actual deflating.
/// Authors of the original java version: Tom Tromey, Jochen Hoenicke ///
internal class DeflaterOutputStream : Stream { #region Constructors /// /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size. /// /// /// the output stream where deflated output should be written. /// public DeflaterOutputStream(Stream baseOutputStream) : this(baseOutputStream, new Deflater(), 512) { } /// /// Creates a new DeflaterOutputStream with the given Deflater and /// default buffer size. /// /// /// the output stream where deflated output should be written. /// /// /// the underlying deflater. /// public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater) : this(baseOutputStream, deflater, 512) { } /// /// Creates a new DeflaterOutputStream with the given Deflater and /// buffer size. /// /// /// The output stream where deflated output is written. /// /// /// The underlying deflater to use /// /// /// The buffer size in bytes to use when deflating (minimum value 512) /// /// /// bufsize is less than or equal to zero. /// /// /// baseOutputStream does not support writing /// /// /// deflater instance is null /// public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize) { if (baseOutputStream == null) { throw new ArgumentNullException("baseOutputStream"); } if (baseOutputStream.CanWrite == false) { throw new ArgumentException("Must support writing", "baseOutputStream"); } if (deflater == null) { throw new ArgumentNullException("deflater"); } if (bufferSize < 512) { throw new ArgumentOutOfRangeException("bufferSize"); } baseOutputStream_ = baseOutputStream; buffer_ = new byte[bufferSize]; deflater_ = deflater; } #endregion #region Public API /// /// Finishes the stream by calling finish() on the deflater. /// /// /// Not all input is deflated /// public virtual void Finish() { deflater_.Finish(); while (!deflater_.IsFinished) { int len = deflater_.Deflate(buffer_, 0, buffer_.Length); if (len <= 0) { break; } #if true//NETCF_1_0 if (keys != null) { #else if (cryptoTransform_ != null) { #endif EncryptBlock(buffer_, 0, len); } baseOutputStream_.Write(buffer_, 0, len); } if (!deflater_.IsFinished) { throw new SharpZipBaseException("Can't deflate all input?"); } baseOutputStream_.Flush(); #if true//NETCF_1_0 if (keys != null) { keys = null; } #else if (cryptoTransform_ != null) { #if !NET_1_1 && !NETCF_2_0 if (cryptoTransform_ is ZipAESTransform) { AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); } #endif cryptoTransform_.Dispose(); cryptoTransform_ = null; } #endif } /// /// Get/set flag indicating ownership of the underlying stream. /// When the flag is true will close the underlying stream also. /// public bool IsStreamOwner { get { return isStreamOwner_; } set { isStreamOwner_ = value; } } /// /// Allows client to determine if an entry can be patched after its added /// public bool CanPatchEntries { get { return baseOutputStream_.CanSeek; } } #endregion #region Encryption string password; #if true//NETCF_1_0 uint[] keys; #else ICryptoTransform cryptoTransform_; /// /// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream. /// protected byte[] AESAuthCode; #endif /// /// Get/set the password used for encryption. /// /// When set to null or if the password is empty no encryption is performed public string Password { get { return password; } set { if ((value != null) && (value.Length == 0)) { password = null; } else { password = value; } } } /// /// Encrypt a block of data /// /// /// Data to encrypt. NOTE the original contents of the buffer are lost /// /// /// Offset of first byte in buffer to encrypt /// /// /// Number of bytes in buffer to encrypt /// protected void EncryptBlock(byte[] buffer, int offset, int length) { #if true//NETCF_1_0 for (int i = offset; i < offset + length; ++i) { byte oldbyte = buffer[i]; buffer[i] ^= EncryptByte(); UpdateKeys(oldbyte); } #else cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0); #endif } /// /// Initializes encryption keys based on given . /// /// The password. protected void InitializePassword(string password) { #if true//NETCF_1_0 keys = new uint[] { 0x12345678, 0x23456789, 0x34567890 }; byte[] rawPassword = ZipConstants.ConvertToArray(password); for (int i = 0; i < rawPassword.Length; ++i) { UpdateKeys((byte)rawPassword[i]); } #else PkzipClassicManaged pkManaged = new PkzipClassicManaged(); byte[] key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password)); cryptoTransform_ = pkManaged.CreateEncryptor(key, null); #endif } #if false//!NET_1_1 && !NETCF_2_0 /// /// Initializes encryption keys based on given password. /// protected void InitializeAESPassword(ZipEntry entry, string rawPassword, out byte[] salt, out byte[] pwdVerifier) { salt = new byte[entry.AESSaltLen]; // Salt needs to be cryptographically random, and unique per file if (_aesRnd == null) _aesRnd = new RNGCryptoServiceProvider(); _aesRnd.GetBytes(salt); int blockSize = entry.AESKeySize / 8; // bits to bytes cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true); pwdVerifier = ((ZipAESTransform)cryptoTransform_).PwdVerifier; } #endif #if true//NETCF_1_0 /// /// Encrypt a single byte /// /// /// The encrypted value /// protected byte EncryptByte() { uint temp = ((keys[2] & 0xFFFF) | 2); return (byte)((temp * (temp ^ 1)) >> 8); } /// /// Update encryption keys /// protected void UpdateKeys(byte ch) { keys[0] = Crc32.ComputeCrc32(keys[0], ch); keys[1] = keys[1] + (byte)keys[0]; keys[1] = keys[1] * 134775813 + 1; keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24)); } #endif #endregion #region Deflation Support /// /// Deflates everything in the input buffers. This will call /// def.deflate() until all bytes from the input buffers /// are processed. /// protected void Deflate() { while (!deflater_.IsNeedingInput) { int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length); if (deflateCount <= 0) { break; } #if true//NETCF_1_0 if (keys != null) #else if (cryptoTransform_ != null) #endif { EncryptBlock(buffer_, 0, deflateCount); } baseOutputStream_.Write(buffer_, 0, deflateCount); } if (!deflater_.IsNeedingInput) { throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?"); } } #endregion #region Stream Overrides /// /// Gets value indicating stream can be read from /// public override bool CanRead { get { return false; } } /// /// Gets a value indicating if seeking is supported for this stream /// This property always returns false /// public override bool CanSeek { get { return false; } } /// /// Get value indicating if this stream supports writing /// public override bool CanWrite { get { return baseOutputStream_.CanWrite; } } /// /// Get current length of stream /// public override long Length { get { return baseOutputStream_.Length; } } /// /// Gets the current position within the stream. /// /// Any attempt to set position public override long Position { get { return baseOutputStream_.Position; } set { throw new NotSupportedException("Position property not supported"); } } /// /// Sets the current position of this stream to the given value. Not supported by this class! /// /// The offset relative to the to seek. /// The to seek from. /// The new position in the stream. /// Any access public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("DeflaterOutputStream Seek not supported"); } /// /// Sets the length of this stream to the given value. Not supported by this class! /// /// The new stream length. /// Any access public override void SetLength(long value) { throw new NotSupportedException("DeflaterOutputStream SetLength not supported"); } /// /// Read a byte from stream advancing position by one /// /// The byte read cast to an int. THe value is -1 if at the end of the stream. /// Any access public override int ReadByte() { throw new NotSupportedException("DeflaterOutputStream ReadByte not supported"); } /// /// Read a block of bytes from stream /// /// The buffer to store read data in. /// The offset to start storing at. /// The maximum number of bytes to read. /// The actual number of bytes read. Zero if end of stream is detected. /// Any access public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException("DeflaterOutputStream Read not supported"); } #if !NETFX_CORE && !UWP /// /// Asynchronous reads are not supported a NotSupportedException is always thrown /// /// The buffer to read into. /// The offset to start storing data at. /// The number of bytes to read /// The async callback to use. /// The state to use. /// Returns an /// Any access public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported"); } #endif #if !NETFX_CORE && !UWP /// /// Asynchronous writes arent supported, a NotSupportedException is always thrown /// /// The buffer to write. /// The offset to begin writing at. /// The number of bytes to write. /// The to use. /// The state object. /// Returns an IAsyncResult. /// Any access public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException("BeginWrite is not supported"); } #endif /// /// Flushes the stream by calling Flush on the deflater and then /// on the underlying stream. This ensures that all bytes are flushed. /// public override void Flush() { deflater_.Flush(); Deflate(); baseOutputStream_.Flush(); } #if !NETFX_CORE && !UWP /// /// Calls and closes the underlying /// stream when is true. /// public override void Close() { if ( !isClosed_ ) { isClosed_ = true; try { Finish(); #if true//NETCF_1_0 keys =null; #else if ( cryptoTransform_ != null ) { GetAuthCodeIfAES(); cryptoTransform_.Dispose(); cryptoTransform_ = null; } #endif } finally { if( isStreamOwner_ ) { baseOutputStream_.Close(); } } } } #else public void Close() { if (!isClosed_) { isClosed_ = true; try { Finish(); #if true//NETCF_1_0 keys = null; #else if ( cryptoTransform_ != null ) { GetAuthCodeIfAES(); cryptoTransform_.Dispose(); cryptoTransform_ = null; } #endif } finally { if (isStreamOwner_) { //baseOutputStream_.Close(); baseOutputStream_.Dispose(); } } } } #endif private void GetAuthCodeIfAES() { #if false//!NET_1_1 && !NETCF_2_0 if (cryptoTransform_ is ZipAESTransform) { AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); } #endif } /// /// Writes a single byte to the compressed output stream. /// /// /// The byte value. /// public override void WriteByte(byte value) { byte[] b = new byte[1]; b[0] = value; Write(b, 0, 1); } /// /// Writes bytes from an array to the compressed stream. /// /// /// The byte array /// /// /// The offset into the byte array where to start. /// /// /// The number of bytes to write. /// public override void Write(byte[] buffer, int offset, int count) { deflater_.SetInput(buffer, offset, count); Deflate(); } #endregion #region Instance Fields /// /// This buffer is used temporarily to retrieve the bytes from the /// deflater and write them to the underlying output stream. /// byte[] buffer_; /// /// The deflater which is used to deflate the stream. /// protected Deflater deflater_; /// /// Base stream the deflater depends on. /// protected Stream baseOutputStream_; #if true || !NETFX_CORE && !UWP bool isClosed_; #endif bool isStreamOwner_ = true; #endregion #region Static Fields #if false//!NET_1_1 && !NETCF_2_0 // Static to help ensure that multiple files within a zip will get different random salt private static RNGCryptoServiceProvider _aesRnd; #endif #endregion } }