From 0951a2656073d847f492587d6101eec95c30301f Mon Sep 17 00:00:00 2001
From: Stefanos Doumpoulakis <dubulak@gmail.com>
Date: Thu, 11 Jan 2024 14:00:35 +0200
Subject: [PATCH] Feat #258: VIF validation by hashing tool

---
 HashingTool/Helper/HashingHelper.cs           | 13 +++++--
 .../ViewModel/UserControl/VectoJobFile.cs     | 11 +++++-
 VectoCommon/VectoHashing/VectoHash.cs         | 35 ++++++++++++++++---
 3 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/HashingTool/Helper/HashingHelper.cs b/HashingTool/Helper/HashingHelper.cs
index 9d1f1db7b7..9a5c494a9d 100644
--- a/HashingTool/Helper/HashingHelper.cs
+++ b/HashingTool/Helper/HashingHelper.cs
@@ -80,11 +80,18 @@ namespace HashingTool.Helper
 			if (x == null || x.DocumentElement == null) {
 				return null;
 			}
-			var valid = x.DocumentElement.LocalName == XMLNames.VectoInputDeclaration &&
-						x.DocumentElement.FirstChild.LocalName == XMLNames.Component_Vehicle;
+			var validSingleStep = (x.DocumentElement.LocalName == XMLNames.VectoInputDeclaration &&
+						x.DocumentElement.FirstChild.LocalName == XMLNames.Component_Vehicle);
+
+			var validMultiStep = (x.DocumentElement.LocalName == XMLNames.VectoOutputMultistep && 
+						x.DocumentElement.FirstChild.LocalName == XMLNames.Bus_PrimaryVehicle);
+
+			var valid = validSingleStep || validMultiStep;
+
 			if (!valid) {
 				errorLog.LogError($"Invalid XML file given ({x.DocumentElement.LocalName}/{x.DocumentElement.FirstChild.LocalName}). " +
-								$"Expected Vehicle XML ({XMLNames.VectoInputDeclaration}/{XMLNames.Component_Vehicle})!");
+								$"Expected Vehicle XML ({XMLNames.VectoInputDeclaration}/{XMLNames.Component_Vehicle}) or " +
+								$"({XMLNames.VectoOutputMultistep}/{XMLNames.Bus_PrimaryVehicle}) !");
 			}
 			return valid;
 		}
diff --git a/HashingTool/ViewModel/UserControl/VectoJobFile.cs b/HashingTool/ViewModel/UserControl/VectoJobFile.cs
index c8f1a7a5ad..acb5c4df0b 100644
--- a/HashingTool/ViewModel/UserControl/VectoJobFile.cs
+++ b/HashingTool/ViewModel/UserControl/VectoJobFile.cs
@@ -37,6 +37,7 @@ using System.Xml;
 using HashingTool.Helper;
 using TUGraz.VectoCommon.Hashing;
 using TUGraz.VectoCommon.Resources;
+using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoHashing;
 using XmlDocumentType = TUGraz.VectoCore.Utils.XmlDocumentType;
 
@@ -51,7 +52,8 @@ namespace HashingTool.ViewModel.UserControl
 
 
 		public VectoJobFile(string name, Func<XmlDocument, IErrorLogger, bool?> contentCheck,
-			Action<XmlDocument, VectoXMLFile> hashValidation = null) : base(name, true, contentCheck, XmlDocumentType.DeclarationJobData, hashValidation)
+			Action<XmlDocument, VectoXMLFile> hashValidation = null) : 
+			base(name, true, contentCheck, XmlDocumentType.DeclarationJobData | XmlDocumentType.MultistepOutputData, hashValidation)
 		{
 			_xmlFile.PropertyChanged += JobFilechanged;
 			Components = new ObservableCollection<ComponentEntry>();
@@ -176,6 +178,13 @@ namespace HashingTool.ViewModel.UserControl
 						entry.Component = component.Count == 1
 							? component.Entry.XMLElementName()
 							: $"{component.Entry.XMLElementName()} ({i + 1})";
+
+						if (entry.Component.IsOneOf(XMLNames.Bus_PrimaryVehicle, XMLNames.ManufacturingStep)
+							|| !h.ElementIsSigned(component.Entry, i)) {
+						
+							continue;
+						}
+
 						entry.Valid = h.ValidateHash(component.Entry, i);
 						entry.CanonicalizationMethod = h.GetCanonicalizationMethods(component.Entry, i).ToArray();
 						entry.DigestMethod = h.GetDigestMethod(component.Entry, i);
diff --git a/VectoCommon/VectoHashing/VectoHash.cs b/VectoCommon/VectoHashing/VectoHash.cs
index 83cd73763e..4a371f543e 100644
--- a/VectoCommon/VectoHashing/VectoHash.cs
+++ b/VectoCommon/VectoHashing/VectoHash.cs
@@ -110,11 +110,13 @@ namespace TUGraz.VectoHashing
 		public IList<VectoComponents> GetContainigComponents()
 		{
 			var retVal = new List<VectoComponents>();
+			var rootName = Document.FirstChild.NextSibling.LocalName;
+
 			foreach (var component in EnumHelper.GetValues<VectoComponents>()) {
 				// special treatment for REESS: can be either supercap or multiple batteries where the component node may contain several sub-components
 				var select = component == VectoComponents.ElectricEnergyStorage
-					? $"//*[local-name()='{XMLNames.VectoInputDeclaration}']//*[local-name()='{component.XMLElementName()}'  or local-name()='Capacitor']//*[local-name()='Data']"
-					: $"//*[local-name()='{XMLNames.VectoInputDeclaration}']//*[local-name()='{component.XMLElementName()}']";
+					? $"//*[local-name()='{rootName}']//*[local-name()='{component.XMLElementName()}'  or local-name()='Capacitor']//*[local-name()='Data']"
+					: $"//*[local-name()='{rootName}']//*[local-name()='{component.XMLElementName()}']";
                 var nodes = Document.SelectNodes(select);
 				var count = nodes?.Count ?? 0;
 				for (var i = 0; i < count; i++) {
@@ -137,7 +139,10 @@ namespace TUGraz.VectoHashing
 
 		public string ComputeHash(IEnumerable<string> canonicalization = null, string digestMethod = null)
 		{
-			var nodes = Document.SelectNodes(GetComponentQueryString());
+			var isMultiStep = (Document.ChildNodes.Count > 0) 
+				&& Document.ChildNodes[1].ChildNodes.Cast<XmlNode>().Any(x => x.LocalName == XMLNames.ManufacturingStep);
+			
+			var nodes = Document.SelectNodes(GetComponentQueryString(null, isMultiStep));
 			if (nodes == null || nodes.Count == 0) {
 				throw new Exception("No component found");
 			}
@@ -420,12 +425,32 @@ namespace TUGraz.VectoHashing
 				ComputeHash(component, index));
 		}
 
+		public bool ElementIsSigned(VectoComponents component, int index = 0)
+		{ 
+			var nodes = GetNodes(component, index);
+
+			var parent = nodes[index].ParentNode;
+			if (parent == null) {
+				throw new Exception("Invalid structure of input XML!");
+			}
+
+			if (nodes[index].Attributes[XMLNames.Component_ID_Attr] == null) {
+				return false;
+			}
+
+			var elementToHash = nodes[index].Attributes[XMLNames.Component_ID_Attr].Value;
+			var nodesDV = parent.SelectNodes(".//*[@URI='#" + elementToHash + "']/*[local-name() = 'DigestValue']");
+			
+			return (nodesDV != null && nodesDV.Count > 0);
+		}
 
-		protected static string GetComponentQueryString(VectoComponents? component = null)
+		protected static string GetComponentQueryString(VectoComponents? component = null, bool isMultiStep = false)
 		{
 			switch (component) {
 				case null:
-					return "(//*[@id])[1]";
+					return isMultiStep 
+						? $"//*[local-name()='{XMLNames.ManufacturingStep}']/*[local-name()='Data']" 
+						: "(//*[@id])[1]";
 				case VectoComponents.Vehicle:
 					return $"//*[local-name()='{component.Value.XMLElementName()}']";
 				case VectoComponents.ElectricEnergyStorage:
-- 
GitLab