| // | 
| // ZipAESStream.cs | 
| // | 
| // Copyright 2009 David Pierson | 
| // | 
| // 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. | 
| //  | 
|   | 
| #if !NET_1_1 && !NETCF_2_0 | 
|   | 
| using System; | 
| using System.IO; | 
| using System.Security.Cryptography; | 
|   | 
| namespace ICSharpCode.SharpZipLib.Encryption { | 
|   | 
|     // Based on information from http://www.winzip.com/aes_info.htm | 
|     // and http://www.gladman.me.uk/cryptography_technology/fileencrypt/ | 
|   | 
|     /// <summary> | 
|     /// Encrypts and decrypts AES ZIP | 
|     /// </summary> | 
|     internal class ZipAESStream : CryptoStream { | 
|   | 
|         /// <summary> | 
|         /// Constructor | 
|         /// </summary> | 
|         /// <param name="stream">The stream on which to perform the cryptographic transformation.</param> | 
|         /// <param name="transform">Instance of ZipAESTransform</param> | 
|         /// <param name="mode">Read or Write</param> | 
|         public ZipAESStream(Stream stream, ZipAESTransform transform, CryptoStreamMode mode) | 
|             : base(stream, transform, mode) { | 
|   | 
|             _stream = stream; | 
|             _transform = transform; | 
|             _slideBuffer = new byte[1024]; | 
|   | 
|             _blockAndAuth = CRYPTO_BLOCK_SIZE + AUTH_CODE_LENGTH; | 
|   | 
|             // mode: | 
|             //  CryptoStreamMode.Read means we read from "stream" and pass decrypted to our Read() method. | 
|             //  Write bypasses this stream and uses the Transform directly. | 
|             if (mode != CryptoStreamMode.Read) { | 
|                 throw new Exception("ZipAESStream only for read"); | 
|             } | 
|         } | 
|   | 
|         // The final n bytes of the AES stream contain the Auth Code. | 
|         private const int AUTH_CODE_LENGTH = 10; | 
|   | 
|         private Stream _stream; | 
|         private ZipAESTransform _transform; | 
|         private byte[] _slideBuffer; | 
|         private int _slideBufStartPos; | 
|         private int _slideBufFreePos; | 
|         // Blocksize is always 16 here, even for AES-256 which has transform.InputBlockSize of 32. | 
|         private const int CRYPTO_BLOCK_SIZE = 16; | 
|         private int _blockAndAuth; | 
|   | 
|         /// <summary> | 
|         /// Reads a sequence of bytes from the current CryptoStream into buffer, | 
|         /// and advances the position within the stream by the number of bytes read. | 
|         /// </summary> | 
|         public override int Read(byte[] outBuffer, int offset, int count) { | 
|             int nBytes = 0; | 
|             while (nBytes < count) { | 
|                 // Calculate buffer quantities vs read-ahead size, and check for sufficient free space | 
|                 int byteCount = _slideBufFreePos - _slideBufStartPos; | 
|   | 
|                 // Need to handle final block and Auth Code specially, but don't know total data length. | 
|                 // Maintain a read-ahead equal to the length of (crypto block + Auth Code).  | 
|                 // When that runs out we can detect these final sections. | 
|                 int lengthToRead = _blockAndAuth - byteCount; | 
|                 if (_slideBuffer.Length - _slideBufFreePos < lengthToRead) { | 
|                     // Shift the data to the beginning of the buffer | 
|                     int iTo = 0; | 
|                     for (int iFrom = _slideBufStartPos; iFrom < _slideBufFreePos; iFrom++, iTo++) { | 
|                         _slideBuffer[iTo] = _slideBuffer[iFrom]; | 
|                     } | 
|                     _slideBufFreePos -= _slideBufStartPos;        // Note the -= | 
|                     _slideBufStartPos = 0; | 
|                 } | 
|                 int obtained = _stream.Read(_slideBuffer, _slideBufFreePos, lengthToRead); | 
|                 _slideBufFreePos += obtained; | 
|   | 
|                 // Recalculate how much data we now have | 
|                 byteCount = _slideBufFreePos - _slideBufStartPos; | 
|                 if (byteCount >= _blockAndAuth) { | 
|                     // At least a 16 byte block and an auth code remains. | 
|                     _transform.TransformBlock(_slideBuffer, | 
|                                               _slideBufStartPos, | 
|                                               CRYPTO_BLOCK_SIZE, | 
|                                               outBuffer, | 
|                                               offset); | 
|                     nBytes += CRYPTO_BLOCK_SIZE; | 
|                     offset += CRYPTO_BLOCK_SIZE; | 
|                     _slideBufStartPos += CRYPTO_BLOCK_SIZE; | 
|                 } else { | 
|                     // Last round. | 
|                     if (byteCount > AUTH_CODE_LENGTH) { | 
|                         // At least one byte of data plus auth code | 
|                         int finalBlock = byteCount - AUTH_CODE_LENGTH; | 
|                         _transform.TransformBlock(_slideBuffer, | 
|                                                   _slideBufStartPos, | 
|                                                   finalBlock, | 
|                                                   outBuffer, | 
|                                                   offset); | 
|   | 
|                         nBytes += finalBlock; | 
|                         _slideBufStartPos += finalBlock; | 
|                     } | 
|                     else if (byteCount < AUTH_CODE_LENGTH) | 
|                         throw new Exception("Internal error missed auth code");    // Coding bug | 
|                     // Final block done. Check Auth code. | 
|                     byte[] calcAuthCode = _transform.GetAuthCode(); | 
|                     for (int i = 0; i < AUTH_CODE_LENGTH; i++) { | 
|                         if (calcAuthCode[i] != _slideBuffer[_slideBufStartPos + i]) { | 
|                             throw new Exception("AES Authentication Code does not match. This is a super-CRC check on the data in the file after compression and encryption. \r\n" | 
|                                 + "The file may be damaged."); | 
|                         } | 
|                     } | 
|   | 
|                     break;    // Reached the auth code | 
|                 } | 
|             } | 
|             return nBytes; | 
|         } | 
|   | 
|         /// <summary> | 
|         /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. | 
|         /// </summary> | 
|         /// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream. </param> | 
|         /// <param name="offset">The byte offset in buffer at which to begin copying bytes to the current stream. </param> | 
|         /// <param name="count">The number of bytes to be written to the current stream. </param> | 
|         public override void Write(byte[] buffer, int offset, int count) { | 
|             // ZipAESStream is used for reading but not for writing. Writing uses the ZipAESTransform directly. | 
|             throw new NotImplementedException(); | 
|         } | 
|     } | 
| } | 
| #endif |