| //#define Trace | 
 |  | 
 | // WinZipAes.cs | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // Copyright (c) 2009-2011 Dino Chiesa. | 
 | // All rights reserved. | 
 | // | 
 | // This code module is part of DotNetZip, a zipfile class library. | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // This code is licensed under the Microsoft Public License. | 
 | // See the file License.txt for the license details. | 
 | // More info on: http://dotnetzip.codeplex.com | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // last saved (in emacs): | 
 | // Time-stamp: <2011-July-12 13:42:06> | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // This module defines the classes for dealing with WinZip's AES encryption, | 
 | // according to the specifications for the format available on WinZip's website. | 
 | // | 
 | // Created: January 2009 | 
 | // | 
 | // ------------------------------------------------------------------ | 
 |  | 
 | using System; | 
 | using System.IO; | 
 | using System.Collections.Generic; | 
 | using System.Security.Cryptography; | 
 |  | 
 | #if AESCRYPTO | 
 | namespace OfficeOpenXml.Packaging.Ionic.Zip | 
 | { | 
 |     /// <summary> | 
 |     ///   This is a helper class supporting WinZip AES encryption. | 
 |     ///   This class is intended for use only by the DotNetZip library. | 
 |     /// </summary> | 
 |     /// | 
 |     /// <remarks> | 
 |     ///   Most uses of the DotNetZip library will not involve direct calls into | 
 |     ///   the WinZipAesCrypto class.  Instead, the WinZipAesCrypto class is | 
 |     ///   instantiated and used by the ZipEntry() class when WinZip AES | 
 |     ///   encryption or decryption on an entry is employed. | 
 |     /// </remarks> | 
 |     internal class WinZipAesCrypto | 
 |     { | 
 |         internal byte[] _Salt; | 
 |         internal byte[] _providedPv; | 
 |         internal byte[] _generatedPv; | 
 |         internal int _KeyStrengthInBits; | 
 |         private byte[] _MacInitializationVector; | 
 |         private byte[] _StoredMac; | 
 |         private byte[] _keyBytes; | 
 |         private Int16 PasswordVerificationStored; | 
 |         private Int16 PasswordVerificationGenerated; | 
 |         private int Rfc2898KeygenIterations = 1000; | 
 |         private string _Password; | 
 |         private bool _cryptoGenerated ; | 
 |  | 
 |         private WinZipAesCrypto(string password, int KeyStrengthInBits) | 
 |         { | 
 |             _Password = password; | 
 |             _KeyStrengthInBits = KeyStrengthInBits; | 
 |         } | 
 |  | 
 |         public static WinZipAesCrypto Generate(string password, int KeyStrengthInBits) | 
 |         { | 
 |             WinZipAesCrypto c = new WinZipAesCrypto(password, KeyStrengthInBits); | 
 |  | 
 |             int saltSizeInBytes = c._KeyStrengthInBytes / 2; | 
 |             c._Salt = new byte[saltSizeInBytes]; | 
 |             Random rnd = new Random(); | 
 |             rnd.NextBytes(c._Salt); | 
 |             return c; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         public static WinZipAesCrypto ReadFromStream(string password, int KeyStrengthInBits, Stream s) | 
 |         { | 
 |             // from http://www.winzip.com/aes_info.htm | 
 |             // | 
 |             // Size(bytes)   Content | 
 |             // ----------------------------------- | 
 |             // Variable      Salt value | 
 |             // 2             Password verification value | 
 |             // Variable      Encrypted file data | 
 |             // 10            Authentication code | 
 |             // | 
 |             // ZipEntry.CompressedSize represents the size of all of those elements. | 
 |  | 
 |             // salt size varies with key length: | 
 |             //    128 bit key => 8 bytes salt | 
 |             //    192 bits => 12 bytes salt | 
 |             //    256 bits => 16 bytes salt | 
 |  | 
 |             WinZipAesCrypto c = new WinZipAesCrypto(password, KeyStrengthInBits); | 
 |  | 
 |             int saltSizeInBytes = c._KeyStrengthInBytes / 2; | 
 |             c._Salt = new byte[saltSizeInBytes]; | 
 |             c._providedPv = new byte[2]; | 
 |  | 
 |             s.Read(c._Salt, 0, c._Salt.Length); | 
 |             s.Read(c._providedPv, 0, c._providedPv.Length); | 
 |  | 
 |             c.PasswordVerificationStored = (Int16)(c._providedPv[0] + c._providedPv[1] * 256); | 
 |             if (password != null) | 
 |             { | 
 |                 c.PasswordVerificationGenerated = (Int16)(c.GeneratedPV[0] + c.GeneratedPV[1] * 256); | 
 |                 if (c.PasswordVerificationGenerated != c.PasswordVerificationStored) | 
 |                     throw new BadPasswordException("bad password"); | 
 |             } | 
 |  | 
 |             return c; | 
 |         } | 
 |  | 
 |         public byte[] GeneratedPV | 
 |         { | 
 |             get | 
 |             { | 
 |                 if (!_cryptoGenerated) _GenerateCryptoBytes(); | 
 |                 return _generatedPv; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         public byte[] Salt | 
 |         { | 
 |             get | 
 |             { | 
 |                 return _Salt; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         private int _KeyStrengthInBytes | 
 |         { | 
 |             get | 
 |             { | 
 |                 return _KeyStrengthInBits / 8; | 
 |  | 
 |             } | 
 |         } | 
 |  | 
 |         public int SizeOfEncryptionMetadata | 
 |         { | 
 |             get | 
 |             { | 
 |                 // 10 bytes after, (n-10) before the compressed data | 
 |                 return _KeyStrengthInBytes / 2 + 10 + 2; | 
 |             } | 
 |         } | 
 |  | 
 |         public string Password | 
 |         { | 
 |             set | 
 |             { | 
 |                 _Password = value; | 
 |                 if (_Password != null) | 
 |                 { | 
 |                     PasswordVerificationGenerated = (Int16)(GeneratedPV[0] + GeneratedPV[1] * 256); | 
 |                     if (PasswordVerificationGenerated != PasswordVerificationStored) | 
 |                         throw new Ionic.Zip.BadPasswordException(); | 
 |                 } | 
 |             } | 
 |             private get | 
 |             { | 
 |                 return _Password; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         private void _GenerateCryptoBytes() | 
 |         { | 
 |             //Console.WriteLine(" provided password: '{0}'", _Password); | 
 |  | 
 |             System.Security.Cryptography.Rfc2898DeriveBytes rfc2898 = | 
 |                 new System.Security.Cryptography.Rfc2898DeriveBytes(_Password, Salt, Rfc2898KeygenIterations); | 
 |  | 
 |             _keyBytes = rfc2898.GetBytes(_KeyStrengthInBytes); // 16 or 24 or 32 ??? | 
 |             _MacInitializationVector = rfc2898.GetBytes(_KeyStrengthInBytes); | 
 |             _generatedPv = rfc2898.GetBytes(2); | 
 |  | 
 |             _cryptoGenerated = true; | 
 |         } | 
 |  | 
 |  | 
 |         public byte[] KeyBytes | 
 |         { | 
 |             get | 
 |             { | 
 |                 if (!_cryptoGenerated) _GenerateCryptoBytes(); | 
 |                 return _keyBytes; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         public byte[] MacIv | 
 |         { | 
 |             get | 
 |             { | 
 |                 if (!_cryptoGenerated) _GenerateCryptoBytes(); | 
 |                 return _MacInitializationVector; | 
 |             } | 
 |         } | 
 |  | 
 |         public byte[] CalculatedMac; | 
 |  | 
 |  | 
 |         public void ReadAndVerifyMac(System.IO.Stream s) | 
 |         { | 
 |             bool invalid = false; | 
 |  | 
 |             // read integrityCheckVector. | 
 |             // caller must ensure that the file pointer is in the right spot! | 
 |             _StoredMac = new byte[10];  // aka "authentication code" | 
 |             s.Read(_StoredMac, 0, _StoredMac.Length); | 
 |  | 
 |             if (_StoredMac.Length != CalculatedMac.Length) | 
 |                 invalid = true; | 
 |  | 
 |             if (!invalid) | 
 |             { | 
 |                 for (int i = 0; i < _StoredMac.Length; i++) | 
 |                 { | 
 |                     if (_StoredMac[i] != CalculatedMac[i]) | 
 |                         invalid = true; | 
 |                 } | 
 |             } | 
 |  | 
 |             if (invalid) | 
 |                 throw new Ionic.Zip.BadStateException("The MAC does not match."); | 
 |         } | 
 |  | 
 |     } | 
 |  | 
 |  | 
 |     #region DONT_COMPILE_BUT_KEEP_FOR_POTENTIAL_FUTURE_USE | 
 | #if NO | 
 |     internal class Util | 
 |     { | 
 |         private static void _Format(System.Text.StringBuilder sb1, | 
 |                                     byte[] b, | 
 |                                     int offset, | 
 |                                     int length) | 
 |         { | 
 |  | 
 |             System.Text.StringBuilder sb2 = new System.Text.StringBuilder(); | 
 |             sb1.Append("0000    "); | 
 |             int i; | 
 |             for (i = 0; i < length; i++) | 
 |             { | 
 |                 int x = offset+i; | 
 |                 if (i != 0 && i % 16 == 0) | 
 |                 { | 
 |                     sb1.Append("    ") | 
 |                         .Append(sb2) | 
 |                         .Append("\n") | 
 |                         .Append(String.Format("{0:X4}    ", i)); | 
 |                     sb2.Remove(0,sb2.Length); | 
 |                 } | 
 |                 sb1.Append(System.String.Format("{0:X2} ", b[x])); | 
 |                 if (b[x] >=32 && b[x] <= 126) | 
 |                     sb2.Append((char)b[x]); | 
 |                 else | 
 |                     sb2.Append("."); | 
 |             } | 
 |             if (sb2.Length > 0) | 
 |             { | 
 |                 sb1.Append(new String(' ', ((16 - i%16) * 3) + 4)) | 
 |                     .Append(sb2); | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         internal static string FormatByteArray(byte[] b, int limit) | 
 |         { | 
 |             System.Text.StringBuilder sb1 = new System.Text.StringBuilder(); | 
 |  | 
 |             if ((limit * 2 > b.Length) || limit == 0) | 
 |             { | 
 |                 _Format(sb1, b, 0, b.Length); | 
 |             } | 
 |             else | 
 |             { | 
 |                 // first N bytes of the buffer | 
 |                 _Format(sb1, b, 0, limit); | 
 |  | 
 |                 if (b.Length > limit * 2) | 
 |                     sb1.Append(String.Format("\n   ...({0} other bytes here)....\n", b.Length - limit * 2)); | 
 |  | 
 |                 // last N bytes of the buffer | 
 |                 _Format(sb1, b, b.Length - limit, limit); | 
 |             } | 
 |  | 
 |             return sb1.ToString(); | 
 |         } | 
 |  | 
 |  | 
 |         internal static string FormatByteArray(byte[] b) | 
 |         { | 
 |             return FormatByteArray(b, 0); | 
 |         } | 
 |     } | 
 |  | 
 | #endif | 
 |     #endregion | 
 |  | 
 |  | 
 |  | 
 |  | 
 |     /// <summary> | 
 |     ///   A stream that encrypts as it writes, or decrypts as it reads.  The | 
 |     ///   Crypto is AES in CTR (counter) mode, which is compatible with the AES | 
 |     ///   encryption employed by WinZip 12.0. | 
 |     /// </summary> | 
 |     /// <remarks> | 
 |     ///   <para> | 
 |     ///     The AES/CTR encryption protocol used by WinZip works like this: | 
 |     /// | 
 |     ///       - start with a counter, initialized to zero. | 
 |     /// | 
 |     ///       - to encrypt, take the data by 16-byte blocks. For each block: | 
 |     ///         - apply the transform to the counter | 
 |     ///         - increement the counter | 
 |     ///         - XOR the result of the transform with the plaintext to | 
 |     ///           get the ciphertext. | 
 |     ///         - compute the mac on the encrypted bytes | 
 |     ///       - when finished with all blocks, store the computed MAC. | 
 |     /// | 
 |     ///       - to decrypt, take the data by 16-byte blocks. For each block: | 
 |     ///         - compute the mac on the encrypted bytes, | 
 |     ///         - apply the transform to the counter | 
 |     ///         - increement the counter | 
 |     ///         - XOR the result of the transform with the ciphertext to | 
 |     ///           get the plaintext. | 
 |     ///       - when finished with all blocks, compare the computed MAC against | 
 |     ///         the stored MAC | 
 |     /// | 
 |     ///   </para> | 
 |     /// </remarks> | 
 |     // | 
 |     internal class WinZipAesCipherStream : Stream | 
 |     { | 
 |         private WinZipAesCrypto _params; | 
 |         private System.IO.Stream _s; | 
 |         private CryptoMode _mode; | 
 |         private int _nonce; | 
 |         private bool _finalBlock; | 
 |  | 
 |         internal HMACSHA1 _mac; | 
 |  | 
 |         // Use RijndaelManaged from .NET 2.0. | 
 |         // AesManaged came in .NET 3.5, but we want to limit | 
 |         // dependency to .NET 2.0.  AES is just a restricted form | 
 |         // of Rijndael (fixed block size of 128, some crypto modes not supported). | 
 |  | 
 |         internal RijndaelManaged _aesCipher; | 
 |         internal ICryptoTransform _xform; | 
 |  | 
 |         private const int BLOCK_SIZE_IN_BYTES = 16; | 
 |  | 
 |         private byte[] counter = new byte[BLOCK_SIZE_IN_BYTES]; | 
 |         private byte[] counterOut = new byte[BLOCK_SIZE_IN_BYTES]; | 
 |  | 
 |         // I've had a problem when wrapping a WinZipAesCipherStream inside | 
 |         // a DeflateStream. Calling Read() on the DeflateStream results in | 
 |         // a Read() on the WinZipAesCipherStream, but the buffer is larger | 
 |         // than the total size of the encrypted data, and larger than the | 
 |         // initial Read() on the DeflateStream!  When the encrypted | 
 |         // bytestream is embedded within a larger stream (As in a zip | 
 |         // archive), the Read() doesn't fail with EOF.  This causes bad | 
 |         // data to be returned, and it messes up the MAC. | 
 |  | 
 |         // This field is used to provide a hard-stop to the size of | 
 |         // data that can be read from the stream.  In Read(), if the buffer or | 
 |         // read request goes beyond the stop, we truncate it. | 
 |  | 
 |         private long _length; | 
 |         private long _totalBytesXferred; | 
 |         private byte[] _PendingWriteBlock; | 
 |         private int _pendingCount; | 
 |         private byte[] _iobuf; | 
 |  | 
 |         /// <summary> | 
 |         /// The constructor. | 
 |         /// </summary> | 
 |         /// <param name="s">The underlying stream</param> | 
 |         /// <param name="mode">To either encrypt or decrypt.</param> | 
 |         /// <param name="cryptoParams">The pre-initialized WinZipAesCrypto object.</param> | 
 |         /// <param name="length">The maximum number of bytes to read from the stream.</param> | 
 |         internal WinZipAesCipherStream(System.IO.Stream s, WinZipAesCrypto cryptoParams, long length, CryptoMode mode) | 
 |             : this(s, cryptoParams, mode) | 
 |         { | 
 |             // don't read beyond this limit! | 
 |             _length = length; | 
 |             //Console.WriteLine("max length of AES stream: {0}", _length); | 
 |         } | 
 |  | 
 |  | 
 | #if WANT_TRACE | 
 |             Stream untransformed; | 
 |         String traceFileUntransformed; | 
 |         Stream transformed; | 
 |         String traceFileTransformed; | 
 | #endif | 
 |  | 
 |  | 
 |         internal WinZipAesCipherStream(System.IO.Stream s, WinZipAesCrypto cryptoParams, CryptoMode mode) | 
 |             : base() | 
 |         { | 
 |             TraceOutput("-------------------------------------------------------"); | 
 |             TraceOutput("Create {0:X8}", this.GetHashCode()); | 
 |  | 
 |             _params = cryptoParams; | 
 |             _s = s; | 
 |             _mode = mode; | 
 |             _nonce = 1; | 
 |  | 
 |             if (_params == null) | 
 |                 throw new BadPasswordException("Supply a password to use AES encryption."); | 
 |  | 
 |             int keySizeInBits = _params.KeyBytes.Length * 8; | 
 |             if (keySizeInBits != 256 && keySizeInBits != 128 && keySizeInBits != 192) | 
 |                 throw new ArgumentOutOfRangeException("keysize", | 
 |                                                       "size of key must be 128, 192, or 256"); | 
 |  | 
 |             _mac = new HMACSHA1(_params.MacIv); | 
 |  | 
 |             _aesCipher = new System.Security.Cryptography.RijndaelManaged(); | 
 |             _aesCipher.BlockSize = 128; | 
 |             _aesCipher.KeySize = keySizeInBits;  // 128, 192, 256 | 
 |             _aesCipher.Mode = CipherMode.ECB; | 
 |             _aesCipher.Padding = PaddingMode.None; | 
 |  | 
 |             byte[] iv = new byte[BLOCK_SIZE_IN_BYTES]; // all zeroes | 
 |  | 
 |             // Create an ENCRYPTOR, regardless whether doing decryption or encryption. | 
 |             // It is reflexive. | 
 |             _xform = _aesCipher.CreateEncryptor(_params.KeyBytes, iv); | 
 |  | 
 |             if (_mode == CryptoMode.Encrypt) | 
 |             { | 
 |                 _iobuf = new byte[2048]; | 
 |                 _PendingWriteBlock = new byte[BLOCK_SIZE_IN_BYTES]; | 
 |             } | 
 |  | 
 |  | 
 | #if WANT_TRACE | 
 |                 traceFileUntransformed = "unpack\\WinZipAesCipherStream.trace.untransformed.out"; | 
 |             traceFileTransformed = "unpack\\WinZipAesCipherStream.trace.transformed.out"; | 
 |  | 
 |             untransformed = System.IO.File.Create(traceFileUntransformed); | 
 |             transformed = System.IO.File.Create(traceFileTransformed); | 
 | #endif | 
 |         } | 
 |  | 
 |         private void XorInPlace(byte[] buffer, int offset, int count) | 
 |         { | 
 |             for (int i = 0; i < count; i++) | 
 |             { | 
 |                 buffer[offset + i] = (byte)(counterOut[i] ^ buffer[offset + i]); | 
 |             } | 
 |         } | 
 |  | 
 |         private void WriteTransformOneBlock(byte[] buffer, int offset) | 
 |         { | 
 |             System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); | 
 |             _xform.TransformBlock(counter, | 
 |                                   0, | 
 |                                   BLOCK_SIZE_IN_BYTES, | 
 |                                   counterOut, | 
 |                                   0); | 
 |             XorInPlace(buffer, offset, BLOCK_SIZE_IN_BYTES); | 
 |             _mac.TransformBlock(buffer, offset, BLOCK_SIZE_IN_BYTES, null, 0); | 
 |         } | 
 |  | 
 |  | 
 |         private void WriteTransformBlocks(byte[] buffer, int offset, int count) | 
 |         { | 
 |             int posn = offset; | 
 |             int last = count + offset; | 
 |  | 
 |             while (posn < buffer.Length && posn < last) | 
 |             { | 
 |                 WriteTransformOneBlock (buffer, posn); | 
 |                 posn += BLOCK_SIZE_IN_BYTES; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         private void WriteTransformFinalBlock() | 
 |         { | 
 |             if (_pendingCount == 0) | 
 |                 throw new InvalidOperationException("No bytes available."); | 
 |  | 
 |             if (_finalBlock) | 
 |                 throw new InvalidOperationException("The final block has already been transformed."); | 
 |  | 
 |             System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); | 
 |             counterOut = _xform.TransformFinalBlock(counter, | 
 |                                                     0, | 
 |                                                     BLOCK_SIZE_IN_BYTES); | 
 |             XorInPlace(_PendingWriteBlock, 0, _pendingCount); | 
 |             _mac.TransformFinalBlock(_PendingWriteBlock, 0, _pendingCount); | 
 |             _finalBlock = true; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |  | 
 |  | 
 |         private int ReadTransformOneBlock(byte[] buffer, int offset, int last) | 
 |         { | 
 |             if (_finalBlock) | 
 |                 throw new NotSupportedException(); | 
 |  | 
 |             int bytesRemaining = last - offset; | 
 |             int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES) | 
 |                 ? BLOCK_SIZE_IN_BYTES | 
 |                 : bytesRemaining; | 
 |  | 
 |             // update the counter | 
 |             System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); | 
 |  | 
 |             // Determine if this is the final block | 
 |             if ((bytesToRead == bytesRemaining) && | 
 |                 (_length > 0) && | 
 |                 (_totalBytesXferred + last == _length)) | 
 |             { | 
 |                 _mac.TransformFinalBlock(buffer, offset, bytesToRead); | 
 |                 counterOut = _xform.TransformFinalBlock(counter, | 
 |                                                         0, | 
 |                                                         BLOCK_SIZE_IN_BYTES); | 
 |                 _finalBlock = true; | 
 |             } | 
 |             else | 
 |             { | 
 |                 _mac.TransformBlock(buffer, offset, bytesToRead, null, 0); | 
 |                 _xform.TransformBlock(counter, | 
 |                                       0, // offset | 
 |                                       BLOCK_SIZE_IN_BYTES, | 
 |                                       counterOut, | 
 |                                       0);  // offset | 
 |             } | 
 |  | 
 |             XorInPlace(buffer, offset, bytesToRead); | 
 |             return bytesToRead; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private void ReadTransformBlocks(byte[] buffer, int offset, int count) | 
 |         { | 
 |             int posn = offset; | 
 |             int last = count + offset; | 
 |  | 
 |             while (posn < buffer.Length && posn < last ) | 
 |             { | 
 |                 int n = ReadTransformOneBlock (buffer, posn, last); | 
 |                 posn += n; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         public override int Read(byte[] buffer, int offset, int count) | 
 |         { | 
 |             if (_mode == CryptoMode.Encrypt) | 
 |                 throw new NotSupportedException(); | 
 |  | 
 |             if (buffer == null) | 
 |                 throw new ArgumentNullException("buffer"); | 
 |  | 
 |             if (offset < 0) | 
 |                 throw new ArgumentOutOfRangeException("offset", | 
 |                                                       "Must not be less than zero."); | 
 |             if (count < 0) | 
 |                 throw new ArgumentOutOfRangeException("count", | 
 |                                                       "Must not be less than zero."); | 
 |  | 
 |             if (buffer.Length < offset + count) | 
 |                 throw new ArgumentException("The buffer is too small"); | 
 |  | 
 |             // When I wrap a WinZipAesStream in a DeflateStream, the | 
 |             // DeflateStream asks its captive to read 4k blocks, even if the | 
 |             // encrypted bytestream is smaller than that.  This is a way to | 
 |             // limit the number of bytes read. | 
 |  | 
 |             int bytesToRead = count; | 
 |  | 
 |             if (_totalBytesXferred >= _length) | 
 |             { | 
 |                 return 0; // EOF | 
 |             } | 
 |  | 
 |             long bytesRemaining = _length - _totalBytesXferred; | 
 |             if (bytesRemaining < count) bytesToRead = (int)bytesRemaining; | 
 |  | 
 |             int n = _s.Read(buffer, offset, bytesToRead); | 
 |  | 
 |  | 
 | #if WANT_TRACE | 
 |                 untransformed.Write(buffer, offset, bytesToRead); | 
 | #endif | 
 |  | 
 |             ReadTransformBlocks(buffer, offset, bytesToRead); | 
 |  | 
 | #if WANT_TRACE | 
 |                 transformed.Write(buffer, offset, bytesToRead); | 
 | #endif | 
 |             _totalBytesXferred += n; | 
 |             return n; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Returns the final HMAC-SHA1-80 for the data that was encrypted. | 
 |         /// </summary> | 
 |         public byte[] FinalAuthentication | 
 |         { | 
 |             get | 
 |             { | 
 |                 if (!_finalBlock) | 
 |                 { | 
 |                     // special-case zero-byte files | 
 |                     if ( _totalBytesXferred != 0) | 
 |                         throw new BadStateException("The final hash has not been computed."); | 
 |  | 
 |                     // Must call ComputeHash on an empty byte array when no data | 
 |                     // has run through the MAC. | 
 |  | 
 |                     byte[] b = {  }; | 
 |                     _mac.ComputeHash(b); | 
 |                     // fall through | 
 |                 } | 
 |                 byte[] macBytes10 = new byte[10]; | 
 |                 System.Array.Copy(_mac.Hash, 0, macBytes10, 0, 10); | 
 |                 return macBytes10; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         public override void Write(byte[] buffer, int offset, int count) | 
 |         { | 
 |             if (_finalBlock) | 
 |                 throw new InvalidOperationException("The final block has already been transformed."); | 
 |  | 
 |             if (_mode == CryptoMode.Decrypt) | 
 |                 throw new NotSupportedException(); | 
 |  | 
 |             if (buffer == null) | 
 |                 throw new ArgumentNullException("buffer"); | 
 |  | 
 |             if (offset < 0) | 
 |                 throw new ArgumentOutOfRangeException("offset", | 
 |                                                       "Must not be less than zero."); | 
 |             if (count < 0) | 
 |                 throw new ArgumentOutOfRangeException("count", | 
 |                                                       "Must not be less than zero."); | 
 |             if (buffer.Length < offset + count) | 
 |                 throw new ArgumentException("The offset and count are too large"); | 
 |  | 
 |             if (count == 0) | 
 |                 return; | 
 |  | 
 |             TraceOutput("Write off({0}) count({1})", offset, count); | 
 |  | 
 | #if WANT_TRACE | 
 |             untransformed.Write(buffer, offset, count); | 
 | #endif | 
 |  | 
 |             // For proper AES encryption, an AES encryptor application calls | 
 |             // TransformBlock repeatedly for all 16-byte blocks except the | 
 |             // last. For the last block, it then calls TransformFinalBlock(). | 
 |             // | 
 |             // This class is a stream that encrypts via Write().  But, it's not | 
 |             // possible to recognize which are the "last" bytes from within the call | 
 |             // to Write(). The caller can call Write() several times in succession, | 
 |             // with varying buffers. This class only "knows" that the last bytes | 
 |             // have been written when the app calls Close(). | 
 |             // | 
 |             // Therefore, this class buffers writes: After completion every Write(), | 
 |             // a 16-byte "pending" block (_PendingWriteBlock) must hold between 1 | 
 |             // and 16 bytes, which will be used in TransformFinalBlock if the app | 
 |             // calls Close() immediately thereafter. Also, every write must | 
 |             // transform any pending bytes, before transforming the data passed in | 
 |             // to the current call. | 
 |             // | 
 |             // In operation, after the first call to Write() and before the call to | 
 |             // Close(), one full or partial block of bytes is always available, | 
 |             // pending.  At time of Close(), this class calls | 
 |             // WriteTransformFinalBlock() to flush the pending bytes. | 
 |             // | 
 |             // This approach works whether the caller writes in odd-sized batches, | 
 |             // for example 5000 bytes, or in batches that are neat multiples of the | 
 |             // blocksize (16). | 
 |             // | 
 |             // Logicaly, what we do is this: | 
 |             // | 
 |             //  1. if there are fewer than 16 bytes (pending + current), then | 
 |             //     just copy them into th pending buffer and return. | 
 |             // | 
 |             //  2. there are more than 16 bytes to write. So, take the leading slice | 
 |             //     of bytes from the current buffer, enough to fill the pending | 
 |             //     buffer. Transform the pending block, and write it out. | 
 |             // | 
 |             //  3. Take the trailing slice of bytes (a full block or a partial block), | 
 |             //     and copy it to the pending block for next time. | 
 |             // | 
 |             //  4. transform and write all the other blocks, the middle slice. | 
 |             // | 
 |  | 
 |             // There are 16 or fewer bytes, so just buffer the bytes. | 
 |             if (count + _pendingCount <= BLOCK_SIZE_IN_BYTES) | 
 |             { | 
 |                 Buffer.BlockCopy(buffer, | 
 |                                  offset, | 
 |                                  _PendingWriteBlock, | 
 |                                  _pendingCount, | 
 |                                  count); | 
 |                 _pendingCount += count; | 
 |  | 
 |                 // At this point, _PendingWriteBlock contains up to | 
 |                 // BLOCK_SIZE_IN_BYTES bytes, and _pendingCount ranges from 0 to | 
 |                 // BLOCK_SIZE_IN_BYTES. We don't want to xform+write them yet, | 
 |                 // because this may have been the last block.  The last block gets | 
 |                 // written at Close(). | 
 |                 return; | 
 |             } | 
 |  | 
 |             // We know there are at least 17 bytes, counting those in the current | 
 |             // buffer, along with the (possibly empty) pending block. | 
 |  | 
 |             int bytesRemaining = count; | 
 |             int curOffset = offset; | 
 |  | 
 |             // workitem 12815 | 
 |             // | 
 |             // xform chunkwise ... Cannot transform in place using the original | 
 |             // buffer because that is user-maintained. | 
 |  | 
 |             if (_pendingCount != 0) | 
 |             { | 
 |                 // We have more than one block of data to write, therefore it is safe | 
 |                 // to xform+write. | 
 |                 int fillCount = BLOCK_SIZE_IN_BYTES - _pendingCount; | 
 |  | 
 |                 // fillCount is possibly zero here. That happens when the pending | 
 |                 // buffer held 16 bytes (one complete block) before this call to | 
 |                 // Write. | 
 |                 if (fillCount > 0) | 
 |                 { | 
 |                     Buffer.BlockCopy(buffer, | 
 |                                      offset, | 
 |                                      _PendingWriteBlock, | 
 |                                      _pendingCount, | 
 |                                      fillCount); | 
 |  | 
 |                     // adjust counts: | 
 |                     bytesRemaining -= fillCount; | 
 |                     curOffset += fillCount; | 
 |                 } | 
 |  | 
 |                 // xform and write: | 
 |                 WriteTransformOneBlock(_PendingWriteBlock, 0); | 
 |                 _s.Write(_PendingWriteBlock, 0, BLOCK_SIZE_IN_BYTES); | 
 |                 _totalBytesXferred += BLOCK_SIZE_IN_BYTES; | 
 |                 _pendingCount = 0; | 
 |             } | 
 |  | 
 |             // At this point _PendingWriteBlock is empty, and bytesRemaining is | 
 |             // always greater than 0. | 
 |  | 
 |             // Now, xform N blocks, where N = floor((bytesRemaining-1)/16).  If | 
 |             // writing 32 bytes, then xform 1 block, and stage the remaining 16.  If | 
 |             // writing 10037 bytes, xform 627 blocks of 16 bytes, then stage the | 
 |             // remaining 5 bytes. | 
 |  | 
 |             int blocksToXform = (bytesRemaining-1)/BLOCK_SIZE_IN_BYTES; | 
 |             _pendingCount = bytesRemaining - (blocksToXform * BLOCK_SIZE_IN_BYTES); | 
 |  | 
 |             // _pendingCount is ALWAYS between 1 and 16. | 
 |             // Put the last _pendingCount bytes into the pending block. | 
 |             Buffer.BlockCopy(buffer, | 
 |                              curOffset + bytesRemaining - _pendingCount, | 
 |                              _PendingWriteBlock, | 
 |                              0, | 
 |                              _pendingCount); | 
 |             bytesRemaining -= _pendingCount; | 
 |             _totalBytesXferred += bytesRemaining; // will be true after the loop | 
 |  | 
 |             // now, transform all the full blocks preceding that. | 
 |             // bytesRemaining is always a multiple of 16 . | 
 |             if (blocksToXform > 0) | 
 |             { | 
 |                 do | 
 |                 { | 
 |                     int c = _iobuf.Length; | 
 |                     if (c > bytesRemaining) c = bytesRemaining; | 
 |                     Buffer.BlockCopy(buffer, | 
 |                                      curOffset, | 
 |                                      _iobuf, | 
 |                                      0, | 
 |                                      c); | 
 |  | 
 |                     WriteTransformBlocks(_iobuf, 0, c); | 
 |                     _s.Write(_iobuf, 0, c); | 
 |                     bytesRemaining -= c; | 
 |                     curOffset += c; | 
 |                 } while(bytesRemaining > 0); | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Close the stream. | 
 |         /// </summary> | 
 |         public override void Close() | 
 |         { | 
 |             TraceOutput("Close {0:X8}", this.GetHashCode()); | 
 |  | 
 |             // In the degenerate case, no bytes have been written to the | 
 |             // stream at all.  Need to check here, and NOT emit the | 
 |             // final block if Write has not been called. | 
 |             if (_pendingCount > 0) | 
 |             { | 
 |                 WriteTransformFinalBlock(); | 
 |                 _s.Write(_PendingWriteBlock, 0, _pendingCount); | 
 |                 _totalBytesXferred += _pendingCount; | 
 |                 _pendingCount = 0; | 
 |             } | 
 |             _s.Close(); | 
 |  | 
 | #if WANT_TRACE | 
 |             untransformed.Close(); | 
 |             transformed.Close(); | 
 |             Console.WriteLine("\nuntransformed bytestream is in  {0}", traceFileUntransformed); | 
 |             Console.WriteLine("\ntransformed bytestream is in  {0}", traceFileTransformed); | 
 | #endif | 
 |             TraceOutput("-------------------------------------------------------"); | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Returns true if the stream can be read. | 
 |         /// </summary> | 
 |         public override bool CanRead | 
 |         { | 
 |             get | 
 |             { | 
 |                 if (_mode != CryptoMode.Decrypt) return false; | 
 |                 return true; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Always returns false. | 
 |         /// </summary> | 
 |         public override bool CanSeek | 
 |         { | 
 |             get { return false; } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Returns true if the CryptoMode is Encrypt. | 
 |         /// </summary> | 
 |         public override bool CanWrite | 
 |         { | 
 |             get { return (_mode == CryptoMode.Encrypt); } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Flush the content in the stream. | 
 |         /// </summary> | 
 |         public override void Flush() | 
 |         { | 
 |             _s.Flush(); | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Getting this property throws a NotImplementedException. | 
 |         /// </summary> | 
 |         public override long Length | 
 |         { | 
 |             get { throw new NotImplementedException(); } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Getting or Setting this property throws a NotImplementedException. | 
 |         /// </summary> | 
 |         public override long Position | 
 |         { | 
 |             get { throw new NotImplementedException(); } | 
 |             set { throw new NotImplementedException(); } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// This method throws a NotImplementedException. | 
 |         /// </summary> | 
 |         public override long Seek(long offset, System.IO.SeekOrigin origin) | 
 |         { | 
 |             throw new NotImplementedException(); | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// This method throws a NotImplementedException. | 
 |         /// </summary> | 
 |         public override void SetLength(long value) | 
 |         { | 
 |             throw new NotImplementedException(); | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         [System.Diagnostics.ConditionalAttribute("Trace")] | 
 |         private void TraceOutput(string format, params object[] varParams) | 
 |         { | 
 |             lock(_outputLock) | 
 |             { | 
 |                 int tid = System.Threading.Thread.CurrentThread.GetHashCode(); | 
 |                 Console.ForegroundColor = (ConsoleColor) (tid % 8 + 8); | 
 |                 Console.Write("{0:000} WZACS ", tid); | 
 |                 Console.WriteLine(format, varParams); | 
 |                 Console.ResetColor(); | 
 |             } | 
 |         } | 
 |  | 
 |         private object _outputLock = new Object(); | 
 |     } | 
 | } | 
 | #endif |