| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- //
- // System.Security.Cryptography SymmetricAlgorithm Class implementation
- //
- // Authors:
- // Thomas Neidhart ([email protected])
- // Sebastien Pouliot ([email protected])
- //
- // 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: 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 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;
- // transform buffers
- workBuff = new byte [BlockSizeByte];
- workout = new byte [BlockSizeByte];
- }
- ~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;
- 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-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, byte [] outputBuffer, int outputOffset)
- {
- if (m_disposed)
- throw new ObjectDisposedException ("Object is disposed");
- if (outputOffset + inputCount > outputBuffer.Length)
- throw new 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) {
- if ((inputCount % BlockSizeByte) != 0)
- throw new CryptographicException ("Invalid input block size.");
- full = inputCount / BlockSizeByte;
- }
- else
- full = 1;
- 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, 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, res, full);
- return res;
- }
- private byte[] FinalDecrypt (byte [] inputBuffer, int inputOffset, int inputCount)
- {
- if ((inputCount % BlockSizeByte) > 0)
- throw new CryptographicException ("Invalid input block size.");
- int total = inputCount;
- byte[] res = new byte [total];
- while (inputCount > 0) {
- TransformBlock (inputBuffer, inputOffset, BlockSizeByte, 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 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 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 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 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 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 CryptographicException ("padding mode not available");
- }
- }
- /// <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 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 Create ("System.Security.Cryptography.SymmetricAlgorithm");
- }
- /// <summary>
- /// Creates a specific implementation of the given symmetric algorithm.
- /// </summary>
- /// <param name="algName">Specifies which derived class to create</param>
- public static SymmetricAlgorithm Create (string algName)
- {
- return (SymmetricAlgorithm) CryptoConfig.CreateFromName (algName);
- }
- }
- }
|