2002-11-15 Sebastien Pouliot <spouliot@videotron.ca>
[mono.git] / mcs / class / corlib / System.Security.Cryptography / SymmetricAlgorithm.cs
1 //
2 // System.Security.Cryptography SymmetricAlgorithm Class implementation
3 //
4 // Authors:
5 //   Thomas Neidhart (tome@sbox.tugraz.at)
6 //   Sebastien Pouliot (spouliot@motus.com)
7 //
8 // Portions (C) 2002 Motus Technologies Inc. (http://www.motus.com)
9 //
10
11 using System;
12
13 namespace System.Security.Cryptography {
14
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...
19         //
20         // Descendants MUST:
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;
28                 private byte[] temp;
29                 private byte[] temp2;
30                 private int FeedBackByte;
31                 private int FeedBackIter;
32                 private bool m_disposed = false;
33
34                 public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV) 
35                 {
36                         algo = symmAlgo;
37                         encrypt = encryption;
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;
44                 }
45
46                 ~SymmetricTransform () 
47                 {
48                         Dispose (false);
49                 }
50
51                 void IDisposable.Dispose () 
52                 {
53                         Dispose (true);
54                         GC.SuppressFinalize (this);  // Finalization is now unnecessary
55                 }
56
57                 // MUST be overriden by classes using unmanaged ressources
58                 // the override method must call the base class
59                 protected void Dispose (bool disposing) 
60                 {
61                         if (!m_disposed) {
62                                 if (disposing) {
63                                         // dispose managed object: zeroize and free
64                                         Array.Clear (temp, 0, BlockSizeByte);
65                                         temp = null;
66                                         Array.Clear (temp2, 0, BlockSizeByte);
67                                         temp2 = null;
68                                 }
69                                 m_disposed = true;
70                         }
71                 }
72
73                 public virtual bool CanTransformMultipleBlocks {
74                         get { return false; }
75                 }
76
77                 public bool CanReuseTransform {
78                         get { return false; }
79                 }
80
81                 public virtual int InputBlockSize {
82                         get { return BlockSizeByte; }
83                 }
84
85                 public virtual int OutputBlockSize {
86                         get { return BlockSizeByte; }
87                 }
88
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) 
92                 {
93                         switch (algo.Mode) {
94                         case CipherMode.ECB:
95                                 ECB (input, output);
96                                 break;
97                         case CipherMode.CBC:
98                                 CBC (input, output);
99                                 break;
100                         case CipherMode.CFB:
101                                 CFB (input, output);
102                                 break;
103                         case CipherMode.OFB:
104                                 OFB (input, output);
105                                 break;
106                         case CipherMode.CTS:
107                                 CTS (input, output);
108                                 break;
109                         default:
110                                 throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
111                         }
112                 }
113
114                 // Electronic Code Book (ECB)
115                 protected abstract void ECB (byte[] input, byte[] output); 
116
117                 // Cipher-Block-Chaining (CBC)
118                 protected virtual void CBC (byte[] input, byte[] output) 
119                 {
120                         if (encrypt) {
121                                 for (int i = 0; i < BlockSizeByte; i++)
122                                         temp[i] ^= input[i];
123                                 ECB (temp, output);
124                                 Array.Copy (output, 0, temp, 0, BlockSizeByte);
125                         }
126                         else {
127                                 Array.Copy (input, 0, temp2, 0, BlockSizeByte);
128                                 ECB (input, output);
129                                 for (int i = 0; i < BlockSizeByte; i++)
130                                         output[i] ^= temp[i];
131                                 Array.Copy (temp2, 0, temp, 0, BlockSizeByte);
132                         }
133                 }
134
135                 // Cipher-FeedBack (CFB)
136                 protected virtual void CFB (byte[] input, byte[] output) 
137                 {
138                         if (encrypt) {
139                                 for (int x = 0; x < FeedBackIter; x++) {
140                                         // temp is first initialized with the IV
141                                         ECB (temp, temp2);
142
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);
147                                 }
148                         }
149                         else {
150                                 for (int x = 0; x < FeedBackIter; x++) {
151                                         // we do not really decrypt this data!
152                                         encrypt = true;
153                                         // temp is first initialized with the IV
154                                         ECB (temp, temp2);
155                                         encrypt = false;
156
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]);
161                                 }
162                         }
163                 }
164
165                 // Output-FeedBack (OFB)
166                 protected virtual void OFB (byte[] input, byte[] output) 
167                 {
168                         throw new NotImplementedException ("OFB not yet supported");
169                 }
170
171                 // Cipher Text Stealing (CTS)
172                 protected virtual void CTS (byte[] input, byte[] output) 
173                 {
174                         throw new NotImplementedException ("CTS not yet supported");
175                 }
176
177                 public virtual int TransformBlock (byte [] inputBuffer, int inputOffset, int inputCount, byte [] outputBuffer, int outputOffset) 
178                 {
179                         if (m_disposed)
180                                 throw new ObjectDisposedException ("Object is disposed");
181
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.");
185
186                         if (outputOffset + inputCount > outputBuffer.Length)
187                                 throw new CryptographicException ("Insufficient output buffer size.");
188
189                         int step = InputBlockSize;
190                         int offs = inputOffset;
191                         int full = inputCount / step;
192
193                         byte [] workBuff = new byte [step];
194                         byte[] workout =  new byte [step];
195
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);
200                                 offs += step;
201                                 outputOffset += step;
202                         }
203
204                         return (full * step);
205                 }
206
207                 public virtual byte [] TransformFinalBlock (byte [] inputBuffer, int inputOffset, int inputCount) 
208                 {
209                         if (m_disposed)
210                                 throw new ObjectDisposedException ("Object is disposed");
211
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;
216
217                         // are there still full block to process ?
218                         if (full > 0)
219                                 TransformBlock (inputBuffer, inputOffset, full, res, 0);
220
221                         //int rem = inputCount & (BlockSizeByte-1);
222                         int rem = inputCount - full; 
223
224                         // is there a partial block to pad ?
225                         if (rem > 0) {
226                                 if (encrypt) {
227                                         int padding = BlockSizeByte - (inputCount % BlockSizeByte);
228                                         switch (algo.Padding) {
229                                         case PaddingMode.None:
230                                                 break;
231                                         case PaddingMode.PKCS7:
232                                                 for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);) 
233                                                         res [i] = (byte) padding;
234                                                 break;
235                                         case PaddingMode.Zeros:
236                                                 for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
237                                                         res [i] = 0;
238                                                 break;
239                                         }
240
241                                         Array.Copy (inputBuffer, inputOffset + full, res, full, rem);
242
243                                         // the last padded block will be transformed in-place
244                                         TransformBlock (res, full, BlockSizeByte, res, full);
245                                 }
246                                 else {
247                                         switch (algo.Padding) {
248                                         case PaddingMode.None:
249                                                 break;
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;
255                                                 }
256                                                 break;
257                                         case PaddingMode.Zeros:
258                                                 break;
259                                         }
260                                 }
261                         }
262                         return res;
263                 }
264
265         }
266
267         /// <summary>
268         /// Abstract base class for all cryptographic symmetric algorithms.
269         /// Available algorithms include:
270         /// DES, RC2, Rijndael, TripleDES
271         /// </summary>
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;
283
284                 /// <summary>
285                 /// Called from constructor of derived class.
286                 /// </summary>
287                 public SymmetricAlgorithm () 
288                 {
289                         ModeValue = CipherMode.CBC;
290                         PaddingValue = PaddingMode.PKCS7;
291                         m_disposed = false;
292                 }
293                 
294                 /// <summary>
295                 /// Called from constructor of derived class.
296                 /// </summary>
297                 ~SymmetricAlgorithm () 
298                 {
299                         Dispose (false);
300                 }
301
302                 public void Clear() 
303                 {
304                         Dispose (true);
305                 }
306
307                 void IDisposable.Dispose () 
308                 {
309                         Dispose (true);
310                         GC.SuppressFinalize (this);  // Finalization is now unnecessary
311                 }
312
313                 protected virtual void Dispose (bool disposing) 
314                 {
315                         if (!m_disposed) {
316                                 // always zeroize keys
317                                 if (KeyValue != null) {
318                                         // Zeroize the secret key and free
319                                         Array.Clear (KeyValue, 0, KeyValue.Length);
320                                         KeyValue = null;
321                                 }
322                                 // dispose unmanaged managed objects
323                                 if (disposing) {
324                                         // dispose managed objects
325                                 }
326                                 m_disposed = true;
327                         }
328                 }
329
330                 /// <summary>
331                 /// Gets or sets the actual BlockSize
332                 /// </summary>
333                 public virtual int BlockSize {
334                         get { return this.BlockSizeValue; }
335                         set {
336                                 if (IsLegalKeySize(this.LegalBlockSizesValue, value))
337                                         this.BlockSizeValue = value;
338                                 else
339                                         throw new CryptographicException("block size not supported by algorithm");
340                         }
341                 }
342
343                 /// <summary>
344                 /// Gets or sets the actual FeedbackSize
345                 /// </summary>
346                 public virtual int FeedbackSize {
347                         get { return this.FeedbackSizeValue; }
348                         set {
349                                 if (value > this.BlockSizeValue)
350                                         throw new CryptographicException("feedback size larger than block size");
351                                 else
352                                         this.FeedbackSizeValue = value;
353                         }
354                 }
355                 
356                 /// <summary>
357                 /// Gets or sets the actual Initial Vector
358                 /// </summary>
359                 [MonoTODO]
360                 public virtual byte[] IV {
361                         get {
362                                 if (this.IVValue == null)
363                                         GenerateIV();
364
365                                 return this.IVValue;
366                         }
367                         set {
368                                 if (value == null)
369                                         throw new ArgumentNullException ("tried setting initial vector to null");
370                                         
371                                 if (value.Length * 8 != this.BlockSizeValue)
372                                         throw new CryptographicException ("IV length must match block size");
373                                 
374                                 this.IVValue = new byte [value.Length];
375                                 System.Array.Copy (value, 0, this.IVValue, 0, value.Length);
376                         }
377                 }
378
379                 /// <summary>
380                 /// Gets or sets the actual key
381                 /// </summary>
382                 public virtual byte[] Key {
383                         get {
384                                 if (this.KeyValue == null)
385                                         GenerateKey();
386
387                                 return this.KeyValue;
388                         }
389                         set {
390                                 if (value == null)
391                                         throw new ArgumentNullException ("tried setting key to null");
392
393                                 if (!IsLegalKeySize (this.LegalKeySizesValue, value.Length * 8))
394                                         throw new CryptographicException ("key size not supported by algorithm");
395
396                                 this.KeySizeValue = value.Length * 8;
397                                 this.KeyValue = new byte [value.Length];
398                                 System.Array.Copy (value, 0, this.KeyValue, 0, value.Length);
399                         }
400                 }
401                 
402                 /// <summary>
403                 /// Gets or sets the actual key size in bits
404                 /// </summary>
405                 public virtual int KeySize {
406                         get { return this.KeySizeValue; }
407                         set {
408                                 if (!IsLegalKeySize (this.LegalKeySizesValue, value))
409                                         throw new CryptographicException ("key size not supported by algorithm");
410                                 
411                                 this.KeyValue = null;
412                                 this.KeySizeValue = value;
413                         }
414                 }
415
416                 /// <summary>
417                 /// Gets all legal block sizes
418                 /// </summary>
419                 public virtual KeySizes[] LegalBlockSizes {
420                         get { return this.LegalBlockSizesValue; }
421                 }
422
423                 /// <summary>
424                 /// Gets all legal key sizes
425                 /// </summary>
426                 public virtual KeySizes[] LegalKeySizes {
427                         get { return this.LegalKeySizesValue; }
428                 }
429
430                 /// <summary>
431                 /// Gets or sets the actual cipher mode
432                 /// </summary>
433                 public virtual CipherMode Mode {
434                         get { return this.ModeValue; }
435                         set {
436                                 if (Enum.IsDefined( ModeValue.GetType (), value))
437                                         this.ModeValue = value;
438                                 else
439                                         throw new CryptographicException ("padding mode not available");
440                         }
441                 }
442
443                 /// <summary>
444                 /// Gets or sets the actual padding
445                 /// </summary>
446                 public virtual PaddingMode Padding {
447                         get { return this.PaddingValue; }
448                         set {
449                                 if (Enum.IsDefined (PaddingValue.GetType (), value))
450                                         this.PaddingValue = value;
451                                 else
452                                         throw new CryptographicException ("padding mode not available");
453                         }
454                 }
455
456                 /// <summary>
457                 /// Gets an Decryptor transform object to work with a CryptoStream
458                 /// </summary>
459                 public virtual ICryptoTransform CreateDecryptor () 
460                 {
461                         return CreateDecryptor (Key, IV);
462                 }
463
464                 /// <summary>
465                 /// Gets an Decryptor transform object to work with a CryptoStream
466                 /// </summary>
467                 public abstract ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgbIV);
468
469                 /// <summary>
470                 /// Gets an Encryptor transform object to work with a CryptoStream
471                 /// </summary>
472                 public virtual ICryptoTransform CreateEncryptor() 
473                 {
474                         return CreateEncryptor (Key, IV);
475                 }
476
477                 /// <summary>
478                 /// Gets an Encryptor transform object to work with a CryptoStream
479                 /// </summary>
480                 public abstract ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgbIV);
481
482                 /// <summary>
483                 /// used to generate an inital vector if none is specified
484                 /// </summary>
485                 public abstract void GenerateIV ();
486
487                 /// </summary>
488                 /// used to generate a random key if none is specified
489                 /// </summary>
490                 public abstract void GenerateKey ();
491
492                 internal bool IsLegalKeySize (KeySizes[] LegalKeys, int Size) 
493                 {
494                         foreach (KeySizes LegalKeySize in LegalKeys) {
495                                 for (int i=LegalKeySize.MinSize; i<=LegalKeySize.MaxSize; i+=LegalKeySize.SkipSize) {
496                                         if (i == Size)
497                                                 return true;
498                                 }
499                         }
500                         return false;
501                 }
502                 
503                 /// <summary>
504                 /// Checks wether the given keyLength is valid for the current algorithm
505                 /// </summary>
506                 /// <param name="bitLength">the given keyLength</param>
507                 public bool ValidKeySize (int bitLength) 
508                 {
509                         return IsLegalKeySize (LegalKeySizesValue, bitLength);
510                 }
511                 
512                 /// <summary>
513                 /// Creates the default implementation of the default symmetric algorithm (Rijndael).
514                 /// </summary>
515                 // LAMESPEC: Default is Rijndael - not TripleDES
516                 public static SymmetricAlgorithm Create () 
517                 {
518                         return Create ("System.Security.Cryptography.SymmetricAlgorithm");
519                 }
520
521                 /// <summary>
522                 /// Creates a specific implementation of the given symmetric algorithm.
523                 /// </summary>
524                 /// <param name="algName">Specifies which derived class to create</param>
525                 public static SymmetricAlgorithm Create (string algName) 
526                 {
527                         return (SymmetricAlgorithm) CryptoConfig.CreateFromName (algName);
528                 }
529         }
530 }
531