Merge pull request #2916 from ludovic-henry/fix-40306
[mono.git] / mcs / class / System.Web / System.Web.Configuration_2.0 / MachineKeySection.cs
index 193b9c2dcd4d3d8d8333bf7d7298da4402b091a3..1ec33313b0c61c469f9631481fe9ee2474f5039a 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.Security.Cryptography;
-
-#if NET_2_0
+using System.Web.Util;
 
 namespace System.Web.Configuration {
 
@@ -44,6 +45,8 @@ namespace System.Web.Configuration {
                static ConfigurationProperty validationProp;
                static ConfigurationProperty validationKeyProp;
                static ConfigurationPropertyCollection properties;
+               static MachineKeyValidationConverter converter = new MachineKeyValidationConverter ();
+               MachineKeyValidation validation;
 
                static MachineKeySection ()
                {
@@ -55,9 +58,9 @@ namespace System.Web.Configuration {
                                                                       PropertyHelper.WhiteSpaceTrimStringConverter,
                                                                       PropertyHelper.NonEmptyStringValidator,
                                                                       ConfigurationPropertyOptions.None);
-                       validationProp = new ConfigurationProperty ("validation", typeof (MachineKeyValidation), MachineKeyValidation.SHA1,
-                                                                   new MachineKeyValidationConverter (),
-                                                                   PropertyHelper.DefaultValidator,
+                       validationProp = new ConfigurationProperty ("validation", typeof (string), "HMACSHA256",
+                                                                   PropertyHelper.WhiteSpaceTrimStringConverter,
+                                                                   PropertyHelper.NonEmptyStringValidator,
                                                                    ConfigurationPropertyOptions.None);
                        validationKeyProp = new ConfigurationProperty ("validationKey", typeof (string), "AutoGenerate,IsolateApps",
                                                                       PropertyHelper.WhiteSpaceTrimStringConverter,
@@ -71,12 +74,28 @@ namespace System.Web.Configuration {
                        properties.Add (validationProp);
                        properties.Add (validationKeyProp);
 
-                       MachineKeySectionUtils.AutoGenKeys ();
+                       Config.AutoGenerate (MachineKeyRegistryStorage.KeyType.Encryption);
+                       Config.AutoGenerate (MachineKeyRegistryStorage.KeyType.Validation);
                }
 
-               protected override void Reset (ConfigurationElement parentElement)
+               public MachineKeySection ()
+               {
+                       // get DefaultValue from ValidationAlgorithm
+                       validation = (MachineKeyValidation) converter.ConvertFrom (null, null, ValidationAlgorithm);
+               }
+
+               [MonoTODO]
+               public MachineKeyCompatibilityMode CompatibilityMode {
+                       get; set;
+               }
+
+               protected internal 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 +103,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,15 +116,39 @@ namespace System.Web.Configuration {
                        get { return (string) base [decryptionKeyProp];}
                        set {
                                base[decryptionKeyProp] = value;
-                               MachineKeySectionUtils.SetDecryptionKey (value);
+                               SetDecryptionKey (value);
                        }
                }
 
-               [TypeConverter (typeof (MachineKeyValidationConverter))]
-               [ConfigurationProperty ("validation", DefaultValue = "SHA1")]
+               // property exists for backward compatibility
                public MachineKeyValidation Validation {
-                       get { return (MachineKeyValidation) base [validationProp];}
-                       set { base[validationProp] = value; }
+                       get { return validation; }
+                       set {
+                               if (value == MachineKeyValidation.Custom)
+                                       throw new ArgumentException ();
+
+                               string algo = value.ToString ();
+                               // enum and accept values differs for TripleDES
+                               ValidationAlgorithm = (algo == "TripleDES") ? "3DES" : algo;
+                       }
+               }
+
+               [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;
+                       }
                }
 
                [TypeConverter (typeof (WhiteSpaceTrimStringConverter))]
@@ -112,14 +158,136 @@ namespace System.Web.Configuration {
                        get { return (string) base [validationKeyProp];}
                        set {
                                base[validationKeyProp] = value;
-                               MachineKeySectionUtils.SetValidationKey (value);
+                               SetValidationKey (value);
                        }
                }
 
-               protected override ConfigurationPropertyCollection Properties {
+               protected internal override ConfigurationPropertyCollection Properties {
                        get { return properties; }
                }
+
+
+               internal static MachineKeySection Config {
+                       get { return WebConfigurationManager.GetSection ("system.web/machineKey") as MachineKeySection; }
+               }
+
+               byte[] decryption_key;
+               byte[] validation_key;
+               SymmetricAlgorithm decryption_template;
+               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
+               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
+               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;
+                       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;
+                       }
+                       // 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;
+                               MachineKeyRegistryStorage.Store (key, type);
+                       }
+                       return key;
+               }
        }
 }
 
-#endif