Summary

Class:ICSharpCode.SharpZipLib.Encryption.ZipAESTransform
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Encryption\ZipAESTransform.cs
Covered lines:0
Uncovered lines:47
Coverable lines:47
Total lines:183
Line coverage:0%
Branch coverage:0%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor(...)400
TransformBlock(...)600
GetAuthCode()200
TransformFinalBlock(...)100
Dispose()100

File(s)

C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Encryption\ZipAESTransform.cs

#LineLine coverage
 1using System;
 2using System.Security.Cryptography;
 3
 4namespace ICSharpCode.SharpZipLib.Encryption
 5{
 6  /// <summary>
 7  /// Transforms stream using AES in CTR mode
 8  /// </summary>
 9  internal class ZipAESTransform : ICryptoTransform
 10  {
 11    private const int PWD_VER_LENGTH = 2;
 12
 13    // WinZip use iteration count of 1000 for PBKDF2 key generation
 14    private const int KEY_ROUNDS = 1000;
 15
 16    // For 128-bit AES (16 bytes) the encryption is implemented as expected.
 17    // For 256-bit AES (32 bytes) WinZip do full 256 bit AES of the nonce to create the encryption
 18    // block but use only the first 16 bytes of it, and discard the second half.
 19    private const int ENCRYPT_BLOCK = 16;
 20
 21    private int _blockSize;
 22    private readonly ICryptoTransform _encryptor;
 23    private readonly byte[] _counterNonce;
 24    private byte[] _encryptBuffer;
 25    private int _encrPos;
 26    private byte[] _pwdVerifier;
 27    private HMACSHA1 _hmacsha1;
 28    private bool _finalised;
 29
 30    private bool _writeMode;
 31
 32    /// <summary>
 33    /// Constructor.
 34    /// </summary>
 35    /// <param name="key">Password string</param>
 36    /// <param name="saltBytes">Random bytes, length depends on encryption strength.
 37    /// 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes.</param>
 38    /// <param name="blockSize">The encryption strength, in bytes eg 16 for 128 bits.</param>
 39    /// <param name="writeMode">True when creating a zip, false when reading. For the AuthCode.</param>
 40    ///
 041    public ZipAESTransform(string key, byte[] saltBytes, int blockSize, bool writeMode)
 42    {
 43
 044       if (blockSize != 16 && blockSize != 32) // 24 valid for AES but not supported by Winzip
 045        throw new Exception("Invalid blocksize " + blockSize + ". Must be 16 or 32.");
 046       if (saltBytes.Length != blockSize / 2)
 047        throw new Exception("Invalid salt len. Must be " + blockSize / 2 + " for blocksize " + blockSize);
 48      // initialise the encryption buffer and buffer pos
 049      _blockSize = blockSize;
 050      _encryptBuffer = new byte[_blockSize];
 051      _encrPos = ENCRYPT_BLOCK;
 52
 53      // Performs the equivalent of derive_key in Dr Brian Gladman's pwd2key.c
 054      var pdb = new Rfc2898DeriveBytes(key, saltBytes, KEY_ROUNDS);
 055      var rm = new RijndaelManaged();
 056      rm.Mode = CipherMode.ECB;           // No feedback from cipher for CTR mode
 057      _counterNonce = new byte[_blockSize];
 058      byte[] byteKey1 = pdb.GetBytes(_blockSize);
 059      byte[] byteKey2 = pdb.GetBytes(_blockSize);
 060      _encryptor = rm.CreateEncryptor(byteKey1, byteKey2);
 061      _pwdVerifier = pdb.GetBytes(PWD_VER_LENGTH);
 62      //
 063      _hmacsha1 = new HMACSHA1(byteKey2);
 064      _writeMode = writeMode;
 065    }
 66
 67    /// <summary>
 68    /// Implement the ICryptoTransform method.
 69    /// </summary>
 70    public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset
 71    {
 72
 73      // Pass the data stream to the hash algorithm for generating the Auth Code.
 74      // This does not change the inputBuffer. Do this before decryption for read mode.
 075       if (!_writeMode) {
 076        _hmacsha1.TransformBlock(inputBuffer, inputOffset, inputCount, inputBuffer, inputOffset);
 77      }
 78      // Encrypt with AES in CTR mode. Regards to Dr Brian Gladman for this.
 079      int ix = 0;
 080       while (ix < inputCount) {
 081         if (_encrPos == ENCRYPT_BLOCK) {
 82          /* increment encryption nonce   */
 083          int j = 0;
 084           while (++_counterNonce[j] == 0) {
 085            ++j;
 86          }
 87          /* encrypt the nonce to form next xor buffer    */
 088          _encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0);
 089          _encrPos = 0;
 90        }
 091        outputBuffer[ix + outputOffset] = (byte)(inputBuffer[ix + inputOffset] ^ _encryptBuffer[_encrPos++]);
 92        //
 093        ix++;
 94      }
 095       if (_writeMode) {
 96        // This does not change the buffer.
 097        _hmacsha1.TransformBlock(outputBuffer, outputOffset, inputCount, outputBuffer, outputOffset);
 98      }
 099      return inputCount;
 100    }
 101
 102    /// <summary>
 103    /// Returns the 2 byte password verifier
 104    /// </summary>
 105    public byte[] PwdVerifier {
 106      get {
 0107        return _pwdVerifier;
 108      }
 109    }
 110
 111    /// <summary>
 112    /// Returns the 10 byte AUTH CODE to be checked or appended immediately following the AES data stream.
 113    /// </summary>
 114    public byte[] GetAuthCode()
 115    {
 116      // We usually don't get advance notice of final block. Hash requres a TransformFinal.
 0117       if (!_finalised) {
 0118        byte[] dummy = new byte[0];
 0119        _hmacsha1.TransformFinalBlock(dummy, 0, 0);
 0120        _finalised = true;
 121      }
 0122      return _hmacsha1.Hash;
 123    }
 124
 125    #region ICryptoTransform Members
 126
 127    /// <summary>
 128    /// Not implemented.
 129    /// </summary>
 130    public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
 131    {
 132
 0133      throw new NotImplementedException("ZipAESTransform.TransformFinalBlock");
 134    }
 135
 136    /// <summary>
 137    /// Gets the size of the input data blocks in bytes.
 138    /// </summary>
 139    public int InputBlockSize {
 140      get {
 0141        return _blockSize;
 142      }
 143    }
 144
 145    /// <summary>
 146    /// Gets the size of the output data blocks in bytes.
 147    /// </summary>
 148    public int OutputBlockSize {
 149      get {
 0150        return _blockSize;
 151      }
 152    }
 153
 154    /// <summary>
 155    /// Gets a value indicating whether multiple blocks can be transformed.
 156    /// </summary>
 157    public bool CanTransformMultipleBlocks {
 158      get {
 0159        return true;
 160      }
 161    }
 162
 163    /// <summary>
 164    /// Gets a value indicating whether the current transform can be reused.
 165    /// </summary>
 166    public bool CanReuseTransform {
 167      get {
 0168        return true;
 169      }
 170    }
 171
 172    /// <summary>
 173    /// Cleanup internal state.
 174    /// </summary>
 175    public void Dispose()
 176    {
 0177      _encryptor.Dispose();
 0178    }
 179
 180    #endregion
 181
 182  }
 183}