219 lines
7.1 KiB
C#
219 lines
7.1 KiB
C#
|
//
|
||
|
// 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
|