Summary

Class:ICSharpCode.SharpZipLib.Encryption.PkzipClassic
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Encryption\PkzipClassic.cs
Covered lines:26
Uncovered lines:2
Coverable lines:28
Total lines:445
Line coverage:92.8%
Branch coverage:66.6%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
GenerateKeys(...)492.3171.43

File(s)

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

#LineLine coverage
 1using System;
 2using System.Security.Cryptography;
 3using ICSharpCode.SharpZipLib.Checksum;
 4
 5namespace ICSharpCode.SharpZipLib.Encryption
 6{
 7  /// <summary>
 8  /// PkzipClassic embodies the classic or original encryption facilities used in Pkzip archives.
 9  /// While it has been superceded by more recent and more powerful algorithms, its still in use and
 10  /// is viable for preventing casual snooping
 11  /// </summary>
 12  public abstract class PkzipClassic : SymmetricAlgorithm
 13  {
 14    /// <summary>
 15    /// Generates new encryption keys based on given seed
 16    /// </summary>
 17    /// <param name="seed">The seed value to initialise keys with.</param>
 18    /// <returns>A new key value.</returns>
 19    static public byte[] GenerateKeys(byte[] seed)
 20    {
 6821       if (seed == null) {
 022        throw new ArgumentNullException(nameof(seed));
 23      }
 24
 6825       if (seed.Length == 0) {
 026        throw new ArgumentException("Length is zero", nameof(seed));
 27      }
 28
 6829      uint[] newKeys = {
 6830        0x12345678,
 6831        0x23456789,
 6832        0x34567890
 6833       };
 34
 103435       for (int i = 0; i < seed.Length; ++i) {
 44936        newKeys[0] = Crc32.ComputeCrc32(newKeys[0], seed[i]);
 44937        newKeys[1] = newKeys[1] + (byte)newKeys[0];
 44938        newKeys[1] = newKeys[1] * 134775813 + 1;
 44939        newKeys[2] = Crc32.ComputeCrc32(newKeys[2], (byte)(newKeys[1] >> 24));
 40      }
 41
 6842      byte[] result = new byte[12];
 6843      result[0] = (byte)(newKeys[0] & 0xff);
 6844      result[1] = (byte)((newKeys[0] >> 8) & 0xff);
 6845      result[2] = (byte)((newKeys[0] >> 16) & 0xff);
 6846      result[3] = (byte)((newKeys[0] >> 24) & 0xff);
 6847      result[4] = (byte)(newKeys[1] & 0xff);
 6848      result[5] = (byte)((newKeys[1] >> 8) & 0xff);
 6849      result[6] = (byte)((newKeys[1] >> 16) & 0xff);
 6850      result[7] = (byte)((newKeys[1] >> 24) & 0xff);
 6851      result[8] = (byte)(newKeys[2] & 0xff);
 6852      result[9] = (byte)((newKeys[2] >> 8) & 0xff);
 6853      result[10] = (byte)((newKeys[2] >> 16) & 0xff);
 6854      result[11] = (byte)((newKeys[2] >> 24) & 0xff);
 6855      return result;
 56    }
 57  }
 58
 59  /// <summary>
 60  /// PkzipClassicCryptoBase provides the low level facilities for encryption
 61  /// and decryption using the PkzipClassic algorithm.
 62  /// </summary>
 63  class PkzipClassicCryptoBase
 64  {
 65    /// <summary>
 66    /// Transform a single byte
 67    /// </summary>
 68    /// <returns>
 69    /// The transformed value
 70    /// </returns>
 71    protected byte TransformByte()
 72    {
 73      uint temp = ((keys[2] & 0xFFFF) | 2);
 74      return (byte)((temp * (temp ^ 1)) >> 8);
 75    }
 76
 77    /// <summary>
 78    /// Set the key schedule for encryption/decryption.
 79    /// </summary>
 80    /// <param name="keyData">The data use to set the keys from.</param>
 81    protected void SetKeys(byte[] keyData)
 82    {
 83      if (keyData == null) {
 84        throw new ArgumentNullException(nameof(keyData));
 85      }
 86
 87      if (keyData.Length != 12) {
 88        throw new InvalidOperationException("Key length is not valid");
 89      }
 90
 91      keys = new uint[3];
 92      keys[0] = (uint)((keyData[3] << 24) | (keyData[2] << 16) | (keyData[1] << 8) | keyData[0]);
 93      keys[1] = (uint)((keyData[7] << 24) | (keyData[6] << 16) | (keyData[5] << 8) | keyData[4]);
 94      keys[2] = (uint)((keyData[11] << 24) | (keyData[10] << 16) | (keyData[9] << 8) | keyData[8]);
 95    }
 96
 97    /// <summary>
 98    /// Update encryption keys
 99    /// </summary>
 100    protected void UpdateKeys(byte ch)
 101    {
 102      keys[0] = Crc32.ComputeCrc32(keys[0], ch);
 103      keys[1] = keys[1] + (byte)keys[0];
 104      keys[1] = keys[1] * 134775813 + 1;
 105      keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));
 106    }
 107
 108    /// <summary>
 109    /// Reset the internal state.
 110    /// </summary>
 111    protected void Reset()
 112    {
 113      keys[0] = 0;
 114      keys[1] = 0;
 115      keys[2] = 0;
 116    }
 117
 118    #region Instance Fields
 119    uint[] keys;
 120    #endregion
 121  }
 122
 123  /// <summary>
 124  /// PkzipClassic CryptoTransform for encryption.
 125  /// </summary>
 126  class PkzipClassicEncryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform
 127  {
 128    /// <summary>
 129    /// Initialise a new instance of <see cref="PkzipClassicEncryptCryptoTransform"></see>
 130    /// </summary>
 131    /// <param name="keyBlock">The key block to use.</param>
 132    internal PkzipClassicEncryptCryptoTransform(byte[] keyBlock)
 133    {
 134      SetKeys(keyBlock);
 135    }
 136
 137    #region ICryptoTransform Members
 138
 139    /// <summary>
 140    /// Transforms the specified region of the specified byte array.
 141    /// </summary>
 142    /// <param name="inputBuffer">The input for which to compute the transform.</param>
 143    /// <param name="inputOffset">The offset into the byte array from which to begin using data.</param>
 144    /// <param name="inputCount">The number of bytes in the byte array to use as data.</param>
 145    /// <returns>The computed transform.</returns>
 146    public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
 147    {
 148      byte[] result = new byte[inputCount];
 149      TransformBlock(inputBuffer, inputOffset, inputCount, result, 0);
 150      return result;
 151    }
 152
 153    /// <summary>
 154    /// Transforms the specified region of the input byte array and copies
 155    /// the resulting transform to the specified region of the output byte array.
 156    /// </summary>
 157    /// <param name="inputBuffer">The input for which to compute the transform.</param>
 158    /// <param name="inputOffset">The offset into the input byte array from which to begin using data.</param>
 159    /// <param name="inputCount">The number of bytes in the input byte array to use as data.</param>
 160    /// <param name="outputBuffer">The output to which to write the transform.</param>
 161    /// <param name="outputOffset">The offset into the output byte array from which to begin writing data.</param>
 162    /// <returns>The number of bytes written.</returns>
 163    public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset
 164    {
 165      for (int i = inputOffset; i < inputOffset + inputCount; ++i) {
 166        byte oldbyte = inputBuffer[i];
 167        outputBuffer[outputOffset++] = (byte)(inputBuffer[i] ^ TransformByte());
 168        UpdateKeys(oldbyte);
 169      }
 170      return inputCount;
 171    }
 172
 173    /// <summary>
 174    /// Gets a value indicating whether the current transform can be reused.
 175    /// </summary>
 176    public bool CanReuseTransform {
 177      get {
 178        return true;
 179      }
 180    }
 181
 182    /// <summary>
 183    /// Gets the size of the input data blocks in bytes.
 184    /// </summary>
 185    public int InputBlockSize {
 186      get {
 187        return 1;
 188      }
 189    }
 190
 191    /// <summary>
 192    /// Gets the size of the output data blocks in bytes.
 193    /// </summary>
 194    public int OutputBlockSize {
 195      get {
 196        return 1;
 197      }
 198    }
 199
 200    /// <summary>
 201    /// Gets a value indicating whether multiple blocks can be transformed.
 202    /// </summary>
 203    public bool CanTransformMultipleBlocks {
 204      get {
 205        return true;
 206      }
 207    }
 208
 209    #endregion
 210
 211    #region IDisposable Members
 212
 213    /// <summary>
 214    /// Cleanup internal state.
 215    /// </summary>
 216    public void Dispose()
 217    {
 218      Reset();
 219    }
 220
 221    #endregion
 222  }
 223
 224
 225  /// <summary>
 226  /// PkzipClassic CryptoTransform for decryption.
 227  /// </summary>
 228  class PkzipClassicDecryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform
 229  {
 230    /// <summary>
 231    /// Initialise a new instance of <see cref="PkzipClassicDecryptCryptoTransform"></see>.
 232    /// </summary>
 233    /// <param name="keyBlock">The key block to decrypt with.</param>
 234    internal PkzipClassicDecryptCryptoTransform(byte[] keyBlock)
 235    {
 236      SetKeys(keyBlock);
 237    }
 238
 239    #region ICryptoTransform Members
 240
 241    /// <summary>
 242    /// Transforms the specified region of the specified byte array.
 243    /// </summary>
 244    /// <param name="inputBuffer">The input for which to compute the transform.</param>
 245    /// <param name="inputOffset">The offset into the byte array from which to begin using data.</param>
 246    /// <param name="inputCount">The number of bytes in the byte array to use as data.</param>
 247    /// <returns>The computed transform.</returns>
 248    public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
 249    {
 250      byte[] result = new byte[inputCount];
 251      TransformBlock(inputBuffer, inputOffset, inputCount, result, 0);
 252      return result;
 253    }
 254
 255    /// <summary>
 256    /// Transforms the specified region of the input byte array and copies
 257    /// the resulting transform to the specified region of the output byte array.
 258    /// </summary>
 259    /// <param name="inputBuffer">The input for which to compute the transform.</param>
 260    /// <param name="inputOffset">The offset into the input byte array from which to begin using data.</param>
 261    /// <param name="inputCount">The number of bytes in the input byte array to use as data.</param>
 262    /// <param name="outputBuffer">The output to which to write the transform.</param>
 263    /// <param name="outputOffset">The offset into the output byte array from which to begin writing data.</param>
 264    /// <returns>The number of bytes written.</returns>
 265    public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset
 266    {
 267      for (int i = inputOffset; i < inputOffset + inputCount; ++i) {
 268        var newByte = (byte)(inputBuffer[i] ^ TransformByte());
 269        outputBuffer[outputOffset++] = newByte;
 270        UpdateKeys(newByte);
 271      }
 272      return inputCount;
 273    }
 274
 275    /// <summary>
 276    /// Gets a value indicating whether the current transform can be reused.
 277    /// </summary>
 278    public bool CanReuseTransform {
 279      get {
 280        return true;
 281      }
 282    }
 283
 284    /// <summary>
 285    /// Gets the size of the input data blocks in bytes.
 286    /// </summary>
 287    public int InputBlockSize {
 288      get {
 289        return 1;
 290      }
 291    }
 292
 293    /// <summary>
 294    /// Gets the size of the output data blocks in bytes.
 295    /// </summary>
 296    public int OutputBlockSize {
 297      get {
 298        return 1;
 299      }
 300    }
 301
 302    /// <summary>
 303    /// Gets a value indicating whether multiple blocks can be transformed.
 304    /// </summary>
 305    public bool CanTransformMultipleBlocks {
 306      get {
 307        return true;
 308      }
 309    }
 310
 311    #endregion
 312
 313    #region IDisposable Members
 314
 315    /// <summary>
 316    /// Cleanup internal state.
 317    /// </summary>
 318    public void Dispose()
 319    {
 320      Reset();
 321    }
 322
 323    #endregion
 324  }
 325
 326  /// <summary>
 327  /// Defines a wrapper object to access the Pkzip algorithm.
 328  /// This class cannot be inherited.
 329  /// </summary>
 330  public sealed class PkzipClassicManaged : PkzipClassic
 331  {
 332    /// <summary>
 333    /// Get / set the applicable block size in bits.
 334    /// </summary>
 335    /// <remarks>The only valid block size is 8.</remarks>
 336    public override int BlockSize {
 337      get {
 338        return 8;
 339      }
 340
 341      set {
 342        if (value != 8) {
 343          throw new CryptographicException("Block size is invalid");
 344        }
 345      }
 346    }
 347
 348    /// <summary>
 349    /// Get an array of legal <see cref="KeySizes">key sizes.</see>
 350    /// </summary>
 351    public override KeySizes[] LegalKeySizes {
 352      get {
 353        KeySizes[] keySizes = new KeySizes[1];
 354        keySizes[0] = new KeySizes(12 * 8, 12 * 8, 0);
 355        return keySizes;
 356      }
 357    }
 358
 359    /// <summary>
 360    /// Generate an initial vector.
 361    /// </summary>
 362    public override void GenerateIV()
 363    {
 364      // Do nothing.
 365    }
 366
 367    /// <summary>
 368    /// Get an array of legal <see cref="KeySizes">block sizes</see>.
 369    /// </summary>
 370    public override KeySizes[] LegalBlockSizes {
 371      get {
 372        KeySizes[] keySizes = new KeySizes[1];
 373        keySizes[0] = new KeySizes(1 * 8, 1 * 8, 0);
 374        return keySizes;
 375      }
 376    }
 377
 378    /// <summary>
 379    /// Get / set the key value applicable.
 380    /// </summary>
 381    public override byte[] Key {
 382      get {
 383        if (key_ == null) {
 384          GenerateKey();
 385        }
 386
 387        return (byte[])key_.Clone();
 388      }
 389
 390      set {
 391        if (value == null) {
 392          throw new ArgumentNullException(nameof(value));
 393        }
 394
 395        if (value.Length != 12) {
 396          throw new CryptographicException("Key size is illegal");
 397        }
 398
 399        key_ = (byte[])value.Clone();
 400      }
 401    }
 402
 403    /// <summary>
 404    /// Generate a new random key.
 405    /// </summary>
 406    public override void GenerateKey()
 407    {
 408      key_ = new byte[12];
 409      var rnd = new Random();
 410      rnd.NextBytes(key_);
 411    }
 412
 413    /// <summary>
 414    /// Create an encryptor.
 415    /// </summary>
 416    /// <param name="rgbKey">The key to use for this encryptor.</param>
 417    /// <param name="rgbIV">Initialisation vector for the new encryptor.</param>
 418    /// <returns>Returns a new PkzipClassic encryptor</returns>
 419    public override ICryptoTransform CreateEncryptor(
 420      byte[] rgbKey,
 421      byte[] rgbIV)
 422    {
 423      key_ = rgbKey;
 424      return new PkzipClassicEncryptCryptoTransform(Key);
 425    }
 426
 427    /// <summary>
 428    /// Create a decryptor.
 429    /// </summary>
 430    /// <param name="rgbKey">Keys to use for this new decryptor.</param>
 431    /// <param name="rgbIV">Initialisation vector for the new decryptor.</param>
 432    /// <returns>Returns a new decryptor.</returns>
 433    public override ICryptoTransform CreateDecryptor(
 434      byte[] rgbKey,
 435      byte[] rgbIV)
 436    {
 437      key_ = rgbKey;
 438      return new PkzipClassicDecryptCryptoTransform(Key);
 439    }
 440
 441    #region Instance Fields
 442    byte[] key_;
 443    #endregion
 444  }
 445}