| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 | using System;using System.IO;using System.Security.Cryptography;namespace ICSharpCode.SharpZipLib.Encryption{	/// <summary>	/// Encrypts and decrypts AES ZIP	/// </summary>	/// <remarks>	/// Based on information from http://www.winzip.com/aes_info.htm	/// and http://www.gladman.me.uk/cryptography_technology/fileencrypt/	/// </remarks>	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[] buffer, 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,											  buffer,											  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,												  buffer,												  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();		}	}}
 |