Mercurial > repos > SharpZipLib
view Encryption/ZipAESTransform.cs @ 1:94e25b786321
Re #311: can't read ZIP file packed by Linux app Archive Manager/File Roller
Initial commit of clean SharpZipLib 0860 source. Only change is build paths.
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sat, 30 Oct 2010 14:03:17 +0000 |
parents | |
children |
line wrap: on
line source
// // ZipAESTransform.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 // Framework version 2.0 required for Rfc2898DeriveBytes using System; using System.Security.Cryptography; namespace ICSharpCode.SharpZipLib.Encryption { /// <summary> /// Transforms stream using AES in CTR mode /// </summary> internal class ZipAESTransform : ICryptoTransform { private const int PWD_VER_LENGTH = 2; // WinZip use iteration count of 1000 for PBKDF2 key generation private const int KEY_ROUNDS = 1000; // For 128-bit AES (16 bytes) the encryption is implemented as expected. // For 256-bit AES (32 bytes) WinZip do full 256 bit AES of the nonce to create the encryption // block but use only the first 16 bytes of it, and discard the second half. private const int ENCRYPT_BLOCK = 16; private int _blockSize; private ICryptoTransform _encryptor; private readonly byte[] _counterNonce; private byte[] _encryptBuffer; private int _encrPos; private byte[] _pwdVerifier; private HMACSHA1 _hmacsha1; private bool _finalised; private bool _writeMode; /// <summary> /// Constructor. /// </summary> /// <param name="key">Password string</param> /// <param name="saltBytes">Random bytes, length depends on encryption strength. /// 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes.</param> /// <param name="blockSize">The encryption strength, in bytes eg 16 for 128 bits.</param> /// <param name="writeMode">True when creating a zip, false when reading. For the AuthCode.</param> /// public ZipAESTransform(string key, byte[] saltBytes, int blockSize, bool writeMode) { if (blockSize != 16 && blockSize != 32) // 24 valid for AES but not supported by Winzip throw new Exception("Invalid blocksize " + blockSize + ". Must be 16 or 32."); if (saltBytes.Length != blockSize / 2) throw new Exception("Invalid salt len. Must be " + blockSize / 2 + " for blocksize " + blockSize); // initialise the encryption buffer and buffer pos _blockSize = blockSize; _encryptBuffer = new byte[_blockSize]; _encrPos = ENCRYPT_BLOCK; // Performs the equivalent of derive_key in Dr Brian Gladman's pwd2key.c Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(key, saltBytes, KEY_ROUNDS); RijndaelManaged rm = new RijndaelManaged(); rm.Mode = CipherMode.ECB; // No feedback from cipher for CTR mode _counterNonce = new byte[_blockSize]; byte[] byteKey1 = pdb.GetBytes(_blockSize); byte[] byteKey2 = pdb.GetBytes(_blockSize); _encryptor = rm.CreateEncryptor(byteKey1, byteKey2); _pwdVerifier = pdb.GetBytes(PWD_VER_LENGTH); // _hmacsha1 = new HMACSHA1(byteKey2); _writeMode = writeMode; } /// <summary> /// Implement the ICryptoTransform method. /// </summary> public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { // Pass the data stream to the hash algorithm for generating the Auth Code. // This does not change the inputBuffer. Do this before decryption for read mode. if (!_writeMode) { _hmacsha1.TransformBlock(inputBuffer, inputOffset, inputCount, inputBuffer, inputOffset); } // Encrypt with AES in CTR mode. Regards to Dr Brian Gladman for this. int ix = 0; while (ix < inputCount) { if (_encrPos == ENCRYPT_BLOCK) { /* increment encryption nonce */ int j = 0; while (++_counterNonce[j] == 0) { ++j; } /* encrypt the nonce to form next xor buffer */ _encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0); _encrPos = 0; } outputBuffer[ix + outputOffset] = (byte)(inputBuffer[ix + inputOffset] ^ _encryptBuffer[_encrPos++]); // ix++; } if (_writeMode) { // This does not change the buffer. _hmacsha1.TransformBlock(outputBuffer, outputOffset, inputCount, outputBuffer, outputOffset); } return inputCount; } /// <summary> /// Returns the 2 byte password verifier /// </summary> public byte[] PwdVerifier { get { return _pwdVerifier; } } /// <summary> /// Returns the 10 byte AUTH CODE to be checked or appended immediately following the AES data stream. /// </summary> public byte[] GetAuthCode() { // We usually don't get advance notice of final block. Hash requres a TransformFinal. if (!_finalised) { byte[] dummy = new byte[0]; _hmacsha1.TransformFinalBlock(dummy, 0, 0); _finalised = true; } return _hmacsha1.Hash; } #region ICryptoTransform Members /// <summary> /// Not implemented. /// </summary> public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { throw new NotImplementedException("ZipAESTransform.TransformFinalBlock"); } /// <summary> /// Gets the size of the input data blocks in bytes. /// </summary> public int InputBlockSize { get { return _blockSize; } } /// <summary> /// Gets the size of the output data blocks in bytes. /// </summary> public int OutputBlockSize { get { return _blockSize; } } /// <summary> /// Gets a value indicating whether multiple blocks can be transformed. /// </summary> public bool CanTransformMultipleBlocks { get { return true; } } /// <summary> /// Gets a value indicating whether the current transform can be reused. /// </summary> public bool CanReuseTransform { get { return true; } } /// <summary> /// Cleanup internal state. /// </summary> public void Dispose() { _encryptor.Dispose(); } #endregion } } #endif