2 // Mono.Security.Cryptography.SymmetricTransform implementation
5 // Thomas Neidhart (tome@sbox.tugraz.at)
6 // Sebastien Pouliot <sebastien@ximian.com>
8 // Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Copyright (C) 2004-2008 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Security.Cryptography;
34 namespace Mono.Security.Cryptography {
36 // This class implement most of the common code required for symmetric
37 // algorithm transforms, like:
38 // - CipherMode: Builds CBC and CFB on top of (descendant supplied) ECB
39 // - PaddingMode, transform properties, multiple blocks, reuse...
42 // - intialize themselves (like key expansion, ...)
43 // - override the ECB (Electronic Code Book) method which will only be
44 // called using BlockSize byte[] array.
45 internal abstract class SymmetricTransform : ICryptoTransform {
46 protected SymmetricAlgorithm algo;
47 protected bool encrypt;
48 protected int BlockSizeByte;
49 protected byte[] temp;
50 protected byte[] temp2;
51 private byte[] workBuff;
52 private byte[] workout;
53 protected PaddingMode padmode;
55 // Silverlight 2.0 does not support any feedback mode
56 protected int FeedBackByte;
58 private bool m_disposed = false;
59 protected bool lastBlock;
61 public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV)
65 BlockSizeByte = (algo.BlockSize >> 3);
68 rgbIV = KeyBuilder.IV (BlockSizeByte);
70 rgbIV = (byte[]) rgbIV.Clone ();
72 // compare the IV length with the "currently selected" block size and *ignore* IV that are too big
73 if (rgbIV.Length < BlockSizeByte) {
74 string msg = Locale.GetText ("IV is too small ({0} bytes), it should be {1} bytes long.",
75 rgbIV.Length, BlockSizeByte);
76 throw new CryptographicException (msg);
78 padmode = algo.Padding;
80 temp = new byte [BlockSizeByte];
81 Buffer.BlockCopy (rgbIV, 0, temp, 0, System.Math.Min (BlockSizeByte, rgbIV.Length));
82 temp2 = new byte [BlockSizeByte];
84 FeedBackByte = (algo.FeedbackSize >> 3);
87 workBuff = new byte [BlockSizeByte];
88 workout = new byte [BlockSizeByte];
91 ~SymmetricTransform ()
96 void IDisposable.Dispose ()
99 GC.SuppressFinalize (this); // Finalization is now unnecessary
102 // MUST be overriden by classes using unmanaged ressources
103 // the override method must call the base class
104 protected virtual void Dispose (bool disposing)
108 // dispose managed object: zeroize and free
109 Array.Clear (temp, 0, BlockSizeByte);
111 Array.Clear (temp2, 0, BlockSizeByte);
118 public virtual bool CanTransformMultipleBlocks {
122 public virtual bool CanReuseTransform {
123 get { return false; }
126 public virtual int InputBlockSize {
127 get { return BlockSizeByte; }
130 public virtual int OutputBlockSize {
131 get { return BlockSizeByte; }
134 // note: Each block MUST be BlockSizeValue in size!!!
135 // i.e. Any padding must be done before calling this method
136 protected virtual void Transform (byte[] input, byte[] output)
139 // Silverlight 2.0 only supports CBC
159 throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
164 // Electronic Code Book (ECB)
165 protected abstract void ECB (byte[] input, byte[] output);
167 // Cipher-Block-Chaining (CBC)
168 protected virtual void CBC (byte[] input, byte[] output)
171 for (int i = 0; i < BlockSizeByte; i++)
174 Buffer.BlockCopy (output, 0, temp, 0, BlockSizeByte);
177 Buffer.BlockCopy (input, 0, temp2, 0, BlockSizeByte);
179 for (int i = 0; i < BlockSizeByte; i++)
180 output[i] ^= temp[i];
181 Buffer.BlockCopy (temp2, 0, temp, 0, BlockSizeByte);
186 // Cipher-FeedBack (CFB)
187 // this is how *CryptoServiceProvider implements CFB
188 // only AesCryptoServiceProvider support CFB > 8
189 // RijndaelManaged is incompatible with this implementation (and overrides it in it's own transform)
190 protected virtual void CFB (byte[] input, byte[] output)
193 for (int x = 0; x < BlockSizeByte; x++) {
194 // temp is first initialized with the IV
196 output [x] = (byte) (temp2 [0] ^ input [x]);
197 Buffer.BlockCopy (temp, 1, temp, 0, BlockSizeByte - 1);
198 Buffer.BlockCopy (output, x, temp, BlockSizeByte - 1, 1);
202 for (int x = 0; x < BlockSizeByte; x++) {
203 // we do not really decrypt this data!
205 // temp is first initialized with the IV
209 Buffer.BlockCopy (temp, 1, temp, 0, BlockSizeByte - 1);
210 Buffer.BlockCopy (input, x, temp, BlockSizeByte - 1, 1);
211 output [x] = (byte) (temp2 [0] ^ input [x]);
216 // Output-FeedBack (OFB)
217 protected virtual void OFB (byte[] input, byte[] output)
219 throw new CryptographicException ("OFB isn't supported by the framework");
222 // Cipher Text Stealing (CTS)
223 protected virtual void CTS (byte[] input, byte[] output)
225 throw new CryptographicException ("CTS isn't supported by the framework");
229 private void CheckInput (byte[] inputBuffer, int inputOffset, int inputCount)
231 if (inputBuffer == null)
232 throw new ArgumentNullException ("inputBuffer");
234 throw new ArgumentOutOfRangeException ("inputOffset", "< 0");
236 throw new ArgumentOutOfRangeException ("inputCount", "< 0");
237 // ordered to avoid possible integer overflow
238 if (inputOffset > inputBuffer.Length - inputCount)
239 throw new ArgumentException ("inputBuffer", Locale.GetText ("Overflow"));
242 // this method may get called MANY times so this is the one to optimize
243 public virtual int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
246 throw new ObjectDisposedException ("Object is disposed");
247 CheckInput (inputBuffer, inputOffset, inputCount);
248 // check output parameters
249 if (outputBuffer == null)
250 throw new ArgumentNullException ("outputBuffer");
251 if (outputOffset < 0)
252 throw new ArgumentOutOfRangeException ("outputOffset", "< 0");
254 // ordered to avoid possible integer overflow
255 int len = outputBuffer.Length - inputCount - outputOffset;
257 // only PKCS7 is supported Silverlight 2.0
260 if (!encrypt && (0 > len) && ((padmode == PaddingMode.None) || (padmode == PaddingMode.Zeros))) {
261 throw new CryptographicException ("outputBuffer", Locale.GetText ("Overflow"));
262 } else if (KeepLastBlock) {
264 if (0 > len + BlockSizeByte) {
265 throw new CryptographicException ("outputBuffer", Locale.GetText ("Overflow"));
269 // there's a special case if this is the end of the decryption process
270 if (inputBuffer.Length - inputOffset - outputBuffer.Length == BlockSizeByte)
271 inputCount = outputBuffer.Length - outputOffset;
273 throw new CryptographicException ("outputBuffer", Locale.GetText ("Overflow"));
276 return InternalTransformBlock (inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
279 private bool KeepLastBlock {
282 // only PKCS7 is supported Silverlight 2.0
285 return ((!encrypt) && (padmode != PaddingMode.None) && (padmode != PaddingMode.Zeros));
290 private int InternalTransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
292 int offs = inputOffset;
295 // this way we don't do a modulo every time we're called
296 // and we may save a division
297 if (inputCount != BlockSizeByte) {
298 if ((inputCount % BlockSizeByte) != 0)
299 throw new CryptographicException ("Invalid input block size.");
301 full = inputCount / BlockSizeByte;
312 Transform (workBuff, workout);
313 Buffer.BlockCopy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
314 outputOffset += BlockSizeByte;
315 total += BlockSizeByte;
319 for (int i = 0; i < full; i++) {
320 Buffer.BlockCopy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
321 Transform (workBuff, workout);
322 Buffer.BlockCopy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
323 offs += BlockSizeByte;
324 outputOffset += BlockSizeByte;
325 total += BlockSizeByte;
329 Buffer.BlockCopy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
337 RandomNumberGenerator _rng;
339 private void Random (byte[] buffer, int start, int length)
342 _rng = RandomNumberGenerator.Create ();
344 byte[] random = new byte [length];
345 _rng.GetBytes (random);
346 Buffer.BlockCopy (random, 0, buffer, start, length);
349 private void ThrowBadPaddingException (PaddingMode padding, int length, int position)
351 string msg = String.Format (Locale.GetText ("Bad {0} padding."), padding);
353 msg += String.Format (Locale.GetText (" Invalid length {0}."), length);
355 msg += String.Format (Locale.GetText (" Error found at position {0}."), position);
356 throw new CryptographicException (msg);
360 protected virtual byte[] FinalEncrypt (byte[] inputBuffer, int inputOffset, int inputCount)
362 // are there still full block to process ?
363 int full = (inputCount / BlockSizeByte) * BlockSizeByte;
364 int rem = inputCount - full;
368 // only PKCS7 is supported Silverlight 2.0
369 total += BlockSizeByte;
372 case PaddingMode.ANSIX923:
373 case PaddingMode.ISO10126:
374 case PaddingMode.PKCS7:
375 // we need to add an extra block for padding
376 total += BlockSizeByte;
382 if (padmode == PaddingMode.None)
383 throw new CryptographicException ("invalid block length");
384 // zero padding the input (by adding a block for the partial data)
385 byte[] paddedInput = new byte [full + BlockSizeByte];
386 Buffer.BlockCopy (inputBuffer, inputOffset, paddedInput, 0, inputCount);
387 inputBuffer = paddedInput;
389 inputCount = paddedInput.Length;
396 byte[] res = new byte [total];
397 int outputOffset = 0;
399 // process all blocks except the last (final) block
400 while (total > BlockSizeByte) {
401 InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
402 inputOffset += BlockSizeByte;
403 outputOffset += BlockSizeByte;
404 total -= BlockSizeByte;
407 // now we only have a single last block to encrypt
408 byte padding = (byte) (BlockSizeByte - rem);
410 // only PKCS7 is supported Silverlight 2.0
411 for (int i = res.Length; --i >= (res.Length - padding);)
413 Buffer.BlockCopy (inputBuffer, inputOffset, res, full, rem);
414 InternalTransformBlock (res, full, BlockSizeByte, res, full);
417 case PaddingMode.ANSIX923:
418 // XX 00 00 00 00 00 00 07 (zero + padding length)
419 res [res.Length - 1] = padding;
420 Buffer.BlockCopy (inputBuffer, inputOffset, res, full, rem);
421 // the last padded block will be transformed in-place
422 InternalTransformBlock (res, full, BlockSizeByte, res, full);
424 case PaddingMode.ISO10126:
425 // XX 3F 52 2A 81 AB F7 07 (random + padding length)
426 Random (res, res.Length - padding, padding - 1);
427 res [res.Length - 1] = padding;
428 Buffer.BlockCopy (inputBuffer, inputOffset, res, full, rem);
429 // the last padded block will be transformed in-place
430 InternalTransformBlock (res, full, BlockSizeByte, res, full);
432 case PaddingMode.PKCS7:
433 // XX 07 07 07 07 07 07 07 (padding length)
434 for (int i = res.Length; --i >= (res.Length - padding);)
436 Buffer.BlockCopy (inputBuffer, inputOffset, res, full, rem);
437 // the last padded block will be transformed in-place
438 InternalTransformBlock (res, full, BlockSizeByte, res, full);
441 InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
448 protected virtual byte[] FinalDecrypt (byte[] inputBuffer, int inputOffset, int inputCount)
450 int full = inputCount;
451 int total = inputCount;
453 total += BlockSizeByte;
455 byte[] res = new byte [total];
456 int outputOffset = 0;
459 int len = InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
460 inputOffset += BlockSizeByte;
462 full -= BlockSizeByte;
466 Transform (workBuff, workout);
467 Buffer.BlockCopy (workout, 0, res, outputOffset, BlockSizeByte);
468 outputOffset += BlockSizeByte;
472 // total may be 0 (e.g. PaddingMode.None)
473 byte padding = ((total > 0) ? res [total - 1] : (byte) 0);
475 // only PKCS7 is supported Silverlight 2.0
476 if ((padding == 0) || (padding > BlockSizeByte))
477 throw new CryptographicException (Locale.GetText ("Bad padding length."));
478 for (int i = padding - 1; i > 0; i--) {
479 if (res [total - 1 - i] != padding)
480 throw new CryptographicException (Locale.GetText ("Bad padding at position {0}.", i));
485 case PaddingMode.ANSIX923:
486 if ((padding == 0) || (padding > BlockSizeByte))
487 ThrowBadPaddingException (padmode, padding, -1);
488 for (int i = padding - 1; i > 0; i--) {
489 if (res [total - 1 - i] != 0x00)
490 ThrowBadPaddingException (padmode, -1, i);
494 case PaddingMode.ISO10126:
495 if ((padding == 0) || (padding > BlockSizeByte))
496 ThrowBadPaddingException (padmode, padding, -1);
499 case PaddingMode.PKCS7:
500 if ((padding == 0) || (padding > BlockSizeByte))
501 ThrowBadPaddingException (padmode, padding, -1);
502 for (int i = padding - 1; i > 0; i--) {
503 if (res [total - 1 - i] != padding)
504 ThrowBadPaddingException (padmode, -1, i);
508 case PaddingMode.None: // nothing to do - it's a multiple of block size
509 case PaddingMode.Zeros: // nothing to do - user must unpad himself
514 // return output without padding
516 byte[] data = new byte [total];
517 Buffer.BlockCopy (res, 0, data, 0, total);
518 // zeroize decrypted data (copy with padding)
519 Array.Clear (res, 0, res.Length);
526 public virtual byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
529 throw new ObjectDisposedException ("Object is disposed");
530 CheckInput (inputBuffer, inputOffset, inputCount);
533 return FinalEncrypt (inputBuffer, inputOffset, inputCount);
535 return FinalDecrypt (inputBuffer, inputOffset, inputCount);