Transform.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / clr / src / ManagedLibraries / Security / System / Security / Cryptography / Xml / Transform.cs / 1 / Transform.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 

// 
// Transform.cs 
//
 
// This file contains the classes necessary to represent the Transform processing model used in
// XMLDSIG. The basic idea is as follows. A Reference object contains within it a TransformChain, which
// is an ordered set of XMLDSIG transforms (represented by ... clauses in the XML).
// A transform in XMLDSIG operates on an input of either an octet stream or a node set and produces 
// either an octet stream or a node set. Conversion between the two types is performed by parsing (octet stream->
// node set) or C14N (node set->octet stream). We generalize this slightly to allow a transform to define an array of 
// input and output types (because I believe in the future there will be perf gains by being smarter about what goes in & comes out) 
// Each XMLDSIG transform is represented by a subclass of the abstract Transform class. We need to use CryptoConfig to
// associate Transform classes with URLs for transform extensibility, but that's a future concern for this code. 
// Once the Transform chain is constructed, call TransformToOctetStream to convert some sort of input type to an octet
// stream. (We only bother implementing that much now since every use of transform chains in XmlDsig ultimately yields something to hash).

namespace System.Security.Cryptography.Xml 
{
    using System; 
    using System.Collections; 
    using System.IO;
    using System.Runtime.InteropServices; 
    using System.Security;
    using System.Security.Policy;
    using System.Text;
    using System.Xml; 
    using System.Xml.XPath;
    using System.Xml.Xsl; 
 
    // This class represents an ordered chain of transforms
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class TransformChain {
        private ArrayList m_transforms;
 
        public TransformChain () {
            m_transforms = new ArrayList(); 
        } 

        public void Add (Transform transform) { 
            if (transform != null)
                m_transforms.Add(transform);
        }
 
        public IEnumerator GetEnumerator() {
            return m_transforms.GetEnumerator(); 
        } 

        public int Count { 
            get { return m_transforms.Count; }
        }

        public Transform this[int index] { 
            get {
                if (index >= m_transforms.Count) 
                    throw new ArgumentException( SecurityResources.GetResourceString("ArgumentOutOfRange_Index"), "index"); 
                return (Transform) m_transforms[index];
            } 
        }

        // The goal behind this method is to pump the input stream through the transforms and get back something that
        // can be hashed 
        internal Stream TransformToOctetStream(Object inputObject, Type inputType, XmlResolver resolver, string baseUri) {
            Object currentInput = inputObject; 
            foreach (Transform transform in m_transforms) { 
                if (currentInput == null || transform.AcceptsType(currentInput.GetType())) {
                    //in this case, no translation necessary, pump it through 
                    transform.Resolver = resolver;
                    transform.BaseURI = baseUri;
                    transform.LoadInput(currentInput);
                    currentInput = transform.GetOutput(); 
                } else {
                    // We need translation 
                    // For now, we just know about Stream->{XmlNodeList,XmlDocument} and {XmlNodeList,XmlDocument}->Stream 
                    if (currentInput is Stream) {
                        if (transform.AcceptsType(typeof(XmlDocument))) { 
                            Stream currentInputStream = currentInput as Stream;
                            XmlDocument doc = new XmlDocument();
                            doc.PreserveWhitespace = true;
                            XmlReader valReader = Utils.PreProcessStreamInput(currentInputStream, resolver, baseUri); 
                            doc.Load(valReader);
                            transform.LoadInput(doc); 
                            currentInputStream.Close(); 
                            currentInput = transform.GetOutput();
                            continue; 
                        } else {
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
                        }
                    } 
                    if (currentInput is XmlNodeList) {
                        if (transform.AcceptsType(typeof(Stream))) { 
                            CanonicalXml c14n = new CanonicalXml((XmlNodeList) currentInput, resolver, false); 
                            MemoryStream ms = new MemoryStream(c14n.GetBytes());
                            transform.LoadInput(ms); 
                            currentInput = transform.GetOutput();
                            ms.Close();
                            continue;
                        } else { 
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
                        } 
                    } 
                    if (currentInput is XmlDocument) {
                        if (transform.AcceptsType(typeof(Stream))) { 
                            CanonicalXml c14n = new CanonicalXml((XmlDocument) currentInput, resolver);
                            MemoryStream ms = new MemoryStream(c14n.GetBytes());
                            transform.LoadInput(ms);
                            currentInput = transform.GetOutput(); 
                            ms.Close();
                            continue; 
                        } else { 
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
                        } 
                    }
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
                }
            } 

            // Final processing, either we already have a stream or have to canonicalize 
            if (currentInput is Stream) { 
                return currentInput as Stream;
            } 
            if (currentInput is XmlNodeList) {
                CanonicalXml c14n = new CanonicalXml((XmlNodeList) currentInput, resolver, false);
                MemoryStream ms = new MemoryStream(c14n.GetBytes());
                return ms; 
            }
            if (currentInput is XmlDocument) { 
                CanonicalXml c14n = new CanonicalXml((XmlDocument) currentInput, resolver); 
                MemoryStream ms = new MemoryStream(c14n.GetBytes());
                return ms; 
            }
            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
        }
 
        internal Stream TransformToOctetStream(Stream input, XmlResolver resolver, string baseUri) {
            return TransformToOctetStream(input, typeof(Stream), resolver, baseUri); 
        } 

        internal Stream TransformToOctetStream(XmlDocument document, XmlResolver resolver, string baseUri) { 
            return TransformToOctetStream(document, typeof(XmlDocument), resolver, baseUri);
        }

        internal XmlElement GetXml (XmlDocument document, string ns) { 
            XmlElement transformsElement = document.CreateElement("Transforms", ns);
            foreach (Transform transform in m_transforms) { 
                if (transform != null) { 
                    // Construct the individual transform element
                    XmlElement transformElement = transform.GetXml(document); 
                    if (transformElement != null)
                        transformsElement.AppendChild(transformElement);
                }
            } 
            return transformsElement;
        } 
 
        internal void LoadXml (XmlElement value) {
            if (value == null) 
                throw new ArgumentNullException("value");

            XmlNamespaceManager nsm = new XmlNamespaceManager(value.OwnerDocument.NameTable);
            nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); 

            XmlNodeList transformNodes = value.SelectNodes("ds:Transform", nsm); 
            if (transformNodes.Count == 0) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_InvalidElement"), "Transforms");
 
            m_transforms.Clear();
            for (int i = 0; i < transformNodes.Count; ++i) {
                XmlElement transformElement = (XmlElement) transformNodes.Item(i);
                string algorithm = Utils.GetAttribute(transformElement, "Algorithm", SignedXml.XmlDsigNamespaceUrl); 
                Transform transform = CryptoConfig.CreateFromName(algorithm) as Transform;
                if (transform == null) 
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform")); 
                // let the transform read the children of the transformElement for data
                transform.LoadInnerXml(transformElement.ChildNodes); 
                m_transforms.Add(transform);
            }
        }
    } 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] 
    public abstract class Transform { 
        private string m_algorithm;
        private string m_baseUri = null; 
        internal XmlResolver m_xmlResolver = null;
        private bool m_bResolverSet = false;
        private SignedXml m_signedXml = null;
        private Reference m_reference = null; 
        private Hashtable m_propagatedNamespaces = null;
        private XmlElement m_context = null; 
 
        internal string BaseURI {
            get { return m_baseUri; } 
            set { m_baseUri = value; }
        }

        internal SignedXml SignedXml { 
            get { return m_signedXml; }
            set { m_signedXml = value; } 
        } 

        internal Reference Reference { 
            get { return m_reference; }
            set { m_reference = value; }
        }
 
        //
        // protected constructors 
        // 

        protected Transform() {} 

        //
        // public properties
        // 

        public string Algorithm { 
            get { return m_algorithm; } 
            set { m_algorithm = value; }
        } 

        [ComVisible(false)]
        public XmlResolver Resolver {
            // This property only has a setter. The rationale for this is that we don't have a good value 
            // to return when it has not been explicitely set, as we are using XmlSecureResolver by default
            set { 
                m_xmlResolver = value; 
                m_bResolverSet = true;
            } 
        }

        internal bool ResolverSet {
            get { return m_bResolverSet; } 
        }
 
        public abstract Type[] InputTypes { 
            get;
        } 

        public abstract Type[] OutputTypes {
            get;
        } 

        internal bool AcceptsType(Type inputType) { 
            if (InputTypes != null) { 
                for (int i=0; i 0) ? attrib.Prefix + ":" + attrib.LocalName : attrib.LocalName);
                    if (!m_propagatedNamespaces.Contains(key)) 
                        m_propagatedNamespaces.Add(key, attrib.Value);
                }
                return m_propagatedNamespaces;
            } 
        }
    } 
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigC14NTransform : Transform { 
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlDocument), typeof(XmlNodeList) };
        private Type[] _outputTypes = { typeof(Stream) };
        private CanonicalXml _cXml;
        private bool _includeComments = false; 

        public XmlDsigC14NTransform() { 
            Algorithm = SignedXml.XmlDsigC14NTransformUrl; 
        }
 
        public XmlDsigC14NTransform(bool includeComments) {
            _includeComments = includeComments;
            Algorithm = (includeComments ? SignedXml.XmlDsigC14NWithCommentsTransformUrl : SignedXml.XmlDsigC14NTransformUrl);
        } 

        public override Type[] InputTypes { 
            get { return _inputTypes; } 
        }
 
        public override Type[] OutputTypes {
            get { return _outputTypes; }
        }
 
        public override void LoadInnerXml(XmlNodeList nodeList) {}
 
        protected override XmlNodeList GetInnerXml() { 
            return null;
        } 

        public override void LoadInput(Object obj) {
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI));
            if (obj is Stream) { 
                _cXml = new CanonicalXml((Stream) obj, _includeComments, resolver, this.BaseURI);
                return; 
            } 
            if (obj is XmlDocument) {
                _cXml = new CanonicalXml((XmlDocument) obj, resolver, _includeComments); 
                return;
            }
            if (obj is XmlNodeList) {
                _cXml = new CanonicalXml((XmlNodeList) obj, resolver, _includeComments); 
            }
 	     else { 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_IncorrectObjectType"), "obj"); 
	     }
        } 

        public override Object GetOutput() {
            return new MemoryStream(_cXml.GetBytes());
        } 

        public override Object GetOutput(Type type) { 
            if (type != typeof(Stream) && !type.IsSubclassOf(typeof(Stream))) 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
            return new MemoryStream(_cXml.GetBytes()); 
        }

        [ComVisible(false)]
        public override byte[] GetDigestedOutput(HashAlgorithm hash) { 
            return _cXml.GetDigestedBytes(hash);
        } 
    } 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] 
    public class XmlDsigC14NWithCommentsTransform : XmlDsigC14NTransform {
        public XmlDsigC14NWithCommentsTransform()
            : base(true) {
            Algorithm = SignedXml.XmlDsigC14NWithCommentsTransformUrl; 
        }
    } 
 
    // 
    //      
    // 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigExcC14NTransform : Transform { 
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlDocument), typeof(XmlNodeList) };
        private Type[] _outputTypes = { typeof(Stream) }; 
        private bool _includeComments = false; 
        private string _inclusiveNamespacesPrefixList;
        private ExcCanonicalXml _excCanonicalXml; 

        public XmlDsigExcC14NTransform() : this(false, null) {}

        public XmlDsigExcC14NTransform(bool includeComments) : this(includeComments, null) {} 

        public XmlDsigExcC14NTransform(string inclusiveNamespacesPrefixList) : this(false, inclusiveNamespacesPrefixList) {} 
 
        public XmlDsigExcC14NTransform(bool includeComments, string inclusiveNamespacesPrefixList) {
            _includeComments = includeComments; 
            _inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList;
            Algorithm = (includeComments ? SignedXml.XmlDsigExcC14NWithCommentsTransformUrl : SignedXml.XmlDsigExcC14NTransformUrl);
        }
 
        public string InclusiveNamespacesPrefixList {
            get { return _inclusiveNamespacesPrefixList; } 
            set { _inclusiveNamespacesPrefixList = value; } 
        }
 
        public override Type[] InputTypes {
            get { return _inputTypes; }
        }
 
        public override Type[] OutputTypes {
            get { return _outputTypes; } 
        } 

        public override void LoadInnerXml(XmlNodeList nodeList) { 
            if (nodeList != null) {
                foreach (XmlNode n in nodeList) {
                    XmlElement e = n as XmlElement;
                    if (e != null && e.LocalName.Equals("InclusiveNamespaces") && 
                        e.NamespaceURI.Equals(SignedXml.XmlDsigExcC14NTransformUrl) &&
                        Utils.HasAttribute(e, "PrefixList", SignedXml.XmlDsigNamespaceUrl)) { 
                        this.InclusiveNamespacesPrefixList = Utils.GetAttribute(e, "PrefixList", SignedXml.XmlDsigNamespaceUrl); 
                        return;
                    } 
                }
            }
        }
 
        public override void LoadInput(Object obj) {
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI)); 
            if (obj is Stream) { 
                _excCanonicalXml = new ExcCanonicalXml((Stream) obj, _includeComments, _inclusiveNamespacesPrefixList, resolver, this.BaseURI);
            } 
            else if (obj is XmlDocument) {
                _excCanonicalXml = new ExcCanonicalXml((XmlDocument) obj, _includeComments, _inclusiveNamespacesPrefixList, resolver);
            }
            else if (obj is XmlNodeList) { 
                _excCanonicalXml = new ExcCanonicalXml((XmlNodeList) obj, _includeComments, _inclusiveNamespacesPrefixList, resolver);
            } else 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_IncorrectObjectType"), "obj"); 
        }
 
        protected override XmlNodeList GetInnerXml() {
            if (InclusiveNamespacesPrefixList == null)
                return null;
            XmlDocument document = new XmlDocument(); 
            XmlElement element = document.CreateElement("Transform", SignedXml.XmlDsigNamespaceUrl);
            if (!String.IsNullOrEmpty(this.Algorithm)) 
                element.SetAttribute("Algorithm", this.Algorithm); 
            XmlElement prefixListElement = document.CreateElement("InclusiveNamespaces", SignedXml.XmlDsigExcC14NTransformUrl);
            prefixListElement.SetAttribute("PrefixList", InclusiveNamespacesPrefixList); 
            element.AppendChild(prefixListElement);
            return element.ChildNodes;
        }
 
        public override Object GetOutput() {
            return new MemoryStream(_excCanonicalXml.GetBytes()); 
        } 

        public override Object GetOutput(Type type) { 
            if (type != typeof(Stream) && !type.IsSubclassOf(typeof(Stream)))
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
            return new MemoryStream(_excCanonicalXml.GetBytes());
        } 

        public override byte[] GetDigestedOutput(HashAlgorithm hash) { 
            return _excCanonicalXml.GetDigestedBytes(hash); 
        }
    } 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigExcC14NWithCommentsTransform : XmlDsigExcC14NTransform {
        public XmlDsigExcC14NWithCommentsTransform() : base(true) { 
            Algorithm = SignedXml.XmlDsigExcC14NWithCommentsTransformUrl;
        } 
 
        public XmlDsigExcC14NWithCommentsTransform(string inclusiveNamespacesPrefixList) : base(true, inclusiveNamespacesPrefixList) {
            Algorithm = SignedXml.XmlDsigExcC14NWithCommentsTransformUrl; 
        }
    }

    // A class representing conversion from Base64 using CryptoStream 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigBase64Transform : Transform { 
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlNodeList), typeof(XmlDocument) }; 
        private Type[] _outputTypes = { typeof(Stream) };
        private CryptoStream _cs = null; 

        public XmlDsigBase64Transform() {
            Algorithm = SignedXml.XmlDsigBase64TransformUrl;
        } 

        public override Type[] InputTypes { 
            get { return _inputTypes; } 
        }
 
        public override Type[] OutputTypes {
            get { return _outputTypes; }
        }
 
        public override void LoadInnerXml(XmlNodeList nodeList) {
        } 
 
        protected override XmlNodeList GetInnerXml() {
            return null; 
        }

        public override void LoadInput(Object obj) {
            if (obj is Stream) { 
                LoadStreamInput((Stream) obj);
                return; 
            } 
            if (obj is XmlNodeList) {
                LoadXmlNodeListInput((XmlNodeList) obj); 
                return;
            }
            if (obj is XmlDocument) {
                LoadXmlNodeListInput(((XmlDocument) obj).SelectNodes("//.")); 
                return;
            } 
        } 

        private void LoadStreamInput(Stream inputStream) { 
            if (inputStream == null) throw new ArgumentException("obj");
            MemoryStream ms = new MemoryStream();
            byte[] buffer = new byte[1024];
            int bytesRead; 
            do {
                bytesRead = inputStream.Read(buffer,0,1024); 
                if (bytesRead > 0) { 
                    int i = 0;
                    int j = 0; 
                    while ((j < bytesRead) && (!Char.IsWhiteSpace((char) buffer[j]))) j++;
                    i = j; j++;
                    while (j < bytesRead) {
                        if (!Char.IsWhiteSpace((char) buffer[j])) { 
                            buffer[i] = buffer[j];
                            i++; 
                        } 
                        j++;
                    } 
                    ms.Write(buffer,0,i);
                }
            } while (bytesRead > 0);
            ms.Position = 0; 
            _cs = new CryptoStream(ms, new FromBase64Transform(), CryptoStreamMode.Read);
        } 
 
        private void LoadXmlNodeListInput(XmlNodeList nodeList) {
            StringBuilder sb = new StringBuilder(); 
            foreach (XmlNode node in nodeList) {
                XmlNode result = node.SelectSingleNode("self::text()");
                if (result != null)
                    sb.Append(result.OuterXml); 
            }
            UTF8Encoding utf8 = new UTF8Encoding(false); 
            byte[] buffer = utf8.GetBytes(sb.ToString()); 
            int i = 0;
            int j = 0; 
            while ((j  0) 
                                element.SetAttribute("xmlns:" + prefix, _nsm.LookupNamespace(prefix));
                            break; 
                    }
                }
            }
            // Add the XPath as the inner xml of the element 
            element.InnerXml = _xpathexpr;
            document.AppendChild(element); 
            return document.ChildNodes; 
        }
 
        public override void LoadInput(Object obj) {
            if (obj is Stream) {
                LoadStreamInput((Stream) obj);
            } else if (obj is XmlNodeList) { 
                LoadXmlNodeListInput((XmlNodeList) obj);
            } else if (obj is XmlDocument) { 
                LoadXmlDocumentInput((XmlDocument) obj); 
            }
        } 

        private void LoadStreamInput(Stream stream) {
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI));
            XmlReader valReader = Utils.PreProcessStreamInput(stream, resolver, this.BaseURI); 
            _document = new XmlDocument();
            _document.PreserveWhitespace = true; 
            _document.Load(valReader); 
        }
 
        private void LoadXmlNodeListInput(XmlNodeList nodeList) {
            // Use C14N to get a document
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI));
            CanonicalXml c14n = new CanonicalXml((XmlNodeList) nodeList, resolver, true); 
            using (MemoryStream ms = new MemoryStream(c14n.GetBytes())) {
                _document = new XmlDocument(); 
                _document.PreserveWhitespace = true; 
                _document.Load(ms);
            } 
        }

        private void LoadXmlDocumentInput(XmlDocument doc) {
            _document = doc; 
        }
 
        public override Object GetOutput() { 
            CanonicalXmlNodeList resultNodeList = new CanonicalXmlNodeList();
            if (!String.IsNullOrEmpty(_xpathexpr)) { 
                XPathNavigator navigator = _document.CreateNavigator();
                XPathNodeIterator it = navigator.Select("//. | //@*");

                XPathExpression xpathExpr = navigator.Compile("boolean(" + _xpathexpr + ")"); 
                xpathExpr.SetContext(_nsm);
 
                while (it.MoveNext()) { 
                    XmlNode node = ((IHasXmlNode) it.Current).GetNode();
 
                    bool include = (bool) it.Current.Evaluate(xpathExpr);
                    if (include == true)
                        resultNodeList.Add(node);
                } 

                // keep namespaces 
                it = navigator.Select("//namespace::*"); 
                while (it.MoveNext()) {
                    XmlNode node = ((IHasXmlNode) it.Current).GetNode(); 
                    resultNodeList.Add(node);
                }
            }
 
            return resultNodeList;
        } 
 
        public override Object GetOutput(Type type) {
            if (type != typeof(XmlNodeList) && !type.IsSubclassOf(typeof(XmlNodeList))) 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
            return (XmlNodeList) GetOutput();
        }
    } 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] 
    public class XmlDsigXsltTransform : Transform { 
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlDocument), typeof(XmlNodeList) };
        private Type[] _outputTypes = { typeof(Stream) }; 
        private XmlNodeList _xslNodes;
        private string _xslFragment;
        private Stream _inputStream;
        private bool _includeComments = false; 

        public XmlDsigXsltTransform() { 
            Algorithm = SignedXml.XmlDsigXsltTransformUrl; 
        }
 
        public XmlDsigXsltTransform(bool includeComments) {
            _includeComments = includeComments;
            Algorithm = SignedXml.XmlDsigXsltTransformUrl;
        } 

        public override Type[] InputTypes { 
            get { 
                return _inputTypes;
            } 
        }

        public override Type[] OutputTypes {
            get { 
                return _outputTypes;
            } 
        } 

        public override void LoadInnerXml(XmlNodeList nodeList) { 
            if (nodeList == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
            // check that the XSLT element is well formed
            XmlElement firstDataElement = null; 
            int count = 0;
            foreach (XmlNode node in nodeList) { 
                // ignore white spaces, but make sure only one child element is present 
                if (node is XmlWhitespace) continue;
                if (node is XmlElement) { 
                    if (count != 0)
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
                    firstDataElement = node as XmlElement;
                    count++; 
                    continue;
                } 
                // Only allow white spaces 
                count++;
            } 
            if (count != 1 || firstDataElement == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
            _xslNodes = nodeList;
            _xslFragment = firstDataElement.OuterXml.Trim(null); 
        }
 
        protected override XmlNodeList GetInnerXml() { 
            return _xslNodes;
        } 

        public override void LoadInput(Object obj) {
            if (_inputStream != null)
                _inputStream.Close(); 
            _inputStream = new MemoryStream();
            if (obj is Stream) { 
                _inputStream = (Stream) obj; 
            }
            else if (obj is XmlNodeList) { 
                CanonicalXml xmlDoc = new CanonicalXml((XmlNodeList) obj, null, _includeComments);
                byte[] buffer = xmlDoc.GetBytes();
                if (buffer == null) return;
                _inputStream.Write(buffer, 0, buffer.Length); 
                _inputStream.Flush();
                _inputStream.Position = 0; 
            } 
            else if (obj is XmlDocument) {
                CanonicalXml xmlDoc = new CanonicalXml((XmlDocument) obj, null, _includeComments); 
                byte[] buffer = xmlDoc.GetBytes();
                if (buffer == null) return;
                _inputStream.Write(buffer, 0, buffer.Length);
                _inputStream.Flush(); 
                _inputStream.Position = 0;
            } 
        } 

        public override Object GetOutput() { 
            //  XSL transforms expose many powerful features by default:
            //  1- we need to pass a null evidence to prevent script execution.
            //  2- XPathDocument will expand entities, we don't want this, so set the resolver to null
            //  3- We don't want the document function feature of XslTransforms. 

            // load the XSL Transform 
            XslCompiledTransform xslt = new XslCompiledTransform(); 
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.XmlResolver = null; 
            using (StringReader sr = new StringReader(_xslFragment)) {
                XmlReader readerXsl = XmlReader.Create(sr, settings, null as string);
                xslt.Load(readerXsl, XsltSettings.Default, null);
 
                // Now load the input stream, XmlDocument can be used but is less efficient
                XmlReader reader = XmlReader.Create(_inputStream, settings, this.BaseURI); 
                XPathDocument inputData = new XPathDocument(reader, XmlSpace.Preserve); 

                // Create an XmlTextWriter 
                MemoryStream ms = new MemoryStream();
                XmlWriter writer = new XmlTextWriter(ms, null);

                // Transform the data and send the output to the memory stream 
                xslt.Transform(inputData, null, writer);
                ms.Position = 0; 
                return ms; 
            }
        } 

        public override Object GetOutput(Type type) {
            if (type != typeof(Stream) && !type.IsSubclassOf(typeof(Stream)))
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type"); 
            return (Stream) GetOutput();
        } 
    } 

 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigEnvelopedSignatureTransform : Transform {
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlNodeList), typeof(XmlDocument) };
        private Type[] _outputTypes = { typeof(XmlNodeList), typeof(XmlDocument) }; 
        private XmlNodeList _inputNodeList;
        private bool _includeComments = false; 
        private XmlNamespaceManager _nsm = null; 
        private XmlDocument _containingDocument = null;
        private int _signaturePosition = 0; 

        internal int SignaturePosition {
            set { _signaturePosition = value; }
        } 

        public XmlDsigEnvelopedSignatureTransform() { 
            Algorithm = SignedXml.XmlDsigEnvelopedSignatureTransformUrl; 
        }
 
        /// 
        public XmlDsigEnvelopedSignatureTransform(bool includeComments) {
            _includeComments = includeComments;
            Algorithm = SignedXml.XmlDsigEnvelopedSignatureTransformUrl; 
        }
 
        public override Type[] InputTypes { 
            get { return _inputTypes; }
        } 

        public override Type[] OutputTypes {
            get { return _outputTypes; }
        } 

        // An enveloped signature has no inner XML elements 
        public override void LoadInnerXml(XmlNodeList nodeList) {} 

        // An enveloped signature has no inner XML elements 
        protected override XmlNodeList GetInnerXml() {
            return null;
        }
 
        public override void LoadInput(Object obj) {
            if (obj is Stream) { 
                LoadStreamInput((Stream) obj); 
                return;
            } 
            if (obj is XmlNodeList) {
                LoadXmlNodeListInput((XmlNodeList) obj);
                return;
            } 
            if (obj is XmlDocument) {
                LoadXmlDocumentInput((XmlDocument) obj); 
                return; 
            }
        } 

        private void LoadStreamInput(Stream stream) {
            XmlDocument doc = new XmlDocument();
            doc.PreserveWhitespace = true; 
            doc.Load(stream);
            _containingDocument = doc; 
            if (_containingDocument == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_EnvelopedSignatureRequiresContext"));
            _nsm = new XmlNamespaceManager(_containingDocument.NameTable); 
            _nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl);
        }

        private void LoadXmlNodeListInput(XmlNodeList nodeList) { 
            // Empty node list is not acceptable
            if (nodeList == null) 
                throw new ArgumentNullException("nodeList"); 
            _containingDocument = Utils.GetOwnerDocument(nodeList);
            if (_containingDocument == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_EnvelopedSignatureRequiresContext"));

            _nsm = new XmlNamespaceManager(_containingDocument.NameTable);
            _nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl); 
            _inputNodeList = nodeList;
        } 
 
        private void LoadXmlDocumentInput(XmlDocument doc) {
            if (doc == null) 
                throw new ArgumentNullException("doc");
            _containingDocument = doc;
            _nsm = new XmlNamespaceManager(_containingDocument.NameTable);
            _nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl); 
        }
 
        public override Object GetOutput() { 
            if (_containingDocument == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_EnvelopedSignatureRequiresContext")); 

            // If we have received an XmlNodeList as input
            if (_inputNodeList != null) {
                // If the position has not been set, then we don't want to remove any signature tags 
                if (_signaturePosition == 0) return _inputNodeList;
                XmlNodeList signatureList = _containingDocument.SelectNodes("//dsig:Signature", _nsm); 
                if (signatureList == null) return _inputNodeList; 

                CanonicalXmlNodeList resultNodeList = new CanonicalXmlNodeList(); 
                foreach (XmlNode node in _inputNodeList) {
                    if (node == null)  continue;
                    // keep namespaces
                    if (Utils.IsXmlNamespaceNode(node) || Utils.IsNamespaceNode(node)) { 
                        resultNodeList.Add(node);
                    } else { 
                        // SelectSingleNode throws an exception for xmldecl PI for example, so we will just ignore those exceptions 
                        try {
                            // Find the nearest signature ancestor tag 
                            XmlNode result = node.SelectSingleNode("ancestor-or-self::dsig:Signature[1]", _nsm);
                            int position = 0;
                            foreach (XmlNode node1 in signatureList) {
                                position++; 
                                if (node1 == result) break;
                            } 
                            if (result == null || (result != null && position != _signaturePosition)) { 
                                resultNodeList.Add(node);
                            } 
                        }
                        catch {}
                    }
                } 
                return resultNodeList;
            } 
            // Else we have received either a stream or a document as input 
            else {
                XmlNodeList signatureList = _containingDocument.SelectNodes("//dsig:Signature", _nsm); 
                if (signatureList == null) return _containingDocument;
                if (signatureList.Count < _signaturePosition || _signaturePosition <= 0) return _containingDocument;

                // Remove the signature node with all its children nodes 
                signatureList[_signaturePosition - 1].ParentNode.RemoveChild(signatureList[_signaturePosition - 1]);
                return _containingDocument; 
            } 
        }
 
        public override Object GetOutput(Type type) {
            if (type == typeof(XmlNodeList) || type.IsSubclassOf(typeof(XmlNodeList))) {
                if (_inputNodeList == null) {
                    _inputNodeList = Utils.AllDescendantNodes(_containingDocument, true); 
                }
                return (XmlNodeList) GetOutput(); 
            } else if (type == typeof(XmlDocument) || type.IsSubclassOf(typeof(XmlDocument))) { 
                if (_inputNodeList != null) throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
                return (XmlDocument) GetOutput(); 
            } else {
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
            }
        } 
    }
 
    [Serializable] 
    internal enum TransformInputType {
        XmlDocument = 1, 
        XmlStream   = 2,
        XmlNodeSet  = 3
    }
 
    // XML Decryption Transform is used to specify the order of XML Digital Signature
    // and XML Encryption when performed on the same document. 
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDecryptionTransform : Transform { 
        private Type[] m_inputTypes = { typeof(Stream), typeof(XmlDocument) };
        private Type[] m_outputTypes = { typeof(XmlDocument) };
        private XmlNodeList m_encryptedDataList = null;
        private ArrayList m_arrayListUri = null; // this ArrayList object represents the Uri's to be excluded 
        private EncryptedXml m_exml = null; // defines the XML encryption processing rules
        private XmlDocument m_containingDocument = null; 
        private XmlNamespaceManager m_nsm = null; 
        private const string XmlDecryptionTransformNamespaceUrl = "http://www.w3.org/2002/07/decrypt#";
 
        public XmlDecryptionTransform() {
            Algorithm = SignedXml.XmlDecryptionTransformUrl;
        }
 
        private ArrayList ExceptUris {
            get { 
                if (m_arrayListUri == null) 
                    m_arrayListUri = new ArrayList();
                return m_arrayListUri; 
            }
        }

        protected virtual bool IsTargetElement (XmlElement inputElement, string idValue) { 
            if (inputElement == null)
                return false; 
            if (inputElement.GetAttribute("Id") == idValue || inputElement.GetAttribute("id") == idValue || 
                inputElement.GetAttribute("ID") == idValue)
                return true; 

            return false;
        }
 
        public EncryptedXml EncryptedXml {
            get { 
                if (m_exml != null) 
                    return m_exml;
 
                Reference reference = this.Reference;
                SignedXml signedXml = (reference == null ? this.SignedXml : reference.SignedXml);
                if (signedXml == null || signedXml.EncryptedXml == null)
                    m_exml = new EncryptedXml(m_containingDocument); // default processing rules 
                else
                    m_exml = signedXml.EncryptedXml; 
 
                return m_exml;
            } 
            set { m_exml = value; }
        }

        public override Type[] InputTypes { 
            get { return m_inputTypes; }
        } 
 
        public override Type[] OutputTypes {
            get { return m_outputTypes; } 
        }

        public void AddExceptUri (string uri) {
            if (uri == null) 
                throw new ArgumentNullException("uri");
            ExceptUris.Add(uri); 
        } 

        public override void LoadInnerXml(XmlNodeList nodeList) { 
            if (nodeList == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
            ExceptUris.Clear();
            foreach (XmlNode node in nodeList) { 
                XmlElement elem = node as XmlElement;
                if (elem != null && elem.LocalName == "Except" && elem.NamespaceURI == XmlDecryptionTransformNamespaceUrl) { 
                    // the Uri is required 
                    string uri = Utils.GetAttribute(elem, "URI", XmlDecryptionTransformNamespaceUrl);
                    if (uri == null || uri.Length == 0 || uri[0] != '#') 
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriRequired"));
                    string idref = Utils.ExtractIdFromLocalUri(uri);
                    ExceptUris.Add(idref);
                } 
            }
        } 
 
        protected override XmlNodeList GetInnerXml() {
            if (ExceptUris.Count == 0) 
                return null;
            XmlDocument document = new XmlDocument();
            XmlElement element = document.CreateElement("Transform", SignedXml.XmlDsigNamespaceUrl);
            if (!String.IsNullOrEmpty(this.Algorithm)) 
                element.SetAttribute("Algorithm", this.Algorithm);
            foreach (string uri in ExceptUris) { 
                XmlElement exceptUriElement = document.CreateElement("Except", XmlDecryptionTransformNamespaceUrl); 
                exceptUriElement.SetAttribute("URI", uri);
                element.AppendChild(exceptUriElement); 
            }
            return element.ChildNodes;
        }
 
        public override void LoadInput(Object obj) {
            if (obj is Stream) { 
                LoadStreamInput((Stream) obj); 
            } else if (obj is XmlDocument) {
                LoadXmlDocumentInput((XmlDocument) obj); 
            }
        }

        private void LoadStreamInput(Stream stream) { 
            XmlDocument document = new XmlDocument();
            document.PreserveWhitespace = true; 
            document.Load(stream); 
            m_containingDocument = document;
            m_nsm = new XmlNamespaceManager(m_containingDocument.NameTable); 
            m_nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
            // select all EncryptedData elements
            m_encryptedDataList = document.SelectNodes("//enc:EncryptedData", m_nsm);
        } 

        private void LoadXmlDocumentInput(XmlDocument document) { 
            if (document == null) 
                throw new ArgumentNullException("document");
            m_containingDocument = document; 
            m_nsm = new XmlNamespaceManager(document.NameTable);
            m_nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
            // select all EncryptedData elements
            m_encryptedDataList = document.SelectNodes("//enc:EncryptedData", m_nsm); 
        }
 
        private bool ProcessEncryptedDataItem (XmlElement encryptedDataElement) { 
            // first see whether we want to ignore this one
            if (ExceptUris.Count > 0) { 
                for (int index = 0; index < ExceptUris.Count; index++) {
                    if (IsTargetElement(encryptedDataElement, (string) ExceptUris[index]))
                        return false;
                } 
            }
            EncryptedData ed = new EncryptedData(); 
            ed.LoadXml(encryptedDataElement); 
            SymmetricAlgorithm symAlg = this.EncryptedXml.GetDecryptionKey(ed, null);
            if (symAlg == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingDecryptionKey"));
            byte[] decrypted = EncryptedXml.DecryptData(ed, symAlg);
            this.EncryptedXml.ReplaceData(encryptedDataElement, decrypted);
            return true; 
        }
 
        private void ProcessElementRecursively (XmlNodeList encryptedDatas) { 
            if (encryptedDatas == null || encryptedDatas.Count == 0)
                return; 
            Queue encryptedDatasQueue = new Queue();
            foreach (XmlNode value in encryptedDatas) {
                encryptedDatasQueue.Enqueue(value);
            } 
            XmlNode node = encryptedDatasQueue.Dequeue() as XmlNode;
            while (node != null) { 
                XmlElement encryptedDataElement = node as XmlElement; 
                if (encryptedDataElement != null && encryptedDataElement.LocalName == "EncryptedData" &&
                    encryptedDataElement.NamespaceURI == EncryptedXml.XmlEncNamespaceUrl) { 
                    XmlNode sibling = encryptedDataElement.NextSibling;
                    XmlNode parent = encryptedDataElement.ParentNode;
                    if (ProcessEncryptedDataItem(encryptedDataElement)) {
                        // find the new decrypted element. 
                        XmlNode child = parent.FirstChild;
                        while (child != null && child.NextSibling != sibling) 
                            child = child.NextSibling; 
                        if (child != null) {
                            XmlNodeList nodes = child.SelectNodes("//enc:EncryptedData", m_nsm); 
                            if (nodes.Count > 0) {
                                foreach (XmlNode value in nodes) {
                                    encryptedDatasQueue.Enqueue(value);
                                } 
                            }
                        } 
                    } 
                }
                if (encryptedDatasQueue.Count == 0) 
                    break;
                node = encryptedDatasQueue.Dequeue() as XmlNode;
            }
        } 

        public override Object GetOutput() { 
            // decrypt the encrypted sections 
            if (m_encryptedDataList != null)
                ProcessElementRecursively(m_encryptedDataList); 
            // propagate namespaces
            Utils.AddNamespaces(m_containingDocument.DocumentElement, this.PropagatedNamespaces);
            return m_containingDocument;
        } 

        public override Object GetOutput(Type type) { 
            if (type == typeof(XmlDocument)) 
                return (XmlDocument) GetOutput();
            else 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
        }
    }
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlLicenseTransform : Transform { 
        private Type[]              inputTypes       = { typeof(XmlDocument) }; 
        private Type[]              outputTypes      = { typeof(XmlDocument) };
        private XmlNamespaceManager namespaceManager = null; 
        private XmlDocument         license          = null;
        private IRelDecryptor       relDecryptor     = null;
        private const string        ElementIssuer    = "issuer";
        private const string        NamespaceUriCore = "urn:mpeg:mpeg21:2003:01-REL-R-NS"; 

        public XmlLicenseTransform() { 
            Algorithm = SignedXml.XmlLicenseTransformUrl; 
        }
 
        public override Type[] InputTypes {
            get { return inputTypes; }
        }
 
        public override Type[] OutputTypes {
            get { return outputTypes; } 
        } 

        public IRelDecryptor Decryptor { 
            get { return relDecryptor; }
            set { relDecryptor = value; }
        }
 
        private void DecryptEncryptedGrants(XmlNodeList encryptedGrantList, IRelDecryptor decryptor) {
            XmlElement       encryptionMethod    = null; 
            XmlElement       keyInfo             = null; 
            XmlElement       cipherData          = null;
            EncryptionMethod encryptionMethodObj = null; 
            KeyInfo          keyInfoObj          = null;
            CipherData       cipherDataObj       = null;

            for (int i = 0, count = encryptedGrantList.Count; i < count; i++) { 
                encryptionMethod = encryptedGrantList[i].SelectSingleNode("//r:encryptedGrant/enc:EncryptionMethod", namespaceManager) as XmlElement;
                keyInfo          = encryptedGrantList[i].SelectSingleNode("//r:encryptedGrant/dsig:KeyInfo", namespaceManager) as XmlElement; 
                cipherData       = encryptedGrantList[i].SelectSingleNode("//r:encryptedGrant/enc:CipherData", namespaceManager) as XmlElement; 
                if ((encryptionMethod != null) &&
                    (keyInfo != null) && 
                    (cipherData != null)) {
                    encryptionMethodObj = new EncryptionMethod();
                    keyInfoObj          = new KeyInfo();
                    cipherDataObj       = new CipherData(); 

                    encryptionMethodObj.LoadXml(encryptionMethod); 
                    keyInfoObj.LoadXml(keyInfo); 
                    cipherDataObj.LoadXml(cipherData);
 
                    MemoryStream toDecrypt        = null;
                    Stream       decryptedContent = null;
                    StreamReader streamReader     = null;
 
                    try {
                        toDecrypt = new MemoryStream(cipherDataObj.CipherValue); 
                        decryptedContent = relDecryptor.Decrypt(encryptionMethodObj, 
                                                                keyInfoObj, toDecrypt);
 
                        if ((decryptedContent == null) || (decryptedContent.Length == 0))
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlUnableToDecryptGrant"));

                        streamReader = new StreamReader(decryptedContent); 
                        string clearContent = streamReader.ReadToEnd();
 
                        encryptedGrantList[i].ParentNode.InnerXml = clearContent; 
                    }
                    finally { 
                        if (toDecrypt != null)
                            toDecrypt.Close();

                        if (decryptedContent != null) 
                            decryptedContent.Close();
 
                        if (streamReader != null) 
                            streamReader.Close();
                    } 

                    encryptionMethodObj = null;
                    keyInfoObj          = null;
                    cipherDataObj       = null; 
                }
 
                encryptionMethod = null; 
                keyInfo          = null;
                cipherData       = null; 
            }
        }

        // License transform has no inner XML elements 
        protected override XmlNodeList GetInnerXml() {
            return null; 
        } 

        public override object GetOutput() { 
            return license;
        }

        public override object GetOutput(Type type) { 
            if ((type != typeof(XmlDocument)) || (!type.IsSubclassOf(typeof(XmlDocument))))
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type"); 
 
            return GetOutput();
        } 

        // License transform has no inner XML elements
        public override void LoadInnerXml(XmlNodeList nodeList) {}
 
        public override void LoadInput (object obj) {
            // Check if the Context property is set before this transform is invoked. 
            if (Context == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlMissingContext"));
 
            license = new XmlDocument();
            license.PreserveWhitespace = true;
            namespaceManager = new XmlNamespaceManager(license.NameTable);
            namespaceManager.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl); 
            namespaceManager.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
            namespaceManager.AddNamespace("r", NamespaceUriCore); 
 
            XmlElement currentIssuerContext  = null;
            XmlElement currentLicenseContext = null; 
            XmlNode    signatureNode         = null;

            // Get the nearest issuer node
            currentIssuerContext = Context.SelectSingleNode("ancestor-or-self::r:issuer[1]", namespaceManager) as XmlElement; 
            if (currentIssuerContext == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlMissingIssuer")); 
 
            signatureNode = currentIssuerContext.SelectSingleNode("descendant-or-self::dsig:Signature[1]", namespaceManager) as XmlElement;
            if (signatureNode != null) 
                signatureNode.ParentNode.RemoveChild(signatureNode);

            // Get the nearest license node
            currentLicenseContext = currentIssuerContext.SelectSingleNode("ancestor-or-self::r:license[1]", namespaceManager) as XmlElement; 
            if (currentLicenseContext == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlMissingLicence")); 
 
            XmlNodeList issuerList = currentLicenseContext.SelectNodes("descendant-or-self::r:license[1]/r:issuer", namespaceManager);
 
            // Remove all issuer nodes except current
            for (int i = 0, count = issuerList.Count; i < count; i++) {
                if (issuerList[i] == currentIssuerContext)
                    continue; 

                if ((issuerList[i].LocalName == ElementIssuer) && 
                    (issuerList[i].NamespaceURI == NamespaceUriCore)) 
                    issuerList[i].ParentNode.RemoveChild(issuerList[i]);
            } 

            XmlNodeList encryptedGrantList = currentLicenseContext.SelectNodes("/r:license/r:grant/r:encryptedGrant", namespaceManager);

            if (encryptedGrantList.Count > 0) { 
                if (relDecryptor == null)
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlMissingIRelDecryptor")); 
 
                DecryptEncryptedGrants(encryptedGrantList, relDecryptor);
            } 

            license.InnerXml = currentLicenseContext.OuterXml;
        }
    } 

    public interface IRelDecryptor { 
        Stream Decrypt(EncryptionMethod encryptionMethod, KeyInfo keyInfo, Stream toDecrypt); 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 

// 
// Transform.cs 
//
 
// This file contains the classes necessary to represent the Transform processing model used in
// XMLDSIG. The basic idea is as follows. A Reference object contains within it a TransformChain, which
// is an ordered set of XMLDSIG transforms (represented by ... clauses in the XML).
// A transform in XMLDSIG operates on an input of either an octet stream or a node set and produces 
// either an octet stream or a node set. Conversion between the two types is performed by parsing (octet stream->
// node set) or C14N (node set->octet stream). We generalize this slightly to allow a transform to define an array of 
// input and output types (because I believe in the future there will be perf gains by being smarter about what goes in & comes out) 
// Each XMLDSIG transform is represented by a subclass of the abstract Transform class. We need to use CryptoConfig to
// associate Transform classes with URLs for transform extensibility, but that's a future concern for this code. 
// Once the Transform chain is constructed, call TransformToOctetStream to convert some sort of input type to an octet
// stream. (We only bother implementing that much now since every use of transform chains in XmlDsig ultimately yields something to hash).

namespace System.Security.Cryptography.Xml 
{
    using System; 
    using System.Collections; 
    using System.IO;
    using System.Runtime.InteropServices; 
    using System.Security;
    using System.Security.Policy;
    using System.Text;
    using System.Xml; 
    using System.Xml.XPath;
    using System.Xml.Xsl; 
 
    // This class represents an ordered chain of transforms
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class TransformChain {
        private ArrayList m_transforms;
 
        public TransformChain () {
            m_transforms = new ArrayList(); 
        } 

        public void Add (Transform transform) { 
            if (transform != null)
                m_transforms.Add(transform);
        }
 
        public IEnumerator GetEnumerator() {
            return m_transforms.GetEnumerator(); 
        } 

        public int Count { 
            get { return m_transforms.Count; }
        }

        public Transform this[int index] { 
            get {
                if (index >= m_transforms.Count) 
                    throw new ArgumentException( SecurityResources.GetResourceString("ArgumentOutOfRange_Index"), "index"); 
                return (Transform) m_transforms[index];
            } 
        }

        // The goal behind this method is to pump the input stream through the transforms and get back something that
        // can be hashed 
        internal Stream TransformToOctetStream(Object inputObject, Type inputType, XmlResolver resolver, string baseUri) {
            Object currentInput = inputObject; 
            foreach (Transform transform in m_transforms) { 
                if (currentInput == null || transform.AcceptsType(currentInput.GetType())) {
                    //in this case, no translation necessary, pump it through 
                    transform.Resolver = resolver;
                    transform.BaseURI = baseUri;
                    transform.LoadInput(currentInput);
                    currentInput = transform.GetOutput(); 
                } else {
                    // We need translation 
                    // For now, we just know about Stream->{XmlNodeList,XmlDocument} and {XmlNodeList,XmlDocument}->Stream 
                    if (currentInput is Stream) {
                        if (transform.AcceptsType(typeof(XmlDocument))) { 
                            Stream currentInputStream = currentInput as Stream;
                            XmlDocument doc = new XmlDocument();
                            doc.PreserveWhitespace = true;
                            XmlReader valReader = Utils.PreProcessStreamInput(currentInputStream, resolver, baseUri); 
                            doc.Load(valReader);
                            transform.LoadInput(doc); 
                            currentInputStream.Close(); 
                            currentInput = transform.GetOutput();
                            continue; 
                        } else {
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
                        }
                    } 
                    if (currentInput is XmlNodeList) {
                        if (transform.AcceptsType(typeof(Stream))) { 
                            CanonicalXml c14n = new CanonicalXml((XmlNodeList) currentInput, resolver, false); 
                            MemoryStream ms = new MemoryStream(c14n.GetBytes());
                            transform.LoadInput(ms); 
                            currentInput = transform.GetOutput();
                            ms.Close();
                            continue;
                        } else { 
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
                        } 
                    } 
                    if (currentInput is XmlDocument) {
                        if (transform.AcceptsType(typeof(Stream))) { 
                            CanonicalXml c14n = new CanonicalXml((XmlDocument) currentInput, resolver);
                            MemoryStream ms = new MemoryStream(c14n.GetBytes());
                            transform.LoadInput(ms);
                            currentInput = transform.GetOutput(); 
                            ms.Close();
                            continue; 
                        } else { 
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
                        } 
                    }
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
                }
            } 

            // Final processing, either we already have a stream or have to canonicalize 
            if (currentInput is Stream) { 
                return currentInput as Stream;
            } 
            if (currentInput is XmlNodeList) {
                CanonicalXml c14n = new CanonicalXml((XmlNodeList) currentInput, resolver, false);
                MemoryStream ms = new MemoryStream(c14n.GetBytes());
                return ms; 
            }
            if (currentInput is XmlDocument) { 
                CanonicalXml c14n = new CanonicalXml((XmlDocument) currentInput, resolver); 
                MemoryStream ms = new MemoryStream(c14n.GetBytes());
                return ms; 
            }
            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"));
        }
 
        internal Stream TransformToOctetStream(Stream input, XmlResolver resolver, string baseUri) {
            return TransformToOctetStream(input, typeof(Stream), resolver, baseUri); 
        } 

        internal Stream TransformToOctetStream(XmlDocument document, XmlResolver resolver, string baseUri) { 
            return TransformToOctetStream(document, typeof(XmlDocument), resolver, baseUri);
        }

        internal XmlElement GetXml (XmlDocument document, string ns) { 
            XmlElement transformsElement = document.CreateElement("Transforms", ns);
            foreach (Transform transform in m_transforms) { 
                if (transform != null) { 
                    // Construct the individual transform element
                    XmlElement transformElement = transform.GetXml(document); 
                    if (transformElement != null)
                        transformsElement.AppendChild(transformElement);
                }
            } 
            return transformsElement;
        } 
 
        internal void LoadXml (XmlElement value) {
            if (value == null) 
                throw new ArgumentNullException("value");

            XmlNamespaceManager nsm = new XmlNamespaceManager(value.OwnerDocument.NameTable);
            nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); 

            XmlNodeList transformNodes = value.SelectNodes("ds:Transform", nsm); 
            if (transformNodes.Count == 0) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_InvalidElement"), "Transforms");
 
            m_transforms.Clear();
            for (int i = 0; i < transformNodes.Count; ++i) {
                XmlElement transformElement = (XmlElement) transformNodes.Item(i);
                string algorithm = Utils.GetAttribute(transformElement, "Algorithm", SignedXml.XmlDsigNamespaceUrl); 
                Transform transform = CryptoConfig.CreateFromName(algorithm) as Transform;
                if (transform == null) 
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform")); 
                // let the transform read the children of the transformElement for data
                transform.LoadInnerXml(transformElement.ChildNodes); 
                m_transforms.Add(transform);
            }
        }
    } 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] 
    public abstract class Transform { 
        private string m_algorithm;
        private string m_baseUri = null; 
        internal XmlResolver m_xmlResolver = null;
        private bool m_bResolverSet = false;
        private SignedXml m_signedXml = null;
        private Reference m_reference = null; 
        private Hashtable m_propagatedNamespaces = null;
        private XmlElement m_context = null; 
 
        internal string BaseURI {
            get { return m_baseUri; } 
            set { m_baseUri = value; }
        }

        internal SignedXml SignedXml { 
            get { return m_signedXml; }
            set { m_signedXml = value; } 
        } 

        internal Reference Reference { 
            get { return m_reference; }
            set { m_reference = value; }
        }
 
        //
        // protected constructors 
        // 

        protected Transform() {} 

        //
        // public properties
        // 

        public string Algorithm { 
            get { return m_algorithm; } 
            set { m_algorithm = value; }
        } 

        [ComVisible(false)]
        public XmlResolver Resolver {
            // This property only has a setter. The rationale for this is that we don't have a good value 
            // to return when it has not been explicitely set, as we are using XmlSecureResolver by default
            set { 
                m_xmlResolver = value; 
                m_bResolverSet = true;
            } 
        }

        internal bool ResolverSet {
            get { return m_bResolverSet; } 
        }
 
        public abstract Type[] InputTypes { 
            get;
        } 

        public abstract Type[] OutputTypes {
            get;
        } 

        internal bool AcceptsType(Type inputType) { 
            if (InputTypes != null) { 
                for (int i=0; i 0) ? attrib.Prefix + ":" + attrib.LocalName : attrib.LocalName);
                    if (!m_propagatedNamespaces.Contains(key)) 
                        m_propagatedNamespaces.Add(key, attrib.Value);
                }
                return m_propagatedNamespaces;
            } 
        }
    } 
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigC14NTransform : Transform { 
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlDocument), typeof(XmlNodeList) };
        private Type[] _outputTypes = { typeof(Stream) };
        private CanonicalXml _cXml;
        private bool _includeComments = false; 

        public XmlDsigC14NTransform() { 
            Algorithm = SignedXml.XmlDsigC14NTransformUrl; 
        }
 
        public XmlDsigC14NTransform(bool includeComments) {
            _includeComments = includeComments;
            Algorithm = (includeComments ? SignedXml.XmlDsigC14NWithCommentsTransformUrl : SignedXml.XmlDsigC14NTransformUrl);
        } 

        public override Type[] InputTypes { 
            get { return _inputTypes; } 
        }
 
        public override Type[] OutputTypes {
            get { return _outputTypes; }
        }
 
        public override void LoadInnerXml(XmlNodeList nodeList) {}
 
        protected override XmlNodeList GetInnerXml() { 
            return null;
        } 

        public override void LoadInput(Object obj) {
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI));
            if (obj is Stream) { 
                _cXml = new CanonicalXml((Stream) obj, _includeComments, resolver, this.BaseURI);
                return; 
            } 
            if (obj is XmlDocument) {
                _cXml = new CanonicalXml((XmlDocument) obj, resolver, _includeComments); 
                return;
            }
            if (obj is XmlNodeList) {
                _cXml = new CanonicalXml((XmlNodeList) obj, resolver, _includeComments); 
            }
 	     else { 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_IncorrectObjectType"), "obj"); 
	     }
        } 

        public override Object GetOutput() {
            return new MemoryStream(_cXml.GetBytes());
        } 

        public override Object GetOutput(Type type) { 
            if (type != typeof(Stream) && !type.IsSubclassOf(typeof(Stream))) 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
            return new MemoryStream(_cXml.GetBytes()); 
        }

        [ComVisible(false)]
        public override byte[] GetDigestedOutput(HashAlgorithm hash) { 
            return _cXml.GetDigestedBytes(hash);
        } 
    } 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] 
    public class XmlDsigC14NWithCommentsTransform : XmlDsigC14NTransform {
        public XmlDsigC14NWithCommentsTransform()
            : base(true) {
            Algorithm = SignedXml.XmlDsigC14NWithCommentsTransformUrl; 
        }
    } 
 
    // 
    //      
    // 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigExcC14NTransform : Transform { 
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlDocument), typeof(XmlNodeList) };
        private Type[] _outputTypes = { typeof(Stream) }; 
        private bool _includeComments = false; 
        private string _inclusiveNamespacesPrefixList;
        private ExcCanonicalXml _excCanonicalXml; 

        public XmlDsigExcC14NTransform() : this(false, null) {}

        public XmlDsigExcC14NTransform(bool includeComments) : this(includeComments, null) {} 

        public XmlDsigExcC14NTransform(string inclusiveNamespacesPrefixList) : this(false, inclusiveNamespacesPrefixList) {} 
 
        public XmlDsigExcC14NTransform(bool includeComments, string inclusiveNamespacesPrefixList) {
            _includeComments = includeComments; 
            _inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList;
            Algorithm = (includeComments ? SignedXml.XmlDsigExcC14NWithCommentsTransformUrl : SignedXml.XmlDsigExcC14NTransformUrl);
        }
 
        public string InclusiveNamespacesPrefixList {
            get { return _inclusiveNamespacesPrefixList; } 
            set { _inclusiveNamespacesPrefixList = value; } 
        }
 
        public override Type[] InputTypes {
            get { return _inputTypes; }
        }
 
        public override Type[] OutputTypes {
            get { return _outputTypes; } 
        } 

        public override void LoadInnerXml(XmlNodeList nodeList) { 
            if (nodeList != null) {
                foreach (XmlNode n in nodeList) {
                    XmlElement e = n as XmlElement;
                    if (e != null && e.LocalName.Equals("InclusiveNamespaces") && 
                        e.NamespaceURI.Equals(SignedXml.XmlDsigExcC14NTransformUrl) &&
                        Utils.HasAttribute(e, "PrefixList", SignedXml.XmlDsigNamespaceUrl)) { 
                        this.InclusiveNamespacesPrefixList = Utils.GetAttribute(e, "PrefixList", SignedXml.XmlDsigNamespaceUrl); 
                        return;
                    } 
                }
            }
        }
 
        public override void LoadInput(Object obj) {
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI)); 
            if (obj is Stream) { 
                _excCanonicalXml = new ExcCanonicalXml((Stream) obj, _includeComments, _inclusiveNamespacesPrefixList, resolver, this.BaseURI);
            } 
            else if (obj is XmlDocument) {
                _excCanonicalXml = new ExcCanonicalXml((XmlDocument) obj, _includeComments, _inclusiveNamespacesPrefixList, resolver);
            }
            else if (obj is XmlNodeList) { 
                _excCanonicalXml = new ExcCanonicalXml((XmlNodeList) obj, _includeComments, _inclusiveNamespacesPrefixList, resolver);
            } else 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_IncorrectObjectType"), "obj"); 
        }
 
        protected override XmlNodeList GetInnerXml() {
            if (InclusiveNamespacesPrefixList == null)
                return null;
            XmlDocument document = new XmlDocument(); 
            XmlElement element = document.CreateElement("Transform", SignedXml.XmlDsigNamespaceUrl);
            if (!String.IsNullOrEmpty(this.Algorithm)) 
                element.SetAttribute("Algorithm", this.Algorithm); 
            XmlElement prefixListElement = document.CreateElement("InclusiveNamespaces", SignedXml.XmlDsigExcC14NTransformUrl);
            prefixListElement.SetAttribute("PrefixList", InclusiveNamespacesPrefixList); 
            element.AppendChild(prefixListElement);
            return element.ChildNodes;
        }
 
        public override Object GetOutput() {
            return new MemoryStream(_excCanonicalXml.GetBytes()); 
        } 

        public override Object GetOutput(Type type) { 
            if (type != typeof(Stream) && !type.IsSubclassOf(typeof(Stream)))
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
            return new MemoryStream(_excCanonicalXml.GetBytes());
        } 

        public override byte[] GetDigestedOutput(HashAlgorithm hash) { 
            return _excCanonicalXml.GetDigestedBytes(hash); 
        }
    } 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigExcC14NWithCommentsTransform : XmlDsigExcC14NTransform {
        public XmlDsigExcC14NWithCommentsTransform() : base(true) { 
            Algorithm = SignedXml.XmlDsigExcC14NWithCommentsTransformUrl;
        } 
 
        public XmlDsigExcC14NWithCommentsTransform(string inclusiveNamespacesPrefixList) : base(true, inclusiveNamespacesPrefixList) {
            Algorithm = SignedXml.XmlDsigExcC14NWithCommentsTransformUrl; 
        }
    }

    // A class representing conversion from Base64 using CryptoStream 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigBase64Transform : Transform { 
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlNodeList), typeof(XmlDocument) }; 
        private Type[] _outputTypes = { typeof(Stream) };
        private CryptoStream _cs = null; 

        public XmlDsigBase64Transform() {
            Algorithm = SignedXml.XmlDsigBase64TransformUrl;
        } 

        public override Type[] InputTypes { 
            get { return _inputTypes; } 
        }
 
        public override Type[] OutputTypes {
            get { return _outputTypes; }
        }
 
        public override void LoadInnerXml(XmlNodeList nodeList) {
        } 
 
        protected override XmlNodeList GetInnerXml() {
            return null; 
        }

        public override void LoadInput(Object obj) {
            if (obj is Stream) { 
                LoadStreamInput((Stream) obj);
                return; 
            } 
            if (obj is XmlNodeList) {
                LoadXmlNodeListInput((XmlNodeList) obj); 
                return;
            }
            if (obj is XmlDocument) {
                LoadXmlNodeListInput(((XmlDocument) obj).SelectNodes("//.")); 
                return;
            } 
        } 

        private void LoadStreamInput(Stream inputStream) { 
            if (inputStream == null) throw new ArgumentException("obj");
            MemoryStream ms = new MemoryStream();
            byte[] buffer = new byte[1024];
            int bytesRead; 
            do {
                bytesRead = inputStream.Read(buffer,0,1024); 
                if (bytesRead > 0) { 
                    int i = 0;
                    int j = 0; 
                    while ((j < bytesRead) && (!Char.IsWhiteSpace((char) buffer[j]))) j++;
                    i = j; j++;
                    while (j < bytesRead) {
                        if (!Char.IsWhiteSpace((char) buffer[j])) { 
                            buffer[i] = buffer[j];
                            i++; 
                        } 
                        j++;
                    } 
                    ms.Write(buffer,0,i);
                }
            } while (bytesRead > 0);
            ms.Position = 0; 
            _cs = new CryptoStream(ms, new FromBase64Transform(), CryptoStreamMode.Read);
        } 
 
        private void LoadXmlNodeListInput(XmlNodeList nodeList) {
            StringBuilder sb = new StringBuilder(); 
            foreach (XmlNode node in nodeList) {
                XmlNode result = node.SelectSingleNode("self::text()");
                if (result != null)
                    sb.Append(result.OuterXml); 
            }
            UTF8Encoding utf8 = new UTF8Encoding(false); 
            byte[] buffer = utf8.GetBytes(sb.ToString()); 
            int i = 0;
            int j = 0; 
            while ((j  0) 
                                element.SetAttribute("xmlns:" + prefix, _nsm.LookupNamespace(prefix));
                            break; 
                    }
                }
            }
            // Add the XPath as the inner xml of the element 
            element.InnerXml = _xpathexpr;
            document.AppendChild(element); 
            return document.ChildNodes; 
        }
 
        public override void LoadInput(Object obj) {
            if (obj is Stream) {
                LoadStreamInput((Stream) obj);
            } else if (obj is XmlNodeList) { 
                LoadXmlNodeListInput((XmlNodeList) obj);
            } else if (obj is XmlDocument) { 
                LoadXmlDocumentInput((XmlDocument) obj); 
            }
        } 

        private void LoadStreamInput(Stream stream) {
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI));
            XmlReader valReader = Utils.PreProcessStreamInput(stream, resolver, this.BaseURI); 
            _document = new XmlDocument();
            _document.PreserveWhitespace = true; 
            _document.Load(valReader); 
        }
 
        private void LoadXmlNodeListInput(XmlNodeList nodeList) {
            // Use C14N to get a document
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI));
            CanonicalXml c14n = new CanonicalXml((XmlNodeList) nodeList, resolver, true); 
            using (MemoryStream ms = new MemoryStream(c14n.GetBytes())) {
                _document = new XmlDocument(); 
                _document.PreserveWhitespace = true; 
                _document.Load(ms);
            } 
        }

        private void LoadXmlDocumentInput(XmlDocument doc) {
            _document = doc; 
        }
 
        public override Object GetOutput() { 
            CanonicalXmlNodeList resultNodeList = new CanonicalXmlNodeList();
            if (!String.IsNullOrEmpty(_xpathexpr)) { 
                XPathNavigator navigator = _document.CreateNavigator();
                XPathNodeIterator it = navigator.Select("//. | //@*");

                XPathExpression xpathExpr = navigator.Compile("boolean(" + _xpathexpr + ")"); 
                xpathExpr.SetContext(_nsm);
 
                while (it.MoveNext()) { 
                    XmlNode node = ((IHasXmlNode) it.Current).GetNode();
 
                    bool include = (bool) it.Current.Evaluate(xpathExpr);
                    if (include == true)
                        resultNodeList.Add(node);
                } 

                // keep namespaces 
                it = navigator.Select("//namespace::*"); 
                while (it.MoveNext()) {
                    XmlNode node = ((IHasXmlNode) it.Current).GetNode(); 
                    resultNodeList.Add(node);
                }
            }
 
            return resultNodeList;
        } 
 
        public override Object GetOutput(Type type) {
            if (type != typeof(XmlNodeList) && !type.IsSubclassOf(typeof(XmlNodeList))) 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
            return (XmlNodeList) GetOutput();
        }
    } 

    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] 
    public class XmlDsigXsltTransform : Transform { 
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlDocument), typeof(XmlNodeList) };
        private Type[] _outputTypes = { typeof(Stream) }; 
        private XmlNodeList _xslNodes;
        private string _xslFragment;
        private Stream _inputStream;
        private bool _includeComments = false; 

        public XmlDsigXsltTransform() { 
            Algorithm = SignedXml.XmlDsigXsltTransformUrl; 
        }
 
        public XmlDsigXsltTransform(bool includeComments) {
            _includeComments = includeComments;
            Algorithm = SignedXml.XmlDsigXsltTransformUrl;
        } 

        public override Type[] InputTypes { 
            get { 
                return _inputTypes;
            } 
        }

        public override Type[] OutputTypes {
            get { 
                return _outputTypes;
            } 
        } 

        public override void LoadInnerXml(XmlNodeList nodeList) { 
            if (nodeList == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
            // check that the XSLT element is well formed
            XmlElement firstDataElement = null; 
            int count = 0;
            foreach (XmlNode node in nodeList) { 
                // ignore white spaces, but make sure only one child element is present 
                if (node is XmlWhitespace) continue;
                if (node is XmlElement) { 
                    if (count != 0)
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
                    firstDataElement = node as XmlElement;
                    count++; 
                    continue;
                } 
                // Only allow white spaces 
                count++;
            } 
            if (count != 1 || firstDataElement == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
            _xslNodes = nodeList;
            _xslFragment = firstDataElement.OuterXml.Trim(null); 
        }
 
        protected override XmlNodeList GetInnerXml() { 
            return _xslNodes;
        } 

        public override void LoadInput(Object obj) {
            if (_inputStream != null)
                _inputStream.Close(); 
            _inputStream = new MemoryStream();
            if (obj is Stream) { 
                _inputStream = (Stream) obj; 
            }
            else if (obj is XmlNodeList) { 
                CanonicalXml xmlDoc = new CanonicalXml((XmlNodeList) obj, null, _includeComments);
                byte[] buffer = xmlDoc.GetBytes();
                if (buffer == null) return;
                _inputStream.Write(buffer, 0, buffer.Length); 
                _inputStream.Flush();
                _inputStream.Position = 0; 
            } 
            else if (obj is XmlDocument) {
                CanonicalXml xmlDoc = new CanonicalXml((XmlDocument) obj, null, _includeComments); 
                byte[] buffer = xmlDoc.GetBytes();
                if (buffer == null) return;
                _inputStream.Write(buffer, 0, buffer.Length);
                _inputStream.Flush(); 
                _inputStream.Position = 0;
            } 
        } 

        public override Object GetOutput() { 
            //  XSL transforms expose many powerful features by default:
            //  1- we need to pass a null evidence to prevent script execution.
            //  2- XPathDocument will expand entities, we don't want this, so set the resolver to null
            //  3- We don't want the document function feature of XslTransforms. 

            // load the XSL Transform 
            XslCompiledTransform xslt = new XslCompiledTransform(); 
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.XmlResolver = null; 
            using (StringReader sr = new StringReader(_xslFragment)) {
                XmlReader readerXsl = XmlReader.Create(sr, settings, null as string);
                xslt.Load(readerXsl, XsltSettings.Default, null);
 
                // Now load the input stream, XmlDocument can be used but is less efficient
                XmlReader reader = XmlReader.Create(_inputStream, settings, this.BaseURI); 
                XPathDocument inputData = new XPathDocument(reader, XmlSpace.Preserve); 

                // Create an XmlTextWriter 
                MemoryStream ms = new MemoryStream();
                XmlWriter writer = new XmlTextWriter(ms, null);

                // Transform the data and send the output to the memory stream 
                xslt.Transform(inputData, null, writer);
                ms.Position = 0; 
                return ms; 
            }
        } 

        public override Object GetOutput(Type type) {
            if (type != typeof(Stream) && !type.IsSubclassOf(typeof(Stream)))
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type"); 
            return (Stream) GetOutput();
        } 
    } 

 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigEnvelopedSignatureTransform : Transform {
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlNodeList), typeof(XmlDocument) };
        private Type[] _outputTypes = { typeof(XmlNodeList), typeof(XmlDocument) }; 
        private XmlNodeList _inputNodeList;
        private bool _includeComments = false; 
        private XmlNamespaceManager _nsm = null; 
        private XmlDocument _containingDocument = null;
        private int _signaturePosition = 0; 

        internal int SignaturePosition {
            set { _signaturePosition = value; }
        } 

        public XmlDsigEnvelopedSignatureTransform() { 
            Algorithm = SignedXml.XmlDsigEnvelopedSignatureTransformUrl; 
        }
 
        /// 
        public XmlDsigEnvelopedSignatureTransform(bool includeComments) {
            _includeComments = includeComments;
            Algorithm = SignedXml.XmlDsigEnvelopedSignatureTransformUrl; 
        }
 
        public override Type[] InputTypes { 
            get { return _inputTypes; }
        } 

        public override Type[] OutputTypes {
            get { return _outputTypes; }
        } 

        // An enveloped signature has no inner XML elements 
        public override void LoadInnerXml(XmlNodeList nodeList) {} 

        // An enveloped signature has no inner XML elements 
        protected override XmlNodeList GetInnerXml() {
            return null;
        }
 
        public override void LoadInput(Object obj) {
            if (obj is Stream) { 
                LoadStreamInput((Stream) obj); 
                return;
            } 
            if (obj is XmlNodeList) {
                LoadXmlNodeListInput((XmlNodeList) obj);
                return;
            } 
            if (obj is XmlDocument) {
                LoadXmlDocumentInput((XmlDocument) obj); 
                return; 
            }
        } 

        private void LoadStreamInput(Stream stream) {
            XmlDocument doc = new XmlDocument();
            doc.PreserveWhitespace = true; 
            doc.Load(stream);
            _containingDocument = doc; 
            if (_containingDocument == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_EnvelopedSignatureRequiresContext"));
            _nsm = new XmlNamespaceManager(_containingDocument.NameTable); 
            _nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl);
        }

        private void LoadXmlNodeListInput(XmlNodeList nodeList) { 
            // Empty node list is not acceptable
            if (nodeList == null) 
                throw new ArgumentNullException("nodeList"); 
            _containingDocument = Utils.GetOwnerDocument(nodeList);
            if (_containingDocument == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_EnvelopedSignatureRequiresContext"));

            _nsm = new XmlNamespaceManager(_containingDocument.NameTable);
            _nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl); 
            _inputNodeList = nodeList;
        } 
 
        private void LoadXmlDocumentInput(XmlDocument doc) {
            if (doc == null) 
                throw new ArgumentNullException("doc");
            _containingDocument = doc;
            _nsm = new XmlNamespaceManager(_containingDocument.NameTable);
            _nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl); 
        }
 
        public override Object GetOutput() { 
            if (_containingDocument == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_EnvelopedSignatureRequiresContext")); 

            // If we have received an XmlNodeList as input
            if (_inputNodeList != null) {
                // If the position has not been set, then we don't want to remove any signature tags 
                if (_signaturePosition == 0) return _inputNodeList;
                XmlNodeList signatureList = _containingDocument.SelectNodes("//dsig:Signature", _nsm); 
                if (signatureList == null) return _inputNodeList; 

                CanonicalXmlNodeList resultNodeList = new CanonicalXmlNodeList(); 
                foreach (XmlNode node in _inputNodeList) {
                    if (node == null)  continue;
                    // keep namespaces
                    if (Utils.IsXmlNamespaceNode(node) || Utils.IsNamespaceNode(node)) { 
                        resultNodeList.Add(node);
                    } else { 
                        // SelectSingleNode throws an exception for xmldecl PI for example, so we will just ignore those exceptions 
                        try {
                            // Find the nearest signature ancestor tag 
                            XmlNode result = node.SelectSingleNode("ancestor-or-self::dsig:Signature[1]", _nsm);
                            int position = 0;
                            foreach (XmlNode node1 in signatureList) {
                                position++; 
                                if (node1 == result) break;
                            } 
                            if (result == null || (result != null && position != _signaturePosition)) { 
                                resultNodeList.Add(node);
                            } 
                        }
                        catch {}
                    }
                } 
                return resultNodeList;
            } 
            // Else we have received either a stream or a document as input 
            else {
                XmlNodeList signatureList = _containingDocument.SelectNodes("//dsig:Signature", _nsm); 
                if (signatureList == null) return _containingDocument;
                if (signatureList.Count < _signaturePosition || _signaturePosition <= 0) return _containingDocument;

                // Remove the signature node with all its children nodes 
                signatureList[_signaturePosition - 1].ParentNode.RemoveChild(signatureList[_signaturePosition - 1]);
                return _containingDocument; 
            } 
        }
 
        public override Object GetOutput(Type type) {
            if (type == typeof(XmlNodeList) || type.IsSubclassOf(typeof(XmlNodeList))) {
                if (_inputNodeList == null) {
                    _inputNodeList = Utils.AllDescendantNodes(_containingDocument, true); 
                }
                return (XmlNodeList) GetOutput(); 
            } else if (type == typeof(XmlDocument) || type.IsSubclassOf(typeof(XmlDocument))) { 
                if (_inputNodeList != null) throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
                return (XmlDocument) GetOutput(); 
            } else {
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
            }
        } 
    }
 
    [Serializable] 
    internal enum TransformInputType {
        XmlDocument = 1, 
        XmlStream   = 2,
        XmlNodeSet  = 3
    }
 
    // XML Decryption Transform is used to specify the order of XML Digital Signature
    // and XML Encryption when performed on the same document. 
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDecryptionTransform : Transform { 
        private Type[] m_inputTypes = { typeof(Stream), typeof(XmlDocument) };
        private Type[] m_outputTypes = { typeof(XmlDocument) };
        private XmlNodeList m_encryptedDataList = null;
        private ArrayList m_arrayListUri = null; // this ArrayList object represents the Uri's to be excluded 
        private EncryptedXml m_exml = null; // defines the XML encryption processing rules
        private XmlDocument m_containingDocument = null; 
        private XmlNamespaceManager m_nsm = null; 
        private const string XmlDecryptionTransformNamespaceUrl = "http://www.w3.org/2002/07/decrypt#";
 
        public XmlDecryptionTransform() {
            Algorithm = SignedXml.XmlDecryptionTransformUrl;
        }
 
        private ArrayList ExceptUris {
            get { 
                if (m_arrayListUri == null) 
                    m_arrayListUri = new ArrayList();
                return m_arrayListUri; 
            }
        }

        protected virtual bool IsTargetElement (XmlElement inputElement, string idValue) { 
            if (inputElement == null)
                return false; 
            if (inputElement.GetAttribute("Id") == idValue || inputElement.GetAttribute("id") == idValue || 
                inputElement.GetAttribute("ID") == idValue)
                return true; 

            return false;
        }
 
        public EncryptedXml EncryptedXml {
            get { 
                if (m_exml != null) 
                    return m_exml;
 
                Reference reference = this.Reference;
                SignedXml signedXml = (reference == null ? this.SignedXml : reference.SignedXml);
                if (signedXml == null || signedXml.EncryptedXml == null)
                    m_exml = new EncryptedXml(m_containingDocument); // default processing rules 
                else
                    m_exml = signedXml.EncryptedXml; 
 
                return m_exml;
            } 
            set { m_exml = value; }
        }

        public override Type[] InputTypes { 
            get { return m_inputTypes; }
        } 
 
        public override Type[] OutputTypes {
            get { return m_outputTypes; } 
        }

        public void AddExceptUri (string uri) {
            if (uri == null) 
                throw new ArgumentNullException("uri");
            ExceptUris.Add(uri); 
        } 

        public override void LoadInnerXml(XmlNodeList nodeList) { 
            if (nodeList == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
            ExceptUris.Clear();
            foreach (XmlNode node in nodeList) { 
                XmlElement elem = node as XmlElement;
                if (elem != null && elem.LocalName == "Except" && elem.NamespaceURI == XmlDecryptionTransformNamespaceUrl) { 
                    // the Uri is required 
                    string uri = Utils.GetAttribute(elem, "URI", XmlDecryptionTransformNamespaceUrl);
                    if (uri == null || uri.Length == 0 || uri[0] != '#') 
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UriRequired"));
                    string idref = Utils.ExtractIdFromLocalUri(uri);
                    ExceptUris.Add(idref);
                } 
            }
        } 
 
        protected override XmlNodeList GetInnerXml() {
            if (ExceptUris.Count == 0) 
                return null;
            XmlDocument document = new XmlDocument();
            XmlElement element = document.CreateElement("Transform", SignedXml.XmlDsigNamespaceUrl);
            if (!String.IsNullOrEmpty(this.Algorithm)) 
                element.SetAttribute("Algorithm", this.Algorithm);
            foreach (string uri in ExceptUris) { 
                XmlElement exceptUriElement = document.CreateElement("Except", XmlDecryptionTransformNamespaceUrl); 
                exceptUriElement.SetAttribute("URI", uri);
                element.AppendChild(exceptUriElement); 
            }
            return element.ChildNodes;
        }
 
        public override void LoadInput(Object obj) {
            if (obj is Stream) { 
                LoadStreamInput((Stream) obj); 
            } else if (obj is XmlDocument) {
                LoadXmlDocumentInput((XmlDocument) obj); 
            }
        }

        private void LoadStreamInput(Stream stream) { 
            XmlDocument document = new XmlDocument();
            document.PreserveWhitespace = true; 
            document.Load(stream); 
            m_containingDocument = document;
            m_nsm = new XmlNamespaceManager(m_containingDocument.NameTable); 
            m_nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
            // select all EncryptedData elements
            m_encryptedDataList = document.SelectNodes("//enc:EncryptedData", m_nsm);
        } 

        private void LoadXmlDocumentInput(XmlDocument document) { 
            if (document == null) 
                throw new ArgumentNullException("document");
            m_containingDocument = document; 
            m_nsm = new XmlNamespaceManager(document.NameTable);
            m_nsm.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
            // select all EncryptedData elements
            m_encryptedDataList = document.SelectNodes("//enc:EncryptedData", m_nsm); 
        }
 
        private bool ProcessEncryptedDataItem (XmlElement encryptedDataElement) { 
            // first see whether we want to ignore this one
            if (ExceptUris.Count > 0) { 
                for (int index = 0; index < ExceptUris.Count; index++) {
                    if (IsTargetElement(encryptedDataElement, (string) ExceptUris[index]))
                        return false;
                } 
            }
            EncryptedData ed = new EncryptedData(); 
            ed.LoadXml(encryptedDataElement); 
            SymmetricAlgorithm symAlg = this.EncryptedXml.GetDecryptionKey(ed, null);
            if (symAlg == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_MissingDecryptionKey"));
            byte[] decrypted = EncryptedXml.DecryptData(ed, symAlg);
            this.EncryptedXml.ReplaceData(encryptedDataElement, decrypted);
            return true; 
        }
 
        private void ProcessElementRecursively (XmlNodeList encryptedDatas) { 
            if (encryptedDatas == null || encryptedDatas.Count == 0)
                return; 
            Queue encryptedDatasQueue = new Queue();
            foreach (XmlNode value in encryptedDatas) {
                encryptedDatasQueue.Enqueue(value);
            } 
            XmlNode node = encryptedDatasQueue.Dequeue() as XmlNode;
            while (node != null) { 
                XmlElement encryptedDataElement = node as XmlElement; 
                if (encryptedDataElement != null && encryptedDataElement.LocalName == "EncryptedData" &&
                    encryptedDataElement.NamespaceURI == EncryptedXml.XmlEncNamespaceUrl) { 
                    XmlNode sibling = encryptedDataElement.NextSibling;
                    XmlNode parent = encryptedDataElement.ParentNode;
                    if (ProcessEncryptedDataItem(encryptedDataElement)) {
                        // find the new decrypted element. 
                        XmlNode child = parent.FirstChild;
                        while (child != null && child.NextSibling != sibling) 
                            child = child.NextSibling; 
                        if (child != null) {
                            XmlNodeList nodes = child.SelectNodes("//enc:EncryptedData", m_nsm); 
                            if (nodes.Count > 0) {
                                foreach (XmlNode value in nodes) {
                                    encryptedDatasQueue.Enqueue(value);
                                } 
                            }
                        } 
                    } 
                }
                if (encryptedDatasQueue.Count == 0) 
                    break;
                node = encryptedDatasQueue.Dequeue() as XmlNode;
            }
        } 

        public override Object GetOutput() { 
            // decrypt the encrypted sections 
            if (m_encryptedDataList != null)
                ProcessElementRecursively(m_encryptedDataList); 
            // propagate namespaces
            Utils.AddNamespaces(m_containingDocument.DocumentElement, this.PropagatedNamespaces);
            return m_containingDocument;
        } 

        public override Object GetOutput(Type type) { 
            if (type == typeof(XmlDocument)) 
                return (XmlDocument) GetOutput();
            else 
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
        }
    }
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlLicenseTransform : Transform { 
        private Type[]              inputTypes       = { typeof(XmlDocument) }; 
        private Type[]              outputTypes      = { typeof(XmlDocument) };
        private XmlNamespaceManager namespaceManager = null; 
        private XmlDocument         license          = null;
        private IRelDecryptor       relDecryptor     = null;
        private const string        ElementIssuer    = "issuer";
        private const string        NamespaceUriCore = "urn:mpeg:mpeg21:2003:01-REL-R-NS"; 

        public XmlLicenseTransform() { 
            Algorithm = SignedXml.XmlLicenseTransformUrl; 
        }
 
        public override Type[] InputTypes {
            get { return inputTypes; }
        }
 
        public override Type[] OutputTypes {
            get { return outputTypes; } 
        } 

        public IRelDecryptor Decryptor { 
            get { return relDecryptor; }
            set { relDecryptor = value; }
        }
 
        private void DecryptEncryptedGrants(XmlNodeList encryptedGrantList, IRelDecryptor decryptor) {
            XmlElement       encryptionMethod    = null; 
            XmlElement       keyInfo             = null; 
            XmlElement       cipherData          = null;
            EncryptionMethod encryptionMethodObj = null; 
            KeyInfo          keyInfoObj          = null;
            CipherData       cipherDataObj       = null;

            for (int i = 0, count = encryptedGrantList.Count; i < count; i++) { 
                encryptionMethod = encryptedGrantList[i].SelectSingleNode("//r:encryptedGrant/enc:EncryptionMethod", namespaceManager) as XmlElement;
                keyInfo          = encryptedGrantList[i].SelectSingleNode("//r:encryptedGrant/dsig:KeyInfo", namespaceManager) as XmlElement; 
                cipherData       = encryptedGrantList[i].SelectSingleNode("//r:encryptedGrant/enc:CipherData", namespaceManager) as XmlElement; 
                if ((encryptionMethod != null) &&
                    (keyInfo != null) && 
                    (cipherData != null)) {
                    encryptionMethodObj = new EncryptionMethod();
                    keyInfoObj          = new KeyInfo();
                    cipherDataObj       = new CipherData(); 

                    encryptionMethodObj.LoadXml(encryptionMethod); 
                    keyInfoObj.LoadXml(keyInfo); 
                    cipherDataObj.LoadXml(cipherData);
 
                    MemoryStream toDecrypt        = null;
                    Stream       decryptedContent = null;
                    StreamReader streamReader     = null;
 
                    try {
                        toDecrypt = new MemoryStream(cipherDataObj.CipherValue); 
                        decryptedContent = relDecryptor.Decrypt(encryptionMethodObj, 
                                                                keyInfoObj, toDecrypt);
 
                        if ((decryptedContent == null) || (decryptedContent.Length == 0))
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlUnableToDecryptGrant"));

                        streamReader = new StreamReader(decryptedContent); 
                        string clearContent = streamReader.ReadToEnd();
 
                        encryptedGrantList[i].ParentNode.InnerXml = clearContent; 
                    }
                    finally { 
                        if (toDecrypt != null)
                            toDecrypt.Close();

                        if (decryptedContent != null) 
                            decryptedContent.Close();
 
                        if (streamReader != null) 
                            streamReader.Close();
                    } 

                    encryptionMethodObj = null;
                    keyInfoObj          = null;
                    cipherDataObj       = null; 
                }
 
                encryptionMethod = null; 
                keyInfo          = null;
                cipherData       = null; 
            }
        }

        // License transform has no inner XML elements 
        protected override XmlNodeList GetInnerXml() {
            return null; 
        } 

        public override object GetOutput() { 
            return license;
        }

        public override object GetOutput(Type type) { 
            if ((type != typeof(XmlDocument)) || (!type.IsSubclassOf(typeof(XmlDocument))))
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type"); 
 
            return GetOutput();
        } 

        // License transform has no inner XML elements
        public override void LoadInnerXml(XmlNodeList nodeList) {}
 
        public override void LoadInput (object obj) {
            // Check if the Context property is set before this transform is invoked. 
            if (Context == null) 
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlMissingContext"));
 
            license = new XmlDocument();
            license.PreserveWhitespace = true;
            namespaceManager = new XmlNamespaceManager(license.NameTable);
            namespaceManager.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl); 
            namespaceManager.AddNamespace("enc", EncryptedXml.XmlEncNamespaceUrl);
            namespaceManager.AddNamespace("r", NamespaceUriCore); 
 
            XmlElement currentIssuerContext  = null;
            XmlElement currentLicenseContext = null; 
            XmlNode    signatureNode         = null;

            // Get the nearest issuer node
            currentIssuerContext = Context.SelectSingleNode("ancestor-or-self::r:issuer[1]", namespaceManager) as XmlElement; 
            if (currentIssuerContext == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlMissingIssuer")); 
 
            signatureNode = currentIssuerContext.SelectSingleNode("descendant-or-self::dsig:Signature[1]", namespaceManager) as XmlElement;
            if (signatureNode != null) 
                signatureNode.ParentNode.RemoveChild(signatureNode);

            // Get the nearest license node
            currentLicenseContext = currentIssuerContext.SelectSingleNode("ancestor-or-self::r:license[1]", namespaceManager) as XmlElement; 
            if (currentLicenseContext == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlMissingLicence")); 
 
            XmlNodeList issuerList = currentLicenseContext.SelectNodes("descendant-or-self::r:license[1]/r:issuer", namespaceManager);
 
            // Remove all issuer nodes except current
            for (int i = 0, count = issuerList.Count; i < count; i++) {
                if (issuerList[i] == currentIssuerContext)
                    continue; 

                if ((issuerList[i].LocalName == ElementIssuer) && 
                    (issuerList[i].NamespaceURI == NamespaceUriCore)) 
                    issuerList[i].ParentNode.RemoveChild(issuerList[i]);
            } 

            XmlNodeList encryptedGrantList = currentLicenseContext.SelectNodes("/r:license/r:grant/r:encryptedGrant", namespaceManager);

            if (encryptedGrantList.Count > 0) { 
                if (relDecryptor == null)
                    throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_XrmlMissingIRelDecryptor")); 
 
                DecryptEncryptedGrants(encryptedGrantList, relDecryptor);
            } 

            license.InnerXml = currentLicenseContext.OuterXml;
        }
    } 

    public interface IRelDecryptor { 
        Stream Decrypt(EncryptionMethod encryptionMethod, KeyInfo keyInfo, Stream toDecrypt); 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK