Advertisement
Guest User

XmlHash

a guest
Apr 6th, 2020
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.00 KB | None | 0 0
  1. namespace XmlSignatureTests
  2. {
  3.     using System.Xml;
  4.  
  5.     public class XmlHash
  6.     {
  7.         private class HashAlgorithm
  8.         {
  9.             public ulong Hash;
  10.  
  11.             internal static ulong GetHash(string data)
  12.             {
  13.                 return GetHash(data, 0);
  14.             }
  15.  
  16.             internal void AddString(string data)
  17.             {
  18.                 Hash = GetHash(data, Hash);
  19.             }
  20.  
  21.             internal void AddInt(int i)
  22.             {
  23.                 Hash += (Hash << 11 ) + (ulong)i;
  24.             }
  25.  
  26.             internal void AddULong(ulong u)
  27.             {
  28.                 Hash += (Hash << 11 ) + u;
  29.             }
  30.  
  31.             private static ulong GetHash( string data, ulong hash )
  32.             {
  33.                 hash += ( hash << 13 ) + (ulong)data.Length;
  34.                 // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
  35.                 foreach (var t in data)
  36.                 {
  37.                     hash += ( hash << 17 ) + t;
  38.                 }
  39.                 return hash;
  40.             }
  41.         }
  42.  
  43.         private const string Delimiter = "\0x01";
  44.  
  45.         public bool IgnoreChildOrder;
  46.         public bool IgnoreComments;
  47.         public bool IgnoreDtd;
  48.         public bool IgnoreNamespaces;
  49.         public bool IgnorePi;
  50.         public bool IgnorePrefixes;
  51.         public bool IgnoreWhitespace;
  52.         public bool IgnoreXmlDecl;
  53.  
  54.         internal ulong ComputeHash(XmlNode node)
  55.         {
  56.             if (node.NodeType == XmlNodeType.Document ||
  57.                 node.NodeType == XmlNodeType.DocumentFragment)
  58.             {
  59.                 return ComputeHashXmlFragment(node);
  60.             }
  61.            
  62.             return ComputeHashXmlNode(node);
  63.         }
  64.  
  65.         private ulong ComputeHashXmlFragment(XmlNode frag)
  66.         {
  67.             var ha = new HashAlgorithm();
  68.             ComputeHashXmlChildren(ha, frag);
  69.             return ha.Hash;
  70.         }
  71.  
  72.         private void ComputeHashXmlChildren(HashAlgorithm ha, XmlNode parent)
  73.         {
  74.             if (parent is XmlElement el)
  75.             {
  76.                 ulong attrHashSum = 0;
  77.                 var attrsCount = 0;
  78.                 var attrs = el.Attributes;
  79.                 for (var i = 0; i < attrs.Count; i++)
  80.                 {
  81.                     var attr = (XmlAttribute) attrs.Item(i);
  82.  
  83.                     ulong hashValue;
  84.  
  85.                     if (attr.LocalName == "xmlns" && attr.Prefix == string.Empty)
  86.                     {
  87.                         if (IgnoreNamespaces) continue;
  88.                         hashValue = HashNamespace(string.Empty, attr.Value);
  89.                     }
  90.                     else if (attr.Prefix == "xmlns")
  91.                     {
  92.                         if (IgnoreNamespaces) continue;
  93.                         hashValue = HashNamespace(attr.LocalName, attr.Value);
  94.                     }
  95.                     else
  96.                     {
  97.                         hashValue = HashAttribute(attr.LocalName,
  98.                             attr.Prefix,
  99.                             attr.NamespaceURI,
  100.                             IgnoreWhitespace
  101.                                 ? NormalizeText(attr.Value)
  102.                                 : attr.Value);
  103.                     }
  104.  
  105.                     attrsCount++;
  106.                     attrHashSum += hashValue;
  107.                 }
  108.  
  109.                 if (attrsCount != 0)
  110.                 {
  111.                     ha.AddULong(attrHashSum);
  112.                     ha.AddInt(attrsCount);
  113.                 }
  114.             }
  115.  
  116.             var childrenCount = 0;
  117.             if (IgnoreChildOrder)
  118.             {
  119.                 ulong totalHashSum = 0;
  120.                 var curChild = parent.FirstChild;
  121.                 while (curChild != null)
  122.                 {
  123.                     var hashValue = ComputeHashXmlNode(curChild);
  124.                     if (hashValue != 0)
  125.                     {
  126.                         totalHashSum += hashValue;
  127.                         childrenCount++;
  128.                     }
  129.  
  130.                     curChild = curChild.NextSibling;
  131.                 }
  132.  
  133.                 ha.AddULong(totalHashSum);
  134.             }
  135.             else
  136.             {
  137.                 var curChild = parent.FirstChild;
  138.                 while (curChild != null)
  139.                 {
  140.                     var hashValue = ComputeHashXmlNode(curChild);
  141.                     if (hashValue != 0)
  142.                     {
  143.                         ha.AddULong(hashValue);
  144.                         childrenCount++;
  145.                     }
  146.  
  147.                     curChild = curChild.NextSibling;
  148.                 }
  149.             }
  150.  
  151.             if (childrenCount != 0)
  152.                 ha.AddInt(childrenCount);
  153.         }
  154.  
  155.         private ulong ComputeHashXmlNode(XmlNode node)
  156.         {
  157.             switch (node.NodeType)
  158.             {
  159.                 case XmlNodeType.Element:
  160.                 {
  161.                     var el = (XmlElement) node;
  162.                     var ha = new HashAlgorithm();
  163.  
  164.                     HashElement(ha, el.LocalName, el.Prefix, el.NamespaceURI);
  165.                     ComputeHashXmlChildren(ha, el);
  166.  
  167.                     return ha.Hash;
  168.                 }
  169.                 case XmlNodeType.Attribute:
  170.                     return 0;
  171.  
  172.                 case XmlNodeType.Whitespace:
  173.                     return 0;
  174.  
  175.                 case XmlNodeType.SignificantWhitespace:
  176.                     if (!IgnoreWhitespace)
  177.                         goto case XmlNodeType.Text;
  178.                     return 0;
  179.                 case XmlNodeType.Comment:
  180.                     if (!IgnoreComments)
  181.                         return HashCharacterNode(XmlNodeType.Comment, ((XmlCharacterData) node).Value);
  182.                     return 0;
  183.                 case XmlNodeType.Text:
  184.                 {
  185.                     var cd = (XmlCharacterData) node;
  186.                     if (IgnoreWhitespace)
  187.                         return HashCharacterNode(cd.NodeType, NormalizeText(cd.Value));
  188.                     return HashCharacterNode(cd.NodeType, cd.Value);
  189.                 }
  190.                 case XmlNodeType.CDATA:
  191.                 {
  192.                     var cd = (XmlCharacterData) node;
  193.                     return HashCharacterNode(cd.NodeType, cd.Value);
  194.                 }
  195.                 case XmlNodeType.ProcessingInstruction:
  196.                 {
  197.                     if (IgnorePi)
  198.                         return 0;
  199.  
  200.                     var pi = (XmlProcessingInstruction) node;
  201.                     return HashPi(pi.Target, pi.Value);
  202.                 }
  203.                 case XmlNodeType.EntityReference:
  204.                 {
  205.                     var er = (XmlEntityReference) node;
  206.                     return HashEr(er.Name);
  207.                 }
  208.                 case XmlNodeType.XmlDeclaration:
  209.                 {
  210.                     if (IgnoreXmlDecl)
  211.                         return 0;
  212.                     var decl = (XmlDeclaration) node;
  213.                     return HashXmlDeclaration(NormalizeXmlDeclaration(decl.Value));
  214.                 }
  215.                 case XmlNodeType.DocumentType:
  216.                 {
  217.                     if (IgnoreDtd)
  218.                         return 0;
  219.                     var docType = (XmlDocumentType) node;
  220.                     return HashDocumentType(docType.Name, docType.PublicId, docType.SystemId, docType.InternalSubset);
  221.                 }
  222.                 case XmlNodeType.DocumentFragment:
  223.                     return 0;
  224.                 default:
  225.                     return 0;
  226.             }
  227.         }
  228.  
  229.         private void HashElement(HashAlgorithm ha, string localName, string prefix, string ns)
  230.         {
  231.             ha.AddString((int) XmlNodeType.Element +
  232.                          Delimiter +
  233.                          (IgnoreNamespaces || IgnorePrefixes ? string.Empty : prefix) +
  234.                          Delimiter +
  235.                          (IgnoreNamespaces ? string.Empty : ns) +
  236.                          Delimiter +
  237.                          localName);
  238.         }
  239.  
  240.         private ulong HashAttribute(string localName, string prefix, string ns, string value)
  241.         {
  242.             return HashAlgorithm.GetHash((int) XmlNodeType.Attribute +
  243.                                          Delimiter +
  244.                                          (IgnoreNamespaces || IgnorePrefixes ? string.Empty : prefix) +
  245.                                          Delimiter +
  246.                                          (IgnoreNamespaces ? string.Empty : ns) +
  247.                                          Delimiter +
  248.                                          localName +
  249.                                          Delimiter +
  250.                                          value);
  251.         }
  252.  
  253.         private ulong HashNamespace(string prefix, string ns)
  254.         {
  255.             return HashAlgorithm.GetHash(100 +
  256.                                          Delimiter +
  257.                                          (IgnorePrefixes ? string.Empty : prefix) +
  258.                                          Delimiter +
  259.                                          ns);
  260.         }
  261.  
  262.         private static ulong HashCharacterNode(XmlNodeType nodeType, string value)
  263.         {
  264.             return HashAlgorithm.GetHash((int) nodeType +
  265.                                          Delimiter +
  266.                                          value);
  267.         }
  268.  
  269.         private static ulong HashPi(string target, string value)
  270.         {
  271.             return HashAlgorithm.GetHash((int) XmlNodeType.ProcessingInstruction +
  272.                                          Delimiter +
  273.                                          target +
  274.                                          Delimiter +
  275.                                          value);
  276.         }
  277.  
  278.         private static ulong HashEr(string name)
  279.         {
  280.             return HashAlgorithm.GetHash((int) XmlNodeType.EntityReference +
  281.                                          Delimiter +
  282.                                          name);
  283.         }
  284.  
  285.         private static ulong HashXmlDeclaration(string value)
  286.         {
  287.             return HashAlgorithm.GetHash((int) XmlNodeType.XmlDeclaration +
  288.                                          Delimiter +
  289.                                          value);
  290.         }
  291.  
  292.         private static ulong HashDocumentType(string name, string publicId, string systemId, string subset)
  293.         {
  294.             return HashAlgorithm.GetHash((int) XmlNodeType.DocumentType +
  295.                                          Delimiter +
  296.                                          name +
  297.                                          Delimiter +
  298.                                          publicId +
  299.                                          Delimiter +
  300.                                          systemId +
  301.                                          Delimiter +
  302.                                          subset);
  303.         }
  304.  
  305.         private static string NormalizeXmlDeclaration( string value )
  306.         {
  307.             value = value.Replace( '\'', '"' );
  308.             return NormalizeText( value );
  309.         }
  310.  
  311.         private static string NormalizeText( string text )
  312.         {
  313.             var chars = text.ToCharArray();
  314.             var i = 0;
  315.             var j = 0;
  316.  
  317.             for (;;)
  318.             {
  319.                 while (j < chars.Length && char.IsWhiteSpace(text[j]))
  320.                 {
  321.                     j++;
  322.                 }
  323.  
  324.                 while (j < chars.Length && !char.IsWhiteSpace(text[j]))
  325.                 {
  326.                     chars[i++]=chars[j++];
  327.                 }
  328.  
  329.                 if (j < chars.Length)
  330.                 {
  331.                     chars[i++] = ' ';
  332.                     j++;
  333.                 }
  334.                 else
  335.                 {
  336.                     if (j == 0 || i == 0)
  337.                     {
  338.                         return string.Empty;
  339.                     }
  340.  
  341.                     if (char.IsWhiteSpace(chars[j - 1]))
  342.                     {
  343.                         i--;
  344.                     }
  345.  
  346.                     return new string(chars, 0, i);
  347.                 }
  348.             }
  349.         }
  350.     }
  351. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement