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 byte[] workBuff;
31 private byte[] workout;
32 private int FeedBackByte;
33 private int FeedBackIter;
34 private bool m_disposed = false;
36 public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV)
40 BlockSizeByte = (algo.BlockSize >> 3);
42 temp = new byte [BlockSizeByte];
43 Array.Copy (rgbIV, 0, temp, 0, BlockSizeByte);
44 temp2 = new byte [BlockSizeByte];
45 FeedBackByte = (algo.FeedbackSize >> 3);
46 FeedBackIter = (int) BlockSizeByte / FeedBackByte;
48 workBuff = new byte [BlockSizeByte];
49 workout = new byte [BlockSizeByte];
52 ~SymmetricTransform ()
57 void IDisposable.Dispose ()
60 GC.SuppressFinalize (this); // Finalization is now unnecessary
63 // MUST be overriden by classes using unmanaged ressources
64 // the override method must call the base class
65 protected void Dispose (bool disposing)
69 // dispose managed object: zeroize and free
70 Array.Clear (temp, 0, BlockSizeByte);
72 Array.Clear (temp2, 0, BlockSizeByte);
79 public virtual bool CanTransformMultipleBlocks {
83 public bool CanReuseTransform {
87 public virtual int InputBlockSize {
88 get { return BlockSizeByte; }
91 public virtual int OutputBlockSize {
92 get { return BlockSizeByte; }
95 // note: Each block MUST be BlockSizeValue in size!!!
96 // i.e. Any padding must be done before calling this method
97 protected void Transform (byte[] input, byte[] output)
116 throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
120 // Electronic Code Book (ECB)
121 protected abstract void ECB (byte[] input, byte[] output);
123 // Cipher-Block-Chaining (CBC)
124 protected virtual void CBC (byte[] input, byte[] output)
127 for (int i = 0; i < BlockSizeByte; i++)
130 Array.Copy (output, 0, temp, 0, BlockSizeByte);
133 Array.Copy (input, 0, temp2, 0, BlockSizeByte);
135 for (int i = 0; i < BlockSizeByte; i++)
136 output[i] ^= temp[i];
137 Array.Copy (temp2, 0, temp, 0, BlockSizeByte);
141 // Cipher-FeedBack (CFB)
142 protected virtual void CFB (byte[] input, byte[] output)
145 for (int x = 0; x < FeedBackIter; x++) {
146 // temp is first initialized with the IV
149 for (int i = 0; i < FeedBackByte; i++)
150 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
151 Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
152 Array.Copy (output, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
156 for (int x = 0; x < FeedBackIter; x++) {
157 // we do not really decrypt this data!
159 // temp is first initialized with the IV
163 Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
164 Array.Copy (input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
165 for (int i = 0; i < FeedBackByte; i++)
166 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
171 // Output-FeedBack (OFB)
172 protected virtual void OFB (byte[] input, byte[] output)
174 throw new NotImplementedException ("OFB not yet supported");
177 // Cipher Text Stealing (CTS)
178 protected virtual void CTS (byte[] input, byte[] output)
180 throw new NotImplementedException ("CTS not yet supported");
183 // this method may get called MANY times so this is the one to optimize
184 public virtual int TransformBlock (byte [] inputBuffer, int inputOffset, int inputCount, byte [] outputBuffer, int outputOffset)
187 throw new ObjectDisposedException ("Object is disposed");
189 if (outputOffset + inputCount > outputBuffer.Length)
190 throw new CryptographicException ("Insufficient output buffer size.");
192 int offs = inputOffset;
195 // this way we don't do a modulo every time we're called
196 // and we may save a division
197 if (inputCount != BlockSizeByte) {
198 if ((inputCount % BlockSizeByte) != 0)
199 throw new CryptographicException ("Invalid input block size.");
201 full = inputCount / BlockSizeByte;
207 for (int i = 0; i < full; i++) {
208 Array.Copy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
209 Transform (workBuff, workout);
210 Array.Copy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
211 offs += BlockSizeByte;
212 outputOffset += BlockSizeByte;
213 total += BlockSizeByte;
219 private byte[] FinalEncrypt (byte [] inputBuffer, int inputOffset, int inputCount)
221 if (inputCount == 0) return new byte[0];
223 // are there still full block to process ?
224 int full = (inputCount / BlockSizeByte) * BlockSizeByte;
225 int rem = inputCount - full;
228 // we need to add an extra block if...
229 // a. the last block isn't complate (partial);
230 // b. the last block is complete but we use padding
231 if ((rem > 0) || (algo.Padding != PaddingMode.None))
232 total += BlockSizeByte;
233 byte[] res = new byte [total];
235 // process all blocks except the last (final) block
236 while (total > BlockSizeByte) {
237 TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
238 inputOffset += BlockSizeByte;
239 total -= BlockSizeByte;
242 // now we only have a single last block to encrypt
243 int padding = BlockSizeByte - rem;
244 switch (algo.Padding) {
245 case PaddingMode.None:
247 case PaddingMode.PKCS7:
248 for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
249 res [i] = (byte) padding;
251 case PaddingMode.Zeros:
252 for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
256 Array.Copy (inputBuffer, inputOffset, res, full, rem);
258 // the last padded block will be transformed in-place
259 TransformBlock (res, full, BlockSizeByte, res, full);
263 private byte[] FinalDecrypt (byte [] inputBuffer, int inputOffset, int inputCount)
265 if ((inputCount % BlockSizeByte) > 0)
266 throw new CryptographicException ("Invalid input block size.");
268 int total = inputCount;
269 byte[] res = new byte [total];
270 while (inputCount > 0) {
271 TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
272 inputOffset += BlockSizeByte;
273 inputCount -= BlockSizeByte;
276 switch (algo.Padding) {
277 case PaddingMode.None:
279 case PaddingMode.PKCS7:
280 total -= res [total - 1];
282 case PaddingMode.Zeros:
287 // return output without padding
288 byte[] data = new byte [total];
289 Array.Copy (res, 0, data, 0, total);
290 // zeroize decrypted data (copy with padding)
291 Array.Clear (res, 0, res.Length);
295 public virtual byte [] TransformFinalBlock (byte [] inputBuffer, int inputOffset, int inputCount)
298 throw new ObjectDisposedException ("Object is disposed");
301 return FinalEncrypt (inputBuffer, inputOffset, inputCount);
303 return FinalDecrypt (inputBuffer, inputOffset, inputCount);
308 /// Abstract base class for all cryptographic symmetric algorithms.
309 /// Available algorithms include:
310 /// DES, RC2, Rijndael, TripleDES
312 public abstract class SymmetricAlgorithm : IDisposable {
313 protected int BlockSizeValue; // The block size of the cryptographic operation in bits.
314 protected int FeedbackSizeValue; // The feedback size of the cryptographic operation in bits.
315 protected byte[] IVValue; // The initialization vector ( IV) for the symmetric algorithm.
316 protected int KeySizeValue; // The size of the secret key used by the symmetric algorithm in bits.
317 protected byte[] KeyValue; // The secret key for the symmetric algorithm.
318 protected KeySizes[] LegalBlockSizesValue; // Specifies the block sizes that are supported by the symmetric algorithm.
319 protected KeySizes[] LegalKeySizesValue; // Specifies the key sizes that are supported by the symmetric algorithm.
320 protected CipherMode ModeValue; // Represents the cipher mode used in the symmetric algorithm.
321 protected PaddingMode PaddingValue; // Represents the padding mode used in the symmetric algorithm.
322 private bool m_disposed;
325 /// Called from constructor of derived class.
327 public SymmetricAlgorithm ()
329 ModeValue = CipherMode.CBC;
330 PaddingValue = PaddingMode.PKCS7;
335 /// Called from constructor of derived class.
337 ~SymmetricAlgorithm ()
347 void IDisposable.Dispose ()
350 GC.SuppressFinalize (this); // Finalization is now unnecessary
353 protected virtual void Dispose (bool disposing)
356 // always zeroize keys
357 if (KeyValue != null) {
358 // Zeroize the secret key and free
359 Array.Clear (KeyValue, 0, KeyValue.Length);
362 // dispose unmanaged managed objects
364 // dispose managed objects
371 /// Gets or sets the actual BlockSize
373 public virtual int BlockSize {
374 get { return this.BlockSizeValue; }
376 if (KeySizes.IsLegalKeySize (this.LegalBlockSizesValue, value))
377 this.BlockSizeValue = value;
379 throw new CryptographicException("block size not supported by algorithm");
384 /// Gets or sets the actual FeedbackSize
386 public virtual int FeedbackSize {
387 get { return this.FeedbackSizeValue; }
389 if (value > this.BlockSizeValue)
390 throw new CryptographicException("feedback size larger than block size");
392 this.FeedbackSizeValue = value;
397 /// Gets or sets the actual Initial Vector
399 public virtual byte[] IV {
401 if (this.IVValue == null)
408 throw new ArgumentNullException ("tried setting initial vector to null");
410 if (value.Length * 8 != this.BlockSizeValue)
411 throw new CryptographicException ("IV length must match block size");
413 this.IVValue = new byte [value.Length];
414 Array.Copy (value, 0, this.IVValue, 0, value.Length);
419 /// Gets or sets the actual key
421 public virtual byte[] Key {
423 if (this.KeyValue == null)
426 return this.KeyValue;
430 throw new ArgumentNullException ("tried setting key to null");
432 if (!KeySizes.IsLegalKeySize (this.LegalKeySizesValue, value.Length * 8))
433 throw new CryptographicException ("key size not supported by algorithm");
435 this.KeySizeValue = value.Length * 8;
436 this.KeyValue = new byte [value.Length];
437 Array.Copy (value, 0, this.KeyValue, 0, value.Length);
442 /// Gets or sets the actual key size in bits
444 public virtual int KeySize {
445 get { return this.KeySizeValue; }
447 if (!KeySizes.IsLegalKeySize (this.LegalKeySizesValue, value))
448 throw new CryptographicException ("key size not supported by algorithm");
450 this.KeyValue = null;
451 this.KeySizeValue = value;
456 /// Gets all legal block sizes
458 public virtual KeySizes[] LegalBlockSizes {
459 get { return this.LegalBlockSizesValue; }
463 /// Gets all legal key sizes
465 public virtual KeySizes[] LegalKeySizes {
466 get { return this.LegalKeySizesValue; }
470 /// Gets or sets the actual cipher mode
472 public virtual CipherMode Mode {
473 get { return this.ModeValue; }
475 if (Enum.IsDefined( ModeValue.GetType (), value))
476 this.ModeValue = value;
478 throw new CryptographicException ("padding mode not available");
483 /// Gets or sets the actual padding
485 public virtual PaddingMode Padding {
486 get { return this.PaddingValue; }
488 if (Enum.IsDefined (PaddingValue.GetType (), value))
489 this.PaddingValue = value;
491 throw new CryptographicException ("padding mode not available");
496 /// Gets an Decryptor transform object to work with a CryptoStream
498 public virtual ICryptoTransform CreateDecryptor ()
500 return CreateDecryptor (Key, IV);
504 /// Gets an Decryptor transform object to work with a CryptoStream
506 public abstract ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgbIV);
509 /// Gets an Encryptor transform object to work with a CryptoStream
511 public virtual ICryptoTransform CreateEncryptor()
513 return CreateEncryptor (Key, IV);
517 /// Gets an Encryptor transform object to work with a CryptoStream
519 public abstract ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgbIV);
522 /// used to generate an inital vector if none is specified
524 public abstract void GenerateIV ();
527 /// used to generate a random key if none is specified
529 public abstract void GenerateKey ();
532 /// Checks wether the given keyLength is valid for the current algorithm
534 /// <param name="bitLength">the given keyLength</param>
535 public bool ValidKeySize (int bitLength)
537 return KeySizes.IsLegalKeySize (LegalKeySizesValue, bitLength);
541 /// Creates the default implementation of the default symmetric algorithm (Rijndael).
543 // LAMESPEC: Default is Rijndael - not TripleDES
544 public static SymmetricAlgorithm Create ()
546 return Create ("System.Security.Cryptography.SymmetricAlgorithm");
550 /// Creates a specific implementation of the given symmetric algorithm.
552 /// <param name="algName">Specifies which derived class to create</param>
553 public static SymmetricAlgorithm Create (string algName)
555 return (SymmetricAlgorithm) CryptoConfig.CreateFromName (algName);