2 // System.Security.Cryptography SymmetricAlgorithm Class implementation
5 // Thomas Neidhart (tome@sbox.tugraz.at)
6 // Sebastien Pouliot (spouliot@motus.com)
8 // Portions (C) 2002 Motus Technologies Inc. (http://www.motus.com)
13 namespace System.Security.Cryptography {
15 // This class implement most of the common code required for symmetric
16 // algorithm transforms, like:
17 // - CipherMode: Builds CBC and CFB on top of (descendant supplied) ECB
18 // - PaddingMode, transform properties, multiple blocks, reuse...
21 // - intialize themselves (like key expansion, ...)
22 // - override the ECB (Electronic Code Book) method which will only be
23 // called using BlockSize byte[] array.
24 internal abstract class SymmetricTransform : ICryptoTransform {
25 protected SymmetricAlgorithm algo;
26 protected bool encrypt;
27 private int BlockSizeByte;
30 private int FeedBackByte;
31 private int FeedBackIter;
32 private bool m_disposed = false;
34 public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV)
38 BlockSizeByte = (algo.BlockSize >> 3);
39 temp = new byte [BlockSizeByte];
40 Array.Copy (rgbIV, 0, temp, 0, BlockSizeByte);
41 temp2 = new byte [BlockSizeByte];
42 FeedBackByte = (algo.FeedbackSize >> 3);
43 FeedBackIter = (int) BlockSizeByte / FeedBackByte;
46 ~SymmetricTransform ()
51 void IDisposable.Dispose ()
54 GC.SuppressFinalize (this); // Finalization is now unnecessary
57 // MUST be overriden by classes using unmanaged ressources
58 // the override method must call the base class
59 protected void Dispose (bool disposing)
63 // dispose managed object: zeroize and free
64 Array.Clear (temp, 0, BlockSizeByte);
66 Array.Clear (temp2, 0, BlockSizeByte);
73 public virtual bool CanTransformMultipleBlocks {
77 public bool CanReuseTransform {
81 public virtual int InputBlockSize {
82 get { return BlockSizeByte; }
85 public virtual int OutputBlockSize {
86 get { return BlockSizeByte; }
89 // note: Each block MUST be BlockSizeValue in size!!!
90 // i.e. Any padding must be done before calling this method
91 protected void Transform (byte[] input, byte[] output)
110 throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
114 // Electronic Code Book (ECB)
115 protected abstract void ECB (byte[] input, byte[] output);
117 // Cipher-Block-Chaining (CBC)
118 protected virtual void CBC (byte[] input, byte[] output)
121 for (int i = 0; i < BlockSizeByte; i++)
124 Array.Copy (output, 0, temp, 0, BlockSizeByte);
127 Array.Copy (input, 0, temp2, 0, BlockSizeByte);
129 for (int i = 0; i < BlockSizeByte; i++)
130 output[i] ^= temp[i];
131 Array.Copy (temp2, 0, temp, 0, BlockSizeByte);
135 // Cipher-FeedBack (CFB)
136 protected virtual void CFB (byte[] input, byte[] output)
139 for (int x = 0; x < FeedBackIter; x++) {
140 // temp is first initialized with the IV
143 for (int i = 0; i < FeedBackByte; i++)
144 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
145 Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
146 Array.Copy (output, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
150 for (int x = 0; x < FeedBackIter; x++) {
151 // we do not really decrypt this data!
153 // temp is first initialized with the IV
157 Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
158 Array.Copy (input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
159 for (int i = 0; i < FeedBackByte; i++)
160 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
165 // Output-FeedBack (OFB)
166 protected virtual void OFB (byte[] input, byte[] output)
168 throw new NotImplementedException ("OFB not yet supported");
171 // Cipher Text Stealing (CTS)
172 protected virtual void CTS (byte[] input, byte[] output)
174 throw new NotImplementedException ("CTS not yet supported");
177 public virtual int TransformBlock (byte [] inputBuffer, int inputOffset, int inputCount, byte [] outputBuffer, int outputOffset)
180 throw new ObjectDisposedException ("Object is disposed");
182 // if ((inputCount & (BlockSizeByte-1)) != 0) didn't work for Rijndael block = 192
183 if ((inputCount % BlockSizeByte) != 0)
184 throw new CryptographicException ("Invalid input block size.");
186 if (outputOffset + inputCount > outputBuffer.Length)
187 throw new CryptographicException ("Insufficient output buffer size.");
189 int step = InputBlockSize;
190 int offs = inputOffset;
191 int full = inputCount / step;
193 byte [] workBuff = new byte [step];
194 byte[] workout = new byte [step];
196 for (int i = 0; i < full; i++) {
197 Array.Copy (inputBuffer, offs, workBuff, 0, step);
198 Transform (workBuff, workout);
199 Array.Copy (workout, 0, outputBuffer, outputOffset, step);
201 outputOffset += step;
204 return (full * step);
207 public virtual byte [] TransformFinalBlock (byte [] inputBuffer, int inputOffset, int inputCount)
210 throw new ObjectDisposedException ("Object is disposed");
212 int num = (inputCount + BlockSizeByte) & (~(BlockSizeByte-1));
213 byte [] res = new byte [num];
214 //int full = (num - BlockSizeByte); // didn't work for bs 192
215 int full = (inputCount / BlockSizeByte) * BlockSizeByte;
217 // are there still full block to process ?
219 TransformBlock (inputBuffer, inputOffset, full, res, 0);
221 //int rem = inputCount & (BlockSizeByte-1);
222 int rem = inputCount - full;
224 // is there a partial block to pad ?
227 int padding = BlockSizeByte - (inputCount % BlockSizeByte);
228 switch (algo.Padding) {
229 case PaddingMode.None:
231 case PaddingMode.PKCS7:
232 for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
233 res [i] = (byte) padding;
235 case PaddingMode.Zeros:
236 for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
241 Array.Copy (inputBuffer, inputOffset + full, res, full, rem);
243 // the last padded block will be transformed in-place
244 TransformBlock (res, full, BlockSizeByte, res, full);
247 switch (algo.Padding) {
248 case PaddingMode.None:
250 case PaddingMode.PKCS7:
251 byte padding = res [inputCount - 1];
252 for (int i = 0; i < padding; i++) {
253 if (res [inputCount - 1 - i] == padding)
254 res[inputCount - 1 - i] = 0x00;
257 case PaddingMode.Zeros:
268 /// Abstract base class for all cryptographic symmetric algorithms.
269 /// Available algorithms include:
270 /// DES, RC2, Rijndael, TripleDES
272 public abstract class SymmetricAlgorithm : IDisposable {
273 protected int BlockSizeValue; // The block size of the cryptographic operation in bits.
274 protected int FeedbackSizeValue; // The feedback size of the cryptographic operation in bits.
275 protected byte[] IVValue; // The initialization vector ( IV) for the symmetric algorithm.
276 protected int KeySizeValue; // The size of the secret key used by the symmetric algorithm in bits.
277 protected byte[] KeyValue; // The secret key for the symmetric algorithm.
278 protected KeySizes[] LegalBlockSizesValue; // Specifies the block sizes that are supported by the symmetric algorithm.
279 protected KeySizes[] LegalKeySizesValue; // Specifies the key sizes that are supported by the symmetric algorithm.
280 protected CipherMode ModeValue; // Represents the cipher mode used in the symmetric algorithm.
281 protected PaddingMode PaddingValue; // Represents the padding mode used in the symmetric algorithm.
282 private bool m_disposed;
285 /// Called from constructor of derived class.
287 public SymmetricAlgorithm ()
289 ModeValue = CipherMode.CBC;
290 PaddingValue = PaddingMode.PKCS7;
295 /// Called from constructor of derived class.
297 ~SymmetricAlgorithm ()
307 void IDisposable.Dispose ()
310 GC.SuppressFinalize (this); // Finalization is now unnecessary
313 protected virtual void Dispose (bool disposing)
316 // always zeroize keys
317 if (KeyValue != null) {
318 // Zeroize the secret key and free
319 Array.Clear (KeyValue, 0, KeyValue.Length);
322 // dispose unmanaged managed objects
324 // dispose managed objects
331 /// Gets or sets the actual BlockSize
333 public virtual int BlockSize {
334 get { return this.BlockSizeValue; }
336 if (IsLegalKeySize(this.LegalBlockSizesValue, value))
337 this.BlockSizeValue = value;
339 throw new CryptographicException("block size not supported by algorithm");
344 /// Gets or sets the actual FeedbackSize
346 public virtual int FeedbackSize {
347 get { return this.FeedbackSizeValue; }
349 if (value > this.BlockSizeValue)
350 throw new CryptographicException("feedback size larger than block size");
352 this.FeedbackSizeValue = value;
357 /// Gets or sets the actual Initial Vector
360 public virtual byte[] IV {
362 if (this.IVValue == null)
369 throw new ArgumentNullException ("tried setting initial vector to null");
371 if (value.Length * 8 != this.BlockSizeValue)
372 throw new CryptographicException ("IV length must match block size");
374 this.IVValue = new byte [value.Length];
375 System.Array.Copy (value, 0, this.IVValue, 0, value.Length);
380 /// Gets or sets the actual key
382 public virtual byte[] Key {
384 if (this.KeyValue == null)
387 return this.KeyValue;
391 throw new ArgumentNullException ("tried setting key to null");
393 if (!IsLegalKeySize (this.LegalKeySizesValue, value.Length * 8))
394 throw new CryptographicException ("key size not supported by algorithm");
396 this.KeySizeValue = value.Length * 8;
397 this.KeyValue = new byte [value.Length];
398 System.Array.Copy (value, 0, this.KeyValue, 0, value.Length);
403 /// Gets or sets the actual key size in bits
405 public virtual int KeySize {
406 get { return this.KeySizeValue; }
408 if (!IsLegalKeySize (this.LegalKeySizesValue, value))
409 throw new CryptographicException ("key size not supported by algorithm");
411 this.KeyValue = null;
412 this.KeySizeValue = value;
417 /// Gets all legal block sizes
419 public virtual KeySizes[] LegalBlockSizes {
420 get { return this.LegalBlockSizesValue; }
424 /// Gets all legal key sizes
426 public virtual KeySizes[] LegalKeySizes {
427 get { return this.LegalKeySizesValue; }
431 /// Gets or sets the actual cipher mode
433 public virtual CipherMode Mode {
434 get { return this.ModeValue; }
436 if (Enum.IsDefined( ModeValue.GetType (), value))
437 this.ModeValue = value;
439 throw new CryptographicException ("padding mode not available");
444 /// Gets or sets the actual padding
446 public virtual PaddingMode Padding {
447 get { return this.PaddingValue; }
449 if (Enum.IsDefined (PaddingValue.GetType (), value))
450 this.PaddingValue = value;
452 throw new CryptographicException ("padding mode not available");
457 /// Gets an Decryptor transform object to work with a CryptoStream
459 public virtual ICryptoTransform CreateDecryptor ()
461 return CreateDecryptor (Key, IV);
465 /// Gets an Decryptor transform object to work with a CryptoStream
467 public abstract ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgbIV);
470 /// Gets an Encryptor transform object to work with a CryptoStream
472 public virtual ICryptoTransform CreateEncryptor()
474 return CreateEncryptor (Key, IV);
478 /// Gets an Encryptor transform object to work with a CryptoStream
480 public abstract ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgbIV);
483 /// used to generate an inital vector if none is specified
485 public abstract void GenerateIV ();
488 /// used to generate a random key if none is specified
490 public abstract void GenerateKey ();
492 internal bool IsLegalKeySize (KeySizes[] LegalKeys, int Size)
494 foreach (KeySizes LegalKeySize in LegalKeys) {
495 for (int i=LegalKeySize.MinSize; i<=LegalKeySize.MaxSize; i+=LegalKeySize.SkipSize) {
504 /// Checks wether the given keyLength is valid for the current algorithm
506 /// <param name="bitLength">the given keyLength</param>
507 public bool ValidKeySize (int bitLength)
509 return IsLegalKeySize (LegalKeySizesValue, bitLength);
513 /// Creates the default implementation of the default symmetric algorithm (Rijndael).
515 // LAMESPEC: Default is Rijndael - not TripleDES
516 public static SymmetricAlgorithm Create ()
518 return Create ("System.Security.Cryptography.SymmetricAlgorithm");
522 /// Creates a specific implementation of the given symmetric algorithm.
524 /// <param name="algName">Specifies which derived class to create</param>
525 public static SymmetricAlgorithm Create (string algName)
527 return (SymmetricAlgorithm) CryptoConfig.CreateFromName (algName);