diff --git a/VectoCommon/VectoHashing/IVectoHash.cs b/VectoCommon/VectoHashing/IVectoHash.cs index 4a476e39b49070ca1f46ec609b68ffa8d98418fe..dbff49bfe07a8d6e7c0aa503515df074aaaba285 100644 --- a/VectoCommon/VectoHashing/IVectoHash.cs +++ b/VectoCommon/VectoHashing/IVectoHash.cs @@ -29,60 +29,111 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System.Collections.Generic; -using System.Xml.Linq; - -namespace TUGraz.VectoHashing -{ - public interface IVectoHash - { - IList<VectoComponents> GetContainigComponents(); - - /** - * Computes the hash-value of the top-level Data element (or vehicle) - * Note: the top-level Data element is required to have an id attribute! - * @return base64 encoded hash value - */ - string ComputeHash(); - - /** - * Computes the hash-value for the given component. If a component can exist multiple times - * (i.e., Tyres) the index specifies for which component the hash is computed - * Note: the Data element is required to have an id attribute! - * @return base64 encoded hash value - */ - string ComputeHash(VectoComponents component, int index = 0); - - /** - * Computes the hash-value of the outer Data element and adds the according Signature element - * after the Data element. - * Note: the id attribute is added to the Data element automatically. if an id attribute is already - * present its value is overwritten. - * @return returns the document including the Signature element with the hash of the Data block - */ - XDocument AddHash(); - - /** - * Reads the hash-value of the top-level Signature element - * @return base64 encoded hash value - */ - string ReadHash(); - - /** - * Reads the hash-value of the Signature element for the given component. If a component can exist - * multiple times (i.e., Tyres), the index specifies for which component the hash is computed - * @return base64 encoded hash value - */ - string ReadHash(VectoComponents component, int index = 0); - - /** - * Validates the hash of the top-level component (or vehicle) - */ - bool ValidateHash(); - - /** - * Validates the hash for the given component. - */ - bool ValidateHash(VectoComponents component, int index = 0); - } -} \ No newline at end of file +using System.Collections.Generic; +using System.Xml.Linq; + +namespace TUGraz.VectoHashing +{ + public interface IVectoHash + { + /** + * Get a list of all vecto components contained in the XML file. If a certain + * component appears multiple times (e.g. tires) it is provided multiple times + * in the returned list. + * to get a list with unique entries (and the number of occurences) use e.g. + * GetContainigComponents().GroupBy(s => s).Select(g => new { Entry = g.Key, Count = g.Count() }) + */ + IList<VectoComponents> GetContainigComponents(); + + /** + * Get the digest method used to compute the digest value of the top-level Signature element + * if there is no top-level Signature element, the default digest method is returned (see XMLHashProvider.DefaultDigestMethod) + * @return identifier (urn) of the digest method + */ + string GetDigestMethod(); + + /** + * Get the digest method of the Signature element for the given component. If a component exists + * multiple times (e.g., tires), the index specifies for which component the digest method is returned + * @param component + * @param index + * @return identifier (urn) of the digest method + */ + string GetDigestMethod(VectoComponents component, int index = 0); + + /** + * Get the list of canonicalization methods used to compute the digest value of the top-level Signature element + * If there is no top-level Signature element, the default digest method is returned (see XMLHashProvider.DefaulCanonicalizationMethod) + * @return returns a list of identifiers (urns) of the canonicalization methods + */ + IEnumerable<string> GetCanonicalizationMethods(); + + /** + * Get the list of canonicalization methods used to compute the digest value of the Signature element + * for the given component. If a component exists multiple times (e.g., tires) the indes specifies for which + * component the canonicalization method is returned + * If there is no top-level Signature element, the default digest method is returned (see XMLHashProvider.DefaulCanonicalizationMethod) + * @return returns a list of identifiers (urns) of the canonicalization methods + */ + IEnumerable<string> GetCanonicalizationMethods(VectoComponents component, int index = 0); + + /** + * Reads the hash-value of the top-level Signature element + * @return base64 encoded hash value + */ + string ReadHash(); + + /** + * Reads the hash-value of the Signature element for the given component. If a component can exist + * multiple times (i.e., tires), the index specifies for which component the hash is computed + * @return base64 encoded hash value + */ + string ReadHash(VectoComponents component, int index = 0); + + + /** + * Computes the hash-value of the top-level Data element (or vehicle) + * If the canoonicalizationMethods is null the canonicalizationMethods from + * the signature element are read if available or the default canonicalization is applied + * If the digestMethod is null the digestMethod from the signature element is read if + * available or the default digestMethod is used + * Note: the top-level Data element is required to have an id attribute! + * @return base64 encoded hash value + */ + string ComputeHash(IEnumerable<string> canonicalizationMethods = null, string digestMethod = null); + + /** + * Computes the hash-value for the given component. If a component can exist multiple times + * (i.e., Tyres) the index specifies for which component the hash is computed + * If the canoonicalizationMethods is null the canonicalizationMethods from + * the signature element are read if available or the default canonicalization is applied + * If the digestMethod is null the digestMethod from the signature element is read if + * available or the default digestMethod is used + * Note: the Data element is required to have an id attribute! + * @return base64 encoded hash value + */ + + string ComputeHash(VectoComponents component, int index = 0, IEnumerable<string> canonicalizationMethods = null, + string digestMethod = null); + + /** + * Validates the hash of the top-level component (or vehicle) + */ + bool ValidateHash(); + + /** + * Validates the hash for the given component. + */ + bool ValidateHash(VectoComponents component, int index = 0); + + /** + * Computes the hash-value of the outer Data element and adds the according Signature element + * after the Data element. + * The default CaonocalizationMethods and DigestMethod are used. + * Note: the id attribute is added to the Data element automatically. if an id attribute is already + * present its value is overwritten. + * @return returns the document including the Signature element with the hash of the Data block + */ + XDocument AddHash(); + } +} diff --git a/VectoCommon/VectoHashing/VectoHash.cs b/VectoCommon/VectoHashing/VectoHash.cs index 53b982286da84d66c9173856358f371c6c406302..ecc279df70ebe7cbd910332109856080b7cd84ec 100644 --- a/VectoCommon/VectoHashing/VectoHash.cs +++ b/VectoCommon/VectoHashing/VectoHash.cs @@ -99,29 +99,30 @@ namespace TUGraz.VectoHashing return retVal; } - public XElement ComputeXmlHash() + public XElement ComputeXmlHash(IEnumerable<string> canonicalization = null, string digestMethod = null) { var nodes = Document.SelectNodes(GetComponentQueryString()); if (nodes == null || nodes.Count == 0) { throw new Exception("No component found"); } var componentId = nodes[0].Attributes[XMLNames.Component_ID_Attr].Value; - var hash = DoComputeHash(nodes[0]); + var hash = DoComputeHash(nodes[0], canonicalization, digestMethod); return hash.ToXDocument().Root; } - public string ComputeHash() + public string ComputeHash(IEnumerable<string> canonicalization = null, string digestMethod = null) { var nodes = Document.SelectNodes(GetComponentQueryString()); if (nodes == null || nodes.Count == 0) { throw new Exception("No component found"); } var componentId = nodes[0].Attributes[XMLNames.Component_ID_Attr].Value; - return GetHashValueFromSig(DoComputeHash(nodes[0]), componentId); + return GetHashValueFromSig(DoComputeHash(nodes[0], canonicalization, digestMethod), componentId); } - public string ComputeHash(VectoComponents component, int index = 0) + public string ComputeHash(VectoComponents component, int index = 0, IEnumerable<string> canonicalization = null, + string digestMethod = null) { var nodes = Document.SelectNodes(GetComponentQueryString(component)); @@ -133,16 +134,29 @@ namespace TUGraz.VectoHashing nodes.Count)); } var componentId = nodes[index].Attributes[XMLNames.Component_ID_Attr].Value; - return GetHashValueFromSig(DoComputeHash(nodes[index]), componentId); + return GetHashValueFromSig(DoComputeHash(nodes[index], canonicalization, digestMethod), componentId); } - private static XmlDocument DoComputeHash(XmlNode dataNode) + private static XmlDocument DoComputeHash(XmlNode dataNode, IEnumerable<string> canonicalization, string digestMethod) { var parent = dataNode.ParentNode; if (parent == null) { throw new Exception("Invalid structure of input XML!"); } + + if (canonicalization == null) { + canonicalization = ReadCanonicalizationMethods(parent); + } + if (digestMethod == null) { + digestMethod = ReadDigestMethod(parent); + } + + canonicalization = canonicalization ?? XMLHashProvider.DefaultCanonicalizationMethod; + digestMethod = digestMethod ?? XMLHashProvider.DefaultDigestMethod; + + // copy the provided data Node to a new document before computing the hash + // required if the same component (e.g. tire) is used multiple times var newDoc = new XmlDocument(); var node = newDoc.CreateElement("Dummy"); newDoc.AppendChild(node); @@ -150,7 +164,28 @@ namespace TUGraz.VectoHashing node.AppendChild(newNode); var componentId = dataNode.Attributes[XMLNames.Component_ID_Attr].Value; - return XMLHashProvider.ComputeHash(newDoc, componentId); + return XMLHashProvider.ComputeHash(newDoc, componentId, canonicalization, digestMethod); + } + + private static string ReadDigestMethod(XmlNode rootNode) + { + var nodes = rootNode.SelectNodes("./*[local-name()='Signature']//*[local-name() = 'DigestMethod']/@Algorithm"); + if (nodes == null || nodes.Count == 0) { + return null; + } + if (nodes.Count > 1) { + throw new Exception("Multiple DigestValue elements found!"); + } + return nodes[0].InnerText; + } + + private static IEnumerable<string> ReadCanonicalizationMethods(XmlNode rootNode) + { + var nodes = rootNode.SelectNodes("./*[local-name()='Signature']//*[local-name() = 'Transform']/@Algorithm"); + if (nodes == null || nodes.Count == 0) { + return null; + } + return (from XmlNode node in nodes select node.InnerText).ToArray(); } public XDocument AddHash() @@ -197,7 +232,8 @@ namespace TUGraz.VectoHashing dateNode.FirstChild.Value = XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Utc); - var hash = XMLHashProvider.ComputeHash(Document, id); + var hash = XMLHashProvider.ComputeHash(Document, id, XMLHashProvider.DefaultCanonicalizationMethod, + XMLHashProvider.DefaultDigestMethod); var sig = Document.CreateElement(XMLNames.DI_Signature, node.NamespaceURI); if (node.ParentNode == null || hash.DocumentElement == null) { @@ -235,26 +271,68 @@ namespace TUGraz.VectoHashing throw new Exception("unknown document structure! neither input data nor output data format"); } + public string GetDigestMethod() + { + return DoGetDigestMethod(null, 0); + } + + + public string GetDigestMethod(VectoComponents component, int index = 0) + { + return DoGetDigestMethod(component, index); + } + + private string DoGetDigestMethod(VectoComponents? component, int index) + { + var nodes = GetNodes(component, index); + return ReadDigestMethod(nodes[index].ParentNode); + } + + public IEnumerable<string> GetCanonicalizationMethods() + { + return DoGetCanonicalizationMethods(null, 0); + } + + public IEnumerable<string> GetCanonicalizationMethods(VectoComponents component, int index = 0) + { + return DoGetCanonicalizationMethods(component, index); + } + + private IEnumerable<string> DoGetCanonicalizationMethods(VectoComponents? component, int index) + { + var nodes = GetNodes(component, index); + return ReadCanonicalizationMethods(nodes[index].ParentNode); + } + public string ReadHash() { - var nodes = Document.SelectNodes(GetComponentQueryString()); - if (nodes == null || nodes.Count == 0) { - throw new Exception("No component found"); - } - return ReadHashValue(nodes[0]); + return DoReadHash(null, 0); } public string ReadHash(VectoComponents component, int index = 0) + { + return DoReadHash(component, index); + } + + private string DoReadHash(VectoComponents? component, int index) + { + var nodes = GetNodes(component, index); + return ReadHashValue(nodes[index]); + } + + private XmlNodeList GetNodes(VectoComponents? component, int index) { var nodes = Document.SelectNodes(GetComponentQueryString(component)); if (nodes == null || nodes.Count == 0) { - throw new Exception(string.Format("Component {0} not found", component.XMLElementName())); + throw new Exception(component == null + ? "No component found" + : string.Format("Component {0} not found", component.Value.XMLElementName())); } if (index >= nodes.Count) { throw new Exception(string.Format("index exceeds number of components found! index: {0}, #components: {1}", index, nodes.Count)); } - return ReadHashValue(nodes[index]); + return nodes; } @@ -309,4 +387,4 @@ namespace TUGraz.VectoHashing return nodes[0].InnerText; } } -} \ No newline at end of file +}