Normalization of cryptographic uses in asp.net
authorSebastien Pouliot <sebastien@ximian.com>
Fri, 8 Oct 2010 21:02:20 +0000 (17:02 -0400)
committerSebastien Pouliot <sebastien@ximian.com>
Tue, 12 Oct 2010 15:38:47 +0000 (11:38 -0400)
* System.Web.Configuration_2.0/MachineKeyRegistryStorage.cs:
Remove key length check and generic key generation.

* System.Web.Configuration_2.0/MachineKeySection.cs: Add support for 4.0
ValidationAlgorithm and the use of custom algorithms (validation and
decryption). Allow the use of any, valid, key length (based on the
algorithm). Let each algorithm creates its own key (e.g. special needs,
default length...)

* System.Web.Configuration_2.0/MachineKeySectionUtils.cs: Remove key
generation (from random) code and 192bits key length hack (won't work
with custom algorithms). Add support for new (4.0) algorithms, including
custom ones. Provide uniform/shared code to Encrypt/Decrypt, Sign/Verify
and EncryptSign/VerifyDecrypt using MachineKeySection data.

* System.Web.Configuration_2.0/MachineKeyValidation.cs: Add new (4.0)
values.

* System.Web.Configuration_2.0/MachineKeyValidationConverter.cs: Add
support for new (4.0) algorithms.

* System.Web.Handlers/AssemblyResourceLoader.cs: Use the new common
cryptographic code and base64 the encrypted data.

* System.Web.Security/FormsAuthentication.cs: Use the new common
cryptographic code and base64 the signed and/or encrypted data.

* System.Web.Security/MembershipHelper.cs: Use the new common
cryptographic code - this should be 100% compatible with existing data.

* System.Web.Security/RolePrincipal.cs: Use the new common cryptographic
code.

* System.Web.Security/SqliteMembershipProvider.cs: Adapt code for
internal API change.

* System.Web.UI/LosFormatter.cs: Adapt code for internal API change.
Fix some small behaviro changes wrt NET_4_0

* System.Web.UI/ObjectStateFormatter.cs: Use the new common cryptographic
code.

* System.Web.UI/Page.cs: Remove code that is now unneeded (with the new
common cryptogrraphic code).

* System.Web.Configuration_2.0/MachineKeyCompatibilityMode.cs: New.

* Test/System.Web.Configuration/MachineKeyValidationConverterTest.cs:
Add more, mostly 4.0, test cases.

* Test/System.Web.Security/FormsAuthenticationTest.cs: Add test case to
ensure HashPasswordForStoringInConfigFile is not case sensitive.

* Test/System.Web.UI/LosFormatterTest.cs: Add some rountrip test cases
with the different ctors

25 files changed:
mcs/class/System.Web/System.Web.Configuration_2.0/MachineKeyCompatibilityMode.cs [new file with mode: 0644]
mcs/class/System.Web/System.Web.Configuration_2.0/MachineKeyRegistryStorage.cs
mcs/class/System.Web/System.Web.Configuration_2.0/MachineKeySection.cs
mcs/class/System.Web/System.Web.Configuration_2.0/MachineKeySectionUtils.cs
mcs/class/System.Web/System.Web.Configuration_2.0/MachineKeyValidation.cs
mcs/class/System.Web/System.Web.Configuration_2.0/MachineKeyValidationConverter.cs
mcs/class/System.Web/System.Web.Handlers/AssemblyResourceLoader.cs
mcs/class/System.Web/System.Web.Security/FormsAuthentication.cs
mcs/class/System.Web/System.Web.Security/MembershipHelper.cs
mcs/class/System.Web/System.Web.Security/RolePrincipal.cs
mcs/class/System.Web/System.Web.Security/SqlMembershipProvider.cs
mcs/class/System.Web/System.Web.Security/SqliteMembershipProvider.cs
mcs/class/System.Web/System.Web.SessionState_2.0/SessionId.cs
mcs/class/System.Web/System.Web.UI/LosFormatter.cs
mcs/class/System.Web/System.Web.UI/ObjectStateFormatter.cs
mcs/class/System.Web/System.Web.UI/Page.cs
mcs/class/System.Web/System.Web.dll.sources
mcs/class/System.Web/System.Web_test.dll.sources
mcs/class/System.Web/Test/System.Web.Configuration/MachineKeySectionTest.cs [new file with mode: 0644]
mcs/class/System.Web/Test/System.Web.Configuration/MachineKeySectionUtilsTest.cs [new file with mode: 0644]
mcs/class/System.Web/Test/System.Web.Configuration/MachineKeyValidationConverterTest.cs
mcs/class/System.Web/Test/System.Web.Security/FormsAuthenticationTest.cs
mcs/class/System.Web/Test/System.Web.Security/RolePrincipalTest.cs
mcs/class/System.Web/Test/System.Web.UI/LosFormatterTest.cs
mcs/class/System.Web/Test/System.Web.UI/ObjectStateFormatterTest.cs

diff --git a/mcs/class/System.Web/System.Web.Configuration_2.0/MachineKeyCompatibilityMode.cs b/mcs/class/System.Web/System.Web.Configuration_2.0/MachineKeyCompatibilityMode.cs
new file mode 100644 (file)
index 0000000..5581143
--- /dev/null
@@ -0,0 +1,40 @@
+//
+// System.Web.Configuration.MachineKeyCompatibilityMode
+//
+// Authors:
+//     Sebastien Pouliot  <sebastien@ximian.com>
+//
+// Copyright (C) 2010 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
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_4_0
+
+namespace System.Web.Configuration {
+
+       public enum MachineKeyCompatibilityMode {
+               Framework20SP1 = 0,
+               Framework20SP2 = 1
+       }
+}
+
+#endif
+
index c6f1d52d081ba1b3c3739b0fc2055387e0022e0e..52bc5b3fba6e26c018b7fa6f5df769488ffe811c 100644 (file)
@@ -28,8 +28,6 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
-using System.Security.Cryptography;
 using Microsoft.Win32;
 
 namespace System.Web.Configuration
@@ -42,9 +40,6 @@ namespace System.Web.Configuration
                        Encryption
                };
 
-               const int encryptionKeyLength = 64;
-               const int validationKeyLength = 64;
-               
                static string keyEncryption;
                static string keyValidation;
                
@@ -62,31 +57,16 @@ namespace System.Web.Configuration
                }
                
                public static byte[] Retrieve (KeyType kt)
-               {
-                       byte[] ret = GetKey (kt);
-                       if (ret == null) {
-                               ret = Generate (kt);
-                               if (ret != null)
-                                       Store (ret, kt);
-                       }
-                       
-                       return ret;
-               }
-
-               static byte[] GetKey (KeyType kt)
                {
                        string key = null;
-                       int len;
                        
                        switch (kt) {
                                case KeyType.Validation:
                                        key = keyValidation;
-                                       len = validationKeyLength;
                                        break;
 
                                case KeyType.Encryption:
                                        key = keyEncryption;
-                                       len = validationKeyLength;
                                        break;
 
                                default:
@@ -107,11 +87,7 @@ namespace System.Web.Configuration
 
                        if (o == null || o.GetType () != typeof (byte[]))
                                return null;
-                       byte[] ret = (byte[])o;
-                       if (ret.Length != len)
-                               return null;
-
-                       return ret;
+                       return (byte[]) o;
                }
 
                static RegistryKey OpenRegistryKey (string path, bool write)
@@ -134,23 +110,19 @@ namespace System.Web.Configuration
                        return ret;
                }
                
-               static void Store (byte[] buf, KeyType kt)
+               public static void Store (byte[] buf, KeyType kt)
                {
                        if (buf == null)
                                return;
                        
                        string key = null;
-                       int len;
-                       
                        switch (kt) {
                                case KeyType.Validation:
                                        key = keyValidation;
-                                       len = validationKeyLength;
                                        break;
 
                                case KeyType.Encryption:
                                        key = keyEncryption;
-                                       len = validationKeyLength;
                                        break;
 
                                default:
@@ -160,9 +132,6 @@ namespace System.Web.Configuration
                        if (key == null)
                                return;
 
-                       if (buf.Length != len)
-                               throw new ArgumentException ("Key has invalid length");
-
                        try {
                                using (RegistryKey rk = OpenRegistryKey (key, true)) {
 #if NET_2_0
@@ -180,27 +149,5 @@ namespace System.Web.Configuration
                                throw new ApplicationException ("Failed to store encryption key in the registry.", ex);
                        }
                }
-
-               static byte[] Generate (KeyType kt)
-               {
-                       RandomNumberGenerator rng = RandomNumberGenerator.Create ();
-                       byte[] ret = null;
-                       
-                       switch (kt) {
-                               case KeyType.Validation:
-                                       ret = new byte [validationKeyLength];
-                                       break;
-
-                               case KeyType.Encryption:
-                                       ret = new byte [encryptionKeyLength];
-                                       break;
-
-                               default:
-                                       throw new ArgumentException ("Unknown key type.");
-                       }
-                       
-                       rng.GetBytes (ret);
-                       return ret;
-               }
        }
 }
index 193b9c2dcd4d3d8d8333bf7d7298da4402b091a3..c193e744e0206207cbd38b1d43e86f5d98ddf5bd 100644 (file)
@@ -3,8 +3,9 @@
 //
 // Authors:
 //     Chris Toshok (toshok@ximian.com)
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
-// (c) Copyright 2005 Novell, Inc (http://www.novell.com)
+// (c) Copyright 2005, 2010 Novell, Inc (http://www.novell.com)
 //
 
 //
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+#if NET_2_0
+
 using System;
 using System.ComponentModel;
 using System.Configuration;
 using System.Security.Cryptography;
 
-#if NET_2_0
-
 namespace System.Web.Configuration {
 
        public sealed class MachineKeySection : ConfigurationSection
@@ -44,6 +45,10 @@ namespace System.Web.Configuration {
                static ConfigurationProperty validationProp;
                static ConfigurationProperty validationKeyProp;
                static ConfigurationPropertyCollection properties;
+               static MachineKeyValidationConverter converter = new MachineKeyValidationConverter ();
+#if NET_4_0
+               MachineKeyValidation validation;
+#endif
 
                static MachineKeySection ()
                {
@@ -55,10 +60,17 @@ namespace System.Web.Configuration {
                                                                       PropertyHelper.WhiteSpaceTrimStringConverter,
                                                                       PropertyHelper.NonEmptyStringValidator,
                                                                       ConfigurationPropertyOptions.None);
-                       validationProp = new ConfigurationProperty ("validation", typeof (MachineKeyValidation), MachineKeyValidation.SHA1,
-                                                                   new MachineKeyValidationConverter (),
+#if NET_4_0
+                       validationProp = new ConfigurationProperty ("validation", typeof (string), "HMACSHA256",
+                                                                   PropertyHelper.WhiteSpaceTrimStringConverter,
+                                                                   PropertyHelper.NonEmptyStringValidator,
+                                                                   ConfigurationPropertyOptions.None);
+#else
+                       validationProp = new ConfigurationProperty ("validation", typeof (MachineKeyValidation), 
+                                                                   MachineKeyValidation.SHA1, converter,
                                                                    PropertyHelper.DefaultValidator,
                                                                    ConfigurationPropertyOptions.None);
+#endif
                        validationKeyProp = new ConfigurationProperty ("validationKey", typeof (string), "AutoGenerate,IsolateApps",
                                                                       PropertyHelper.WhiteSpaceTrimStringConverter,
                                                                       PropertyHelper.NonEmptyStringValidator,
@@ -71,12 +83,30 @@ namespace System.Web.Configuration {
                        properties.Add (validationProp);
                        properties.Add (validationKeyProp);
 
-                       MachineKeySectionUtils.AutoGenKeys ();
+                       Config.AutoGenerate (MachineKeyRegistryStorage.KeyType.Encryption);
+                       Config.AutoGenerate (MachineKeyRegistryStorage.KeyType.Validation);
                }
 
+#if NET_4_0
+               public MachineKeySection ()
+               {
+                       // get DefaultValue from ValidationAlgorithm
+                       validation = (MachineKeyValidation) converter.ConvertFrom (null, null, ValidationAlgorithm);
+               }
+
+               [MonoTODO]
+               public MachineKeyCompatibilityMode CompatibilityMode {
+                       get; set;
+               }
+#endif
+
                protected override void Reset (ConfigurationElement parentElement)
                {
                        base.Reset (parentElement);
+                       decryption_key = null;
+                       validation_key = null;
+                       decryption_template = null;
+                       validation_template = null;
                }
 
                [TypeConverter (typeof (WhiteSpaceTrimStringConverter))]
@@ -84,7 +114,10 @@ namespace System.Web.Configuration {
                [ConfigurationProperty ("decryption", DefaultValue = "Auto")]
                public string Decryption {
                        get { return (string) base [decryptionProp];}
-                       set { base[decryptionProp] = value; }
+                       set {
+                               decryption_template = MachineKeySectionUtils.GetDecryptionAlgorithm (value);
+                               base[decryptionProp] = value;
+                       }
                }
 
                [TypeConverter (typeof (WhiteSpaceTrimStringConverter))]
@@ -94,16 +127,46 @@ namespace System.Web.Configuration {
                        get { return (string) base [decryptionKeyProp];}
                        set {
                                base[decryptionKeyProp] = value;
-                               MachineKeySectionUtils.SetDecryptionKey (value);
+//                             SetDecryptionKey (value);
                        }
                }
 
+#if NET_4_0
+               // property exists for backward compatibility
+               public MachineKeyValidation Validation {
+                       get { return validation; }
+                       set {
+                               if (value == MachineKeyValidation.Custom)
+                                       throw new ArgumentException ();
+//                             ValidationAlgorithm = value.ToString ();
+                       }
+               }
+
+               [StringValidator (MinLength = 1)]
+               [TypeConverter (typeof (WhiteSpaceTrimStringConverter))]
+               [ConfigurationProperty ("validation", DefaultValue = "HMACSHA256")]
+               public string ValidationAlgorithm {
+                       get { return (string) base [validationProp];}
+                       set {
+                               if (value == null)
+                                       return;
+
+                               if (value.StartsWith ("alg:"))
+                                       validation = MachineKeyValidation.Custom;
+                               else
+                                       validation = (MachineKeyValidation) converter.ConvertFrom (null, null, value);
+
+                               base[validationProp] = value;
+                       }
+               }
+#else
                [TypeConverter (typeof (MachineKeyValidationConverter))]
                [ConfigurationProperty ("validation", DefaultValue = "SHA1")]
                public MachineKeyValidation Validation {
                        get { return (MachineKeyValidation) base [validationProp];}
                        set { base[validationProp] = value; }
                }
+#endif
 
                [TypeConverter (typeof (WhiteSpaceTrimStringConverter))]
                [StringValidator (MinLength = 1)]
@@ -112,13 +175,142 @@ namespace System.Web.Configuration {
                        get { return (string) base [validationKeyProp];}
                        set {
                                base[validationKeyProp] = value;
-                               MachineKeySectionUtils.SetValidationKey (value);
+//                             SetValidationKey (value);
                        }
                }
 
                protected override ConfigurationPropertyCollection Properties {
                        get { return properties; }
                }
+
+
+               internal static MachineKeySection Config {
+                       get { return WebConfigurationManager.GetSection ("system.web/machineKey") as MachineKeySection; }
+               }
+
+               private byte[] decryption_key;
+               private byte[] validation_key;
+               private SymmetricAlgorithm decryption_template;
+               private KeyedHashAlgorithm validation_template;
+
+               internal SymmetricAlgorithm GetDecryptionAlgorithm ()
+               {
+                       // code location to help with unit testing the code
+                       return MachineKeySectionUtils.GetDecryptionAlgorithm (Decryption);
+               }
+
+               // not to be reused outside algorithm and key validation purpose
+               private SymmetricAlgorithm DecryptionTemplate {
+                       get {
+                               if (decryption_template == null)
+                                       decryption_template = GetDecryptionAlgorithm ();
+                               return decryption_template;
+                       }
+               }
+
+               internal byte [] GetDecryptionKey ()
+               {
+                       if (decryption_key == null)
+                               SetDecryptionKey (DecryptionKey);
+                       return decryption_key;
+               }
+
+               void SetDecryptionKey (string key)
+               {
+                       if ((key == null) || key.StartsWith ("AutoGenerate")) {
+                               decryption_key = AutoGenerate (MachineKeyRegistryStorage.KeyType.Encryption);
+                       } else {
+                               try {
+                                       decryption_key = MachineKeySectionUtils.GetBytes (key, key.Length);
+                                       DecryptionTemplate.Key = decryption_key;
+                               }
+                               catch {
+                                       decryption_key = null;
+                                       throw new ArgumentException ("Invalid key length");
+                               }
+                       }
+               }
+
+               internal KeyedHashAlgorithm GetValidationAlgorithm ()
+               {
+                       // code location to help with unit testing the code
+                       return MachineKeySectionUtils.GetValidationAlgorithm (this);
+               }
+
+               // not to be reused outside algorithm and key validation purpose
+               private KeyedHashAlgorithm ValidationTemplate {
+                       get {
+                               if (validation_template == null)
+                                       validation_template = GetValidationAlgorithm ();
+                               return validation_template;
+                       }
+               }
+
+               internal byte [] GetValidationKey ()
+               {
+                       if (validation_key == null)
+                               SetValidationKey (ValidationKey);
+                       return validation_key;
+               }
+
+               // key can be expended for HMAC - i.e. a small key, e.g. 32 bytes, is still accepted as valid
+               // the HMAC class already deals with keys larger than what it can use (digested to right size)
+               void SetValidationKey (string key)
+               {
+                       if ((key == null) || key.StartsWith ("AutoGenerate")) {
+                               validation_key = AutoGenerate (MachineKeyRegistryStorage.KeyType.Validation);
+                       } else {
+                               try {
+                                       validation_key = MachineKeySectionUtils.GetBytes (key, key.Length);
+                                       ValidationTemplate.Key = validation_key;
+                               }
+                               catch (CryptographicException) {
+                                       // second chance, use the key length that the HMAC really wants
+                                       try {
+                                               byte[] expanded_key = new byte [ValidationTemplate.Key.Length];
+                                               Array.Copy (validation_key, 0, expanded_key, 0, validation_key.Length);
+                                               ValidationTemplate.Key = expanded_key;
+                                               validation_key = expanded_key;
+                                       }
+                                       catch {
+                                               validation_key = null;
+                                               throw new ArgumentException ("Invalid key length");
+                                       }
+                               }
+                       }
+               }
+
+               byte[] AutoGenerate (MachineKeyRegistryStorage.KeyType type)
+               {
+                       byte[] key = null;
+#if TARGET_J2EE
+                       {
+#else
+                       try {
+                               key = MachineKeyRegistryStorage.Retrieve (type);
+
+                               // ensure the stored key is usable with the selection algorithm
+                               if (type == MachineKeyRegistryStorage.KeyType.Encryption)
+                                       DecryptionTemplate.Key = key;
+                               else if (type == MachineKeyRegistryStorage.KeyType.Validation)
+                                       ValidationTemplate.Key = key;
+                       } catch (Exception) {
+                               key = null;
+                       }
+#endif
+                       // some algorithms have special needs for key (e.g. length, parity, weak keys...) 
+                       // so we better ask them to provide a default key (than to generate/use bad ones)
+                       if (key == null) {
+                               if (type == MachineKeyRegistryStorage.KeyType.Encryption)
+                                       key = DecryptionTemplate.Key;
+                               else if (type == MachineKeyRegistryStorage.KeyType.Validation)
+                                       key = ValidationTemplate.Key;
+#if !TARGET_J2EE
+                               MachineKeyRegistryStorage.Store (key, type);
+#endif
+                       }
+                       return key;
+               }
        }
 }
 
index 1f733791a5fd32c0998b391026dc82e6e8ec86d4..51fc6614ff50dbb94cb786a1b39dbab7c47953ac 100644 (file)
@@ -3,8 +3,9 @@
 //
 // Authors:
 //     Chris Toshok (toshok@ximian.com)
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
-// (c) Copyright 2005 Novell, Inc (http://www.novell.com)
+// (c) Copyright 2005, 2010 Novell, Inc (http://www.novell.com)
 //
 
 //
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.ComponentModel;
 using System.Configuration;
+using System.Configuration.Provider;
 using System.Security.Cryptography;
+using System.Text;
 
 #if NET_2_0
 
 namespace System.Web.Configuration {
 
-       internal static class MachineKeySectionUtils
-       {
-               static byte [] autogenerated;
-               static byte [] autogenerated_decrypt;
-               static byte[] decryption_key;
-               static byte[] decryption_key_192bits;
-               static byte[] validation_key;
-
-               internal static void AutoGenKeys ()
-               {
-#if TARGET_J2EE
-                       {
-#else
-                       try {
-                               if (autogenerated == null)
-                                       autogenerated = MachineKeyRegistryStorage.Retrieve (
-                                               MachineKeyRegistryStorage.KeyType.Validation);
-                               if (autogenerated_decrypt == null)
-                                       autogenerated_decrypt = MachineKeyRegistryStorage.Retrieve (
-                                               MachineKeyRegistryStorage.KeyType.Encryption);
-                       } catch (Exception) {
-#endif
-                               // Fall back to old method
-                               RandomNumberGenerator rng = RandomNumberGenerator.Create ();
-                               
-                               if (autogenerated == null) {
-                                       autogenerated = new byte [64];
-                                       rng.GetBytes (autogenerated);
-                               }
-
-                               if (autogenerated_decrypt == null) {
-                                       autogenerated_decrypt = new byte [64];
-                                       rng.GetBytes (autogenerated_decrypt);
-                               }
-                       }
-               }
-
+       internal static class MachineKeySectionUtils {
                static byte ToHexValue (char c, bool high)
                {
                        byte v;
@@ -91,7 +57,7 @@ namespace System.Web.Configuration {
 
                        return v;
                }
-               
+
                internal static byte [] GetBytes (string key, int len)
                {
                        byte [] result = new byte [len / 2];
@@ -101,85 +67,246 @@ namespace System.Web.Configuration {
                        return result;
                }
 
-               static byte [] MakeKey (string key, bool decryption) //, out bool isolate)
+               static public string GetHexString (byte [] bytes)
                {
-                       if (key == null || key.StartsWith ("AutoGenerate")){
-                               //isolate = key.IndexOf ("IsolateApps") != 1;
-                               AutoGenKeys ();
-                               return (decryption) ? autogenerated_decrypt : autogenerated;
+                       StringBuilder sb = new StringBuilder (bytes.Length * 2);
+                       int letterPart = 55;
+                       const int numberPart = 48;
+                       for (int i = 0; i < bytes.Length; i++) {
+                               int tmp = (int) bytes [i];
+                               int second = tmp & 15;
+                               int first = (tmp >> 4) & 15;
+                               sb.Append ((char) (first > 9 ? letterPart + first : numberPart + first));
+                               sb.Append ((char) (second > 9 ? letterPart + second : numberPart + second));
                        }
+                       return sb.ToString ();
+               }
 
-                       //isolate = false;
-
-                       int len = key.Length;
-                       if (len < 40 || len > 128 || (len % 2) == 1)
-                               throw new ArgumentException ("Invalid key length");
 
-                       return GetBytes (key, len);
+               // decryption="Auto" [Auto | DES | 3DES | AES | alg:algorithm_name]
+               // http://msdn.microsoft.com/en-us/library/w8h3skw9.aspx
+               public static SymmetricAlgorithm GetDecryptionAlgorithm (string name)
+               {
+                       SymmetricAlgorithm sa = null;
+                       switch (name) {
+                       case "AES":
+                       case "Auto":
+                               sa = Rijndael.Create ();
+                               break;
+                       case "DES":
+                               sa = DES.Create ();
+                               break;
+                       case "3DES":
+                               sa = TripleDES.Create ();
+                               break;
+                       default:
+#if NET_4_0
+                               if (name.StartsWith ("alg:"))
+                                       sa = SymmetricAlgorithm.Create (name.Substring (4));
+                               else
+#endif
+                                       throw new ConfigurationErrorsException ();
+                               break;
+                       }
+                       return sa;
                }
 
-               internal static void SetDecryptionKey (string n)
+               // validation="HMACSHA256" [SHA1 | MD5 | 3DES | AES | HMACSHA256 | HMACSHA384 | HMACSHA512 | alg:algorithm_name]
+               // [1] http://msdn.microsoft.com/en-us/library/system.web.configuration.machinekeyvalidation.aspx
+               // [2] http://msdn.microsoft.com/en-us/library/w8h3skw9.aspx
+               public static KeyedHashAlgorithm GetValidationAlgorithm (MachineKeySection section)
                {
-                       decryption_key = MakeKey (n, true); //, out isolate_decryption);
-                       decryption_key_192bits = new byte [24];
-                       int count = 24;
-                       if (decryption_key.Length < 24)
-                               count = decryption_key.Length;
-                       Buffer.BlockCopy (decryption_key, 0, decryption_key_192bits, 0, count);
+                       KeyedHashAlgorithm kha = null;
+                       switch (section.Validation) {
+                       case MachineKeyValidation.MD5:
+                               kha = new HMACMD5 ();
+                               break;
+                       case MachineKeyValidation.AES:          // see link [1] or [2]
+                       case MachineKeyValidation.TripleDES:    // see link [2]
+                       case MachineKeyValidation.SHA1:
+                               kha = new HMACSHA1 ();
+                               break;
+#if NET_4_0
+                       case MachineKeyValidation.HMACSHA256:
+                               kha = new HMACSHA256 ();
+                               break;
+                       case MachineKeyValidation.HMACSHA384:
+                               kha = new HMACSHA384 ();
+                               break;
+                       case MachineKeyValidation.HMACSHA512:
+                               kha = new HMACSHA512 ();
+                               break;
+                       case MachineKeyValidation.Custom:
+                               // remove the "alg:" from the start of the string
+                               string algo = section.ValidationAlgorithm;
+                               if (algo.StartsWith ("alg:"))
+                                       kha = KeyedHashAlgorithm.Create (algo.Substring (4));
+                               break;
+#endif
+                       }
+                       return kha;
                }
 
-               internal static void SetValidationKey (string n)
+               // helpers to ease unit testing of the cryptographic code
+#if TEST
+               static byte [] decryption_key;
+               static byte [] validation_key;
+
+               static SymmetricAlgorithm GetDecryptionAlgorithm (MachineKeySection section)
                {
-                       validation_key = MakeKey (n, false); //, out isolate_validation);
+                       return GetDecryptionAlgorithm (section.Decryption);
                }
 
-               static MachineKeySection Config {
-                       get { return WebConfigurationManager.GetSection ("system.web/machineKey") as MachineKeySection; }
-               }               
-
-               internal static byte [] ValidationKeyBytes ()
+               static byte [] GetDecryptionKey (MachineKeySection section)
                {
-                       return ValidationKeyBytes (Config);
+                       if (decryption_key == null)
+                               decryption_key = GetDecryptionAlgorithm (section).Key;
+                       return decryption_key;
                }
-               
-               internal static byte [] ValidationKeyBytes (MachineKeySection section)
+
+               static byte [] GetValidationKey (MachineKeySection section)
                {
-                       if (section == null)
-                               throw new ArgumentNullException ("section");
-                       
                        if (validation_key == null)
-                               SetValidationKey (section.ValidationKey);
+                               validation_key = GetValidationAlgorithm (section).Key;
                        return validation_key;
                }
+#else
+               static SymmetricAlgorithm GetDecryptionAlgorithm (MachineKeySection section)
+               {
+                       return section.GetDecryptionAlgorithm ();
+               }
+
+               static byte[] GetDecryptionKey (MachineKeySection section)
+               {
+                       return section.GetDecryptionKey ();
+               }
 
-               internal static byte [] DecryptionKeyBytes ()
+               static byte [] GetValidationKey (MachineKeySection section)
                {
-                       return DecryptionKeyBytes (Config);
+                       return section.GetValidationKey ();
                }
-               
-               internal static byte [] DecryptionKeyBytes (MachineKeySection section)
+#endif
+
+               static public byte [] Decrypt (MachineKeySection section, byte [] encodedData)
                {
-                       if (section == null)
-                               throw new ArgumentNullException ("section");
-                       
-                       if (decryption_key == null)
-                               SetDecryptionKey (section.DecryptionKey);
-                       return decryption_key;
+                       return Decrypt (section, encodedData, 0, encodedData.Length);
                }
 
-               internal static byte [] DecryptionKey192Bits ()
+               static byte [] Decrypt (MachineKeySection section, byte [] encodedData, int offset, int length)
                {
-                       return DecryptionKey192Bits (Config);
+                       using (SymmetricAlgorithm sa = GetDecryptionAlgorithm (section)) {
+                               sa.Key = GetDecryptionKey (section);
+                               return Decrypt (sa, encodedData, offset, length);
+                       }
                }
-               
-               internal static byte [] DecryptionKey192Bits (MachineKeySection section)
+
+               static public byte [] Decrypt (SymmetricAlgorithm alg, byte [] encodedData, int offset, int length)
                {
-                       if (section == null)
-                               throw new ArgumentNullException ("section");
-                       
-                       if (decryption_key_192bits == null)
-                               SetDecryptionKey (section.DecryptionKey);
-                       return decryption_key_192bits;
+                       // alg.IV is randomly set (default behavior) and perfect for our needs
+                       // iv is the first part of the encodedPassword
+                       byte [] iv = new byte [alg.IV.Length];
+                       Array.Copy (encodedData, 0, iv, 0, iv.Length);
+                       using (ICryptoTransform decryptor = alg.CreateDecryptor (alg.Key, iv)) {
+                               try {
+                                       return decryptor.TransformFinalBlock (encodedData, iv.Length + offset, length - iv.Length);
+                               }
+                               catch (CryptographicException) {
+                                       return null;
+                               }
+                       }
+               }
+
+               static public byte [] Encrypt (MachineKeySection section, byte [] data)
+               {
+                       using (SymmetricAlgorithm sa = GetDecryptionAlgorithm (section)) {
+                               sa.Key = GetDecryptionKey (section);
+                               return Encrypt (sa, data);
+                       }
+               }
+
+               static public byte [] Encrypt (SymmetricAlgorithm alg, byte [] data)
+               {
+                       // alg.IV is randomly set (default behavior) and perfect for our needs
+                       byte [] iv = alg.IV;
+                       using (ICryptoTransform encryptor = alg.CreateEncryptor (alg.Key, iv)) {
+                               byte [] encrypted = encryptor.TransformFinalBlock (data, 0, data.Length);
+                               byte [] output = new byte [iv.Length + encrypted.Length];
+                               // note: the IV can be public, however it should not be based on the password
+                               Array.Copy (iv, 0, output, 0, iv.Length);
+                               Array.Copy (encrypted, 0, output, iv.Length, encrypted.Length);
+                               return output;
+                       }
+               }
+
+               // in           [data]
+               // return       [data][signature]
+               public static byte [] Sign (MachineKeySection section, byte [] data)
+               {
+                       return Sign (section, data, 0, data.Length);
+               }
+
+               static byte [] Sign (MachineKeySection section, byte [] data, int offset, int length)
+               {
+                       using (KeyedHashAlgorithm kha = GetValidationAlgorithm (section)) {
+                               kha.Key = GetValidationKey (section);
+                               byte [] signature = kha.ComputeHash (data, offset, length);
+                               byte [] block = new byte [length + signature.Length];
+                               Array.Copy (data, block, length);
+                               Array.Copy (signature, 0, block, length, signature.Length);
+                               return block;
+                       }
+               }
+
+               public static byte [] Verify (MachineKeySection section, byte [] data)
+               {
+                       byte [] unsigned_data = null;
+                       bool valid = true;
+                       using (KeyedHashAlgorithm kha = GetValidationAlgorithm (section)) {
+                               kha.Key = GetValidationKey (section);
+                               int signlen = kha.HashSize >> 3; // bits to bytes
+                               byte [] signature = Sign (section, data, 0, data.Length - signlen);
+                               for (int i = 0; i < signature.Length; i++) {
+                                       if (signature [i] != data [data.Length - signature.Length + i])
+                                               valid = false; // do not return (timing attack)
+                               }
+                               unsigned_data = new byte [data.Length - signlen];
+                               Array.Copy (data, 0, unsigned_data, 0, unsigned_data.Length);
+                       }
+                       return valid ? unsigned_data : null;
+               }
+
+               // do NOT sign then encrypt
+
+               public static byte [] EncryptSign (MachineKeySection section, byte [] data)
+               {
+                       byte [] encdata = Encrypt (section, data);
+                       return Sign (section, encdata);
+               }
+
+               // note: take no shortcut (timing attack) while verifying or decrypting
+               public static byte [] VerifyDecrypt (MachineKeySection section, byte [] block)
+               {
+                       bool valid = true;
+                       int signlen;
+
+                       using (KeyedHashAlgorithm kha = GetValidationAlgorithm (section)) {
+                               kha.Key = GetValidationKey (section);
+                               signlen = kha.HashSize >> 3; // bits to bytes
+                               byte [] signature = Sign (section, block, 0, block.Length - signlen);
+                               for (int i = 0; i < signature.Length; i++) {
+                                       if (signature [i] != block [block.Length - signature.Length + i])
+                                               valid = false; // do not return (timing attack)
+                               }
+                       }
+
+                       // whatever the signature continue with decryption
+                       try {
+                               byte [] decdata = Decrypt (section, block, 0, block.Length - signlen);
+                               return valid ? decdata : null;
+                       }
+                       catch {
+                               return null;
+                       }
                }
        }
 }
index a7b75150edb9174b498c05fe9fab212f452b5fb6..eb7dfe13182604e8087b2518958f239479e676cc 100644 (file)
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004, 2010 Novell, Inc (http://www.novell.com)
 //
 
-using System.Resources;
-
 namespace System.Web.Configuration
 {
 #if NET_2_0
@@ -40,7 +38,13 @@ namespace System.Web.Configuration
                MD5 = 0,
                SHA1 = 1,
                TripleDES = 2,
-               AES = 3
+               AES = 3,
+#if NET_4_0
+               HMACSHA256 = 4,
+               HMACSHA384 = 5,
+               HMACSHA512 = 6,
+               Custom = 7
+#endif
        }
 }
 
index 014ca288d1abc11d372cc2cfd4715bcaa58fcb47..698509cbf0d0ccf39a29c068a1d0780243fac150 100644 (file)
@@ -3,8 +3,9 @@
 //
 // Authors:
 //     Chris Toshok (toshok@ximian.com)
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
-// (c) Copyright 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005, 2010 Novell, Inc (http://www.novell.com)
 //
 
 //
@@ -28,7 +29,6 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.ComponentModel;
 using System.Configuration;
 using System.Globalization;
@@ -39,40 +39,76 @@ namespace System.Web.Configuration {
 
        public sealed class MachineKeyValidationConverter : ConfigurationConverterBase
        {
+#if NET_4_0
+               const string InvalidValue = "The enumeration value must be one of the following: SHA1, MD5, 3DES, AES, HMACSHA256, HMACSHA384, HMACSHA512."; 
+#else
+               const string InvalidValue = "The enumeration value must be one of the following: SHA1, MD5, 3DES, AES."; 
+#endif
                public MachineKeyValidationConverter ()
                {
                }
 
                public override object ConvertFrom (ITypeDescriptorContext ctx, CultureInfo ci, object data)
                {
-                       if ((string)data == "MD5")
+                       switch ((string) data) {
+                       case "MD5":
                                return MachineKeyValidation.MD5;
-                       else if ((string)data == "SHA1")
+                       case "SHA1":
                                return MachineKeyValidation.SHA1;
-                       else if ((string)data == "3DES")
+                       case "3DES":
                                return MachineKeyValidation.TripleDES;
-                       else if ((string)data == "AES")
+                       case "AES":
                                return MachineKeyValidation.AES;
-                       else
-                               throw new ArgumentException ("The enumeration value must be one of the following: SHA1, MD5, 3DES, AES.");
+#if NET_4_0
+                       case "HMACSHA256":
+                               return MachineKeyValidation.HMACSHA256;
+                       case "HMACSHA384":
+                               return MachineKeyValidation.HMACSHA384;
+                       case "HMACSHA512":
+                               return MachineKeyValidation.HMACSHA512;
+#endif
+                       default:
+                               throw new ArgumentException (InvalidValue);
+                       }
                }
 
                public override object ConvertTo (ITypeDescriptorContext ctx, CultureInfo ci, object value, Type type)
                {
+#if NET_4_0
+                       if ((value == null) || (value.GetType () != typeof (MachineKeyValidation)))
+                               throw new ArgumentException (InvalidValue);
+#else
                        if (value.GetType () != typeof (MachineKeyValidation)) {
                                /* MS throws this exception on an invalid */
-                               throw new FormatException ("invalid validation value");
+                               throw new FormatException (InvalidValue);
                        }                               
+#endif
 
-                       MachineKeyValidation v = (MachineKeyValidation)value;
-
-                       if (v == MachineKeyValidation.MD5) return "MD5";
-                       else if (v == MachineKeyValidation.SHA1) return "SHA1";
-                       else if (v == MachineKeyValidation.TripleDES) return "3DES";
-                       else if (v ==  MachineKeyValidation.AES) return "AES";
-                       else
+                       switch ((MachineKeyValidation) value) {
+                       case MachineKeyValidation.MD5:
+                               return "MD5";
+                       case MachineKeyValidation.SHA1:
+                               return "SHA1";
+                       case MachineKeyValidation.TripleDES:
+                               return "3DES";
+                       case MachineKeyValidation.AES:
+                               return "AES";
+#if NET_4_0
+                       case MachineKeyValidation.HMACSHA256:
+                               return "HMACSHA256";
+                       case MachineKeyValidation.HMACSHA384:
+                               return "HMACSHA384";
+                       case MachineKeyValidation.HMACSHA512:
+                               return "HMACSHA512";
+                       default:
+                               // includes MachineKeyValidation.Custom
+                               throw new ArgumentException (InvalidValue);
+#else
+                       default:
                                /* MS throws this exception on an invalid */
-                               throw new FormatException ("invalid validation value");
+                               throw new FormatException (InvalidValue);
+#endif
+                       }
                }
        }
 }
index d1cab4b00aa2688613aeffc68cd06b173b3a7002..1bc956bf5afd8bb588e2ea02e9ed56a8d7c136a7 100644 (file)
@@ -34,7 +34,6 @@ using System.Reflection;
 using System.IO;
 using System.Resources;
 using System.Collections;
-using System.Security.Cryptography;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Web.Configuration;
@@ -58,11 +57,6 @@ namespace System.Web.Handlers {
                const char QueryParamSeparator = '&';
 
                static readonly Hashtable _embeddedResources = Hashtable.Synchronized (new Hashtable ());
-#if SYSTEM_WEB_EXTENSIONS
-               static ScriptResourceHandler () {
-                       MachineKeySectionUtils.AutoGenKeys ();
-               }
-#endif
 
                static void InitEmbeddedResourcesUrls (Assembly assembly, Hashtable hashtable)
                {
@@ -93,67 +87,25 @@ namespace System.Web.Handlers {
                }
 #endif
 
-               static string GetHexString (byte [] bytes)
-               {
-                       const int letterPart = 55;
-                       const int numberPart = 48;
-                       char [] result = new char [bytes.Length * 2];
-                       for (int i = 0; i < bytes.Length; i++) {
-                               int tmp = (int) bytes [i];
-                               int second = tmp & 15;
-                               int first = (tmp >> 4) & 15;
-                               result [(i * 2)] = (char) (first > 9 ? letterPart + first : numberPart + first);
-                               result [(i * 2) + 1] = (char) (second > 9 ? letterPart + second : numberPart + second);
-                       }
-                       return new string (result);
-               }
-               
-               static byte[] GetEncryptionKey ()
-               {
-#if NET_2_0
-                       return MachineKeySectionUtils.DecryptionKey192Bits ();
-#else
-                       MachineKeyConfig config = HttpContext.GetAppConfig ("system.web/machineKey") as MachineKeyConfig;
-                       return config.DecryptionKey192Bits;
-#endif
-               }
-
-               static byte[] GetBytes (string val)
-               {
-#if NET_2_0
-                       return MachineKeySectionUtils.GetBytes (val, val.Length);
-#else
-                       return MachineKeyConfig.GetBytes (val, val.Length);
-#endif
-               }               
-               
-               static byte [] init_vector = { 0xD, 0xE, 0xA, 0xD, 0xB, 0xE, 0xE, 0xF };
-               
                static string EncryptAssemblyResource (string asmName, string resName)
                {
-                       byte[] key = GetEncryptionKey ();
                        byte[] bytes = Encoding.UTF8.GetBytes (String.Concat (asmName, ";", resName));
-                       string result;
-                       
-                       ICryptoTransform encryptor = TripleDES.Create ().CreateEncryptor (key, init_vector);
-                       result = GetHexString (encryptor.TransformFinalBlock (bytes, 0, bytes.Length));
-                       bytes = null;
-
-                       return String.Concat ("d=", result.ToLowerInvariant ());
+                       bytes = MachineKeySectionUtils.Encrypt (MachineKeySection.Config, bytes);
+                       return Convert.ToBase64String (bytes);
                }
 
                static void DecryptAssemblyResource (string val, out string asmName, out string resName)
                {
-                       byte[] key = GetEncryptionKey ();
-                       byte[] bytes = GetBytes (val);
-                       byte[] result;
+                       byte[] bytes = Convert.FromBase64String (val);
 
                        asmName = null;
                        resName = null;                 
 
-                       ICryptoTransform decryptor = TripleDES.Create ().CreateDecryptor (key, init_vector);
-                       result = decryptor.TransformFinalBlock (bytes, 0, bytes.Length);
+                       byte[] result = MachineKeySectionUtils.Decrypt (MachineKeySection.Config, bytes);
                        bytes = null;
+                       // null will be returned if, for any reason, decryption fails
+                       if (result == null)
+                               return;
 
                        string data = Encoding.UTF8.GetString (result);
                        result = null;
@@ -201,7 +153,7 @@ namespace System.Web.Handlers {
                        if (apath != String.Empty)
                                atime = String.Concat (QueryParamSeparator, "t=", File.GetLastWriteTimeUtc (apath).Ticks);
 #endif
-                       string href = HandlerFileName + "?" + EncryptAssemblyResource (aname, resourceName) + atime + extra;
+                       string href = HandlerFileName + "?d=" + EncryptAssemblyResource (aname, resourceName) + atime + extra;
 
                        HttpContext ctx = HttpContext.Current;
                        if (ctx != null && ctx.Request != null) {
index 4b9244793f4af5fbca2e4ffcb37604896ff6cab0..ac9ec767a2b7088391fa213af3063f3e9e72f27b 100644 (file)
@@ -45,9 +45,6 @@ namespace System.Web.Security
        [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        public sealed class FormsAuthentication
        {
-               const int MD5_hash_size = 16;
-               const int SHA1_hash_size = 20;
-
                static string authConfigPath = "system.web/authentication";
                static string machineKeyConfigPath = "system.web/machineKey";
 #if TARGET_J2EE
@@ -56,7 +53,6 @@ namespace System.Web.Security
                const string Forms_cookiePath = "Forms.cookiePath";
                const string Forms_timeout = "Forms.timeout";
                const string Forms_protection = "Forms.protection";
-               const string Forms_init_vector = "Forms.init_vector";
                static bool initialized
                {
                        get {
@@ -88,11 +84,6 @@ namespace System.Web.Security
                        get { return (FormsProtectionEnum) AppDomain.CurrentDomain.GetData (Forms_protection); }
                        set { AppDomain.CurrentDomain.SetData (Forms_protection, value); }
                }
-               static byte [] init_vector
-               {
-                       get { return (byte []) AppDomain.CurrentDomain.GetData (Forms_init_vector); }
-                       set { AppDomain.CurrentDomain.SetData (Forms_init_vector, value); }
-               }
                static object locker = new object ();
 #else
                static bool initialized;
@@ -101,7 +92,6 @@ namespace System.Web.Security
                static int timeout;
                static FormsProtectionEnum protection;
                static object locker = new object ();
-               static byte [] init_vector; // initialization vector used for 3DES
 #endif
 #if NET_1_1
 #if TARGET_J2EE
@@ -237,18 +227,6 @@ namespace System.Web.Security
                        return String.Compare (password, stored, caseInsensitive ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0;
                }
 
-#if NET_2_0
-               static byte [] GetDecryptionKey (MachineKeySection config)
-               {
-                       return MachineKeySectionUtils.DecryptionKey192Bits (config);
-               }
-#else
-               static byte [] GetDecryptionKey (MachineKeyConfig config)
-               {
-                       return config.DecryptionKey192Bits;
-               }
-#endif
-               
                static FormsAuthenticationTicket Decrypt2 (byte [] bytes)
                {
                        if (protection == FormsProtectionEnum.None)
@@ -259,59 +237,14 @@ namespace System.Web.Security
 #else
                        MachineKeyConfig config = HttpContext.GetAppConfig (machineKeyConfigPath) as MachineKeyConfig;
 #endif
-                       bool all = (protection == FormsProtectionEnum.All);
-
-                       byte [] result = bytes;
-                       if (all || protection == FormsProtectionEnum.Encryption) {
-                               ICryptoTransform decryptor;
-                               decryptor = TripleDES.Create ().CreateDecryptor (GetDecryptionKey (config), init_vector);
-                               result = decryptor.TransformFinalBlock (bytes, 0, bytes.Length);
-                               bytes = null;
-                       }
-
-                       if (all || protection == FormsProtectionEnum.Validation) {
-                               int count;
-                               MachineKeyValidation validationType;
-
-#if NET_2_0
-                               validationType = config.Validation;
-#else
-                               validationType = config.ValidationType;
-#endif
-                               if (validationType == MachineKeyValidation.MD5)
-                                       count = MD5_hash_size;
-                               else
-                                       count = SHA1_hash_size; // 3DES and SHA1
-
-#if NET_2_0
-                               byte [] vk = MachineKeySectionUtils.ValidationKeyBytes (config);
-#else
-                               byte [] vk = config.ValidationKey;
-#endif
-                               byte [] mix = new byte [result.Length - count + vk.Length];
-                               Buffer.BlockCopy (result, 0, mix, 0, result.Length - count);
-                               Buffer.BlockCopy (vk, 0, mix, result.Length - count, vk.Length);
-
-                               byte [] hash = null;
-                               switch (validationType) {
-                               case MachineKeyValidation.MD5:
-                                       hash = MD5.Create ().ComputeHash (mix);
-                                       break;
-                               // From MS docs: "When 3DES is specified, forms authentication defaults to SHA1"
-                               case MachineKeyValidation.TripleDES:
-                               case MachineKeyValidation.SHA1:
-                                       hash = SHA1.Create ().ComputeHash (mix);
-                                       break;
-                               }
-
-                               if (result.Length < count)
-                                       throw new ArgumentException ("Error validating ticket (length).", "encryptedTicket");
 
-                               int i, k;
-                               for (i = result.Length - count, k = 0; k < count; i++, k++) {
-                                       if (result [i] != hash [k])
-                                               throw new ArgumentException ("Error validating ticket.", "encryptedTicket");
-                               }
+                       byte [] result = null;
+                       if (protection == FormsProtectionEnum.All) {
+                               result = MachineKeySectionUtils.VerifyDecrypt (config, bytes);
+                       } else if (protection == FormsProtectionEnum.Encryption) {
+                               result = MachineKeySectionUtils.Decrypt (config, bytes);
+                       } else if (protection == FormsProtectionEnum.Validation) {
+                               result = MachineKeySectionUtils.Verify (config, bytes);
                        }
 
                        return FormsAuthenticationTicket.FromByteArray (result);
@@ -325,11 +258,8 @@ namespace System.Web.Security
                        Initialize ();
 
                        FormsAuthenticationTicket ticket;
-#if NET_2_0
-                       byte [] bytes = MachineKeySectionUtils.GetBytes (encryptedTicket, encryptedTicket.Length);
-#else
-                       byte [] bytes = MachineKeyConfig.GetBytes (encryptedTicket, encryptedTicket.Length);
-#endif
+                       byte [] bytes = Convert.FromBase64String (encryptedTicket);
+
                        try {
                                ticket = Decrypt2 (bytes);
                        } catch (Exception) {
@@ -347,57 +277,23 @@ namespace System.Web.Security
                        Initialize ();
                        byte [] ticket_bytes = ticket.ToByteArray ();
                        if (protection == FormsProtectionEnum.None)
-                               return GetHexString (ticket_bytes);
+                               return Convert.ToBase64String (ticket_bytes);
 
-                       byte [] result = ticket_bytes;
+                       byte [] result = null;
 #if NET_2_0
                        MachineKeySection config = (MachineKeySection) WebConfigurationManager.GetWebApplicationSection (machineKeyConfigPath);
 #else
                        MachineKeyConfig config = HttpContext.GetAppConfig (machineKeyConfigPath) as MachineKeyConfig;
 #endif
-                       bool all = (protection == FormsProtectionEnum.All);
-                       if (all || protection == FormsProtectionEnum.Validation) {
-                               byte [] valid_bytes = null;
-#if NET_2_0
-                               byte [] vk = MachineKeySectionUtils.ValidationKeyBytes (config);
-#else
-                               byte [] vk = config.ValidationKey;
-#endif
-                               byte [] mix = new byte [ticket_bytes.Length + vk.Length];
-                               Buffer.BlockCopy (ticket_bytes, 0, mix, 0, ticket_bytes.Length);
-                               Buffer.BlockCopy (vk, 0, mix, result.Length, vk.Length);
-
-                               switch (
-#if NET_2_0
-                                       config.Validation
-#else
-                                       config.ValidationType
-#endif
-                                       ) {
-                               case MachineKeyValidation.MD5:
-                                       valid_bytes = MD5.Create ().ComputeHash (mix);
-                                       break;
-                               // From MS docs: "When 3DES is specified, forms authentication defaults to SHA1"
-                               case MachineKeyValidation.TripleDES:
-                               case MachineKeyValidation.SHA1:
-                                       valid_bytes = SHA1.Create ().ComputeHash (mix);
-                                       break;
-                               }
-
-                               int tlen = ticket_bytes.Length;
-                               int vlen = valid_bytes.Length;
-                               result = new byte [tlen + vlen];
-                               Buffer.BlockCopy (ticket_bytes, 0, result, 0, tlen);
-                               Buffer.BlockCopy (valid_bytes, 0, result, tlen, vlen);
+                       if (protection == FormsProtectionEnum.All) {
+                               result = MachineKeySectionUtils.EncryptSign (config, ticket_bytes);
+                       } else if (protection == FormsProtectionEnum.Encryption) {
+                               result = MachineKeySectionUtils.Encrypt (config, ticket_bytes);
+                       } else if (protection == FormsProtectionEnum.Validation) {
+                               result = MachineKeySectionUtils.Sign (config, ticket_bytes);
                        }
 
-                       if (all || protection == FormsProtectionEnum.Encryption) {
-                               ICryptoTransform encryptor;
-                               encryptor = TripleDES.Create ().CreateEncryptor (GetDecryptionKey (config), init_vector);
-                               result = encryptor.TransformFinalBlock (result, 0, result.Length);
-                       }
-
-                       return GetHexString (result);
+                       return Convert.ToBase64String (result);
                }
 
                public static HttpCookie GetAuthCookie (string userName, bool createPersistentCookie)
@@ -478,21 +374,6 @@ namespace System.Web.Security
                        return returnUrl;
                }
 
-               static string GetHexString (byte [] bytes)
-               {
-                       const int letterPart = 55;
-                       const int numberPart = 48;
-                       char [] result = new char [bytes.Length * 2];
-                       for (int i = 0; i < bytes.Length; i++) {
-                               int tmp = (int) bytes [i];
-                               int second = tmp & 15;
-                               int first = (tmp >> 4) & 15;
-                               result [(i * 2)] = (char) (first > 9 ? letterPart + first : numberPart + first);
-                               result [(i * 2) + 1] = (char) (second > 9 ? letterPart + second : numberPart + second);
-                       }
-                       return new string (result);
-               }
-
                static string HashPasswordForStoringInConfigFile (string password, FormsAuthPasswordFormat passwordFormat)
                {
                        if (password == null)
@@ -512,7 +393,7 @@ namespace System.Web.Security
                                        throw new ArgumentException ("The format must be either MD5 or SHA1", "passwordFormat");
                        }
 
-                       return GetHexString (bytes);
+                       return MachineKeySectionUtils.GetHexString (bytes);
                }
                
                public static string HashPasswordForStoringInConfigFile (string password, string passwordFormat)
@@ -580,16 +461,6 @@ namespace System.Web.Security
                                }
 #endif
 
-                               // IV is 8 bytes long for 3DES
-                               init_vector = new byte [8];
-                               int len = cookieName.Length;
-                               for (int i = 0; i < 8; i++) {
-                                       if (i >= len)
-                                               break;
-
-                                       init_vector [i] = (byte) cookieName [i];
-                               }
-
                                initialized = true;
                        }
                }
index 2292db0db2fa951c55cf8755954ea3472545052a..822d194d37972746ec1f5c081b8ca8b5b78e8a38 100644 (file)
@@ -49,56 +49,32 @@ namespace System.Web.Security
                        get { return Membership.Providers; }
                }
                
-               static SymmetricAlgorithm GetAlg ()
+               static SymmetricAlgorithm GetAlgorithm ()
                {
-                       MachineKeySection section = (MachineKeySection) WebConfigurationManager.GetSection ("system.web/machineKey");
+                       MachineKeySection section = MachineKeySection.Config;
 
                        if (section.DecryptionKey.StartsWith ("AutoGenerate"))
                                throw new ProviderException ("You must explicitly specify a decryption key in the <machineKey> section when using encrypted passwords.");
 
-                       string alg_type = section.Decryption;
-                       if (alg_type == "Auto")
-                               alg_type = "AES";
+                       SymmetricAlgorithm sa = section.GetDecryptionAlgorithm ();
+                       if (sa == null)
+                               throw new ProviderException (String.Format ("Unsupported decryption attribute '{0}' in <machineKey> configuration section", section.Decryption));
 
-                       SymmetricAlgorithm alg = null;
-                       if (alg_type == "AES")
-                               alg = Rijndael.Create ();
-                       else if (alg_type == "3DES")
-                               alg = TripleDES.Create ();
-                       else
-                               throw new ProviderException (String.Format ("Unsupported decryption attribute '{0}' in <machineKey> configuration section", alg_type));
-
-                       alg.Key = MachineKeySectionUtils.DecryptionKey192Bits (section);
-                       return alg;
+                       sa.Key = section.GetDecryptionKey ();
+                       return sa;
                }
                
                public byte [] DecryptPassword (byte [] encodedPassword)
                {
-                       using (SymmetricAlgorithm alg = GetAlg ()) {
-                               // alg.Key is set in GetAlg based on web.config
-                               // iv is the first part of the encodedPassword
-                               byte [] iv = new byte [alg.IV.Length];
-                               Array.Copy (encodedPassword, 0, iv, 0, iv.Length);
-                               using (ICryptoTransform decryptor = alg.CreateDecryptor (alg.Key, iv)) {
-                                       return decryptor.TransformFinalBlock (encodedPassword, iv.Length, encodedPassword.Length - iv.Length);
-                               }
+                       using (SymmetricAlgorithm sa = GetAlgorithm ()) {
+                               return MachineKeySectionUtils.Decrypt (sa, encodedPassword, 0, encodedPassword.Length);
                        }
                }
 
                public byte[] EncryptPassword (byte[] password)
                {
-                       using (SymmetricAlgorithm alg = GetAlg ()) {
-                               // alg.Key is set in GetAlg based on web.config
-                               // alg.IV is randomly set (default behavior) and perfect for our needs
-                               byte [] iv = alg.IV;
-                               using (ICryptoTransform encryptor = alg.CreateEncryptor (alg.Key, iv)) {
-                                       byte [] encrypted = encryptor.TransformFinalBlock (password, 0, password.Length);
-                                       byte [] output = new byte [iv.Length + encrypted.Length];
-                                       // note: the IV can be public, however it should not be based on the password
-                                       Array.Copy (iv, 0, output, 0, iv.Length);
-                                       Array.Copy (encrypted, 0, output, iv.Length, encrypted.Length);
-                                       return output;
-                               }
+                       using (SymmetricAlgorithm sa = GetAlgorithm ()) {
+                               return MachineKeySectionUtils.Encrypt (sa, password);
                        }
                }
        }
index ed23ec7ba7938bd9b09b309293126a51ed09815e..8840d2ceeada225151e23fc251388ae87a0ea4a6 100644 (file)
@@ -29,7 +29,6 @@
 //
 
 using System.Collections.Specialized;
-using System.Security.Cryptography;
 using System.Security.Permissions;
 using System.Security.Principal;
 using System.Web.Configuration;
@@ -146,42 +145,16 @@ namespace System.Web.Security {
 
                        CookieProtection cookieProtection = RoleManagerConfig.CookieProtection;
 
-                       if (cookieProtection == CookieProtection.None)
-                               return GetBase64FromBytes (ticket.GetBuffer (), 0, (int) ticket.Position);
-                       
-                       if (cookieProtection == CookieProtection.All || cookieProtection == CookieProtection.Validation) {
-
-                               byte [] hashBytes = null;
-                               byte [] validationBytes = MachineKeySectionUtils.ValidationKeyBytes (MachineConfig);
-                               writer.Write (validationBytes);
-
-                               switch (MachineConfig.Validation) {
-                                       case MachineKeyValidation.MD5:
-                                               hashBytes = MD5.Create ().ComputeHash (ticket.GetBuffer (), 0, (int) ticket.Position);
-                                               break;
-
-                                       case MachineKeyValidation.TripleDES:
-                                       case MachineKeyValidation.SHA1:
-                                               hashBytes = SHA1.Create ().ComputeHash (ticket.GetBuffer (), 0, (int) ticket.Position);
-                                               break;
-                               }
-
-                               writer.Seek (-validationBytes.Length, SeekOrigin.Current);
-                               writer.Write (hashBytes);
-                       }
-
-                       byte [] ticketBytes = null;
-                       if (cookieProtection == CookieProtection.All || cookieProtection == CookieProtection.Encryption) {
-                               ICryptoTransform enc;
-                               enc = TripleDES.Create ().CreateEncryptor (MachineKeySectionUtils.DecryptionKey192Bits (MachineConfig),
-                                                                          InitVector);
-                               ticketBytes = enc.TransformFinalBlock (ticket.GetBuffer (), 0, (int) ticket.Position);
+                       byte[] ticket_data = ticket.GetBuffer ();
+                       if (cookieProtection == CookieProtection.All) {
+                               ticket_data = MachineKeySectionUtils.EncryptSign (MachineConfig, ticket_data);
+                       } else if (cookieProtection == CookieProtection.Encryption) {
+                               ticket_data = MachineKeySectionUtils.Encrypt (MachineConfig, ticket_data);
+                       } else if (cookieProtection == CookieProtection.Validation) {
+                               ticket_data = MachineKeySectionUtils.Sign (MachineConfig, ticket_data);
                        }
 
-                       if (ticketBytes == null)
-                               return GetBase64FromBytes (ticket.GetBuffer (), 0, (int) ticket.Position);
-                       else
-                               return GetBase64FromBytes (ticketBytes, 0, ticketBytes.Length);
+                       return GetBase64FromBytes (ticket_data, 0, ticket_data.Length);
                }
 
                void DecryptTicket (string encryptedTicket)
@@ -193,45 +166,18 @@ namespace System.Web.Security {
                        byte [] decryptedTicketBytes = null;
 
                        CookieProtection cookieProtection = RoleManagerConfig.CookieProtection;
-                       if (cookieProtection == CookieProtection.All || cookieProtection == CookieProtection.Encryption) {
-                               ICryptoTransform decryptor;
-                               decryptor = TripleDES.Create ().CreateDecryptor (
-                                       MachineKeySectionUtils.DecryptionKey192Bits (MachineConfig),
-                                       InitVector);
-                               decryptedTicketBytes = decryptor.TransformFinalBlock (ticketBytes, 0, ticketBytes.Length);
-                       }
-                       else
-                               decryptedTicketBytes = ticketBytes;
-
-                       if (cookieProtection == CookieProtection.All || cookieProtection == CookieProtection.Validation) {
-                               byte [] validationBytes = MachineKeySectionUtils.ValidationKeyBytes (MachineConfig);
-                               byte [] rolesWithValidationBytes = null;
-                               byte [] tmpValidation = null;
 
-                               int hashSize = (MachineConfig.Validation == MachineKeyValidation.MD5) ? 16 : 20; //md5 is 16 bytes, sha1 is 20 bytes
-
-                               rolesWithValidationBytes = new byte [decryptedTicketBytes.Length - hashSize + validationBytes.Length];
-
-                               Buffer.BlockCopy (decryptedTicketBytes, 0, rolesWithValidationBytes, 0, decryptedTicketBytes.Length - hashSize);
-                               Buffer.BlockCopy (validationBytes, 0, rolesWithValidationBytes, decryptedTicketBytes.Length - hashSize, validationBytes.Length);
-
-                               switch (MachineConfig.Validation) {
-                                       case MachineKeyValidation.MD5:
-                                               tmpValidation = MD5.Create ().ComputeHash (rolesWithValidationBytes);
-                                               break;
-
-                                       case MachineKeyValidation.TripleDES:
-                                       case MachineKeyValidation.SHA1:
-                                               tmpValidation = SHA1.Create ().ComputeHash (rolesWithValidationBytes);
-                                               break;
-                               }
-                               for (int i = 0; i < tmpValidation.Length; i++) {
-                                       if (i >= decryptedTicketBytes.Length ||
-                                               tmpValidation [i] != decryptedTicketBytes [i + decryptedTicketBytes.Length - hashSize])
-                                               throw new HttpException ("ticket validation failed");
-                               }
+                       if (cookieProtection == CookieProtection.All) {
+                               decryptedTicketBytes = MachineKeySectionUtils.VerifyDecrypt (MachineConfig, ticketBytes);
+                       } else if (cookieProtection == CookieProtection.Encryption) {
+                               decryptedTicketBytes = MachineKeySectionUtils.Decrypt (MachineConfig, ticketBytes);
+                       } else if (cookieProtection == CookieProtection.Validation) {
+                               decryptedTicketBytes = MachineKeySectionUtils.Verify (MachineConfig, ticketBytes);
                        }
 
+                       if (decryptedTicketBytes == null)
+                               throw new HttpException ("ticket validation failed");
+
                        MemoryStream ticket = new MemoryStream (decryptedTicketBytes);
                        BinaryReader reader = new BinaryReader (ticket);
 
@@ -276,11 +222,6 @@ namespace System.Web.Security {
                                _cachedRoles.Add (r, r);
                }
 
-               byte [] InitVector
-               {
-                       get { return new byte [] { 1, 2, 3, 4, 5, 6, 7, 8 }; }
-               }
-               
                public bool CachedListChanged {
                        get { return _listChanged; }
                }
index 5242a204a23a49c9a53724481f076cff34718344..8bfac0ab21100ff5716a2de85abf48b5969f0207 100644 (file)
@@ -1048,11 +1048,22 @@ namespace System.Web.Security {
 
                                        MembershipSection section = (MembershipSection) WebConfigurationManager.GetSection ("system.web/membership");
                                        string alg_type = section.HashAlgorithmType;
-                                       if (alg_type == "") {
-                                               MachineKeySection keysection = (MachineKeySection) WebConfigurationManager.GetSection ("system.web/machineKey");
-                                               alg_type = keysection.Validation.ToString ();
+                                       if (alg_type.Length == 0) {
+                                               alg_type = MachineKeySection.Config.Validation.ToString ();
+#if NET_4_0
+                                               // support new (4.0) custom algorithms
+                                               if (alg_type.StartsWith ("alg:"))
+                                                       alg_type = alg_type.Substring (4);
+#endif
                                        }
                                        using (HashAlgorithm hash = HashAlgorithm.Create (alg_type)) {
+#if NET_4_0
+                                               // for compatibility (with 2.0) we'll allow MD5 and SHA1 not to map to HMACMD5 and HMACSHA1
+                                               // but that won't work with new (4.0) algorithms, like HMACSHA256|384|512 or custom, won't work without using the key
+                                               KeyedHashAlgorithm kha = (hash as KeyedHashAlgorithm);
+                                               if (kha != null)
+                                                       kha.Key = MachineKeySection.Config.GetValidationKey ();
+#endif
                                                hash.TransformFinalBlock (hashBytes, 0, hashBytes.Length);
                                                return Convert.ToBase64String (hash.Hash);
                                        }
index f7be4be34753d4f8bf36e5b1830256d4745cdbd8..2acff57210e70b2f1999f230438a1cf9db361550 100644 (file)
@@ -1432,7 +1432,7 @@ namespace System.Web.Security
                                case MembershipPasswordFormat.Hashed:
                                        HMACSHA1 hash = new HMACSHA1();
                                        if (machineKeyIsAutoGenerated)
-                                               hash.Key = MachineKeySectionUtils.ValidationKeyBytes ();
+                                               hash.Key = MachineKeySection.Config.GetValidationKey ();
                                        else
                                                hash.Key = HexToByte(m_MachineKey.ValidationKey);
                                        encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
index c1eac4d1e315b1f37009e05754232328506d1294..2161745bdcc7e454649cea081e2b8cdc82d1e809 100644 (file)
 
 using System.Text;
 using System.Security.Cryptography;
+using System.Web.Configuration;
 
 namespace System.Web.SessionState {
 
        internal class SessionId {
 
-               static char [] allowed = { '0', '1', '2', '3', '4', '5',
-                                                  '6', '7', '8', '9', 'A', 'B',
-                                                  'C', 'D', 'E', 'F' };
-
                internal const int IdLength = 24;
                const int half_len = IdLength / 2;
                static RandomNumberGenerator rng = RandomNumberGenerator.Create ();
@@ -48,25 +45,9 @@ namespace System.Web.SessionState {
                        lock (rng) {
                                rng.GetBytes (key);
                        }
-                       return Encode (key);
+                       return MachineKeySectionUtils.GetHexString (key);
                }
 
-               internal static string Encode (byte[] key)
-               {
-                       if (key == null)
-                               throw new ArgumentNullException ("key");
-                       if (key.Length != half_len)
-                               throw new ArgumentException (String.Concat ("key must be ", half_len.ToString (), " bytes long."));
-
-                       // Just a standard hex conversion
-                       char[] res = new char [IdLength];
-                       for (int i=0; i < half_len; i++) {
-                               int b = key [i];
-                               res [i * 2] = allowed [b >> 4];
-                               res [(i * 2) + 1] = allowed [b & 0xF];
-                       }
-                       return new String (res);
-               }
 #if !NET_2_0
                internal static string Lookup (HttpRequest request, bool cookieless)
                {
index ef8c41613f1e33a08a4bde7d1424bca725304571..6fa78b69f2450b652b121551433ba402f1e6665d 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+using System.Configuration;
 using System.IO;
-using System.Security.Cryptography;
 using System.Security.Permissions;
 using System.Text;
+using System.Web.Configuration;
 
 namespace System.Web.UI {
 
@@ -46,42 +47,62 @@ namespace System.Web.UI {
                        osf = new ObjectStateFormatter ();
                }
 
-               public LosFormatter (bool enableMac, string macKeyModifier) : this (enableMac, Convert.FromBase64String (macKeyModifier))
+               public LosFormatter (bool enableMac, string macKeyModifier)
                {
+                       osf = new ObjectStateFormatter ();
+                       if (enableMac && !String.IsNullOrEmpty (macKeyModifier)) {
+                               SetMacKey (Convert.FromBase64String (macKeyModifier));
+                       }
                }
 
-               [MonoTODO]
                public LosFormatter (bool enableMac, byte[] macKeyModifier)
                {
-                       if (enableMac)
-                               osf = new ObjectStateFormatter (macKeyModifier);
-                       else
-                               osf = new ObjectStateFormatter ();
+                       osf = new ObjectStateFormatter ();
+                       if (enableMac && (macKeyModifier != null)) {
+                               SetMacKey (macKeyModifier);
+                       }
+               }
+
+               private void SetMacKey (byte[] macKeyModifier)
+               {
+                       try {
+                               osf.Section.ValidationKey = MachineKeySectionUtils.GetHexString (macKeyModifier);
+                       }
+                       catch (ArgumentException) {
+                       }
+                       catch (ConfigurationErrorsException) {
+                               // bad key (e.g. size), default key will be used
+                       }
                }
 
                public object Deserialize (Stream stream)
                {
                        if (stream == null)
                                throw new ArgumentNullException ("stream");
+#if NET_4_0
+                       using (StreamReader sr = new StreamReader (stream)) {
+                               return Deserialize (sr.ReadToEnd ());
+                       }
+#else
                        long streamLength = -1;
                        if (stream.CanSeek)
                                streamLength = stream.Length;
-                       byte [] bytes = new byte [streamLength >= 0 ? streamLength : 2048];
                        MemoryStream ms = null;
                        if (streamLength  != -1 && (stream is MemoryStream) && stream.Position == 0) {
                                // We save allocating a new stream and reading in this case.
                                ms = (MemoryStream) stream;
                        } else {
+                               byte [] bytes = new byte [streamLength >= 0 ? streamLength : 2048];
                                ms = new MemoryStream ();
                                int n;
                                while ((n = stream.Read (bytes, 0, bytes.Length)) > 0)
                                        ms.Write (bytes, 0, n);
                                streamLength = ms.Length;
                        }
-
                        string b64 = Encoding.ASCII.GetString (ms.GetBuffer (),
                                0, (int) streamLength);
                        return Deserialize (b64);
+#endif
                }
 
                public object Deserialize (TextReader input)
@@ -109,7 +130,10 @@ namespace System.Web.UI {
                {
                        if (stream == null)
                                throw new ArgumentNullException ("stream");
-
+#if NET_4_0
+                       if (!stream.CanSeek)
+                               throw new NotSupportedException ();
+#endif
                        string b64 = SerializeToBase64 (value);
                        byte [] bytes = Encoding.ASCII.GetBytes (b64);
                        stream.Write (bytes, 0, bytes.Length);
index 1464636916f37d7f0167835c9148640b541e44a7..b7ea2a8fc90fa6a2df99d384c0c706c5ee4e0f4b 100644 (file)
@@ -44,7 +44,6 @@ using System.Text;
 using System.Web.UI.WebControls;
 using System.Web.Util;
 using System.Diagnostics;
-using System.Security.Cryptography;
 using System.Web.Configuration;
 
 namespace System.Web.UI
@@ -52,8 +51,7 @@ namespace System.Web.UI
        public sealed class ObjectStateFormatter : IFormatter, IStateFormatter
        {
                Page page;
-               HashAlgorithm algo;
-               byte [] vkey;
+               MachineKeySection section;
 
                public ObjectStateFormatter ()
                {
@@ -64,59 +62,29 @@ namespace System.Web.UI
                        this.page = page;
                }
 
-               internal ObjectStateFormatter (byte [] vkey)
-               {
-                       this.vkey = vkey;
-               }
-               
-               internal bool EnableMac {
+               bool EnableMac {
                        get {
-                               if (page == null) {
-                                       if (vkey == null)
-                                               return false;
-                                       return true;
-                               } else                          
-                                       return page.EnableViewStateMac;
+                               return (page == null) ? (section != null) : page.EnableViewStateMac;
                        }
                }
 
-               internal HashAlgorithm GetAlgo ()
-               {
-                       if (algo != null)
-                               return algo;
-                       if (!EnableMac)
-                               return null;
-                       
-                       byte [] algoKey;
-                       if (page != null) {
-                               MachineKeySection mconfig = (MachineKeySection) WebConfigurationManager.GetWebApplicationSection ("system.web/machineKey");
-                               algoKey = MachineKeySectionUtils.ValidationKeyBytes (mconfig);
-                       } else
-                               algoKey = vkey;
-
-                       algo = new HMACSHA1 (algoKey);
-                       return algo;
+               bool NeedViewStateEncryption {
+                       get {
+                               return (page == null) ? false : page.NeedViewStateEncryption;
+                       }
                }
 
-               static int ValidateInput (HashAlgorithm algo, byte [] data, int offset, int size)
-               {
-                       if (algo == null)
-                               throw new HttpException ("Unable to validate data.");
-                       
-                       int hash_size = algo.HashSize / 8;
-                       if (size != 0 && size < hash_size)
-                               throw new HttpException ("Unable to validate data.");
-
-                       int data_length = size - hash_size;
-                       MemoryStream data_stream = new MemoryStream (data, offset, data_length, false, false);
-                       byte [] hash = algo.ComputeHash (data_stream);
-                       for (int i = 0; i < hash_size; i++) {
-                               if (hash [i] != data [data_length + i])
-                                       throw new HttpException ("Unable to validate data.");
-                       }
-                       return data_length;
+               internal MachineKeySection Section {
+                       get {
+                               if (section == null)
+                                       section = (MachineKeySection) WebConfigurationManager.GetWebApplicationSection ("system.web/machineKey");
+                               return section;
+                       }
+                       set {
+                               section = value;
+                       }
                }
-               
+
                public object Deserialize (Stream inputStream)
                {
                        if (inputStream == null)
@@ -132,45 +100,50 @@ namespace System.Web.UI
                        if (inputString.Length == 0)
                                throw new ArgumentNullException ("inputString");
 
-                       byte [] buffer = Convert.FromBase64String (inputString);
-                       int length;
-                       if (buffer == null || (length = buffer.Length) == 0)
+                       byte [] data = Convert.FromBase64String (inputString);
+                       if (data == null || (data.Length) == 0)
                                throw new ArgumentNullException ("inputString");
-                       if (page != null && EnableMac)
-                               length = ValidateInput (GetAlgo (), buffer, 0, length);
 
-                       bool isEncrypted = ((int)buffer [--length] == 1)? true : false;
-                       Stream ms = new MemoryStream (buffer, 0, length, false, false);
-                       if (isEncrypted)
-                               ms = new CryptoStream (ms, page.GetCryptoTransform (CryptoStreamMode.Read), CryptoStreamMode.Read);
-                       return Deserialize (ms);
+                       if (NeedViewStateEncryption) {
+                               if (EnableMac) {
+                                       data = MachineKeySectionUtils.VerifyDecrypt (Section, data);
+                               } else {
+                                       data = MachineKeySectionUtils.Decrypt (Section, data);
+                               }
+                       } else if (EnableMac) {
+                               data = MachineKeySectionUtils.Verify (Section, data);
+                       }
+
+                       if (data == null)
+                               throw new HttpException ("Unable to validate data.");
+
+                       using (MemoryStream ms = new MemoryStream (data)) {
+                               return Deserialize (ms);
+                       }
                }
                
                public string Serialize (object stateGraph)
                {
                        if (stateGraph == null)
                                return String.Empty;
-                       
-                       MemoryStream ms = new MemoryStream ();
-                       Stream output = ms;
-                       bool needEncryption = page == null ? false : page.NeedViewStateEncryption;
-                       if (needEncryption){
-                               output = new CryptoStream (output, page.GetCryptoTransform (CryptoStreamMode.Write), CryptoStreamMode.Write);
-                       }
-                       Serialize (output, stateGraph);
-                       ms.WriteByte((byte)(needEncryption? 1 : 0));                    
-#if TRACE
-                       ms.WriteTo (File.OpenWrite (Path.GetTempFileName ()));
-#endif
-                       if (EnableMac && ms.Length > 0) {
-                               HashAlgorithm algo = GetAlgo ();
-                               if (algo != null) {
-                                       byte [] hash = algo.ComputeHash (ms.GetBuffer (), 0, (int) ms.Length);
-                                       ms.Write (hash, 0, hash.Length);
+
+                       byte[] data = null;
+                       using (MemoryStream ms = new MemoryStream ()) {
+                               Serialize (ms, stateGraph);
+                               data = ms.GetBuffer ();
+                       }
+
+                       if (NeedViewStateEncryption) {
+                               if (EnableMac) {
+                                       data = MachineKeySectionUtils.EncryptSign (Section, data);
+                               } else {
+                                       data = MachineKeySectionUtils.Encrypt (Section, data);
                                }
-                               
+                       } else if (EnableMac) {
+                               data = MachineKeySectionUtils.Sign (Section, data);
                        }
-                       return Convert.ToBase64String (ms.GetBuffer (), 0, (int) ms.Length);
+                       
+                       return Convert.ToBase64String (data, 0, data.Length);
                }
                
                public void Serialize (Stream outputStream, object stateGraph)
index fa6159c6a6dbaec5ccceacb474c845a6133663c0..b44b3180643aae23075bf2aa611d93ad6f1558c4 100644 (file)
@@ -39,7 +39,6 @@ using System.ComponentModel.Design;
 using System.ComponentModel.Design.Serialization;
 using System.Globalization;
 using System.IO;
-using System.Security.Cryptography;
 using System.Security.Permissions;
 using System.Security.Principal;
 using System.Text;
@@ -2375,74 +2374,6 @@ public partial class Page : TemplateControl, IHttpHandler
                controlRegisteredForViewStateEncryption = true;
        }
 
-       static byte [] AES_IV = null;
-       static byte [] TripleDES_IV = null;
-       static object locker = new object ();
-       static bool isEncryptionInitialized = false;
-
-       static void InitializeEncryption () 
-       {
-               if (isEncryptionInitialized)
-                       return;
-
-               lock (locker) {
-                       if (isEncryptionInitialized)
-                               return;
-
-                       string iv_string = "0BA48A9E-736D-40f8-954B-B2F62241F282";
-                       AES_IV = new byte [16];
-                       TripleDES_IV = new byte [8];
-
-                       int i;
-                       for (i = 0; i < AES_IV.Length; i++) {
-                               AES_IV [i] = (byte) iv_string [i];
-                       }
-
-                       for (i = 0; i < TripleDES_IV.Length; i++) {
-                               TripleDES_IV [i] = (byte) iv_string [i];
-                       }
-
-                       isEncryptionInitialized = true;
-               }
-       }
-
-       internal ICryptoTransform GetCryptoTransform (CryptoStreamMode cryptoStreamMode) 
-       {
-               ICryptoTransform transform = null;
-               MachineKeySection config = (MachineKeySection) WebConfigurationManager.GetSection (machineKeyConfigPath);
-               byte [] vk = MachineKeySectionUtils.ValidationKeyBytes (config);
-
-               switch (config.Validation) {
-                       case MachineKeyValidation.SHA1:
-                               transform = SHA1.Create ();
-                               break;
-
-                       case MachineKeyValidation.MD5:
-                               transform = MD5.Create ();
-                               break;
-
-                       case MachineKeyValidation.AES:
-                               InitializeEncryption ();
-                               if (cryptoStreamMode == CryptoStreamMode.Read){
-                                       transform = Rijndael.Create().CreateDecryptor(vk, AES_IV);
-                               } else {
-                                       transform = Rijndael.Create().CreateEncryptor(vk, AES_IV);
-                               }
-                               break;
-
-                       case MachineKeyValidation.TripleDES:
-                               InitializeEncryption ();
-                               if (cryptoStreamMode == CryptoStreamMode.Read){
-                                       transform = TripleDES.Create().CreateDecryptor(vk, TripleDES_IV);
-                               } else {
-                                       transform = TripleDES.Create().CreateEncryptor(vk, TripleDES_IV);
-                               }
-                               break;
-               }
-
-               return transform;
-       }
-
        internal bool NeedViewStateEncryption {
                get {
                        return (ViewStateEncryptionMode == ViewStateEncryptionMode.Always ||
index 776cbb3233f021df1f7b520df140c9a108ee5724..62849bb92589ee79d6c30f945c83ba31100b36b7 100644 (file)
@@ -180,6 +180,7 @@ System.Web.Configuration_2.0/IConfigMapPath.cs
 System.Web.Configuration_2.0/IConfigMapPathFactory.cs
 System.Web.Configuration_2.0/IRemoteWebConfigurationHostServer.cs
 System.Web.Configuration_2.0/LowerCaseStringConverter.cs
+System.Web.Configuration_2.0/MachineKeyCompatibilityMode.cs
 System.Web.Configuration_2.0/MachineKeyRegistryStorage.cs
 System.Web.Configuration_2.0/MachineKeySection.cs
 System.Web.Configuration_2.0/MachineKeySectionUtils.cs
index 8e5a166ff7b43649dc3a5ce14dca7ec6134c5d1d..e40e4256b9d403434ba6ef74df81fb09afc4a73d 100644 (file)
@@ -88,6 +88,8 @@ System.Web.Configuration/CustomErrorCollectionTest.cs
 System.Web.Configuration/DeploymentSectionTest.cs
 System.Web.Configuration/GlobalizationSectionTest.cs
 System.Web.Configuration/HostingEnvironmentSectionTest.cs
+System.Web.Configuration/MachineKeySectionTest.cs
+System.Web.Configuration/MachineKeySectionUtilsTest.cs
 System.Web.Configuration/MachineKeyValidationConverterTest.cs
 System.Web.Configuration/NullableStringValidatorTest.cs
 System.Web.Configuration/ProfilePropertySettingsTest.cs
diff --git a/mcs/class/System.Web/Test/System.Web.Configuration/MachineKeySectionTest.cs b/mcs/class/System.Web/Test/System.Web.Configuration/MachineKeySectionTest.cs
new file mode 100644 (file)
index 0000000..2fa37d4
--- /dev/null
@@ -0,0 +1,157 @@
+//
+// Unit tests for MachineKeySection
+//
+// Author:
+//     Sebastien Pouliot  <sebastien@ximian.com>
+//
+// Copyright (C) 2010 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
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Configuration;
+using System.Web.Configuration;
+using NUnit.Framework;
+
+namespace MonoTests.System.Web.Configuration {
+
+       [TestFixture]
+       public class MachineKeySectionTest {
+
+               [Test]
+               public void DefaultValues ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+#if NET_4_0
+                       Assert.AreEqual (MachineKeyCompatibilityMode.Framework20SP1, section.CompatibilityMode, "CompatibilityMode");
+#endif
+                       Assert.AreEqual ("Auto", section.Decryption, "Decryption");
+                       Assert.AreEqual ("AutoGenerate,IsolateApps", section.DecryptionKey, "DecryptionKey");
+#if NET_4_0
+                       Assert.AreEqual (MachineKeyValidation.HMACSHA256, section.Validation, "Validation");
+                       Assert.AreEqual ("HMACSHA256", section.ValidationAlgorithm, "ValidationAlgorithm");
+#else
+                       Assert.AreEqual (MachineKeyValidation.SHA1, section.Validation, "Validation");
+#endif
+                       Assert.AreEqual ("AutoGenerate,IsolateApps", section.ValidationKey, "ValidationKey");
+               }
+
+               [Test]
+#if NET_4_0
+               [ExpectedException (typeof (NullReferenceException))]
+#else
+               [ExpectedException (typeof (ConfigurationErrorsException))]
+#endif
+               public void Decryption_Null ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Decryption = null;
+               }
+
+               [Test]
+               [ExpectedException (typeof (ConfigurationErrorsException))]
+               public void Decryption_Empty ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Decryption = String.Empty;
+               }
+#if NET_4_0
+               [Test]
+               public void Decryption_RC2 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       Assert.AreEqual ("Auto", section.Decryption, "before");
+
+                       section.Decryption = "alg:RC2";
+                       Assert.AreEqual ("alg:RC2", section.Decryption, "after");
+               }
+
+               [Test]
+               public void Decryption_InvalidName ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       Assert.AreEqual ("Auto", section.Decryption, "before");
+
+                       section.Decryption = "alg:UnexistingType";
+                       // looks like the problem is found (much) later
+                       Assert.AreEqual ("alg:UnexistingType", section.Decryption, "Decryption");
+               }
+
+               [Test]
+               public void ValidationAlgorithm_Null ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.ValidationAlgorithm = null;
+                       Assert.AreEqual ("HMACSHA256", section.ValidationAlgorithm, "ValidationAlgorithm");
+               }
+
+               [Test]
+               [ExpectedException (typeof (ArgumentException))]
+               public void ValidationAlgorithm_Empty ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.ValidationAlgorithm = String.Empty;
+               }
+
+               [Test]
+               [ExpectedException (typeof (ArgumentException))]
+               public void Validation_Custom ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.Custom;
+                       // cannot be set directly
+               }
+
+               [Test]
+               [ExpectedException (typeof (ArgumentException))]
+               public void ValidationAlgorithm ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.ValidationAlgorithm = "HMACRIPEMD160";
+                       // syntax is: alg:something-deriving-from-KeyedHashAlgorithm
+               }
+
+               [Test]
+               public void ValidationAlgorithm_RIPEMD160 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       Assert.AreEqual (MachineKeyValidation.HMACSHA256, section.Validation, "before");
+
+                       section.ValidationAlgorithm = "alg:HMACRIPEMD160";
+                       Assert.AreEqual (MachineKeyValidation.Custom, section.Validation, "after");
+                       Assert.AreEqual ("alg:HMACRIPEMD160", section.ValidationAlgorithm, "ValidationAlgorithm");
+               }
+
+               [Test]
+               public void ValidationAlgorithm_InvalidName ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       Assert.AreEqual (MachineKeyValidation.HMACSHA256, section.Validation, "before");
+
+                       section.ValidationAlgorithm = "alg:UnexistingType";
+                       // looks like the problem is found (much) later
+                       Assert.AreEqual (MachineKeyValidation.Custom, section.Validation, "after");
+                       Assert.AreEqual ("alg:UnexistingType", section.ValidationAlgorithm, "ValidationAlgorithm");
+               }
+#endif
+       }
+}
+
diff --git a/mcs/class/System.Web/Test/System.Web.Configuration/MachineKeySectionUtilsTest.cs b/mcs/class/System.Web/Test/System.Web.Configuration/MachineKeySectionUtilsTest.cs
new file mode 100644 (file)
index 0000000..44a5166
--- /dev/null
@@ -0,0 +1,309 @@
+//
+// Unit tests for MachineKeySectionUtils (internals)
+//
+// Author:
+//     Sebastien Pouliot  <sebastien@ximian.com>
+//
+// Copyright (C) 2010 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
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Web.Configuration;
+using NUnit.Framework;
+
+namespace MonoTests.System.Web.Configuration {
+
+       [TestFixture]
+       public class MachineKeySectionUtilsTest {
+
+               public void Encrypt_RoundTrip (MachineKeySection section)
+               {
+                       byte [] data = new byte [14];
+                       byte [] encdata = MachineKeySectionUtils.Encrypt (section, data);
+                       byte [] decdata = MachineKeySectionUtils.Decrypt (section, encdata);
+                       Assert.AreEqual (data, decdata, "roundtrip");
+
+                       // changing length (missing first byte)
+                       byte [] cut = new byte [encdata.Length - 1];
+                       Array.Copy (encdata, 1, cut, 0, cut.Length);
+                       Assert.IsNull (MachineKeySectionUtils.Decrypt (section, cut), "bad length");
+
+                       // changing last byte (padding)
+                       byte be = encdata [encdata.Length - 1];
+                       encdata [encdata.Length - 1] ^= (byte) (be + 1);
+                       Assert.IsNull (MachineKeySectionUtils.Decrypt (section, encdata), "bad padding");
+               }
+
+               [Test]
+               public void Encrypt_RoundTrip_Default ()
+               {
+                       Encrypt_RoundTrip (new MachineKeySection ());
+               }
+
+               [Test]
+               public void Encrypt_RoundTrip_AES ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.AES;
+                       Encrypt_RoundTrip (section);
+               }
+
+               [Test]
+               public void Encrypt_RoundTrip_TripleDES ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.TripleDES;
+                       Encrypt_RoundTrip (section);
+               }
+
+               [Test]
+               public void Encrypt_RoundTrip_MD5 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.MD5;
+                       Encrypt_RoundTrip (section);
+               }
+
+               [Test]
+               public void Encrypt_RoundTrip_SHA1 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.SHA1;
+                       Encrypt_RoundTrip (section);
+               }
+#if NET_4_0
+               [Test]
+               public void Encrypt_RoundTrip_HMACSHA256 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.HMACSHA256;
+                       EncryptSign_RoundTrip (section);
+               }
+
+               [Test]
+               public void Encrypt_RoundTrip_HMACSHA384 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.HMACSHA384;
+                       EncryptSign_RoundTrip (section);
+               }
+
+               [Test]
+               public void Encrypt_RoundTrip_HMACSHA512 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.HMACSHA512;
+                       EncryptSign_RoundTrip (section);
+               }
+
+               [Test]
+               public void Encrypt_RoundTrip_Custom_RIPEMD160 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.ValidationAlgorithm = "alg:HMACRIPEMD160";
+                       EncryptSign_RoundTrip (section);
+               }
+#endif
+               public void EncryptSign_RoundTrip (MachineKeySection section)
+               {
+                       byte [] data = new byte [14];
+                       byte [] block = MachineKeySectionUtils.EncryptSign (section, data);
+                       byte [] decdata = MachineKeySectionUtils.VerifyDecrypt (section, block);
+                       Assert.AreEqual (data, decdata, "roundtrip");
+
+                       // changing a byte of the data
+                       byte b0 = block [0];
+                       block [0] ^= b0;
+                       Assert.IsNull (MachineKeySectionUtils.VerifyDecrypt (section, block), "bad data");
+                       block [0] = b0;
+
+                       // changing a byte of the signature
+                       byte be = block [block.Length - 1];
+                       block [block.Length - 1] ^= (byte) (be + 1);
+                       Assert.IsNull (MachineKeySectionUtils.VerifyDecrypt (section, block), "bad signature");
+               }
+
+               [Test]
+               public void EncryptSign_RoundTrip_Default ()
+               {
+                       EncryptSign_RoundTrip (new MachineKeySection ());
+               }
+
+               [Test]
+               public void EncryptSign_RoundTrip_AES ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.AES;
+                       EncryptSign_RoundTrip (section);
+               }
+
+               [Test]
+               public void EncryptSign_RoundTrip_TripleDES ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.TripleDES;
+                       EncryptSign_RoundTrip (section);
+               }
+
+               [Test]
+               public void EncryptSign_RoundTrip_MD5 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.MD5;
+                       EncryptSign_RoundTrip (section);
+               }
+
+               [Test]
+               public void EncryptSign_RoundTrip_SHA1 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.SHA1;
+                       EncryptSign_RoundTrip (section);
+               }
+#if NET_4_0
+               [Test]
+               public void EncryptSign_RoundTrip_HMACSHA256 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.HMACSHA256;
+                       EncryptSign_RoundTrip (section);
+               }
+
+               [Test]
+               public void EncryptSign_RoundTrip_HMACSHA384 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.HMACSHA384;
+                       EncryptSign_RoundTrip (section);
+               }
+
+               [Test]
+               public void EncryptSign_RoundTrip_HMACSHA512 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.HMACSHA512;
+                       EncryptSign_RoundTrip (section);
+               }
+
+               [Test]
+               public void EncryptSign_RoundTrip_Custom_RIPEMD160 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.ValidationAlgorithm = "alg:HMACRIPEMD160";
+                       EncryptSign_RoundTrip (section);
+               }
+#endif
+               public void Validation_RoundTrip (MachineKeySection section)
+               {
+                       byte [] data = new byte [] { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 };
+                       byte [] block = MachineKeySectionUtils.Sign (section, data);
+                       Assert.AreEqual (data, MachineKeySectionUtils.Verify (section, block), "OK");
+
+                       // changing last byte
+                       for (int i = 0; i < data.Length; i++) {
+                               byte b = block [i];
+                               block [i] ^= 0xFF;
+                               Assert.IsNull (MachineKeySectionUtils.Verify (section, block), "bad-" + i.ToString ());
+                               block [i] = b;
+                       }
+               }
+
+               [Test]
+               public void Validation_RoundTrip_Default ()
+               {
+                       Validation_RoundTrip (new MachineKeySection ());
+               }
+
+               [Test]
+               public void Validation_RoundTrip_AES ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.AES;
+                       Validation_RoundTrip (section);
+               }
+
+               [Test]
+               public void Validation_RoundTrip_TripleDES ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.TripleDES;
+                       Validation_RoundTrip (section);
+               }
+
+               [Test]
+               public void Validation_RoundTrip_MD5 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.MD5;
+                       Validation_RoundTrip (section);
+               }
+
+               [Test]
+               public void Validation_RoundTrip_SHA1 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.SHA1;
+                       Validation_RoundTrip (section);
+               }
+
+#if NET_4_0
+               [Test]
+               public void Validation_RoundTrip_HMACSHA256 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.HMACSHA256;
+                       Validation_RoundTrip (section);
+               }
+
+               [Test]
+               public void Validation_RoundTrip_HMACSHA384 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.HMACSHA384;
+                       Validation_RoundTrip (section);
+               }
+
+               [Test]
+               public void Validation_RoundTrip_HMACSHA512 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.Validation = MachineKeyValidation.HMACSHA512;
+                       Validation_RoundTrip (section);
+               }
+
+               [Test]
+               public void Validation_RoundTrip_Custom_RIPEMD160 ()
+               {
+                       MachineKeySection section = new MachineKeySection ();
+                       section.ValidationAlgorithm = "alg:HMACRIPEMD160";
+                       Validation_RoundTrip (section);
+               }
+#endif
+               [Test]
+               public void GetHexString ()
+               {
+                       Assert.AreEqual ("DEADC0DE", MachineKeySectionUtils.GetHexString (new byte [] { 0xde, 0xad, 0xc0, 0xde }), "deadcode");
+               }
+       }
+}
+
index 0ed9243b95bbe13589488445815466c3bb4d3970..3df6febb6fc869a04394fc35ccaa508517278c33 100644 (file)
@@ -2,10 +2,11 @@
 // System.Web.Configuration.MachineKeyValidationConverterTest.cs - Unit tests
 // for System.Web.Configuration.MachineKeyValidationConverter.
 //
-// Author:
+// Authors:
 //     Chris Toshok  <toshok@ximian.com>
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
-// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005, 2010 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
@@ -35,8 +36,7 @@ using NUnit.Framework;
 
 namespace MonoTests.System.Web.Configuration {
        [TestFixture]
-       public class MachineKeyValidationConverterTest
-       {
+       public class MachineKeyValidationConverterTest {
                [Test]
                public void CanConvertFrom ()
                {
@@ -66,10 +66,46 @@ namespace MonoTests.System.Web.Configuration {
                        object o;
 
                        o = cv.ConvertFrom (null, null, "MD5");
-                       Assert.AreEqual (typeof (MachineKeyValidation), o.GetType(), "A1");
-                       Assert.AreEqual ("MD5", o.ToString(), "A2");
+                       Assert.AreEqual (typeof (MachineKeyValidation), o.GetType (), "typeof");
+                       Assert.AreEqual ("MD5", o.ToString (), "MD5");
+
+                       o = cv.ConvertFrom (null, null, "SHA1");
+                       Assert.AreEqual ("SHA1", o.ToString (), "SHA1");
+
+                       // 3DES in, TripleDES out
+                       o = cv.ConvertFrom (null, null, "3DES");
+                       Assert.AreEqual ("TripleDES", o.ToString (), "3DES");
+
                        o = cv.ConvertFrom (null, null, "AES");
-                       Assert.AreEqual ("AES", o.ToString(), "A3");
+                       Assert.AreEqual ("AES", o.ToString (), "AES");
+#if NET_4_0
+                       o = cv.ConvertFrom (null, null, "HMACSHA256");
+                       Assert.AreEqual ("HMACSHA256", o.ToString (), "HMACSHA256");
+
+                       o = cv.ConvertFrom (null, null, "HMACSHA384");
+                       Assert.AreEqual ("HMACSHA384", o.ToString (), "HMACSHA384");
+
+                       o = cv.ConvertFrom (null, null, "HMACSHA512");
+                       Assert.AreEqual ("HMACSHA512", o.ToString (), "HMACSHA512");
+#endif
+               }
+
+#if NET_4_0
+               [Test]
+               [ExpectedException (typeof (ArgumentException))]
+               public void ConvertFrom_Custom ()
+               {
+                       MachineKeyValidationConverter cv = new MachineKeyValidationConverter ();
+                       cv.ConvertFrom (null, null, "Custom");
+               }
+#endif
+
+               [Test]
+               [ExpectedException (typeof (ArgumentException))]
+               public void ConvertFrom_CaseSensitive ()
+               {
+                       MachineKeyValidationConverter cv = new MachineKeyValidationConverter ();
+                       cv.ConvertFrom (null, null, "sha1");
                }
 
                [Test]
@@ -91,10 +127,29 @@ namespace MonoTests.System.Web.Configuration {
                        Assert.AreEqual ("MD5", cv.ConvertTo (null, null, MachineKeyValidation.MD5, typeof (string)), "A1");
                        Assert.AreEqual ("SHA1", cv.ConvertTo (null, null, MachineKeyValidation.SHA1, typeof (string)), "A2");
                        Assert.AreEqual ("3DES", cv.ConvertTo (null, null, MachineKeyValidation.TripleDES, typeof (string)), "A3");
+#if NET_4_0
+                       Assert.AreEqual ("HMACSHA256", cv.ConvertTo (null, null, MachineKeyValidation.HMACSHA256, typeof (string)), "HMACSHA256");
+                       Assert.AreEqual ("HMACSHA384", cv.ConvertTo (null, null, MachineKeyValidation.HMACSHA384, typeof (string)), "HMACSHA384");
+                       Assert.AreEqual ("HMACSHA512", cv.ConvertTo (null, null, MachineKeyValidation.HMACSHA512, typeof (string)), "HMACSHA512");
+#endif
+               }
+
+#if NET_4_0
+               [Test]
+               [ExpectedException (typeof (ArgumentException))]
+               public void ConvertTo_Custom ()
+               {
+                       MachineKeyValidationConverter cv = new MachineKeyValidationConverter ();
+                       cv.ConvertTo (null, null, MachineKeyValidation.Custom, typeof (string));
                }
+#endif
 
                [Test]
+#if NET_4_0
+               [ExpectedException (typeof (ArgumentException))]
+#else
                [ExpectedException (typeof (NullReferenceException))]
+#endif
                public void ConvertTo_NullError ()
                {
                        MachineKeyValidationConverter cv = new MachineKeyValidationConverter ();
@@ -103,7 +158,11 @@ namespace MonoTests.System.Web.Configuration {
                }
 
                [Test]
+#if NET_4_0
+               [ExpectedException (typeof (ArgumentException))]
+#else
                [ExpectedException (typeof (FormatException))]
+#endif
                public void ConvertTo_TypeError1 ()
                {
                        MachineKeyValidationConverter cv = new MachineKeyValidationConverter ();
@@ -120,6 +179,17 @@ namespace MonoTests.System.Web.Configuration {
                        Assert.AreEqual ("MD5", cv.ConvertTo (null, null, MachineKeyValidation.MD5, null), "A2");
                }
 
+               [Test]
+#if NET_4_0
+               [ExpectedException (typeof (ArgumentException))]
+#else
+               [ExpectedException (typeof (FormatException))]
+#endif
+               public void ConvertTo_TypeError3 ()
+               {
+                       MachineKeyValidationConverter cv = new MachineKeyValidationConverter ();
+                       cv.ConvertTo (null, null, (MachineKeyValidation)Int32.MinValue, typeof (string));
+               }
        }
 }
 
index 66a2ea6e4d510e174252dc6426a54004e71edb48..63b9c466ddf5e1ce260210a4d555f840688db158 100644 (file)
@@ -129,6 +129,16 @@ namespace MonoTests.System.Web.Security
                {
                        FormsAuthentication.RedirectToLoginPage (String.Empty);
                }
+
+               [Test]
+               [Category ("NotWorking")] // works on .net
+               public void Authenticate ()
+               {
+                       Assert.IsFalse (FormsAuthentication.Authenticate (null, "password"), "null,string");
+                       Assert.IsFalse (FormsAuthentication.Authenticate ("user", null), "string,null");
+                       // not throwing
+                       Assert.IsFalse (FormsAuthentication.Authenticate ("user", "password"), "string,string");
+               }
 #endif
 
                [TestFixtureTearDown]
index 0c6e069919a1b62aa7155ab83b62d75144a06dc1..67de9b948feb29ab8145e56a47004f12c013140a 100644 (file)
@@ -1,6 +1,6 @@
 //
-// MembershipUserCollectionTest.cs
-//     - Unit tests for System.Web.Security.MembershipUserCollection
+// RolePrincipalTest.cs
+//     - Unit tests for System.Web.Security.RolePrincipal
 //
 // Author:
 //     Sebastien Pouliot  <sebastien@ximian.com>
index 2889591608da915b12e9b951d27b6e28432174b4..f1d9b087c44b32dff21dd1f7d716737a15fbbd37 100644 (file)
@@ -1,10 +1,12 @@
 //
 // LosFormatterTest.cs - Unit tests for System.Web.UI.LosFormatter
 //
-// Author:
+// Authors:
 //     Gert Driesen  <drieseng@users.sourceforge.net>
+//     Sebastien Pouliot  <sebastien@ximian.com>
 //
 // Copyright (C) 2007 Gert Driesen
+// Copyright (C) 2010 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
@@ -38,6 +40,145 @@ namespace MonoTests.System.Web.UI
        [TestFixture]
        public class LosFormatterTest
        {
+               static byte [] Empty = new byte [0];
+
+               string Serialize (LosFormatter lf, object value)
+               {
+                       StringWriter sw = new StringWriter ();
+                       lf.Serialize (sw, value);
+                       return sw.ToString ();
+               }
+
+               object Deserialize (LosFormatter lf, string serializedData)
+               {
+                       return lf.Deserialize (serializedData);
+               }
+
+               string NoKeyRoundTrip (LosFormatter lf, string assertionMessage)
+               {
+                       string serializedData = Serialize (lf, "Mono");
+                       Assert.AreEqual ("Mono", (string) Deserialize (lf, serializedData), assertionMessage);
+                       return serializedData;
+               }
+
+               [Test]
+               public void Ctor_BoolByteArray ()
+               {
+                       LosFormatter lf1 = new LosFormatter (false, (byte []) null);
+                       string expected = NoKeyRoundTrip (lf1, "false, null");
+
+                       LosFormatter lf2 = new LosFormatter (true, (byte []) null);
+                       Assert.AreEqual (expected, NoKeyRoundTrip (lf2, "true, null"), "2");
+
+                       LosFormatter lf3 = new LosFormatter (false, Empty);
+                       Assert.AreEqual (expected, NoKeyRoundTrip (lf3, "false, empty"), "3");
+
+                       // an empty key is still a key - a signature is appended
+                       LosFormatter lf4 = new LosFormatter (true, Empty);
+                       string signed = NoKeyRoundTrip (lf4, "true, empty");
+                       Assert.AreNotEqual (expected, signed, "4");
+
+                       byte [] data = Convert.FromBase64String (expected);
+                       byte [] signed_data = Convert.FromBase64String (signed);
+                       Assert.IsTrue (BitConverter.ToString (signed_data).StartsWith (BitConverter.ToString (data)), "4 / same data");
+#if NET_4_0
+                       // 32 bytes == 256 bits -> match HMACSHA256 as default
+                       Assert.AreEqual (32, signed_data.Length - data.Length, "signature length");
+#else
+                       // 20 bytes == 160 bits -> match HMACSHA1 as default
+                       Assert.AreEqual (20, signed_data.Length - data.Length, "signature length");
+#endif
+               }
+
+               [Test]
+               public void Ctor_BoolString ()
+               {
+                       LosFormatter lf1 = new LosFormatter (false, (string) null);
+                       string expected = NoKeyRoundTrip (lf1, "false, null");
+
+                       LosFormatter lf2 = new LosFormatter (true, (string) null);
+                       Assert.AreEqual (expected, NoKeyRoundTrip (lf2, "true, null"), "2");
+
+                       LosFormatter lf3 = new LosFormatter (false, String.Empty);
+                       Assert.AreEqual (expected, NoKeyRoundTrip (lf3, "false, empty"), "3");
+
+                       // an empty string is not an empty key!
+                       LosFormatter lf4 = new LosFormatter (true, String.Empty);
+                       Assert.AreEqual (expected, NoKeyRoundTrip (lf4, "true, empty"), "4");
+
+                       byte [] key = new byte [32];
+                       LosFormatter lf5 = new LosFormatter (true, Convert.ToBase64String (key));
+                       string signed = NoKeyRoundTrip (lf5, "true, b64");
+                       Assert.AreNotEqual (expected, signed, "5");
+
+                       byte [] data = Convert.FromBase64String (expected);
+                       byte [] signed_data = Convert.FromBase64String (signed);
+                       Assert.IsTrue (BitConverter.ToString (signed_data).StartsWith (BitConverter.ToString (data)), "5 / same data");
+#if NET_4_0
+                       // 32 bytes == 256 bits -> match HMACSHA256 as default
+                       Assert.AreEqual (32, signed_data.Length - data.Length, "signature length");
+#else
+                       // 20 bytes == 160 bits -> match HMACSHA1 as default
+                       Assert.AreEqual (20, signed_data.Length - data.Length, "signature length");
+#endif
+               }
+
+               string SerializeOverloads (LosFormatter lf, string message)
+               {
+                       string stream_ser;
+                       using (MemoryStream ms = new MemoryStream ()) {
+                               lf.Serialize (ms, String.Empty);
+                               stream_ser = Convert.ToBase64String (ms.ToArray ());
+                       }
+
+                       string tw_ser;
+                       using (MemoryStream ms = new MemoryStream ()) {
+                               using (TextWriter tw = new StreamWriter (ms)) {
+                                       lf.Serialize (tw, String.Empty);
+                               }
+                               tw_ser = Convert.ToBase64String (ms.ToArray ());
+                       }
+
+                       Assert.AreEqual (stream_ser, tw_ser, message);
+                       return stream_ser;
+               }
+
+               [Test]
+               public void SerializeOverloads ()
+               {
+                       LosFormatter lf1 = new LosFormatter (false, (string) null);
+                       string r1 = SerializeOverloads (lf1, "false, null");
+
+                       LosFormatter lf2 = new LosFormatter (true, (string) null);
+                       string r2 = SerializeOverloads (lf2, "true, null");
+                       Assert.AreEqual (r1, r2, "r1-r2");
+
+                       LosFormatter lf3 = new LosFormatter (false, String.Empty);
+                       string r3 = SerializeOverloads (lf3, "false, empty");
+                       Assert.AreEqual (r2, r3, "r2-r3");
+
+                       // an empty string is not an empty key!
+                       LosFormatter lf4 = new LosFormatter (true, String.Empty);
+                       string r4 = SerializeOverloads (lf4, "true, empty");
+                       Assert.AreEqual (r3, r4, "r3-r4");
+
+                       byte [] key = new byte [32];
+                       LosFormatter lf5 = new LosFormatter (true, Convert.ToBase64String (key));
+                       string r5 = SerializeOverloads (lf5, "false, b64");
+                       Assert.AreNotEqual (r4, r5, "r4-r5");
+               }
+
+#if NET_4_0
+               [Test]
+               [ExpectedException (typeof (NotSupportedException))]
+               public void Deserialize_Stream_NonSeekable ()
+               {
+                       string s1 = "Hello world";
+                       NonSeekableStream ns = new NonSeekableStream ();
+                       LosFormatter lf = new LosFormatter ();
+                       lf.Serialize (ns, s1);
+               }
+#else
                [Test] // bug #411115
                public void Deserialize_Stream_NonSeekable ()
                {
@@ -49,7 +190,7 @@ namespace MonoTests.System.Web.UI
                        string s2 = lf.Deserialize (ns) as string;
                        Assert.AreEqual (s1, s2);
                }
-
+#endif
                [Test] // bug #324526
                public void Serialize ()
                {
index 58f99e6085c27999c3f85e92ac4d799e834a476e..5b3faea8731f8ca3778792e3b4b0c55dc95322b8 100644 (file)
@@ -30,6 +30,7 @@
 
 #if NET_2_0
 using System;
+using System.IO;
 using System.Reflection;
 using System.Web;
 using System.Web.UI;
@@ -115,6 +116,20 @@ namespace MonoTests.System.Web.UI
 
                        HtmlDiff.AssertAreEqual (originalHtml, renderedHtml, "#A1");
                }
+#if false
+               [Test]
+               public void SerializeOverloads ()
+               {
+                       ObjectStateFormatter osf = new ObjectStateFormatter ();
+                       string s1 = osf.Serialize (String.Empty);
+                       string s2;
+                       using (MemoryStream ms = new MemoryStream ()) {
+                               osf.Serialize (ms, String.Empty);
+                               s2 = Convert.ToBase64String (ms.ToArray ());
+                       }
+                       Assert.AreEqual (s1, s2, "identical");
+               }
+#endif
        }
 }
-#endif
\ No newline at end of file
+#endif