// // 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) // using System; namespace System.Security.Cryptography { // This class implement most of the common code required for symmetric // algorithm transforms, like: // - CipherMode: Build CBC... 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 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); temp = new byte [BlockSizeByte]; Array.Copy (rgbIV, 0, temp, 0, BlockSizeByte); temp2 = new byte [BlockSizeByte]; FeedBackByte = (algo.FeedbackSize >> 3); FeedBackIter = (int) BlockSizeByte / FeedBackByte; } ~SymmetricTransform () { Dispose (false); } public void 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 false; } } 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; 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; Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte); Array.Copy (input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte); for (int i = 0; i < FeedBackByte; i++) output[i + x] = (byte)(temp2[i] ^ input[i + x]); // output[i + x] = (byte)(temp2[BlockSizeByte - x - i - 1] ^ input[i + x]); } } } // 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"); } public virtual int TransformBlock (byte [] inputBuffer, int inputOffset, int inputCount, byte [] outputBuffer, int outputOffset) { if (m_disposed) throw new ObjectDisposedException ("Object is disposed"); // if ((inputCount & (BlockSizeByte-1)) != 0) didn't work for Rijndael block = 192 if ((inputCount % BlockSizeByte) != 0) throw new CryptographicException ("Invalid input block size."); if (outputOffset + inputCount > outputBuffer.Length) throw new CryptographicException ("Insufficient output buffer size."); int step = InputBlockSize; int offs = inputOffset; int full = inputCount / step; byte [] workBuff = new byte [step]; byte[] workout = new byte [step]; for (int i = 0; i < full; i++) { Array.Copy (inputBuffer, offs, workBuff, 0, step); Transform (workBuff, workout); Array.Copy (workout, 0, outputBuffer, outputOffset, step); offs += step; outputOffset += step; } return (full * step); } public virtual byte [] TransformFinalBlock (byte [] inputBuffer, int inputOffset, int inputCount) { if (m_disposed) throw new ObjectDisposedException ("Object is disposed"); int num = (inputCount + BlockSizeByte) & (~(BlockSizeByte-1)); byte [] res = new byte [num]; //int full = (num - BlockSizeByte); // didn't work for bs 192 int full = (inputCount / BlockSizeByte) * BlockSizeByte; // are there still full block to process ? if (full > 0) TransformBlock (inputBuffer, inputOffset, full, res, 0); //int rem = inputCount & (BlockSizeByte-1); int rem = inputCount - full; // is there a partial block to pad ? if (rem > 0) { if (encrypt) { int padding = BlockSizeByte - (inputCount % BlockSizeByte); 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 + full, res, full, rem); // the last padded block will be transformed in-place TransformBlock (res, full, BlockSizeByte, res, full); } else { switch (algo.Padding) { case PaddingMode.None: break; case PaddingMode.PKCS7: byte padding = res [inputCount - 1]; for (int i = 0; i < padding; i++) { if (res [inputCount - 1 - i] == padding) res[inputCount - 1 - i] = 0x00; } break; case PaddingMode.Zeros: break; } } } return res; } } /// /// Abstract base class for all cryptographic symmetric algorithms. /// Available algorithms include: /// DES, RC2, Rijndael, TripleDES /// public abstract class SymmetricAlgorithm { 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. /// /// Called from constructor of derived class. /// public SymmetricAlgorithm () { ModeValue = CipherMode.CBC; PaddingValue = PaddingMode.PKCS7; } /// /// Called from constructor of derived class. /// ~SymmetricAlgorithm () { if (KeyValue != null) { // Zeroize the secret key and free Array.Clear (KeyValue, 0, KeyValue.Length); KeyValue = null; } } /// /// Gets or sets the actual BlockSize /// public virtual int BlockSize { get { return this.BlockSizeValue; } set { if (IsLegalKeySize(this.LegalBlockSizesValue, value)) this.BlockSizeValue = value; else throw new CryptographicException("block size not supported by algorithm"); } } /// /// Gets or sets the actual FeedbackSize /// public virtual int FeedbackSize { get { return this.FeedbackSizeValue; } set { if (value > this.BlockSizeValue) throw new CryptographicException("feedback size larger than block size"); else this.FeedbackSizeValue = value; } } /// /// Gets or sets the actual Initial Vector /// [MonoTODO] 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 CryptographicException ("IV length must match block size"); this.IVValue = new byte [value.Length]; System.Array.Copy (value, 0, this.IVValue, 0, value.Length); } } /// /// Gets or sets the actual key /// 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 CryptographicException ("key size not supported by algorithm"); this.KeySizeValue = value.Length * 8; this.KeyValue = new byte [value.Length]; System.Array.Copy (value, 0, this.KeyValue, 0, value.Length); } } /// /// Gets or sets the actual key size in bits /// public virtual int KeySize { get { return this.KeySizeValue; } set { if (!IsLegalKeySize (this.LegalKeySizesValue, value)) throw new CryptographicException ("key size not supported by algorithm"); this.KeyValue = null; this.KeySizeValue = value; } } /// /// Gets all legal block sizes /// public virtual KeySizes[] LegalBlockSizes { get { return this.LegalBlockSizesValue; } } /// /// Gets all legal key sizes /// public virtual KeySizes[] LegalKeySizes { get { return this.LegalKeySizesValue; } } /// /// Gets or sets the actual cipher mode /// public virtual CipherMode Mode { get { return this.ModeValue; } set { if (Enum.IsDefined( ModeValue.GetType (), value)) this.ModeValue = value; else throw new CryptographicException ("padding mode not available"); } } /// /// Gets or sets the actual padding /// public virtual PaddingMode Padding { get { return this.PaddingValue; } set { if (Enum.IsDefined (PaddingValue.GetType (), value)) this.PaddingValue = value; else throw new CryptographicException ("padding mode not available"); } } /// /// Gets an Decryptor transform object to work with a CryptoStream /// public virtual ICryptoTransform CreateDecryptor () { return CreateDecryptor (Key, IV); } /// /// Gets an Decryptor transform object to work with a CryptoStream /// public abstract ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgbIV); /// /// Gets an Encryptor transform object to work with a CryptoStream /// public virtual ICryptoTransform CreateEncryptor() { return CreateEncryptor (Key, IV); } /// /// Gets an Encryptor transform object to work with a CryptoStream /// public abstract ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgbIV); /// /// used to generate an inital vector if none is specified /// public abstract void GenerateIV (); /// /// used to generate a random key if none is specified /// 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; } /// /// Checks wether the given keyLength is valid for the current algorithm /// /// the given keyLength public bool ValidKeySize (int bitLength) { return IsLegalKeySize (LegalKeySizesValue, bitLength); } /// /// Creates the default implementation of the default symmetric algorithm (Rijndael). /// // LAMESPEC: Default is Rijndael - not TripleDES public static SymmetricAlgorithm Create () { return Create ("System.Security.Cryptography.SymmetricAlgorithm"); } /// /// Creates a specific implementation of the given symmetric algorithm. /// /// Specifies which derived class to create public static SymmetricAlgorithm Create (string algName) { return (SymmetricAlgorithm) CryptoConfig.CreateFromName (algName); } } }