File: System\Security\Cryptography\Xml\Transform.cs
Web Access
Project: src\src\libraries\System.Security.Cryptography.Xml\src\System.Security.Cryptography.Xml.csproj (System.Security.Cryptography.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
// 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).
 
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Xml;
 
namespace System.Security.Cryptography.Xml
{
    public abstract class Transform
    {
        private string? _algorithm;
        private string? _baseUri;
        internal XmlResolver? _xmlResolver;
        private bool _bResolverSet;
        private SignedXml? _signedXml;
        private Reference? _reference;
        private Hashtable? _propagatedNamespaces;
        private XmlElement? _context;
 
        internal string? BaseURI
        {
            get { return _baseUri; }
            set { _baseUri = value; }
        }
 
        internal SignedXml? SignedXml
        {
            get { return _signedXml; }
            set { _signedXml = value; }
        }
 
        internal Reference? Reference
        {
            get { return _reference; }
            set { _reference = value; }
        }
 
        //
        // protected constructors
        //
 
        protected Transform() { }
 
        //
        // public properties
        //
 
        public string? Algorithm
        {
            get { return _algorithm; }
            set { _algorithm = value; }
        }
 
        public XmlResolver? Resolver
        {
            internal get
            {
                return _xmlResolver;
            }
            // This property only has a public 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
            {
                _xmlResolver = value;
                _bResolverSet = true;
            }
        }
 
        [MemberNotNullWhen(true, nameof(_xmlResolver))]
        internal bool ResolverSet
        {
            get { return _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(Algorithm))
                transformElement.SetAttribute("Algorithm", Algorithm);
            XmlNodeList? children = 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);
 
        public virtual byte[] GetDigestedOutput(HashAlgorithm hash)
        {
            return hash.ComputeHash((Stream)GetOutput(typeof(Stream)));
        }
 
        public XmlElement? Context
        {
            get
            {
                if (_context != null)
                    return _context;
 
                Reference? reference = Reference;
                SignedXml? signedXml = (reference == null ? SignedXml : reference.SignedXml);
                if (signedXml == null)
                    return null;
 
                return signedXml._context;
            }
            set
            {
                _context = value;
            }
        }
 
        public Hashtable PropagatedNamespaces
        {
            get
            {
                if (_propagatedNamespaces != null)
                    return _propagatedNamespaces;
 
                Reference? reference = Reference;
                SignedXml? signedXml = (reference == null ? 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) ||
                     (string.IsNullOrEmpty(reference.Uri) || reference.Uri[0] != '#')))
                {
                    _propagatedNamespaces = new Hashtable(0);
                    return _propagatedNamespaces;
                }
 
                CanonicalXmlNodeList? namespaces = null;
                if (reference != null)
                    namespaces = reference._namespaces;
                else if (signedXml?._context != null)
                    namespaces = Utils.GetPropagatedAttributes(signedXml._context);
 
                // if no namespaces have been propagated, return an empty hashtable.
                if (namespaces == null)
                {
                    _propagatedNamespaces = new Hashtable(0);
                    return _propagatedNamespaces;
                }
 
                _propagatedNamespaces = new Hashtable(namespaces.Count);
                foreach (XmlNode attrib in namespaces)
                {
                    string key = ((attrib.Prefix.Length > 0) ? attrib.Prefix + ":" + attrib.LocalName : attrib.LocalName);
                    if (!_propagatedNamespaces.Contains(key))
                        _propagatedNamespaces.Add(key, attrib.Value);
                }
                return _propagatedNamespaces;
            }
        }
    }
}