File: system\security\cryptography\xml\transform.cs
Project: ndp\clr\src\managedlibraries\security\System.Security.csproj (System.Security)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// <OWNER>Microsoft</OWNER>
// 
 
//
// 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 <Transform>...</Transform> 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.Diagnostics.CodeAnalysis;
    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 = Utils.CreateFromName<Transform>(algorithm);
                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 get {
                return m_xmlResolver;
            }
        }
 
        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<InputTypes.Length; i++) {
                    if (inputType == InputTypes[i] || inputType.IsSubclassOf(InputTypes[i]))
                        return true;
                }
            }
            return false;
        }
 
        //
        // public methods
        //
 
        public XmlElement GetXml() {
            XmlDocument document = new XmlDocument();
            document.PreserveWhitespace = true;
            return GetXml(document);
        }
 
        internal XmlElement GetXml (XmlDocument document) {
            return GetXml (document, "Transform");
        }
 
        internal XmlElement GetXml (XmlDocument document, string name) {
            XmlElement transformElement = document.CreateElement(name, SignedXml.XmlDsigNamespaceUrl);
            if (!String.IsNullOrEmpty(this.Algorithm))
                transformElement.SetAttribute("Algorithm", this.Algorithm);
            XmlNodeList children = this.GetInnerXml();
            if (children != null) {
                foreach (XmlNode node in children) {
                    transformElement.AppendChild(document.ImportNode(node, true));
                }
            }
            return transformElement;
        }
 
        public abstract void LoadInnerXml(XmlNodeList nodeList);
 
        protected abstract XmlNodeList GetInnerXml();
 
        public abstract void LoadInput(Object obj);
 
        public abstract Object GetOutput();
 
        public abstract Object GetOutput(Type type);
 
        [ComVisible(false)]
        public virtual byte[] GetDigestedOutput(HashAlgorithm hash) {
            return hash.ComputeHash((Stream) GetOutput(typeof(Stream)));
        }
 
        [ComVisible(false)]
        public XmlElement Context {
            get {
                if (m_context != null)
                    return m_context;
 
                Reference reference = this.Reference;
                SignedXml signedXml = (reference == null ? this.SignedXml : reference.SignedXml);
                if (signedXml == null)
                    return null;
 
                return signedXml.m_context;
            }
            set {
                m_context = value;
            }
        }
 
        [ComVisible(false)]
        public Hashtable PropagatedNamespaces {
            get {
                if (m_propagatedNamespaces != null)
                    return m_propagatedNamespaces;
 
                Reference reference = this.Reference;
                SignedXml signedXml = (reference == null ? this.SignedXml : reference.SignedXml);
 
                // If the reference is not a Uri reference with a DataObject target, return an empty hashtable.
                if (reference != null && 
                    ((reference.ReferenceTargetType != ReferenceTargetType.UriReference) ||
                     (reference.Uri == null || reference.Uri.Length == 0 || reference.Uri[0] != '#'))) {
                    m_propagatedNamespaces = new Hashtable(0);
                    return m_propagatedNamespaces;
                }
 
                CanonicalXmlNodeList namespaces = null;
                if (reference != null)
                    namespaces = reference.m_namespaces;
                else if (signedXml.m_context != null)
                    namespaces = Utils.GetPropagatedAttributes(signedXml.m_context);
 
                // if no namespaces have been propagated, return an empty hashtable.
                if (namespaces == null) {
                    m_propagatedNamespaces = new Hashtable(0);
                    return m_propagatedNamespaces;
                }
 
                m_propagatedNamespaces = new Hashtable(namespaces.Count);
                foreach (XmlNode attrib in namespaces) {
                    string key = ((attrib.Prefix.Length > 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) {
            if (!Utils.GetAllowAdditionalSignatureNodes() && nodeList != null && nodeList.Count > 0)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
        }
 
        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;
        }
    }
 
    // <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
    //     <ec:InclusiveNamespaces PrefixList="dsig soap #default" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
    // </ds:Transform>
 
    [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) {
                        if (e.LocalName.Equals("InclusiveNamespaces") 
                        && e.NamespaceURI.Equals(SignedXml.XmlDsigExcC14NTransformUrl) &&
                        Utils.HasAttribute(e, "PrefixList", SignedXml.XmlDsigNamespaceUrl)) {
                            if (!Utils.VerifyAttributes(e, "PrefixList")) {
                                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
                            }
                            this.InclusiveNamespacesPrefixList = Utils.GetAttribute(e, "PrefixList", SignedXml.XmlDsigNamespaceUrl);
                            return;
                        }
                        else if (!Utils.GetAllowAdditionalSignatureNodes()) {
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
                        }
                    }
                }
            }
        }
 
        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) {
            if (!Utils.GetAllowAdditionalSignatureNodes() && nodeList != null && nodeList.Count > 0)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
        }
 
        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 <buffer.Length) && (!Char.IsWhiteSpace((char) buffer[j]))) j++;
            i = j; j++;
            while (j < buffer.Length) {
                if (!Char.IsWhiteSpace((char) buffer[j])) {
                    buffer[i] = buffer[j];
                    i++;
                }
                j++;
            }
            MemoryStream ms = new MemoryStream(buffer, 0, i);
            _cs = new CryptoStream(ms, new FromBase64Transform(), CryptoStreamMode.Read);
        }
 
        public override Object GetOutput() {
            return _cs;
        }
 
        public override Object GetOutput(Type type) {
            if (type != typeof(Stream) && !type.IsSubclassOf(typeof(Stream)))
                throw new ArgumentException(SecurityResources.GetResourceString("Cryptography_Xml_TransformIncorrectInputType"), "type");
            return _cs;
        }
    }
 
    // A class representing DSIG XPath Transforms
 
    [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
    public class XmlDsigXPathTransform : Transform {
        private Type[] _inputTypes = { typeof(Stream), typeof(XmlNodeList), typeof(XmlDocument) };
        private Type[] _outputTypes = { typeof(XmlNodeList) };
        private string _xpathexpr;
        private XmlDocument _document;
        private XmlNamespaceManager _nsm;
 
        public XmlDsigXPathTransform() {
            Algorithm = SignedXml.XmlDsigXPathTransformUrl;
        }
 
        public override Type[] InputTypes {
            get { return _inputTypes; }
        }
 
        public override Type[] OutputTypes {
            get { return _outputTypes; }
        }
 
        public override void LoadInnerXml(XmlNodeList nodeList) {
            // XPath transform is specified by text child of first XPath child
            if (nodeList == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
 
            foreach (XmlNode node in nodeList) {
                string prefix = null;
                string namespaceURI = null;
                XmlElement elem = node as XmlElement;
                if (elem != null) {
                    if (elem.LocalName == "XPath") {
                        _xpathexpr = elem.InnerXml.Trim(null);
                        XmlNodeReader nr = new XmlNodeReader(elem);
                        XmlNameTable nt = nr.NameTable;
                        _nsm = new XmlNamespaceManager(nt);
                        if (!Utils.VerifyAttributes(elem, (string)null)) {
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
                        }
                        // Look for a namespace in the attributes
                        foreach (XmlAttribute attrib in elem.Attributes) {
                            if (attrib.Prefix == "xmlns") {
                                prefix = attrib.LocalName;
                                namespaceURI = attrib.Value;
                                if (prefix == null) {
                                    prefix = elem.Prefix;
                                    namespaceURI = elem.NamespaceURI;
                                }
                                _nsm.AddNamespace(prefix, namespaceURI);
                            }
                        }
                        break;
                    }
                    else if (!Utils.GetAllowAdditionalSignatureNodes()) {
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
                    }
                }
            }
 
            if (_xpathexpr == null)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
        }
 
        protected override XmlNodeList GetInnerXml() {
            XmlDocument document = new XmlDocument();
            XmlElement element = document.CreateElement(null, "XPath", SignedXml.XmlDsigNamespaceUrl);
 
            if (_nsm != null) {
                // Add each of the namespaces as attributes of the element
                foreach (string prefix in _nsm) {
                    switch (prefix) {
                        // Ignore the xml namespaces
                        case "xml":
                        case "xmlns":
                            break;
 
                        // Other namespaces
                        default:
                            // Ignore the default namespace
                            if (prefix != null && prefix.Length > 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())) {
                LoadStreamInput(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;
            settings.MaxCharactersFromEntities = Utils.GetMaxCharactersFromEntities();
            settings.MaxCharactersInDocument = Utils.GetMaxCharactersInDocument();
            using (StringReader sr = new StringReader(_xslFragment)) {
                XmlReader readerXsl = XmlReader.Create(sr, settings, (string)null);
                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;
        }
 
        /// <internalonly/>
        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) {
            if (!Utils.GetAllowAdditionalSignatureNodes() && nodeList != null && nodeList.Count > 0)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
        }
 
        // 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;
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI));
            XmlReader xmlReader = Utils.PreProcessStreamInput(stream, resolver, this.BaseURI);
            doc.Load(xmlReader);
            _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) {
                    if (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"));
                        if (!Utils.VerifyAttributes(elem, "URI")) {
                            throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
                        }
                        string idref = Utils.ExtractIdFromLocalUri(uri);
                        ExceptUris.Add(idref);
                    }
                    else if (!Utils.GetAllowAdditionalSignatureNodes()) {
                        throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
                    }
                }
            }
        }
 
        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;
            XmlResolver resolver = (this.ResolverSet ? this.m_xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), this.BaseURI));
            XmlReader xmlReader = Utils.PreProcessStreamInput(stream, resolver, this.BaseURI);
            document.Load(xmlReader);
            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);
        }
 
        // Replace the encrytped XML element with the decrypted data for signature verification
        private void ReplaceEncryptedData(XmlElement encryptedDataElement, byte[] decrypted) {
            XmlNode parent = encryptedDataElement.ParentNode;
            if (parent.NodeType == XmlNodeType.Document) {
                // We're replacing the root element.  In order to correctly reflect the semantics of the
                // decryption transform, we need to replace the entire document with the decrypted data. 
                // However, EncryptedXml.ReplaceData will preserve other top-level elements such as the XML
                // entity declaration and top level comments.  So, in this case we must do the replacement
                // ourselves.
                parent.InnerXml = EncryptedXml.Encoding.GetString(decrypted);
            }
            else {
                // We're replacing a node in the middle of the document - EncryptedXml knows how to handle
                // this case in conformance with the transform's requirements, so we'll just defer to it.
                EncryptedXml.ReplaceData(encryptedDataElement, decrypted);
            }
        }
 
        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);
 
            ReplaceEncryptedData(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) {
            if (!Utils.GetAllowAdditionalSignatureNodes() && nodeList != null && nodeList.Count > 0)
                throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_UnknownTransform"));
        }
 
        [SuppressMessage("Microsoft.Security.Xml", "CA3058:DoNotUseSetInnerXml", Justification="Operates on inputs which were already parsed by XmlDocument with valid settings and already would have produced errors (DTD or external resolution)")]
        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);
    }
}