Fix CFB for AesCryptoServiceProvider (only CFB8 worked, 16-64 did not) and move the...
[mono.git] / mcs / class / corlib / Mono.Security.Cryptography / SymmetricTransform.cs
index 61f2b3a076516f090d06afa9a6e1190139b0a748..64a1a67ba4165acfc3f7285e6890f3a67ce5dc83 100644 (file)
@@ -6,7 +6,7 @@
 //     Sebastien Pouliot <sebastien@ximian.com>
 //
 // Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2008 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -45,13 +45,15 @@ namespace Mono.Security.Cryptography {
        internal abstract class SymmetricTransform : ICryptoTransform {
                protected SymmetricAlgorithm algo;
                protected bool encrypt;
-               private int BlockSizeByte;
-               private byte[] temp;
-               private byte[] temp2;
+               protected int BlockSizeByte;
+               protected byte[] temp;
+               protected byte[] temp2;
                private byte[] workBuff;
                private byte[] workout;
-               private int FeedBackByte;
-               private int FeedBackIter;
+#if !MOONLIGHT
+               // Silverlight 2.0 does not support any feedback mode
+               protected int FeedBackByte;
+#endif
                private bool m_disposed = false;
                private bool lastBlock;
 
@@ -66,21 +68,19 @@ namespace Mono.Security.Cryptography {
                        } else {
                                rgbIV = (byte[]) rgbIV.Clone ();
                        }
-#if NET_2_0
                        // compare the IV length with the "currently selected" block size and *ignore* IV that are too big
                        if (rgbIV.Length < BlockSizeByte) {
                                string msg = Locale.GetText ("IV is too small ({0} bytes), it should be {1} bytes long.",
                                        rgbIV.Length, BlockSizeByte);
                                throw new CryptographicException (msg);
                        }
-#endif
                        // mode buffers
                        temp = new byte [BlockSizeByte];
                        Buffer.BlockCopy (rgbIV, 0, temp, 0, System.Math.Min (BlockSizeByte, rgbIV.Length));
                        temp2 = new byte [BlockSizeByte];
+#if !MOONLIGHT
                        FeedBackByte = (algo.FeedbackSize >> 3);
-                       if (FeedBackByte != 0)
-                               FeedBackIter = (int) BlockSizeByte / FeedBackByte;
+#endif
                        // transform buffers
                        workBuff = new byte [BlockSizeByte];
                        workout =  new byte [BlockSizeByte];
@@ -133,6 +133,10 @@ namespace Mono.Security.Cryptography {
                // i.e. Any padding must be done before calling this method
                protected virtual void Transform (byte[] input, byte[] output) 
                {
+#if MOONLIGHT
+                       // Silverlight 2.0 only supports CBC
+                       CBC (input, output);
+#else
                        switch (algo.Mode) {
                        case CipherMode.ECB:
                                ECB (input, output);
@@ -152,6 +156,7 @@ namespace Mono.Security.Cryptography {
                        default:
                                throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
                        }
+#endif
                }
 
                // Electronic Code Book (ECB)
@@ -175,31 +180,35 @@ namespace Mono.Security.Cryptography {
                        }
                }
 
+#if !MOONLIGHT
                // Cipher-FeedBack (CFB)
+               // this is how *CryptoServiceProvider implements CFB
+               // only AesCryptoServiceProvider support CFB > 8
+               // RijndaelManaged is incompatible with this implementation (and overrides it in it's own transform)
                protected virtual void CFB (byte[] input, byte[] output) 
                {
                        if (encrypt) {
-                               for (int x = 0; x < FeedBackIter; x++) {
+                               for (int x = 0; x < BlockSizeByte; x++) {
                                        // temp is first initialized with the IV
                                        ECB (temp, temp2);
 
-                                       for (int i = 0; i < FeedBackByte; i++)
+                                       for (int i = 0; i < 1; i++)
                                                output[i + x] = (byte)(temp2[i] ^ input[i + x]);
-                                       Buffer.BlockCopy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
-                                       Buffer.BlockCopy (output, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
+                                       Buffer.BlockCopy (temp, 1, temp, 0, BlockSizeByte - 1);
+                                       Buffer.BlockCopy (output, x, temp, BlockSizeByte - 1, 1);
                                }
                        }
                        else {
-                               for (int x = 0; x < FeedBackIter; x++) {
+                               for (int x = 0; x < BlockSizeByte; x++) {
                                        // we do not really decrypt this data!
                                        encrypt = true;
                                        // temp is first initialized with the IV
                                        ECB (temp, temp2);
                                        encrypt = false;
 
-                                       Buffer.BlockCopy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
-                                       Buffer.BlockCopy (input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
-                                       for (int i = 0; i < FeedBackByte; i++)
+                                       Buffer.BlockCopy (temp, 1, temp, 0, BlockSizeByte - 1);
+                                       Buffer.BlockCopy (input, x, temp, BlockSizeByte - 1, 1);
+                                       for (int i = 0; i < 1; i++)
                                                output[i + x] = (byte)(temp2[i] ^ input[i + x]);
                                }
                        }
@@ -214,8 +223,9 @@ namespace Mono.Security.Cryptography {
                // Cipher Text Stealing (CTS)
                protected virtual void CTS (byte[] input, byte[] output) 
                {
-                       throw new CryptographicException ("CTS  isn't supported by the framework");
+                       throw new CryptographicException ("CTS isn't supported by the framework");
                }
+#endif
 
                private void CheckInput (byte[] inputBuffer, int inputOffset, int inputCount)
                {
@@ -241,16 +251,40 @@ namespace Mono.Security.Cryptography {
                                throw new ArgumentNullException ("outputBuffer");
                        if (outputOffset < 0)
                                throw new ArgumentOutOfRangeException ("outputOffset", "< 0");
-                       // ordered to avoid possible integer overflow
-                       if (outputOffset > outputBuffer.Length - inputCount)
-                               throw new ArgumentException ("outputBuffer", Locale.GetText ("Overflow"));
 
+                       // ordered to avoid possible integer overflow
+                       int len = outputBuffer.Length - inputCount - outputOffset;
+#if MOONLIGHT
+                       // only PKCS7 is supported Silverlight 2.0
+                       if (KeepLastBlock) {
+#else
+                       if (!encrypt && (0 > len) && ((algo.Padding == PaddingMode.None) || (algo.Padding == PaddingMode.Zeros))) {
+                               throw new CryptographicException ("outputBuffer", Locale.GetText ("Overflow"));
+                       } else if (KeepLastBlock) {
+#endif
+                               if (0 > len + BlockSizeByte) {
+                                       throw new CryptographicException ("outputBuffer", Locale.GetText ("Overflow"));
+                               }
+                       } else {
+                               if (0 > len) {
+                                       // there's a special case if this is the end of the decryption process
+                                       if (inputBuffer.Length - inputOffset - outputBuffer.Length == BlockSizeByte)
+                                               inputCount = outputBuffer.Length - outputOffset;
+                                       else
+                                               throw new CryptographicException ("outputBuffer", Locale.GetText ("Overflow"));
+                               }
+                       }
                        return InternalTransformBlock (inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
                }
 
                private bool KeepLastBlock {
                        get {
-                               return ((!encrypt) && (algo.Mode != CipherMode.ECB) && (algo.Padding != PaddingMode.None));
+#if MOONLIGHT
+                               // only PKCS7 is supported Silverlight 2.0
+                               return !encrypt;
+#else
+                               return ((!encrypt) && (algo.Padding != PaddingMode.None) && (algo.Padding != PaddingMode.Zeros));
+#endif
                        }
                }
 
@@ -300,7 +334,7 @@ namespace Mono.Security.Cryptography {
                        return total;
                }
 
-#if NET_2_0
+#if !MOONLIGHT
                RandomNumberGenerator _rng;
 
                private void Random (byte[] buffer, int start, int length)
@@ -331,11 +365,13 @@ namespace Mono.Security.Cryptography {
                        int rem = inputCount - full;
                        int total = full;
 
+#if MOONLIGHT
+                       // only PKCS7 is supported Silverlight 2.0
+                       total += BlockSizeByte;
+#else
                        switch (algo.Padding) {
-#if NET_2_0
                        case PaddingMode.ANSIX923:
                        case PaddingMode.ISO10126:
-#endif
                        case PaddingMode.PKCS7:
                                // we need to add an extra block for padding
                                total += BlockSizeByte;
@@ -356,6 +392,7 @@ namespace Mono.Security.Cryptography {
                                }
                                break;
                        }
+#endif // NET_2_1
 
                        byte[] res = new byte [total];
                        int outputOffset = 0;
@@ -370,8 +407,14 @@ namespace Mono.Security.Cryptography {
 
                        // now we only have a single last block to encrypt
                        byte padding = (byte) (BlockSizeByte - rem);
+#if MOONLIGHT
+                       // only PKCS7 is supported Silverlight 2.0
+                       for (int i = res.Length; --i >= (res.Length - padding);) 
+                               res [i] = padding;
+                       Buffer.BlockCopy (inputBuffer, inputOffset, res, full, rem);
+                       InternalTransformBlock (res, full, BlockSizeByte, res, full);
+#else
                        switch (algo.Padding) {
-#if NET_2_0
                        case PaddingMode.ANSIX923:
                                // XX 00 00 00 00 00 00 07 (zero + padding length)
                                res [res.Length - 1] = padding;
@@ -387,7 +430,6 @@ namespace Mono.Security.Cryptography {
                                // the last padded block will be transformed in-place
                                InternalTransformBlock (res, full, BlockSizeByte, res, full);
                                break;
-#endif
                        case PaddingMode.PKCS7:
                                // XX 07 07 07 07 07 07 07 (padding length)
                                for (int i = res.Length; --i >= (res.Length - padding);) 
@@ -400,6 +442,7 @@ namespace Mono.Security.Cryptography {
                                InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
                                break;
                        }
+#endif // NET_2_1
                        return res;
                }
 
@@ -431,12 +474,21 @@ namespace Mono.Security.Cryptography {
 
                        // total may be 0 (e.g. PaddingMode.None)
                        byte padding = ((total > 0) ? res [total - 1] : (byte) 0);
+#if MOONLIGHT
+                       // only PKCS7 is supported Silverlight 2.0
+                       if ((padding == 0) || (padding > BlockSizeByte))
+                               throw new CryptographicException (Locale.GetText ("Bad padding length."));
+                       for (int i = padding - 1; i > 0; i--) {
+                               if (res [total - 1 - i] != padding)
+                                       throw new CryptographicException (Locale.GetText ("Bad padding at position {0}.", i));
+                       }
+                       total -= padding;
+#else
                        switch (algo.Padding) {
-#if NET_2_0
                        case PaddingMode.ANSIX923:
                                if ((padding == 0) || (padding > BlockSizeByte))
                                        ThrowBadPaddingException (algo.Padding, padding, -1);
-                               for (int i=padding; i > 0; i--) {
+                               for (int i = padding - 1; i > 0; i--) {
                                        if (res [total - 1 - i] != 0x00)
                                                ThrowBadPaddingException (algo.Padding, -1, i);
                                }
@@ -450,21 +502,17 @@ namespace Mono.Security.Cryptography {
                        case PaddingMode.PKCS7:
                                if ((padding == 0) || (padding > BlockSizeByte))
                                        ThrowBadPaddingException (algo.Padding, padding, -1);
-                               for (int i=padding - 1; i > 0; i--) {
+                               for (int i = padding - 1; i > 0; i--) {
                                        if (res [total - 1 - i] != padding)
                                                ThrowBadPaddingException (algo.Padding, -1, i);
                                }
                                total -= padding;
                                break;
-#else
-                       case PaddingMode.PKCS7:
-                               total -= padding;
-                               break;
-#endif
                        case PaddingMode.None:  // nothing to do - it's a multiple of block size
                        case PaddingMode.Zeros: // nothing to do - user must unpad himself
                                break;
                        }
+#endif // NET_2_1
 
                        // return output without padding
                        if (total > 0) {