diff --git a/VectoCommon/VectoHashing/Impl/XmlHashProvider.cs b/VectoCommon/VectoHashing/Impl/XmlHashProvider.cs index 5a6db4e290424fa7c280f93588d409eeecfa5572..a7ac61847266a843658fe92a5bc2fb2124cb1696 100644 --- a/VectoCommon/VectoHashing/Impl/XmlHashProvider.cs +++ b/VectoCommon/VectoHashing/Impl/XmlHashProvider.cs @@ -9,7 +9,7 @@ namespace TUGraz.VectoHashing.Impl { public static XmlDocument ComputeHash(XmlDocument doc, string elementId) { - if (doc == null || doc.DocumentElement == null) { + if (doc == null /*|| doc.DocumentElement == null*/) { throw new Exception("Invalid Document"); } var signedXml = new SignedXml(doc); diff --git a/VectoCommon/VectoHashing/VectoHash.cs b/VectoCommon/VectoHashing/VectoHash.cs index ef31b0c15028f9962dc7e4f222b932a08832aebd..af2706f955636a670f3b5ba63d03d5550ebbf7ce 100644 --- a/VectoCommon/VectoHashing/VectoHash.cs +++ b/VectoCommon/VectoHashing/VectoHash.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Xml; using System.Xml.Linq; using TUGraz.VectoCommon.Utils; @@ -14,7 +15,6 @@ namespace TUGraz.VectoHashing public class VectoHash : IVectoHash { protected XmlDocument Document; - private XmlNamespaceManager Manager; public static VectoHash Load(string filename) { @@ -46,7 +46,6 @@ namespace TUGraz.VectoHashing protected VectoHash(XmlDocument doc) { Document = doc; - Manager = new XmlNamespaceManager(doc.NameTable); } public IList<VectoComponents> GetContainigComponents() @@ -54,7 +53,7 @@ namespace TUGraz.VectoHashing var retVal = new List<VectoComponents>(); foreach (var component in EnumHelper.GetValues<VectoComponents>()) { var count = - Document.SelectNodes(string.Format("//*[local-name()='{0}']", component.XMLElementName()), Manager).Count; + Document.SelectNodes(string.Format("//*[local-name()='{0}']", component.XMLElementName())).Count; for (var i = 0; i < count; i++) { retVal.Add(component); } @@ -64,16 +63,43 @@ namespace TUGraz.VectoHashing public string ComputeHash() { - var toSign = GetIdForElement(GetComponentQueryString()); - var hash = XMLHashProvider.ComputeHash(Document, toSign); - return GetHashValue(hash, toSign); + var nodes = Document.SelectNodes(GetComponentQueryString()); + if (nodes == null || nodes.Count == 0) { + throw new Exception("No component found"); + } + return DoComputeHash(nodes[0]); } public string ComputeHash(VectoComponents component, int index = 0) { - var toSign = GetIdForElement(GetComponentQueryString(component), index); - var hash = XMLHashProvider.ComputeHash(Document, toSign); - return GetHashValue(hash, toSign); + var nodes = Document.SelectNodes(GetComponentQueryString(component)); + + + if (nodes == null || nodes.Count == 0) { + throw new Exception(string.Format("Component {0} not found", component.XMLElementName())); + } + if (index >= nodes.Count) { + throw new Exception(string.Format("index exceeds number of components found! index: {0}, #components: {1}", index, + nodes.Count)); + } + return DoComputeHash(nodes[index]); + } + + private static string DoComputeHash(XmlNode dataNode) + { + var parent = dataNode.ParentNode; + var componentId = dataNode.Attributes[XMLNames.Component_ID_Attr].Value; + if (parent == null) { + throw new Exception("Invalid structure of input XML!"); + } + var newDoc = new XmlDocument(); + var node = newDoc.CreateElement("Dummy"); + newDoc.AppendChild(node); + var newNode = newDoc.ImportNode(parent, true); + node.AppendChild(newNode); + + var hash = XMLHashProvider.ComputeHash(newDoc, componentId); + return GetHashValueFromSig(hash, componentId); } public XDocument AddHash() @@ -128,16 +154,27 @@ namespace TUGraz.VectoHashing public string ReadHash() { - var toRead = GetIdForElement(GetComponentQueryString()); - return GetHashValue(Document, toRead); + var nodes = Document.SelectNodes(GetComponentQueryString()); + if (nodes == null || nodes.Count == 0) { + throw new Exception(string.Format("Component {0} not found", nodes.Count)); + } + return ReadHashValue(nodes[0]); } public string ReadHash(VectoComponents component, int index = 0) { - var toRead = GetIdForElement(GetComponentQueryString(component), index); - return GetHashValue(Document, toRead); + var nodes = Document.SelectNodes(GetComponentQueryString(component)); + if (nodes == null || nodes.Count == 0) { + throw new Exception(string.Format("Component {0} not found", component.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]); } + public bool ValidateHash() { return ReadHash().Equals(ComputeHash(), StringComparison.Ordinal); @@ -149,30 +186,37 @@ namespace TUGraz.VectoHashing ComputeHash(component, index)); } + private static string GetComponentQueryString(VectoComponents? component = null) { if (component == null) { - return "(//*[@id]/@id)[1]"; + return "(//*[@id])[1]"; } return component == VectoComponents.Vehicle - ? string.Format("//*[local-name()='{0}']/@id", component.Value.XMLElementName()) - : string.Format("//*[local-name()='{0}']/*[local-name()='Data']/@id", component.Value.XMLElementName()); + ? string.Format("//*[local-name()='{0}']", component.Value.XMLElementName()) + : string.Format("//*[local-name()='{0}']/*[local-name()='Data']", component.Value.XMLElementName()); } - private string GetIdForElement(string query, int index = 0) + private static string GetHashValueFromSig(XmlDocument hashed, string elementId) { - var node = Document.SelectNodes(query); - if (node == null) { + var nodes = hashed.SelectNodes("//*[@URI='#" + elementId + "']/*[local-name() = 'DigestValue']"); + if (nodes == null || nodes.Count == 0) { return null; } - var toSign = node[index].Value; - return toSign; + if (nodes.Count > 1) { + throw new Exception("Multiple DigestValue elements found!"); + } + return nodes[0].InnerText; } - private static string GetHashValue(XmlDocument hashed, string elementToHash) + private static string ReadHashValue(XmlNode dataNode) { - //var node = hashed.SelectSingleNode("//*[@URI='#" + elementToHash + "']/*[local-name() = 'DigestValue']"); - var nodes = hashed.SelectNodes("//*[@URI='#" + elementToHash + "']/*[local-name() = 'DigestValue']"); + var parent = dataNode.ParentNode; + if (parent == null) { + throw new Exception("Invalid structure of input XML!"); + } + var elementToHash = dataNode.Attributes[XMLNames.Component_ID_Attr].Value; + var nodes = parent.SelectNodes(".//*[@URI='#" + elementToHash + "']/*[local-name() = 'DigestValue']"); if (nodes == null || nodes.Count == 0) { return null; } diff --git a/VectoCommon/VectoHashingTest/VectoHashTest.cs b/VectoCommon/VectoHashingTest/VectoHashTest.cs index 4ccc54996f24d1697dc04f59415c1e72a862a826..beddb40d173c22a9f8f54a6e9aeefb4661b7bb46 100644 --- a/VectoCommon/VectoHashingTest/VectoHashTest.cs +++ b/VectoCommon/VectoHashingTest/VectoHashTest.cs @@ -60,6 +60,20 @@ namespace VectoHashingTest Assert.AreEqual(expectedHash, hash); } + [TestCase(ReferenceXMLEngine)] + public void TestHashComputationInvalidComponent(string file) + { + var h = VectoHash.Load(file); + AssertHelper.Exception<Exception>(() => h.ComputeHash(VectoComponents.Gearbox), "Component Gearbox not found"); + } + + [TestCase(ReferenceXMLEngine)] + public void TestReadHashInvalidComponent(string file) + { + var h = VectoHash.Load(file); + AssertHelper.Exception<Exception>(() => h.ReadHash(VectoComponents.Gearbox), "Component Gearbox not found"); + } + [TestCase(ReferenceXMLVehicle, VectoComponents.Engine, "e0c253b643f7f8f09b963aca4a264d06fbfa599f"), TestCase(ReferenceXMLVehicle, VectoComponents.Gearbox, "d14189366134120e08fa3f2c6e3328dd13c08a23")] public void TestReadHash(string file, VectoComponents component, string expectedHash) @@ -80,6 +94,75 @@ namespace VectoHashingTest Assert.AreEqual(expectedHash, existingHash); } + [TestCase] + public void TestReadTyres1Index() + { + var file = @"Testdata\XML\ToHash\vecto_vehicle-sample_3axle1.xml"; + var h = VectoHash.Load(file); + var expectedHash = new[] { + "5074334bb2c090c5e258e9a664f5d19689a3f13d", + "6074334bb2c090c5e258e9a664f5d19689a3f13d", + "6074334bb2c090c5e258e9a664f5d19689a3f13d" + }; + + for (int i = 0; i < expectedHash.Length; i++) { + var existingHash = h.ReadHash(VectoComponents.Tyre, i); + + Assert.AreEqual(expectedHash[i], existingHash); + } + } + + [TestCase] + public void TestReadTyres2Index() + { + var file = @"Testdata\XML\ToHash\vecto_vehicle-sample_3axle2.xml"; + var h = VectoHash.Load(file); + var expectedHash = new[] { + "5074334bb2c090c5e258e9a664f5d19689a3f13d", + "5074334bb2c090c5e258e9a664f5d19689a3f13d", + "6074334bb2c090c5e258e9a664f5d19689a3f13d" + }; + + for (int i = 0; i < expectedHash.Length; i++) { + var existingHash = h.ReadHash(VectoComponents.Tyre, i); + + Assert.AreEqual(expectedHash[i], existingHash); + } + + AssertHelper.Exception<Exception>(() => h.ReadHash(VectoComponents.Tyre, 3), + "index exceeds number of components found! index: 3, #components: 3"); + } + + [TestCase] + public void TestComputeTyres1Index() + { + var file = @"Testdata\XML\ToHash\vecto_vehicle-sample_3axle1.xml"; + var h = VectoHash.Load(file); + + var hash1 = h.ComputeHash(VectoComponents.Tyre, 1); + var hash2 = h.ComputeHash(VectoComponents.Tyre, 2); + + Assert.AreEqual(hash1, hash2); + + AssertHelper.Exception<Exception>(() => h.ComputeHash(VectoComponents.Tyre, 3), + "index exceeds number of components found! index: 3, #components: 3"); + } + + [TestCase] + public void TestComputeTyres2Index() + { + var file = @"Testdata\XML\ToHash\vecto_vehicle-sample_3axle2.xml"; + var h = VectoHash.Load(file); + + var hash1 = h.ComputeHash(VectoComponents.Tyre, 0); + var hash2 = h.ComputeHash(VectoComponents.Tyre, 1); + + Assert.AreEqual(hash1, hash2); + + AssertHelper.Exception<Exception>(() => h.ComputeHash(VectoComponents.Tyre, 3), + "index exceeds number of components found! index: 3, #components: 3"); + } + [TestCase("vecto_vehicle-sample_FULL_Comments.xml", BasicHasingTests.HashVehicleXML), TestCase("vecto_vehicle-sample_FULL_Entry_Order.xml", BasicHasingTests.HashVehicleXML), TestCase("vecto_vehicle-sample_FULL_Newlines_Linux_LF.xml", BasicHasingTests.HashVehicleXML), @@ -178,6 +261,15 @@ namespace VectoHashingTest AssertHelper.Exception<Exception>(() => { var r = h.AddHash(); }, expectedExceptionMsg); } + [TestCase] + public void TestDuplicateSigElement() + { + var filename = @"Testdata\XML\Invalid\duplicate-sig.xml"; + var h = VectoHash.Load(filename); + + AssertHelper.Exception<Exception>(() => { var r = h.ReadHash(); }, "Multiple DigestValue elements found!"); + } + [TestCase()] public void TestLoadFromStream() @@ -246,6 +338,22 @@ namespace VectoHashingTest Assert.AreEqual(expectedHash, hash); } + [TestCase()] + public void TestInvalidXMLAsFile() + { + var file = @"Testdata\XML\Invalid\invalid-comp.xml"; + + AssertHelper.Exception<Exception>(() => VectoHash.Load(file), "failed to read XML document"); + } + + [TestCase()] + public void TestInvalidXMLAsStream() + { + var file = @"Testdata\XML\Invalid\invalid-comp.xml"; + var stream = File.Open(file, FileMode.Open); + AssertHelper.Exception<Exception>(() => VectoHash.Load(stream), "failed to read XML document"); + } + private static XmlSchemaSet GetXMLSchema(bool job) { var resource = RessourceHelper.LoadResourceAsStream(RessourceHelper.ResourceType.XMLSchema, diff --git a/VectoCommon/VectoHashingTest/VectoHashingTest.csproj b/VectoCommon/VectoHashingTest/VectoHashingTest.csproj index d901790cccad0b97313730f867aaacbdb623aa3b..59549b68fb3f1b6bccfd92236ff65ba2882d6662 100644 --- a/VectoCommon/VectoHashingTest/VectoHashingTest.csproj +++ b/VectoCommon/VectoHashingTest/VectoHashingTest.csproj @@ -65,6 +65,12 @@ <None Include="packages.config" /> </ItemGroup> <ItemGroup> + <Content Include="Testdata\XML\Invalid\duplicate-sig.xml"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="Testdata\XML\Invalid\invalid-comp.xml"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> <Content Include="Testdata\XML\Reference\vecto_engine-sample.xml"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> @@ -92,6 +98,12 @@ <Content Include="Testdata\XML\ToHash\vecto_gearbox-input_nodata.xml"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> + <Content Include="Testdata\XML\ToHash\vecto_vehicle-sample_3axle1.xml"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="Testdata\XML\ToHash\vecto_vehicle-sample_3axle2.xml"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> <Content Include="Testdata\XML\ToHash\vecto_vehicle-sample.xml"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content>