//
// System.Web.Configuration.MachineKeyConfigHandler
//
// Authors:
//	Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// (C) 2002 Ximian, Inc (http://www.ximian.com)
//

using System;
using System.Collections;
using System.Configuration;
using System.Security.Cryptography;
using System.Xml;

namespace System.Web.Configuration
{
	class MachineKeyConfigHandler : IConfigurationSectionHandler
	{
		static byte [] autogenerated;
		static MachineKeyConfigHandler ()
		{
			autogenerated = new byte [64];
			RNGCryptoServiceProvider cp = new RNGCryptoServiceProvider ();
			cp.GetBytes (autogenerated);
		}

		static byte ToHexValue (char c, bool high)
		{
			byte v;
			if (c >= '0' && c <= '9')
				v = (byte) (c - '0');
			else if (c >= 'a' && c <= 'f')
				v = (byte) (c - 'a' + 10);
			else if (c >= 'A' && c <= 'F')
				v = (byte) (c - 'A' + 10);
			else
				throw new ArgumentException ("Invalid hex character");

			if (high)
				v <<= 4;

			return v;
		}
		
		internal static byte [] GetBytes (string key, int len)
		{
			byte [] result = new byte [len / 2];
			for (int i = 0; i < len; i += 2)
				result [i / 2] = (byte) (ToHexValue (key [i], true) + ToHexValue (key [i + 1], false));

			return result;
		}

		static byte [] MakeKey (string key)
		{
			if (key == null || key == "AutoGenerated")
				return autogenerated;

			int len = key.Length;
			if (len < 40 || len > 128 || (len % 2) == 1)
				throw new ArgumentException ("Invalid key length");

			return GetBytes (key, len);
		}
		
		public object Create (object parent, object context, XmlNode section)
		{
			if (section.HasChildNodes)
				ThrowException ("Child nodes not allowed here", section.FirstChild);

			//TODO: context?
			MachineKeyConfig config = new MachineKeyConfig (parent);

			string validationKey = AttValue ("validationKey", section);
			try {
				config.ValidationKey = MakeKey (validationKey);
			} catch (ArgumentException e) {
				ThrowException (e.Message, section);
			}

			string decryptionKey = AttValue ("decryptionKey", section);
			try {
				config.DecryptionKey = MakeKey (decryptionKey);
			} catch (ArgumentException e) {
				ThrowException (e.Message, section);
			}

			string validation = AttValue ("validation", section);
			if (validation != "SHA1" && validation != "MD5" && validation != "3DES")
				ThrowException ("Invalid 'validation' value", section);

			config.ValidationType = validation;

			if (section.Attributes != null && section.Attributes.Count != 0)
				ThrowException ("Unrecognized attribute", section);

			MachineKeyConfig.MachineKey = config;
			return config;
		}

		// A few methods to save some typing
		static string AttValue (string name, XmlNode node)
		{
			return HandlersUtil.ExtractAttributeValue (name, node, true);
		}

		static void ThrowException (string message, XmlNode node)
		{
			HandlersUtil.ThrowException (message, node);
		}
		//

	}
}

