Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
Commit b97c87e2 authored by Markus Quaritsch's avatar Markus Quaritsch
Browse files

some more refactoring/cleanup of the UI, started writing user manual, addin missing files

parent 63f0d770
No related branches found
No related tags found
No related merge requests found
Showing
with 324 additions and 226 deletions
# VECTO Hashing Tool
Version: 1.2
The VECTO Hashing tool provides functionality for hashing coponent data, checking the integrity of component data, checking the integrity of VECTO job data, and checking the integrity of VECTO reports and its job files.
## General
VECTO input data and VECTO results for certification of heavy duty vehicles uses the XML format. The XML schema for
botht, input data and results, can be found under the following URL and are distributed with the VECTO simulation tool.
- https://webgate.ec.europa.eu/CITnet/svn/VECTO/trunk/Share/XML/XSD/
## Method of Hash Computation
### Introduction
The integrity of electronic data, i.e., component data, job data, and VECTO results is of major importance throughout the whole process of vehicle CO2 certification and in particular among data exchange between the involved participants. The Commission and industry partners agreed to use (witnessed) cryptographic hashes as
integrity measure for all kinds of data. The digest value (cryptographic hash) shall be stored at a second
site, i.e., the type approval authority in case of component data, and the CO2 monitoring instance and the customer in case of the VECTO simulation results. Comparing the digest value of a component in the job data or the simulation result data with the digest value stored at the type approval authority allows to confirm the integrity of the component data, for example.
VECTO component data, VECTO job data, and VECTO simulation results are handled in XML format. Consequently, the agreed method for computing the digest value of electronic data is based on the [XML Dsig|https://www.w3.org/TR/xmldsig-core/] standard, which is also used for [eIDAS|http://data.europa.eu/eli/reg/2014/910/oj] and [XML Advanced Electronic Signatures(XAdES)|https://www.w3.org/TR/XAdES/].
For VECTO related data the detached signature approach is used, where the component data and the signature element are in the same XML document.
The XML representation of a certain XML document is ambiguous. Whitespaces, line breaks, comments, etc. may be added in various positions without actually altering the XML document's data. This is a huge drawback when directly applying cryptographic methods on XML documents, because changing for example the indentation (tab vs. spaces) invalidates all cryptographic operations although the content (and semantic) of the XML data is still the same. Therefore, it is crucial to use the same physical representation (called canonical form) of an XML document before applying cryptographic operations.
Consequently, if two documents have the same canonical form, then the two documents are logically equivalent
within the given application context. The [Canonical XML|https://www.w3.org/TR/xml-c14n11/] standard defines general transformations applied to an XML document to derive its canonical form. However, even after applying the canonicalization as described in the canonicalization standard, two XML documents with different canonical forms may still be equivalent in the context of VECTO for the following reasons:
- Entries in loss-maps, engine full-load curve, engine fuel-consumption map, etc. may be listed in
arbitrary order
- Gear entries in the transmission component may be listed in arbitrary order
- The vehicle's axles may be listed in arbitrary order
- Numeric values may be provided in different accuracy without affecting the simulation results.
For the last issue, the XML schema has been designed to require a defined number of digits after the decimal sign and no leading zeros are allowed.
To cope with the first three issues, an additional canonicalization transformation of the XML document is necessary. This canonicalization transformation sorts all ambiguous entries in a defined manner and is described programming language independent as XSLT transformaton. This XSLT transformation can be found under the following address and is shipped together with the VECTO hashing tool.
https://webgate.ec.europa.eu/CITnet/svn/VECTO/trunk/Share/XML/HashingXSLT/
### Hash Computation
Computing the digest value of an XML document in the VECTO context is done applying the following steps:
1. Apply the VECTO-specific canonicalization (i.e., sorting of ambiguous entries). This transformation is
identified via the URI "urn:vecto:xml:2017:canonicalization"
2. Apply the generic XML canonicalization (http://www.w3.org/2001/10/xml-exc-c14n#)
3. Compute the digest value using one of the supported digest methods
Currently, only two canonicalization methods, namely http://www.w3.org/2001/10/xml-exc-c14n#, and urn:vecto:xml:2017:canonicalization, and one digest method, namely http://www.w3.org/2001/04/xmlenc#sha256 are supported. Both canonicalization methods are mandatory (as described above). Further methods may be added later.
## Hashing Component Data
The "Hash Component Data" window allows to compute the digest value and adds the Signature element to an XML component file.
The selected component data file has to contain the structure of an XML component file as described in the XML schema, *except* it *must not* contain the Signature element following the Data element. Moreover, the selected file has to be a component file (engine, gearbox, axlegear, angledrive, tyre, retarder, torque converter), other file types such as job files and report files are *not supported*!.
The component data may already contain an id-attribute in the Data element. However, if the length of the id value is less than 5 characters it will be overwritten by the hashing tool in order to guarantee sufficient uniqueness.
The Date element in the component data will be overwritten in any case with the current time.
As a refeence for the user, the GUI shows the canonicalization method and digest method used as well as the computed digest value. The digest value can be easily copied to be used in other applications or filled into the certification report.
If the generated component file validates against the XML schema it can be saved to disk. Otherwise, the error messges and warnings can be inspected via the /Details.../ button.
## Verifying Integrity Component Data
## Verifying Integrity VECTO Job Data
## Verifying Integrity VECTO Results
## Using the Hashing Library
\ No newline at end of file
HashingTool/HashingIcon.ico

4.19 KiB

using System.Globalization;
namespace HashingTool.Helper
{
public class CultureAwareBinding : System.Windows.Data.Binding
{
public CultureAwareBinding()
{
ConverterCulture = CultureInfo.CurrentCulture;
}
}
}
......@@ -15,36 +15,37 @@ namespace HashingTool.Helper
public const string ToolTipXMLValidationFailed = "XML validation failed!";
public const string ToolTipOk = "Correct file selected";
public const string ToolTipHashInvalid = "Incorrect digest value!";
public const string ToolTipHashValid = "File integrity verified";
public const string ToolTipNone = "";
public static string ToolTipComponentHashInvalid = "Job-Data validation failed!";
public static bool? IsManufacturerReport(XmlDocument x, Collection<string> errorLog)
public static bool? IsManufacturerReport(XmlDocument x,IErrorLogger errorLog)
{
if (x == null || x.DocumentElement == null) {
return null;
}
var valid = x.DocumentElement.LocalName == XMLNames.VectoManufacturerReport;
if (!valid) {
errorLog.Add(String.Format("Invalid XML file given ({0}). Expected Manufacturer Report XML ({1})!",
errorLog.LogError(String.Format("Invalid XML file given ({0}). Expected Manufacturer Report XML ({1})!",
x.DocumentElement.LocalName, XMLNames.VectoManufacturerReport));
}
return valid;
}
public static bool? IsCustomerReport(XmlDocument x, Collection<string> errorLog)
public static bool? IsCustomerReport(XmlDocument x, IErrorLogger errorLog)
{
if (x == null || x.DocumentElement == null) {
return null;
}
var valid = x.DocumentElement != null && x.DocumentElement.LocalName == XMLNames.VectoCustomerReport;
if (!valid) {
errorLog.Add(String.Format("Invalid XML file given ({0}). Expected Customer Report XML ({1})!",
errorLog.LogError(String.Format("Invalid XML file given ({0}). Expected Customer Report XML ({1})!",
x.DocumentElement.LocalName, XMLNames.VectoCustomerReport));
}
return valid;
}
public static bool? IsJobFile(XmlDocument x, Collection<string> errorLog)
public static bool? IsJobFile(XmlDocument x, IErrorLogger errorLog)
{
if (x == null || x.DocumentElement == null) {
return null;
......@@ -52,21 +53,21 @@ namespace HashingTool.Helper
var valid = x.DocumentElement.LocalName == XMLNames.VectoInputDeclaration &&
x.DocumentElement.FirstChild.LocalName == XMLNames.Component_Vehicle;
if (!valid) {
errorLog.Add(String.Format("Invalid XML file given ({0}/{1}). Expected Vehicle XML ({2}/{3})!",
errorLog.LogError(String.Format("Invalid XML file given ({0}/{1}). Expected Vehicle XML ({2}/{3})!",
x.DocumentElement.LocalName, x.DocumentElement.FirstChild.LocalName, XMLNames.VectoInputDeclaration,
XMLNames.Component_Vehicle));
}
return valid;
}
public static bool? IsComponentFile(XmlDocument x, Collection<string> errorLog)
public static bool? IsComponentFile(XmlDocument x, IErrorLogger errorLog)
{
if (x.DocumentElement == null) {
return null;
}
if (x.DocumentElement.LocalName != XMLNames.VectoInputDeclaration) {
errorLog.Add(String.Format("Invalid XML file given ({0}). Expected Component XML ({1})!",
errorLog.LogError(String.Format("Invalid XML file given ({0}). Expected Component XML ({1})!",
x.DocumentElement.LocalName, XMLNames.VectoInputDeclaration));
return false;
......@@ -79,7 +80,7 @@ namespace HashingTool.Helper
};
var valid = components.Where(c => c.XMLElementName() == localName).Any();
if (!valid) {
errorLog.Add(String.Format("Invalid XML file given ({0}). Expected Component XML ({1})!",
errorLog.LogError(String.Format("Invalid XML file given ({0}). Expected Component XML ({1})!",
localName, String.Join(", ", components.Select(c => c.XMLElementName()))));
}
return valid;
......@@ -94,7 +95,7 @@ namespace HashingTool.Helper
xmlViewModel.DigestMethod = h.GetDigestMethod();
xmlViewModel.SetCanonicalizationMethod(h.GetCanonicalizationMethods());
} catch (Exception e) {
xmlViewModel.XMLFile.XMLValidationErrors.Add(e.Message);
xmlViewModel.XMLFile.LogError(e.Message);
xmlViewModel.DigestValueComputed = "";
}
}
......@@ -120,11 +121,11 @@ namespace HashingTool.Helper
report.DigestValueComputed = "";
}
var valid = h.ValidateHash();
report.ValidTooltip = valid ? ToolTipOk : ToolTipHashInvalid;
report.Valid = valid;
report.FileIntegrityTooltip = valid ? ToolTipHashValid : ToolTipHashInvalid;
report.FileIntegrityValid = valid;
} catch (Exception e) {
report.XMLFile.XMLValidationErrors.Add(e.Message);
report.Valid = false;
report.XMLFile.LogError(e.Message);
report.FileIntegrityValid = false;
}
}
}
......
......@@ -131,7 +131,7 @@ namespace HashingTool.ViewModel
ComponentDataValid = true;
var validator = new XMLValidator(r => { ComponentDataValid = r; },
(s, e) => {
Application.Current.Dispatcher.Invoke(() => _xmlFile.XMLValidationErrors.Add(
Application.Current.Dispatcher.Invoke(() => _xmlFile.LogError(
string.Format("Validation {0} Line {2}: {1}", s == XmlSeverityType.Warning ? "WARNING" : "ERROR",
e.ValidationEventArgs == null
? e.Exception.Message +
......@@ -161,7 +161,7 @@ namespace HashingTool.ViewModel
} catch (Exception e) {
ComponentDataValid = false;
DigestValue = "";
_xmlFile.XMLValidationErrors.Add(e.Message);
_xmlFile.LogError(e.Message);
SetCanonicalizationMethod(new string[] { });
DigestMethod = "";
} finally {
......
......@@ -8,7 +8,7 @@ namespace HashingTool.ViewModel.UserControl
{
protected string _digestValueRead;
public HashedXMLFile(string name, Func<XmlDocument, Collection<string>, bool?> contentCheck,
public HashedXMLFile(string name, Func<XmlDocument, IErrorLogger, bool?> contentCheck,
Action<XmlDocument, VectoXMLFile> hashValidation = null) : base(name, true, contentCheck, hashValidation) {}
public string DigestValueRead
......
......@@ -15,7 +15,9 @@ namespace HashingTool.ViewModel.UserControl
{
private ViewModel.ComponentEntry[] _jobComponents;
public ManufacturerReportXMLFile(string name, Func<XmlDocument, Collection<string>, bool?> contentCheck,
private readonly ObservableCollection<string> _validationErrors = new ObservableCollection<string>();
public ManufacturerReportXMLFile(string name, Func<XmlDocument, IErrorLogger, bool?> contentCheck,
Action<XmlDocument, VectoXMLFile> hashValidation = null) : base(name, contentCheck, hashValidation)
{
_xmlFile.PropertyChanged += UpdateComponents;
......@@ -35,6 +37,11 @@ namespace HashingTool.ViewModel.UserControl
private get { return _jobComponents; }
}
public ObservableCollection<string> ValidationErrors
{
get { return _validationErrors; }
}
private void UpdateComponents(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != "UPDATED") {
......@@ -119,6 +126,7 @@ namespace HashingTool.ViewModel.UserControl
public bool ManufacturerReportValid
{
get {
_validationErrors.Clear();
var componentsValid = JobComponents != null && JobComponents.Length > 0;
if (Components == null || JobComponents == null || JobComponents.Length == 0) {
return false;
......@@ -129,11 +137,11 @@ namespace HashingTool.ViewModel.UserControl
var entryCertificationNbr = entry.CertificationNumberMatchesJobComponent == null ||
entry.CertificationNumberMatchesJobComponent.Value;
if (!entryCertificationNbr) {
var msg = string.Format("Certification number for component {0} does not match! Job-File: {1}, Report: {2}",
entry.Component, entry.CertificationNumberExpected, entry.CertificationNumber);
if (!_xmlFile.XMLValidationErrors.Contains(msg)) {
_xmlFile.XMLValidationErrors.Add(msg);
}
var msg =
string.Format(
"Verifying Manufacturer Report: Certification number for component '{0}' does not match! Job-File: '{1}', Report: '{2}'",
entry.Component, entry.CertificationNumberExpected, entry.CertificationNumber);
_validationErrors.Add(msg);
}
componentsValid &= entryCertificationNbr;
// digest value is mandatory (except for tires)
......@@ -143,11 +151,11 @@ namespace HashingTool.ViewModel.UserControl
var entryDigest = entry.DigestValueMatchesJobComponent != null && entry.DigestValueMatchesJobComponent.Value;
if (!entryDigest) {
var msg = string.Format("Digest value for component {0} does not match! Job-File: {1}, Report: {2}",
entry.Component, entry.DigestValueExpected, entry.DigestValue);
if (!_xmlFile.XMLValidationErrors.Contains(msg)) {
_xmlFile.XMLValidationErrors.Add(msg);
}
var msg =
string.Format(
"Verifying Manufacturer Report: Digest value for component '{0}' does not match! Job-File: '{1}', Report: '{2}'",
entry.Component, entry.DigestValueExpected, entry.DigestValue);
_validationErrors.Add(msg);
}
componentsValid &= entryDigest;
}
......@@ -229,4 +237,4 @@ namespace HashingTool.ViewModel.UserControl
public string CertificationNumberExpected { get; set; }
}
}
}
\ No newline at end of file
}
......@@ -15,7 +15,7 @@ namespace HashingTool.ViewModel.UserControl
private bool _jobDigestValid;
private DateTime? _creationDate;
public ReportXMLFile(string name, Func<XmlDocument, Collection<string>, bool?> contentCheck,
public ReportXMLFile(string name, Func<XmlDocument,IErrorLogger, bool?> contentCheck,
Action<XmlDocument, VectoXMLFile> hashValidation = null)
: base(name, contentCheck, hashValidation)
{
......@@ -137,4 +137,4 @@ namespace HashingTool.ViewModel.UserControl
}
}
}
}
\ No newline at end of file
}
......@@ -11,22 +11,23 @@ namespace HashingTool.ViewModel.UserControl
{
public class VectoJobFile : VectoXMLFile
{
private bool _componentDataValid;
private bool? _componentDataValid;
private string _jobValidToolTip;
private string _vin;
private DateTime? _jobDate;
public VectoJobFile(string name, Func<XmlDocument, Collection<string>, bool?> contentCheck,
public VectoJobFile(string name, Func<XmlDocument, IErrorLogger, bool?> contentCheck,
Action<XmlDocument, VectoXMLFile> hashValidation = null) : base(name, true, contentCheck, hashValidation)
{
_xmlFile.PropertyChanged += JobFilechanged;
Components = new ObservableCollection<ComponentEntry>();
JobDataValid = null;
}
public ObservableCollection<ComponentEntry> Components { get; private set; }
public bool JobDataValid
public bool? JobDataValid
{
get { return _componentDataValid; }
set {
......@@ -34,7 +35,7 @@ namespace HashingTool.ViewModel.UserControl
return;
}
_componentDataValid = value;
JobValidToolTip = value ? HashingHelper.ToolTipComponentHashInvalid : HashingHelper.ToolTipOk;
JobValidToolTip = value != null && !value.Value ? HashingHelper.ToolTipComponentHashInvalid : HashingHelper.ToolTipOk;
RaisePropertyChanged("JobDataValid");
}
}
......@@ -121,7 +122,7 @@ namespace HashingTool.ViewModel.UserControl
DigestValueComputed = "";
DigestMethod = "";
SetCanonicalizationMethod(new string[] { });
JobDataValid = false;
JobDataValid = null;
return;
}
try {
......@@ -148,7 +149,7 @@ namespace HashingTool.ViewModel.UserControl
entry.CertificationNumber = h.GetCertificationNumber(component.Entry, i);
entry.CertificationDate = h.GetCertificationDate(component.Entry, i);
if (!entry.Valid) {
_xmlFile.XMLValidationErrors.Add(
_xmlFile.LogError(
string.Format(
"Digest Value mismatch for component \"{0}\". Read digest value: \"{1}\", computed digest value \"{2}\"",
entry.Component, entry.DigestValueRead, entry.DigestValueComputed));
......@@ -163,8 +164,8 @@ namespace HashingTool.ViewModel.UserControl
} catch (Exception e) {
DigestValueComputed = "";
JobDataValid = false;
_xmlFile.XMLValidationErrors.Add(e.Message);
_xmlFile.LogError(e.Message);
}
}
}
}
\ No newline at end of file
}
......@@ -12,7 +12,7 @@ namespace HashingTool.ViewModel.UserControl
protected readonly XMLFileSelector _xmlFile;
protected string _digestValueComputed;
protected bool? _valid;
protected bool? _fileIntegrityValid;
protected string _name;
protected string _tooltip;
protected string _componentType;
......@@ -20,17 +20,17 @@ namespace HashingTool.ViewModel.UserControl
private string _digestMethod;
public VectoXMLFile(string name, bool validate, Func<XmlDocument, Collection<string>, bool?> contentCheck,
public VectoXMLFile(string name, bool validate, Func<XmlDocument, IErrorLogger, bool?> contentCheck,
Action<XmlDocument, VectoXMLFile> hashValidation = null)
{
_validateHashes = hashValidation;
_xmlFile = new XMLFileSelector(IoService, validate, contentCheck);
_xmlFile = new XMLFileSelector(IoService, name, validate, contentCheck);
_xmlFile.PropertyChanged += FileChanged;
Name = name;
CanonicalizationMethods = new ObservableCollection<string>();
Valid = null;
ValidTooltip = HashingHelper.ToolTipNone;
FileIntegrityValid = null;
FileIntegrityTooltip = HashingHelper.ToolTipNone;
}
protected virtual void FileChanged(object sender, PropertyChangedEventArgs e)
......@@ -39,24 +39,10 @@ namespace HashingTool.ViewModel.UserControl
return;
}
if (_xmlFile.IsValid == XmlFileStatus.ValidXML) {
if (_xmlFile.HasContentValidation) {
Valid = _xmlFile.ContentValid;
if (Valid != null && Valid.Value) {
ValidTooltip = HashingHelper.ToolTipOk;
} else {
ValidTooltip = HashingHelper.ToolTipInvalidFileType;
}
} else {
ValidTooltip = HashingHelper.ToolTipOk;
}
} else {
Valid = false;
ValidTooltip = HashingHelper.ToolTipXMLValidationFailed;
}
if (Valid != null && Valid.Value && _validateHashes != null) {
if (_xmlFile.IsValid == XmlFileStatus.ValidXML && _validateHashes != null) {
_validateHashes(_xmlFile.Document, this);
} else {
FileIntegrityValid = null;
}
RaisePropertyChanged("UPDATED");
}
......@@ -116,19 +102,19 @@ namespace HashingTool.ViewModel.UserControl
}
public bool? Valid
public bool? FileIntegrityValid
{
get { return _valid; }
get { return _fileIntegrityValid; }
internal set {
if (_valid == value) {
if (_fileIntegrityValid == value) {
return;
}
_valid = value;
RaisePropertyChanged("Valid");
_fileIntegrityValid = value;
RaisePropertyChanged("FileIntegrityValid");
}
}
public string ValidTooltip
public string FileIntegrityTooltip
{
get { return _tooltip; }
set {
......@@ -136,7 +122,7 @@ namespace HashingTool.ViewModel.UserControl
return;
}
_tooltip = value;
RaisePropertyChanged("ValidTooltip");
RaisePropertyChanged("FileIntegrityTooltip");
}
}
......@@ -152,4 +138,4 @@ namespace HashingTool.ViewModel.UserControl
}
}
}
}
\ No newline at end of file
}
......@@ -21,7 +21,12 @@ namespace HashingTool.ViewModel.UserControl
ValidXML // green
}
public class XMLFileSelector : ObservableObject
public interface IErrorLogger
{
void LogError(string message);
}
public class XMLFileSelector : ObservableObject, IErrorLogger
{
private string _source;
private XmlFileStatus _isValid;
......@@ -30,19 +35,21 @@ namespace HashingTool.ViewModel.UserControl
private readonly bool _validate;
private XmlDocument _document;
private readonly Func<XmlDocument, Collection<string>, bool?> _postVerification;
private readonly Func<XmlDocument, IErrorLogger, bool?> _contentVerification;
private bool? _contentValid;
private RelayCommand _browseFileCommand;
private string _prefix;
public XMLFileSelector(IOService ioservice, bool validate = false,
Func<XmlDocument, Collection<string>, bool?> contentCheck = null)
public XMLFileSelector(IOService ioservice, string prefix, bool validate = false,
Func<XmlDocument, IErrorLogger, bool?> contentCheck = null)
{
IoService = ioservice;
_validate = validate;
_prefix = prefix;
_browseFileCommand = new RelayCommand(BrowseXMLFile, () => !_busy);
XMLValidationErrors = new ObservableCollection<string>();
HasContentValidation = contentCheck != null;
_postVerification = contentCheck ?? ((x, c) => null);
_contentVerification = contentCheck ?? ((x, c) => null);
Source = "";
RaisePropertyChanged("ValidateInput");
RaisePropertyChanged("HasContentValidation");
......@@ -190,13 +197,13 @@ namespace HashingTool.ViewModel.UserControl
}
}
if (HasContentValidation) {
contentValid = _postVerification(document, XMLValidationErrors);
contentValid = _contentVerification(document, this);
if (xmlValid && (contentValid == null || !contentValid.Value)) {
fileValid = XmlFileStatus.IncorrectContent;
}
}
} catch (Exception e) {
XMLValidationErrors.Add(e.Message);
LogError(e.Message);
fileValid = XmlFileStatus.Invalid;
} finally {
IsValid = fileValid;
......@@ -208,6 +215,11 @@ namespace HashingTool.ViewModel.UserControl
}
}
public void LogError(string message)
{
XMLValidationErrors.Add(String.Format("{0}: {1}", _prefix, message));
}
public bool HasContentValidation { get; private set; }
public bool? ContentValid
......@@ -230,7 +242,7 @@ namespace HashingTool.ViewModel.UserControl
(s, e) => {
Application.Current.Dispatcher.Invoke(
() =>
XMLValidationErrors.Add(string.Format("Validation {0} Line {2}: {1}",
LogError(string.Format("Validation {0} Line {2}: {1}",
s == XmlSeverityType.Warning ? "WARNING" : "ERROR",
e.ValidationEventArgs == null
? e.Exception.Message +
......@@ -240,7 +252,7 @@ namespace HashingTool.ViewModel.UserControl
});
await validator.ValidateXML(xml);
} catch (Exception e) {
XMLValidationErrors.Add(e.Message);
LogError(e.Message);
}
return valid;
}
......
......@@ -77,7 +77,7 @@ namespace HashingTool.ViewModel
Component = "";
SetCanonicalizationMethod(new string[] { });
DigestMethod = "";
_xmlFile.XMLValidationErrors.Add(e.Message);
_xmlFile.LogError(e.Message);
}
}
......
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;
using System.Windows.Input;
using HashingTool.Helper;
using HashingTool.ViewModel.UserControl;
......@@ -25,16 +28,40 @@ namespace HashingTool.ViewModel
HashingHelper.ValidateDocumentHash);
Files = new ObservableCollection<VectoXMLFile> { _jobFile, _manufacturerReport, _customerReport };
ErrorsAndWarnings = new CompositeCollection();
AddErrorCollection(_jobFile.XMLFile.XMLValidationErrors);
AddErrorCollection(_manufacturerReport.XMLFile.XMLValidationErrors);
AddErrorCollection(_customerReport.XMLFile.XMLValidationErrors);
AddErrorCollection(_manufacturerReport.ValidationErrors);
RaisePropertyChanged("CanonicalizationMethods");
_customerReport.PropertyChanged += Update;
_manufacturerReport.PropertyChanged += Update;
_jobFile.PropertyChanged += Update;
}
private void AddErrorCollection(ObservableCollection<string> errorCollection)
{
ErrorsAndWarnings.Add(new CollectionContainer { Collection = errorCollection });
errorCollection.CollectionChanged += UpdateCount;
}
private void UpdateCount(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("ErrorCount");
}
public int ErrorCount
{
get { return ErrorsAndWarnings.Cast<CollectionContainer>().Sum(entry => (entry.Collection as ICollection).Count); }
}
private void Update(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != "UPDATED")
if (e.PropertyName != "UPDATED") {
return;
}
UpdateReportJobDigest(_manufacturerReport);
UpdateReportJobDigest(_customerReport);
......@@ -70,10 +97,11 @@ namespace HashingTool.ViewModel
public ObservableCollection<VectoXMLFile> Files { get; private set; }
public CompositeCollection ErrorsAndWarnings { get; private set; }
private void UpdateReportJobDigest(ReportXMLFile reportXML)
{
if (reportXML.Valid == null || !reportXML.Valid.Value || _jobFile.XMLFile.Document == null) {
if (reportXML.FileIntegrityValid == null || !reportXML.FileIntegrityValid.Value || _jobFile.XMLFile.Document == null) {
reportXML.JobDigestValueComputed = "";
return;
}
......@@ -82,7 +110,7 @@ namespace HashingTool.ViewModel
var jobDigest = h.ComputeHash(reportXML.JobCanonicalizationMethodRead,
reportXML.JobDigestMethodRead);
reportXML.JobDigestValueComputed = jobDigest;
} catch (Exception ) {
} catch (Exception) {
reportXML.JobDigestValueComputed = "";
}
}
......
......@@ -8,19 +8,21 @@
xmlns:views="clr-namespace:HashingTool.Views"
d:DataContext="{d:DesignInstance views:XMLValidationErrorsDialog}" Icon="/HashingTool;component/Resources/HashingIcon.ico">
<Grid>
<ListBox x:Name="lbErrors" Margin="10,10,10,62" ItemsSource="{Binding XMLErrors}" />
<ListBox x:Name="lbErrors" Margin="10,10,10,62" ItemsSource="{Binding XMLErrors}" FontFamily="Courier New" />
<Button x:Name="btnCopy" Content="Copy Errors" Margin="0,0,100,10" Height="22" VerticalAlignment="Bottom"
HorizontalAlignment="Right" Width="100" Click="btnCopy_Click" />
<Button IsCancel="True" x:Name="btnClose" Content="Close" Margin="0,0,10,10" Height="22" VerticalAlignment="Bottom"
HorizontalAlignment="Right" Width="75" />
<Label x:Name="lblCount" HorizontalAlignment="Left" Margin="10,0,0,31" Width="340" Height="26"
VerticalAlignment="Bottom">
<Label.Content>
<TextBlock Text="{Binding XMLErrors.Count, StringFormat='{}{0} Warnings/Errors'}" />
<TextBlock Text="{Binding ErrorCount, StringFormat='{}{0} Warnings/Errors'}" />
</Label.Content>
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding XMLErrors.Count}" Value="0">
<DataTrigger Binding="{Binding ErrorCount}" Value="0">
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
</Style.Triggers>
......
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
......@@ -22,8 +23,10 @@ namespace HashingTool.Views
public partial class XMLValidationErrorsDialog : Window
{
public static readonly DependencyProperty XMLErrorsProperty = DependencyProperty.Register("XMLErrors",
typeof(ObservableCollection<string>),
typeof(XMLValidationErrorsDialog));
typeof(ICollection), typeof(XMLValidationErrorsDialog));
public static readonly DependencyProperty ErrorCountProperty = DependencyProperty.Register("ErrorCount",
typeof(int), typeof(XMLValidationErrorsDialog));
public XMLValidationErrorsDialog()
{
......@@ -31,10 +34,29 @@ namespace HashingTool.Views
(Content as FrameworkElement).DataContext = this;
}
public ObservableCollection<string> XMLErrors
public ICollection XMLErrors
{
get { return (ObservableCollection<string>)GetValue(XMLErrorsProperty); }
get { return (ICollection)GetValue(XMLErrorsProperty); }
set { SetValue(XMLErrorsProperty, value); }
}
public int ErrorCount
{
get {
var value = GetValue(ErrorCountProperty);
if (value != null) {
return (int)value;
}
return 0;
}
set { SetValue(ErrorCountProperty, value); }
}
private void btnCopy_Click(object sender, RoutedEventArgs e)
{
var errors = string.Join(Environment.NewLine,(from object item in lbErrors.Items select item.ToString()).ToList());
Clipboard.SetText( errors);
}
}
}
......@@ -23,6 +23,7 @@ namespace HashingTool.Views
{
var dialog = new XMLValidationErrorsDialog();
dialog.XMLErrors = (DataContext as HashComponentDataViewModel).XMLFile.XMLValidationErrors;
dialog.ErrorCount = (DataContext as HashComponentDataViewModel).XMLFile.XMLValidationErrors.Count;
dialog.ShowDialog();
}
......
......@@ -30,7 +30,7 @@ namespace HashingTool.Views
{
var dialog = new XMLValidationErrorsDialog();
dialog.XMLErrors = XMLFile.XMLValidationErrors;
dialog.ErrorCount = XMLFile.XMLValidationErrors.Count;
dialog.ShowDialog();
}
}
......
......@@ -26,10 +26,10 @@
<Style TargetType="ContentControl">
<Setter Property="Content" Value="" />
<Style.Triggers>
<DataTrigger Binding="{Binding Valid}" Value="True">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource Icon_OK}" />
</DataTrigger>
<DataTrigger Binding="{Binding Valid}" Value="False">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="False">
<Setter Property="ContentTemplate" Value="{StaticResource Icon_NOK}" />
</DataTrigger>
</Style.Triggers>
......@@ -73,7 +73,7 @@
<DataTemplate x:Key="ExpanderHeaderManufacturerReport" DataType="userControl:ManufacturerReportXMLFile">
<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="top" MinWidth="450">
<TextBlock DockPanel.Dock="Top" Text="{Binding Name}" FontWeight="Bold" />
<ContentControl DockPanel.Dock="Right" Width="35" Height="35" Margin="10,-10,10,0">
<ContentControl DockPanel.Dock="Right" Width="35" Height="35" Margin="10,-10,10,0" ToolTip="{Binding FileIntegrityTooltip}">
<ContentControl.LayoutTransform>
<ScaleTransform ScaleX=".4" ScaleY=".4" />
</ContentControl.LayoutTransform>
......@@ -81,10 +81,10 @@
<Style TargetType="ContentControl">
<Setter Property="Content" Value="" />
<Style.Triggers>
<DataTrigger Binding="{Binding ManufacturerReportValid}" Value="True">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource Icon_OK}" />
</DataTrigger>
<DataTrigger Binding="{Binding ManufacturerReportValid}" Value="False">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="False">
<Setter Property="ContentTemplate" Value="{StaticResource Icon_NOK}" />
</DataTrigger>
</Style.Triggers>
......@@ -111,37 +111,7 @@
<ColumnDefinition Width="Auto" SharedSizeGroup="LabelsShareGroup" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<Label>
<Label.Content>
<TextBlock
Text="{Binding XMLFile.XMLValidationErrors.Count, StringFormat='{}XML Validation: {0} Warnings/Errors'}" />
</Label.Content>
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding XMLFile.XMLValidationErrors.Count}" Value="0">
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Button Margin="10,0,0,0" Content="Details..." HorizontalAlignment="Left" Width="91" Click="Button_Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding XMLFile.XMLValidationErrors.Count}" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
<Label Grid.Row="1" Grid.Column="0" Content="Canonicalization methods:" />
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding CanonicalizationMethods, Mode=OneWay , Converter={StaticResource CollectionConverter}}"
......@@ -250,43 +220,6 @@
<RowDefinition />
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.Column="0">
<Label DockPanel.Dock="Left">
<Label.Content>
<TextBlock Text="{Binding XMLFile.XMLValidationErrors.Count, StringFormat='{}{0} Warnings/Errors'}" />
</Label.Content>
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding XMLFile.XMLValidationErrors.Count}" Value="0">
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Button DockPanel.Dock="Left" Margin="10,0,0,0" Content="Details..." HorizontalAlignment="Left" Width="91"
Click="Button_Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding XMLFile.XMLValidationErrors.Count}" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<TextBox DockPanel.Dock="Right" Text="{helper:CultureAwareBinding Path=CreationDate, StringFormat='g'}"
Margin="10,2,20,2" IsReadOnly="True" MinWidth="150" />
<Label DockPanel.Dock="Right" Content="Creation Date:" HorizontalAlignment="Right" />
</DockPanel>
<GroupBox Grid.Row="2" Header="Report Integrity" Margin="0,0,0,5" Style="{DynamicResource CustomGroupboxStyle}">
<Grid>
<Grid.ColumnDefinitions>
......@@ -298,25 +231,30 @@
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Canonicalization methods:" />
<TextBox Grid.Row="0" Grid.Column="1"
<Label Grid.Row="0" Grid.Column="0" Content="Creation Date:" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{helper:CultureAwareBinding Path=CreationDate, StringFormat='g'}"
Margin="10,2" MinWidth="150" />
<Label Grid.Row="1" Grid.Column="0" Content="Canonicalization methods:" />
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding CanonicalizationMethods, Mode=OneWay , Converter={StaticResource CollectionConverter}}"
Margin="10,2" IsReadOnly="True" />
<Label Grid.Row="1" Grid.Column="0" Content="Digest method:" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DigestMethod, Mode=OneWay}" Margin="10,2" IsReadOnly="True" />
<Label Grid.Row="2" Grid.Column="0" Content="Digest method:" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding DigestMethod, Mode=OneWay}" Margin="10,2" IsReadOnly="True" />
<Label Grid.Row="2" Grid.Column="0" Content="Digest Value read:" Style="{StaticResource DigestValueLabelStyle}" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding DigestValueRead}" Margin="10,2" IsReadOnly="True">
<Label Grid.Row="3" Grid.Column="0" Content="Digest Value read:" Style="{StaticResource DigestValueLabelStyle}" />
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding DigestValueRead}" Margin="10,2" IsReadOnly="True">
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource DigestValueTextboxStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Valid}" Value="True">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="True">
<Setter Property="Foreground" Value="{StaticResource Color.SuccessGreen}" />
</DataTrigger>
<DataTrigger Binding="{Binding Valid}" Value="False">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="False">
<Setter Property="Foreground" Value="{StaticResource Color.ErrorRed}" />
</DataTrigger>
</Style.Triggers>
......@@ -324,16 +262,16 @@
</TextBox.Style>
</TextBox>
<Label Grid.Row="3" Grid.Column="0" Content="Digest Value computed:"
<Label Grid.Row="4" Grid.Column="0" Content="Digest Value computed:"
Style="{StaticResource DigestValueLabelStyle}" />
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding DigestValueComputed}" Margin="10,2" IsReadOnly="True">
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding DigestValueComputed}" Margin="10,2" IsReadOnly="True">
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource DigestValueTextboxStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Valid}" Value="True">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="True">
<Setter Property="Foreground" Value="{StaticResource Color.SuccessGreen}" />
</DataTrigger>
<DataTrigger Binding="{Binding Valid}" Value="False">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="False">
<Setter Property="Foreground" Value="{StaticResource Color.ErrorRed}" />
</DataTrigger>
</Style.Triggers>
......@@ -410,42 +348,6 @@
<RowDefinition />
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.Column="0">
<Label DockPanel.Dock="Left">
<Label.Content>
<TextBlock Text="{Binding XMLFile.XMLValidationErrors.Count, StringFormat='{}{0} Warnings/Errors'}" />
</Label.Content>
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding XMLFile.XMLValidationErrors.Count}" Value="0">
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Button DockPanel.Dock="Left" Margin="10,0,0,0" Content="Details..." HorizontalAlignment="Left" Width="91"
Click="Button_Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding XMLFile.XMLValidationErrors.Count}" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<TextBox DockPanel.Dock="Right" Text="{helper:CultureAwareBinding Path=CreationDate, StringFormat='g'}"
Margin="10,2,20,2" MinWidth="150" />
<Label DockPanel.Dock="Right" Content="Creation Date:" HorizontalAlignment="Right" />
</DockPanel>
<GroupBox Grid.Row="2" Header="Report Integrity" Margin="0,0,0,5" Style="{DynamicResource CustomGroupboxStyle}">
<Grid>
<Grid.ColumnDefinitions>
......@@ -457,25 +359,30 @@
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Canonicalization methods:" />
<TextBox Grid.Row="0" Grid.Column="1"
<Label Grid.Row="0" Grid.Column="0" Content="Creation Date:" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{helper:CultureAwareBinding Path=CreationDate, StringFormat='g'}"
Margin="10,2" MinWidth="150" />
<Label Grid.Row="1" Grid.Column="0" Content="Canonicalization methods:" />
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding CanonicalizationMethods, Mode=OneWay , Converter={StaticResource CollectionConverter}}"
Margin="10,2" IsReadOnly="True" />
<Label Grid.Row="1" Grid.Column="0" Content="Digest method:" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding DigestMethod, Mode=OneWay}" Margin="10,2" IsReadOnly="True" />
<Label Grid.Row="2" Grid.Column="0" Content="Digest method:" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding DigestMethod, Mode=OneWay}" Margin="10,2" IsReadOnly="True" />
<Label Grid.Row="2" Grid.Column="0" Content="Digest Value read:" Style="{StaticResource DigestValueLabelStyle}" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding DigestValueRead}" Margin="10,2" IsReadOnly="True">
<Label Grid.Row="3" Grid.Column="0" Content="Digest Value read:" Style="{StaticResource DigestValueLabelStyle}" />
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding DigestValueRead}" Margin="10,2" IsReadOnly="True">
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource DigestValueTextboxStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Valid}" Value="True">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="True">
<Setter Property="Foreground" Value="{StaticResource Color.SuccessGreen}" />
</DataTrigger>
<DataTrigger Binding="{Binding Valid}" Value="False">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="False">
<Setter Property="Foreground" Value="{StaticResource Color.ErrorRed}" />
</DataTrigger>
</Style.Triggers>
......@@ -483,16 +390,16 @@
</TextBox.Style>
</TextBox>
<Label Grid.Row="3" Grid.Column="0" Content="Digest Value computed:"
<Label Grid.Row="4" Grid.Column="0" Content="Digest Value computed:"
Style="{StaticResource DigestValueLabelStyle}" />
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding DigestValueComputed}" Margin="10,2" IsReadOnly="True">
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding DigestValueComputed}" Margin="10,2" IsReadOnly="True">
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource DigestValueTextboxStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Valid}" Value="True">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="True">
<Setter Property="Foreground" Value="{StaticResource Color.SuccessGreen}" />
</DataTrigger>
<DataTrigger Binding="{Binding Valid}" Value="False">
<DataTrigger Binding="{Binding FileIntegrityValid}" Value="False">
<Setter Property="Foreground" Value="{StaticResource Color.ErrorRed}" />
</DataTrigger>
</Style.Triggers>
......@@ -710,6 +617,7 @@
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch"
......@@ -757,8 +665,38 @@
</ItemsControl>
</ScrollViewer>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,5">
<Label>
<Label.Content>
<TextBlock Text="{Binding ErrorCount, StringFormat='{}{0} Warnings/Errors'}" />
</Label.Content>
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding ErrorCount}" Value="0">
<Setter Property="Foreground" Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Button Margin="10,0,0,0" Content="Details..." HorizontalAlignment="Left" Width="91" Click="Button_Click_1">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding ErrorCount}" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
<Grid Grid.Row="1" Margin="10,3,10,30" HorizontalAlignment="Center">
<Grid Grid.Row="2" Margin="10,3,10,30" HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
......
......@@ -31,5 +31,14 @@ namespace HashingTool.Views
dialog.XMLErrors = context.XMLFile.XMLValidationErrors;
dialog.ShowDialog();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var dialog = new XMLValidationErrorsDialog();
dialog.XMLErrors = (DataContext as VerifyResultDataViewModel).ErrorsAndWarnings;
dialog.ErrorCount = (DataContext as VerifyResultDataViewModel).ErrorCount;
dialog.ShowDialog();
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment