SymmetricAlgorithm.cs :  » Network-Clients » SharpPrivacyLibrary » SharpPrivacy » Cipher » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Network Clients » SharpPrivacyLibrary 
SharpPrivacyLibrary » SharpPrivacy » Cipher » SymmetricAlgorithm.cs
//
// System.Security.Cryptography SymmetricAlgorithm Class implementation
//
// Authors:
//   Thomas Neidhart (tome@sbox.tugraz.at)
//   Sebastien Pouliot (spouliot@motus.com)
//
// Portions (C) 2002 Motus Technologies Inc. (http://www.motus.com)
//
// Modified by Daniel Fabian to fit SharpPrivacy's needs.
// This file is part of the SharpPrivacy source code contribution.
// Get get the original SymmetricAlgorithm class, please visit the
// mono project at http://www.go-mono.com.
// Changes (C) 2003 Daniel Fabian

using System;

namespace SharpPrivacy.Cipher{

  // This class implement most of the common code required for symmetric
  // algorithm transforms, like:
  // - CipherMode: Builds CBC and CFB on top of (descendant supplied) ECB
  // - PaddingMode, transform properties, multiple blocks, reuse...
  //
  // Descendants MUST:
  // - intialize themselves (like key expansion, ...)
  // - override the ECB (Electronic Code Book) method which will only be
  //   called using BlockSize byte[] array.
  internal abstract class SymmetricTransform : ICryptoTransform {
    protected SymmetricAlgorithm algo;
    protected bool encrypt;
    private int BlockSizeByte;
    private byte[] temp;
    private byte[] temp2;
    private byte[] workBuff;
    private byte[] workout;
    private byte[] openPGPStart; // needed for openpgp's CFB
    private int FeedBackByte;
    private int FeedBackIter;
    private bool m_disposed = false;
    
    public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV) {
      algo = symmAlgo;
      encrypt = encryption;
      BlockSizeByte = (algo.BlockSize >> 3);
      // mode buffers
      temp = new byte [BlockSizeByte];
      Array.Copy (rgbIV, 0, temp, 0, BlockSizeByte);
      temp2 = new byte [BlockSizeByte];
      FeedBackByte = (algo.FeedbackSize >> 3);
      FeedBackIter = (int) BlockSizeByte / FeedBackByte;
      //FeedBackIter = 1;
      // transform buffers
      workBuff = new byte [BlockSizeByte];
      workout =  new byte [BlockSizeByte];
      
      //OpenPGP CFB needs an all zero IV
      if (algo.Mode == CipherMode.OpenPGP_CFB) {
        algo.IV = new byte[algo.BlockSize >> 3];
      }
      
      // needed for openpgp's ciphertext feedback mode
      openPGPStart = new byte[(algo.BlockSize >> 3) + 2];
    }
    
    ~SymmetricTransform () {
      Dispose (false);
    }

    void IDisposable.Dispose () {
      Dispose (true);
      GC.SuppressFinalize (this);  // Finalization is now unnecessary
    }

    // MUST be overriden by classes using unmanaged ressources
    // the override method must call the base class
    protected void Dispose (bool disposing) {
      if (!m_disposed) {
        if (disposing) {
          // dispose managed object: zeroize and free
          Array.Clear (temp, 0, BlockSizeByte);
          temp = null;
          Array.Clear (temp2, 0, BlockSizeByte);
          temp2 = null;
        }
        m_disposed = true;
      }
    }

    public virtual bool CanTransformMultipleBlocks {
      get { return true; }
    }

    public bool CanReuseTransform {
      get { return false; }
    }

    public virtual int InputBlockSize {
      get { return BlockSizeByte; }
    }

    public virtual int OutputBlockSize {
      get { return BlockSizeByte; }
    }

    // note: Each block MUST be BlockSizeValue in size!!!
    // i.e. Any padding must be done before calling this method
    protected void Transform (byte[] input, byte[] output) {
      switch (algo.Mode) {
      case CipherMode.ECB:
        ECB(input, output);
        break;
      case CipherMode.CBC:
        CBC(input, output);
        break;
      case CipherMode.CFB:
        CFB(input, output);
        break;
      case CipherMode.OFB:
        OFB(input, output);
        break;
      case CipherMode.CTS:
        CTS(input, output);
        break;
      case CipherMode.OpenPGP_CFB:
        CFB(input, output);
        break;
      default:
        throw new NotImplementedException("Unkown CipherMode" + algo.Mode.ToString ());
      }
    }

    // Electronic Code Book (ECB)
    protected abstract void ECB (byte[] input, byte[] output); 

    // Cipher-Block-Chaining (CBC)
    protected virtual void CBC (byte[] input, byte[] output) {
      if (encrypt) {
        for (int i = 0; i < BlockSizeByte; i++)
          temp[i] ^= input[i];
        ECB (temp, output);
        Array.Copy (output, 0, temp, 0, BlockSizeByte);
      }
      else {
        Array.Copy (input, 0, temp2, 0, BlockSizeByte);
        ECB (input, output);
        for (int i = 0; i < BlockSizeByte; i++)
          output[i] ^= temp[i];
        Array.Copy (temp2, 0, temp, 0, BlockSizeByte);
      }
    }
    
    // Cipher-FeedBack (CFB)
    protected virtual void CFB (byte[] input, byte[] output) {
      if (encrypt) {
        for (int x = 0; x < FeedBackIter; x++) {
          // temp is first initialized with the IV
          ECB(temp, temp2);
          for (int i = 0; i < FeedBackByte; i++)
            output[i + x] = (byte)(temp2[i] ^ input[i + x]);
          Array.Copy(temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
          Array.Copy(output, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
        }
      } else {
        for (int x = 0; x < FeedBackIter; x++) {
          // we do not really decrypt this data!
          encrypt = true;
          // temp is first initialized with the IV
          ECB(temp, temp2);
          encrypt = false;
          
          for (int i = 0; i < FeedBackByte; i++)
            output[i + x] = (byte)(temp2[i] ^ input[i + x]);
          
          Array.Copy(temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
          Array.Copy(input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
        }
      }
    }
    
    // Output-FeedBack (OFB)
    protected virtual void OFB(byte[] input, byte[] output) {
      throw new NotImplementedException ("OFB not yet supported");
    }

    // Cipher Text Stealing (CTS)
    protected virtual void CTS(byte[] input, byte[] output) {
      throw new NotImplementedException ("CTS not yet supported");
    }

    // this method may get called MANY times so this is the one to optimize
    public virtual int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, ref byte[] outputBuffer, int outputOffset) {
      if (m_disposed)
        throw new ObjectDisposedException("Object is disposed");

      if (outputOffset + inputCount > outputBuffer.Length)
        throw new System.Security.Cryptography.CryptographicException("Insufficient output buffer size.");

      int offs = inputOffset;
      int full;

      // this way we don't do a modulo every time we're called
      // and we may save a division
      if (inputCount != BlockSizeByte) {
        
        // fill the last block with 0's. this doesn't matter for CFB
        if (((inputCount % BlockSizeByte) != 0) && ((algo.Mode == CipherMode.CFB) || (algo.Mode == CipherMode.OpenPGP_CFB))) {
          byte[] oldInputBuffer = new byte[inputCount];
          Array.Copy(inputBuffer, oldInputBuffer, inputCount);
          if (algo.Mode == CipherMode.CFB)
            inputBuffer = new byte[inputCount + (BlockSizeByte - (inputCount % BlockSizeByte))];
          else
            inputBuffer = new byte[inputCount + (BlockSizeByte - (inputCount % BlockSizeByte)) + 2];
          outputBuffer = new byte[inputBuffer.Length];
          Array.Copy(oldInputBuffer, 0, inputBuffer, 0, inputCount);
          inputCount = inputBuffer.Length;
        } else if ((inputCount % BlockSizeByte) != 0) {
          throw new System.Security.Cryptography.CryptographicException("Invalid input block size.");
        }
        
      } else
        full = 1;
      
      // OpenPGP needs some special treatment
      if (offs == 0 && algo.Mode == CipherMode.OpenPGP_CFB) {
        if (encrypt) {
          // at first we have an encrypted random block
          byte[] tmpInput = new byte[BlockSizeByte];
          byte[] tmp2Input = new byte[BlockSizeByte];
          outputBuffer = new byte[outputBuffer.Length + BlockSizeByte + 2];
          System.Security.Cryptography.RandomNumberGenerator rngIntro = System.Security.Cryptography.RandomNumberGenerator.Create();
          rngIntro.GetBytes(tmpInput);
          tmp2Input[0] = tmpInput[tmpInput.Length - 2];
          tmp2Input[1] = tmpInput[tmpInput.Length - 1];
          Transform(tmpInput, workout);
          Array.Copy(workout, 0, outputBuffer, 0, BlockSizeByte);
          outputOffset += BlockSizeByte;
          
          //now the last 2 bytes
          byte[] tmpWorkout = new byte[BlockSizeByte];
          //tmp2Input.Initialize();
          Transform(tmp2Input, tmpWorkout);
          Array.Copy(tmpWorkout, 0, outputBuffer, outputOffset, 2);
          outputOffset += 2;
          
          // Load feedback-register with c3 - c10
          Array.Copy(outputBuffer, 2, temp, 0, BlockSizeByte);
        } else {
          Array.Copy(inputBuffer, offs, workBuff, 0, BlockSizeByte);
          Transform(workBuff, workout);
          Array.Copy(workout, 0, openPGPStart, 0, BlockSizeByte);
          offs += BlockSizeByte;
          
          byte[] tmpInput = new byte[BlockSizeByte];
          tmpInput.Initialize();
          Array.Copy(inputBuffer, offs, tmpInput, 0, 2);
          Transform(tmpInput, workout);
          Array.Copy(workout, 0, openPGPStart, BlockSizeByte, 2);
          int x = openPGPStart.Length;
          if ((openPGPStart[x-1] != openPGPStart[x-3]) || (openPGPStart[x-2] != openPGPStart[x-4]))
            throw new System.Security.Cryptography.CryptographicException("Wrong Key!!");
          
          // Load feedback-register with c3 - c[BS+2]
          Array.Copy(inputBuffer, 2, temp, 0, BlockSizeByte);
          offs += 2;
          
        }
        
      }
      
      full = (inputCount - offs) / BlockSizeByte;
      
      int total = 0;
      for (int i = 0; i < full; i++) {
        Array.Copy(inputBuffer, offs, workBuff, 0, BlockSizeByte);
        Transform(workBuff, workout);
        Array.Copy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
        offs += BlockSizeByte;
        outputOffset += BlockSizeByte;
        total += BlockSizeByte;
      }
      return total;
    }
    
    private byte[] FinalEncrypt(byte [] inputBuffer, int inputOffset, int inputCount) {
      // are there still full block to process ?
      int full = (inputCount / BlockSizeByte) * BlockSizeByte;
      int rem = inputCount - full;
      int total = full;

      // we need to add an extra block if...
      // a. the last block isn't complate (partial);
      // b. the last block is complete but we use padding
      if ((rem > 0) || (algo.Padding != PaddingMode.None))
        total += BlockSizeByte;
      byte[] res = new byte [total];

      // process all blocks except the last (final) block
      while (total > BlockSizeByte) {
        TransformBlock(inputBuffer, inputOffset, BlockSizeByte, ref res, inputOffset);
        inputOffset += BlockSizeByte;
        total -= BlockSizeByte;
      }

      // now we only have a single last block to encrypt
      int padding = BlockSizeByte - rem;
      switch (algo.Padding) {
        case PaddingMode.None:
          break;
        case PaddingMode.PKCS7:
          for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);) 
            res [i] = (byte) padding;
          break;
        case PaddingMode.Zeros:
          for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
            res [i] = 0;
          break;
      }
      Array.Copy (inputBuffer, inputOffset, res, full, rem);

      // the last padded block will be transformed in-place
      TransformBlock(res, full, BlockSizeByte, ref res, full);
      return res;
    }

    private byte[] FinalDecrypt(byte [] inputBuffer, int inputOffset, int inputCount) {
      if ((inputCount % BlockSizeByte) > 0)
        throw new System.Security.Cryptography.CryptographicException ("Invalid input block size.");

      int total = inputCount;
      byte[] res = new byte [total];
      while (inputCount > 0) {
        TransformBlock (inputBuffer, inputOffset, BlockSizeByte, ref res, inputOffset);
        inputOffset += BlockSizeByte;
        inputCount -= BlockSizeByte;
      }

      switch (algo.Padding) {
        case PaddingMode.None:
          break;
        case PaddingMode.PKCS7:
          total -= res [total - 1];
          break;
        case PaddingMode.Zeros:
          // TODO
          break;
      }

      // return output without padding
      byte[] data = new byte [total];
      Array.Copy (res, 0, data, 0, total);
      // zeroize decrypted data (copy with padding)
      Array.Clear (res, 0, res.Length);
      return data;
    }

    public virtual byte [] TransformFinalBlock (byte [] inputBuffer, int inputOffset, int inputCount) {
      if (m_disposed)
        throw new ObjectDisposedException ("Object is disposed");

      if (encrypt)
        return FinalEncrypt (inputBuffer, inputOffset, inputCount);
      else
        return FinalDecrypt (inputBuffer, inputOffset, inputCount);
    }
  }

  /// <summary>
  /// Abstract base class for all cryptographic symmetric algorithms.
  /// Available algorithms include:
  /// DES, RC2, Rijndael, TripleDES
  /// </summary>
  public abstract class SymmetricAlgorithm : IDisposable {
    protected int BlockSizeValue; // The block size of the cryptographic operation in bits. 
    protected int FeedbackSizeValue; // The feedback size of the cryptographic operation in bits. 
    protected byte[] IVValue; // The initialization vector ( IV) for the symmetric algorithm. 
    protected int KeySizeValue; // The size of the secret key used by the symmetric algorithm in bits. 
    protected byte[] KeyValue; // The secret key for the symmetric algorithm. 
    protected KeySizes[] LegalBlockSizesValue; // Specifies the block sizes that are supported by the symmetric algorithm. 
    protected KeySizes[] LegalKeySizesValue; // Specifies the key sizes that are supported by the symmetric algorithm. 
    protected CipherMode ModeValue; // Represents the cipher mode used in the symmetric algorithm. 
    protected PaddingMode PaddingValue; // Represents the padding mode used in the symmetric algorithm. 
    private bool m_disposed;

    /// <summary>
    /// Called from constructor of derived class.
    /// </summary>
    public SymmetricAlgorithm () {
      ModeValue = CipherMode.CBC;
      PaddingValue = PaddingMode.PKCS7;
      m_disposed = false;
    }
    
    /// <summary>
    /// Called from constructor of derived class.
    /// </summary>
    ~SymmetricAlgorithm () {
      Dispose (false);
    }

    public void Clear() {
      Dispose (true);
    }

    void IDisposable.Dispose () {
      Dispose (true);
      GC.SuppressFinalize (this);  // Finalization is now unnecessary
    }

    protected virtual void Dispose (bool disposing) {
      if (!m_disposed) {
        // always zeroize keys
        if (KeyValue != null) {
          // Zeroize the secret key and free
          Array.Clear (KeyValue, 0, KeyValue.Length);
          KeyValue = null;
        }
        // dispose unmanaged managed objects
        if (disposing) {
          // dispose managed objects
        }
        m_disposed = true;
      }
    }

    /// <summary>
    /// Gets or sets the actual BlockSize
    /// </summary>
    public virtual int BlockSize {
      get { return this.BlockSizeValue; }
      set {
        if (IsLegalKeySize(this.LegalBlockSizesValue, value))
          this.BlockSizeValue = value;
        else
          throw new System.Security.Cryptography.CryptographicException("block size not supported by algorithm");
      }
    }

    /// <summary>
    /// Gets or sets the actual FeedbackSize
    /// </summary>
    public virtual int FeedbackSize {
      get { return this.FeedbackSizeValue; }
      set {
        if (value > this.BlockSizeValue)
          throw new System.Security.Cryptography.CryptographicException("feedback size larger than block size");
        else
          this.FeedbackSizeValue = value;
      }
    }
    
    /// <summary>
    /// Gets or sets the actual Initial Vector
    /// </summary>
    public virtual byte[] IV {
      get {
        if (this.IVValue == null)
          GenerateIV();

        return this.IVValue;
      }
      set {
        if (value == null)
          throw new ArgumentNullException ("tried setting initial vector to null");
          
        if (value.Length * 8 != this.BlockSizeValue)
          throw new System.Security.Cryptography.CryptographicException ("IV length must match block size");
        
        this.IVValue = new byte[value.Length];
        Array.Copy (value, 0, this.IVValue, 0, value.Length);
      }
    }

    /// <summary>
    /// Gets or sets the actual key
    /// </summary>
    public virtual byte[] Key {
      get {
        if (this.KeyValue == null)
          GenerateKey();

        return this.KeyValue;
      }
      set {
        if (value == null)
          throw new ArgumentNullException ("tried setting key to null");

        if (!IsLegalKeySize (this.LegalKeySizesValue, value.Length * 8))
          throw new System.Security.Cryptography.CryptographicException ("key size not supported by algorithm");

        this.KeySizeValue = value.Length * 8;
        this.KeyValue = new byte [value.Length];
        Array.Copy (value, 0, this.KeyValue, 0, value.Length);
      }
    }
    
    /// <summary>
    /// Gets or sets the actual key size in bits
    /// </summary>
    public virtual int KeySize {
      get { return this.KeySizeValue; }
      set {
        if (!IsLegalKeySize (this.LegalKeySizesValue, value))
          throw new System.Security.Cryptography.CryptographicException ("key size not supported by algorithm");
        
        this.KeyValue = null;
        this.KeySizeValue = value;
      }
    }

    /// <summary>
    /// Gets all legal block sizes
    /// </summary>
    public virtual KeySizes[] LegalBlockSizes {
      get { return this.LegalBlockSizesValue; }
    }

    /// <summary>
    /// Gets all legal key sizes
    /// </summary>
    public virtual KeySizes[] LegalKeySizes {
      get { return this.LegalKeySizesValue; }
    }

    /// <summary>
    /// Gets or sets the actual cipher mode
    /// </summary>
    public virtual CipherMode Mode {
      get { 
        return this.ModeValue;
      }
      set {
        if (Enum.IsDefined( ModeValue.GetType (), value))
          this.ModeValue = value;
        else
          throw new System.Security.Cryptography.CryptographicException ("padding mode not available");
        
        if (value == CipherMode.OpenPGP_CFB) {
          IV = new byte[BlockSize >> 3];
          this.IVValue.Initialize();
        }
      }
    }

    /// <summary>
    /// Gets or sets the actual padding
    /// </summary>
    public virtual PaddingMode Padding {
      get { return this.PaddingValue; }
      set {
        if (Enum.IsDefined (PaddingValue.GetType (), value))
          this.PaddingValue = value;
        else
          throw new System.Security.Cryptography.CryptographicException ("padding mode not available");
      }
    }

    /// <summary>
    /// Gets an Decryptor transform object to work with a CryptoStream
    /// </summary>
    public virtual ICryptoTransform CreateDecryptor () {
      return CreateDecryptor (Key, IV);
    }

    /// <summary>
    /// Gets an Decryptor transform object to work with a CryptoStream
    /// </summary>
    public abstract ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgbIV);

    /// <summary>
    /// Gets an Encryptor transform object to work with a CryptoStream
    /// </summary>
    public virtual ICryptoTransform CreateEncryptor() {
      return CreateEncryptor (Key, IV);
    }

    /// <summary>
    /// Gets an Encryptor transform object to work with a CryptoStream
    /// </summary>
    public abstract ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgbIV);

    /// <summary>
    /// used to generate an inital vector if none is specified
    /// </summary>
    public abstract void GenerateIV ();

    /// <summary>
    /// used to generate a random key if none is specified
    /// </summary>
    public abstract void GenerateKey ();

    internal bool IsLegalKeySize (KeySizes[] LegalKeys, int Size) {
      foreach (KeySizes LegalKeySize in LegalKeys) {
        for (int i=LegalKeySize.MinSize; i<=LegalKeySize.MaxSize; i+=LegalKeySize.SkipSize) {
          if (i == Size)
            return true;
        }
      }
      return false;
    }
    
    /// <summary>
    /// Checks wether the given keyLength is valid for the current algorithm
    /// </summary>
    /// <param name="bitLength">the given keyLength</param>
    public bool ValidKeySize (int bitLength) {
      return IsLegalKeySize (LegalKeySizesValue, bitLength);
    }
    
    /// <summary>
    /// Creates the default implementation of the default symmetric algorithm (Rijndael).
    /// </summary>
    // LAMESPEC: Default is Rijndael - not TripleDES
    public static SymmetricAlgorithm Create () {
      return (SymmetricAlgorithm)Activator.CreateInstance(Type.GetType("SharpPrivacy.Cipher.SymmetricAlgorithm"), null);
    }
    
  }
}

www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.