diff --git a/.github/workflows/installer.build.yml b/.github/workflows/installer.build.yml
index a08c08fa..a6acf427 100644
--- a/.github/workflows/installer.build.yml
+++ b/.github/workflows/installer.build.yml
@@ -22,7 +22,9 @@ jobs:
JFROG_ACCESS_TOKEN: ${{ secrets.JFROG_ACCESS_TOKEN }}
gh_ci_key: ${{ secrets.GH_CI_KEY }}
ZITI_DEBUG: "yes_please"
-
+ OPENZITI_P12_PASS: ${{ secrets.OPENZITI_P12_PASS }}
+ OPENZITI_P12: ${{ secrets.OPENZITI_P12 }}
+
steps:
- uses: actions/checkout@master
- uses: microsoft/setup-msbuild@v1
@@ -39,7 +41,9 @@ jobs:
run: |
echo "${env:SIGNING_CERT}" > WinSign.b64
echo "${env:ADV_INST_KEY}" > ADV_INST_KEY.key
+ echo "${env:OPENZITI_P12}" > OPENZITI_P12.b64
C:\Windows\System32\certutil.exe -decode WinSign.b64 Installer/WinSign.p12
+ C:\Windows\System32\certutil.exe -decode OPENZITI_P12.b64 Installer/openziti.p12
- name: Trying to run choco install.
run: Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
- name: "Install Choco needs: advanced-installer wixtoolset zip ninja"
diff --git a/.gitignore b/.gitignore
index 9312a38f..cfec3e2b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -268,7 +268,7 @@ service/libuv.dll
Installer/build/
Installer/ziti-tunnel-service.zip
ziti-tunnel-service.zip
-WinSign.p12
+WinSign.*
*.p12
*.pfx
*.pem
diff --git a/DesktopEdge/MainWindow.xaml.cs b/DesktopEdge/MainWindow.xaml.cs
index 3e9bcdca..a727ff65 100644
--- a/DesktopEdge/MainWindow.xaml.cs
+++ b/DesktopEdge/MainWindow.xaml.cs
@@ -1439,7 +1439,8 @@ namespace ZitiDesktopEdge {
public bool CanExecute(object parameter) {
return true;
}
-
+#pragma warning disable CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used
public event EventHandler CanExecuteChanged;
+#pragma warning restore CS0067 //The event 'ActionCommand.CanExecuteChanged' is never used
}
}
diff --git a/DesktopEdge/Properties/AssemblyInfo.cs b/DesktopEdge/Properties/AssemblyInfo.cs
index 69c6b337..a9be57de 100644
--- a/DesktopEdge/Properties/AssemblyInfo.cs
+++ b/DesktopEdge/Properties/AssemblyInfo.cs
@@ -50,6 +50,6 @@ using System.Windows;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.9.6.0")]
-[assembly: AssemblyVersion("1.9.6.0")]
-[assembly: AssemblyFileVersion("1.9.6.0")]
+// [assembly: AssemblyVersion("1.9.8.0")]
+[assembly: AssemblyVersion("1.9.8.0")]
+[assembly: AssemblyFileVersion("1.9.8.0")]
diff --git a/Installer/ZitiDesktopEdge.aip b/Installer/ZitiDesktopEdge.aip
index 891569b9..7bbedc5c 100644
--- a/Installer/ZitiDesktopEdge.aip
+++ b/Installer/ZitiDesktopEdge.aip
@@ -23,10 +23,10 @@
-
+
-
+
diff --git a/Installer/build.ps1 b/Installer/build.ps1
index 67c8559e..e0e51091 100644
--- a/Installer/build.ps1
+++ b/Installer/build.ps1
@@ -70,5 +70,11 @@ if($gituser -eq "ziti-ci") {
} else {
echo "detected user [${gituser}] which is not ziti-ci - skipping installer commit"
}
-(Get-FileHash "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe.sha256"
+
+$exeAbsPath="${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe"
+
+echo "adding additional signature to executable with openziti.org signing certificate"
+signtool sign /f "${scriptPath}\openziti.p12" /p "${env:OPENZITI_P12_PASS}" /tr http://ts.ssl.com /fd sha512 /td sha512 /as "${exeAbsPath}"
+
+(Get-FileHash "${exeAbsPath}").Hash > "${scriptPath}\Output\Ziti Desktop Edge Client-${installerVersion}.exe.sha256"
echo "========================== build.ps1 competed =========================="
diff --git a/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs b/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs
index 4b90a28e..c5d96dca 100644
--- a/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs
+++ b/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs
@@ -165,10 +165,10 @@ namespace ZitiDesktopEdge.ServiceClient {
} else {
//ClientDisconnected(null);
}
- } catch (Exception e) {
+ } catch (Exception) {
try {
ReconnectFailureEvent("reconnect failure");
- } catch (Exception ex){
+ } catch (Exception){
// don't care - just catch it and continue... it's a timeout...
}
var now = DateTime.Now;
diff --git a/ZitiDesktopEdge.sln b/ZitiDesktopEdge.sln
index 8167e22a..70b973ed 100644
--- a/ZitiDesktopEdge.sln
+++ b/ZitiDesktopEdge.sln
@@ -6,7 +6,10 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D66B1C6A-ADFE-437E-B037-C4C849A6CA97}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
+ Installer\build.ps1 = Installer\build.ps1
+ .github\workflows\installer.build.yml = .github\workflows\installer.build.yml
README.md = README.md
+ Installer\reg.bat = Installer\reg.bat
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZitiDesktopEdge", "DesktopEdge\ZitiDesktopEdge.csproj", "{FA4291EA-A97E-4854-959F-91EE7B330B37}"
diff --git a/ZitiUpdateService/App.config b/ZitiUpdateService/App.config
index df91ae79..e2c58379 100644
--- a/ZitiUpdateService/App.config
+++ b/ZitiUpdateService/App.config
@@ -1,4 +1,4 @@
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
-
+
+
diff --git a/ZitiUpdateService/Properties/AssemblyInfo.cs b/ZitiUpdateService/Properties/AssemblyInfo.cs
index 03a5efb4..54e00a6d 100644
--- a/ZitiUpdateService/Properties/AssemblyInfo.cs
+++ b/ZitiUpdateService/Properties/AssemblyInfo.cs
@@ -30,6 +30,6 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.9.6.0")]
-[assembly: AssemblyVersion("1.9.6.0")]
-[assembly: AssemblyFileVersion("1.9.6.0")]
+// [assembly: AssemblyVersion("1.9.8.0")]
+[assembly: AssemblyVersion("1.9.8.0")]
+[assembly: AssemblyFileVersion("1.9.8.0")]
diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs
index bfd6e615..debe9e45 100644
--- a/ZitiUpdateService/UpdateService.cs
+++ b/ZitiUpdateService/UpdateService.cs
@@ -19,6 +19,7 @@ using NLog;
using Newtonsoft.Json;
using System.Net;
using DnsClient;
+using ZitiUpdateService.Checkers.PeFile;
namespace ZitiUpdateService {
public partial class UpdateService : ServiceBase {
@@ -33,12 +34,9 @@ namespace ZitiUpdateService {
public bool useGithubCheck { get; private set; }
- private static string[] expected_subject = new string[] { @"CN=""NetFoundry, Inc."", O=""NetFoundry, Inc."", L=Herndon, S=Virginia, C=US", @"CN=NetFoundry Inc., O=NetFoundry Inc., L=Herndon, S=Virginia, C=US" };
-
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private Timer _updateTimer = new Timer();
- private Timer openzitiRemovalTimer = new Timer();
private bool inUpdateCheck = false;
private string exeLocation = null;
@@ -54,7 +52,7 @@ namespace ZitiUpdateService {
IPCServer svr = new IPCServer();
Task ipcServer = null;
Task eventServer = null;
- checkers.UpdateCheck check = null;
+ Checkers.UpdateCheck check = null;
private Timer dnsProbeTimer = new Timer();
private IPAddress dnsIpAddress = null;
@@ -574,9 +572,9 @@ namespace ZitiUpdateService {
updateUrl = "https://api.github.com/repos/openziti/desktop-edge-win-beta/releases/latest";
}
if (useGithubCheck) {
- check = new checkers.GithubCheck(updateUrl);
+ check = new Checkers.GithubCheck(updateUrl);
} else {
- check = new checkers.FilesystemCheck(1);
+ check = new Checkers.FilesystemCheck(1);
}
}
@@ -605,10 +603,10 @@ namespace ZitiUpdateService {
Logger.Warn("Still in update check. This is abnormal. Please report if you see this warning");
return;
}
- inUpdateCheck = true; //simple semaphone
+ inUpdateCheck = true; //simple semaphore
try {
Logger.Debug("checking for update");
-
+
int avail = check.IsUpdateAvailable(assemblyVersion);
if (avail >= 0) {
Logger.Debug("update check complete. no update available");
@@ -626,8 +624,8 @@ namespace ZitiUpdateService {
string fileDestination = Path.Combine(updateFolder, filename);
if (check.AlreadyDownloaded(updateFolder, filename)) {
- Logger.Info("package has already been downloaded to {0} - moving to install phase", Path.Combine(updateFolder, filename));
- } else {
+ Logger.Info("package has already been downloaded to {0} - moving to install phase", fileDestination);
+ } else {
Logger.Info("copying update package begins");
check.CopyUpdatePackage(updateFolder, filename);
Logger.Info("copying update package complete");
@@ -640,23 +638,8 @@ namespace ZitiUpdateService {
return;
}
Logger.Debug("downloaded file hash was correct. update can continue.");
-
- // check digital signature
- var signer = X509Certificate.CreateFromSignedFile(fileDestination);
- /* keep these commented out lines - just in case we need all the certs from the file use this
- var coll = new X509Certificate2Collection();
- coll.Import(filePath);
- */
-
- var subject = signer.Subject;
- if (!expected_subject.Contains(subject)) {
- Logger.Error("the file downloaded uses a subject that is unknown! the installation will not proceed. [subject:{0}]", subject);
- inUpdateCheck = false;
- return;
-
- } else {
- Logger.Info("the file downloaded uses a known subject. installation and can proceed. [subject:{0}]", subject);
- }
+
+ new SignedFileValidator(fileDestination).Verify();
StopZiti();
StopUI().Wait();
diff --git a/ZitiUpdateService/ZitiUpdateService.csproj b/ZitiUpdateService/ZitiUpdateService.csproj
index cbb3bf6a..78d8eab1 100644
--- a/ZitiUpdateService/ZitiUpdateService.csproj
+++ b/ZitiUpdateService/ZitiUpdateService.csproj
@@ -13,6 +13,7 @@
true
true
false
+
publish\
true
Disk
@@ -28,7 +29,6 @@
false
true
true
-
x64
@@ -98,9 +98,26 @@
..\packages\Microsoft.PowerShell.5.ReferenceAssemblies.1.1.0\lib\net4\System.Management.Automation.dll
+
+ ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll
+
+
+
+ ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll
+
+
+
+ ..\packages\System.Security.Cryptography.Pkcs.5.0.1\lib\net461\System.Security.Cryptography.Pkcs.dll
+
+
+ ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
+
@@ -113,6 +130,10 @@
+
+
+
+
Component
@@ -150,6 +171,7 @@
+
@@ -170,5 +192,6 @@
ZitiDesktopEdge.Client
+
\ No newline at end of file
diff --git a/ZitiUpdateService/checkers/FilesystemCheck.cs b/ZitiUpdateService/checkers/FilesystemCheck.cs
index 6d0d456e..ef56ad32 100644
--- a/ZitiUpdateService/checkers/FilesystemCheck.cs
+++ b/ZitiUpdateService/checkers/FilesystemCheck.cs
@@ -1,13 +1,9 @@
using System;
using System.IO;
-using System.Net;
-
-using System.Security.Cryptography;
using NLog;
-using Newtonsoft.Json.Linq;
-namespace ZitiUpdateService.checkers {
+namespace ZitiUpdateService.Checkers {
internal class FilesystemCheck : UpdateCheck {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
string dest = null;
diff --git a/ZitiUpdateService/checkers/GithubCheck.cs b/ZitiUpdateService/checkers/GithubCheck.cs
index eeca9452..37512d30 100644
--- a/ZitiUpdateService/checkers/GithubCheck.cs
+++ b/ZitiUpdateService/checkers/GithubCheck.cs
@@ -7,8 +7,9 @@ using NLog;
using Newtonsoft.Json.Linq;
using ZitiDesktopEdge.Utility;
+using ZitiUpdateService.Checkers.PeFile;
-namespace ZitiUpdateService.checkers {
+namespace ZitiUpdateService.Checkers {
internal class GithubCheck : UpdateCheck {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
@@ -97,7 +98,7 @@ namespace ZitiUpdateService.checkers {
File.Delete(sha256dest);
return computed.ToLower().Trim() == hash.ToLower().Trim();
}
- }
+ }
override public Version GetNextVersion() {
return nextVersion;
diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs b/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs
new file mode 100644
index 00000000..ce1d2cc9
--- /dev/null
+++ b/ZitiUpdateService/checkers/PeFile/SignedFileStructs.cs
@@ -0,0 +1,539 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ZitiUpdateService.Checkers.PeFile {
+
+ //defined by http://www.pinvoke.net/default.aspx/Structures/IMAGE_FILE_HEADER.html
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_FILE_HEADER {
+ public UInt16 Machine;
+ public UInt16 NumberOfSections;
+ public UInt32 TimeDateStamp;
+ public UInt32 PointerToSymbolTable;
+ public UInt32 NumberOfSymbols;
+ public UInt16 SizeOfOptionalHeader;
+ public UInt16 Characteristics;
+ }
+
+ //see http://www.pinvoke.net/default.aspx/Structures/IMAGE_OPTIONAL_HEADER32.html
+ //defined by http://www.pinvoke.net/default.aspx/Structures/IMAGE_OPTIONAL_HEADER32.html
+ [StructLayout(LayoutKind.Sequential)]
+ public struct IMAGE_DATA_DIRECTORY {
+ public UInt32 VirtualAddress;
+ public UInt32 Size;
+ }
+
+ public enum MachineType : ushort {
+ Native = 0,
+ I386 = 0x014c,
+ Itanium = 0x0200,
+ x64 = 0x8664
+ }
+
+ public sealed class MagicType {
+ public const Int16 IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
+ public const Int16 IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
+ }
+
+ public enum SubSystemType : ushort {
+ IMAGE_SUBSYSTEM_UNKNOWN = 0,
+ IMAGE_SUBSYSTEM_NATIVE = 1,
+ IMAGE_SUBSYSTEM_WINDOWS_GUI = 2,
+ IMAGE_SUBSYSTEM_WINDOWS_CUI = 3,
+ IMAGE_SUBSYSTEM_POSIX_CUI = 7,
+ IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9,
+ IMAGE_SUBSYSTEM_EFI_APPLICATION = 10,
+ IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11,
+ IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12,
+ IMAGE_SUBSYSTEM_EFI_ROM = 13,
+ IMAGE_SUBSYSTEM_XBOX = 14
+
+ }
+
+ public enum DllCharacteristicsType : ushort {
+ RES_0 = 0x0001,
+ RES_1 = 0x0002,
+ RES_2 = 0x0004,
+ RES_3 = 0x0008,
+ IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040,
+ IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080,
+ IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100,
+ IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200,
+ IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400,
+ IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800,
+ RES_4 = 0x1000,
+ IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000,
+ IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct IMAGE_OPTIONAL_HEADER32 {
+ [FieldOffset(0)] public ushort Magic;
+
+ [FieldOffset(2)] public byte MajorLinkerVersion;
+
+ [FieldOffset(3)] public byte MinorLinkerVersion;
+
+ [FieldOffset(4)] public uint SizeOfCode;
+
+ [FieldOffset(8)] public uint SizeOfInitializedData;
+
+ [FieldOffset(12)] public uint SizeOfUninitializedData;
+
+ [FieldOffset(16)] public uint AddressOfEntryPoint;
+
+ [FieldOffset(20)] public uint BaseOfCode;
+
+ // PE32 contains this additional field
+ [FieldOffset(24)] public uint BaseOfData;
+
+ [FieldOffset(28)] public uint ImageBase;
+
+ [FieldOffset(32)] public uint SectionAlignment;
+
+ [FieldOffset(36)] public uint FileAlignment;
+
+ [FieldOffset(40)] public ushort MajorOperatingSystemVersion;
+
+ [FieldOffset(42)] public ushort MinorOperatingSystemVersion;
+
+ [FieldOffset(44)] public ushort MajorImageVersion;
+
+ [FieldOffset(46)] public ushort MinorImageVersion;
+
+ [FieldOffset(48)] public ushort MajorSubsystemVersion;
+
+ [FieldOffset(50)] public ushort MinorSubsystemVersion;
+
+ [FieldOffset(52)] public uint Win32VersionValue;
+
+ [FieldOffset(56)] public uint SizeOfImage;
+
+ [FieldOffset(60)] public uint SizeOfHeaders;
+
+ [FieldOffset(64)] public uint CheckSum;
+
+ [FieldOffset(68)] public SubSystemType Subsystem;
+
+ [FieldOffset(70)] public DllCharacteristicsType DllCharacteristics;
+
+ [FieldOffset(72)] public uint SizeOfStackReserve;
+
+ [FieldOffset(76)] public uint SizeOfStackCommit;
+
+ [FieldOffset(80)] public uint SizeOfHeapReserve;
+
+ [FieldOffset(84)] public uint SizeOfHeapCommit;
+
+ [FieldOffset(88)] public uint LoaderFlags;
+
+ [FieldOffset(92)] public uint NumberOfRvaAndSizes;
+
+ [FieldOffset(96)] public IMAGE_DATA_DIRECTORY ExportTable;
+
+ [FieldOffset(104)] public IMAGE_DATA_DIRECTORY ImportTable;
+
+ [FieldOffset(112)] public IMAGE_DATA_DIRECTORY ResourceTable;
+
+ [FieldOffset(120)] public IMAGE_DATA_DIRECTORY ExceptionTable;
+
+ [FieldOffset(128)] public IMAGE_DATA_DIRECTORY CertificateTable;
+
+ [FieldOffset(136)] public IMAGE_DATA_DIRECTORY BaseRelocationTable;
+
+ [FieldOffset(144)] public IMAGE_DATA_DIRECTORY Debug;
+
+ [FieldOffset(152)] public IMAGE_DATA_DIRECTORY Architecture;
+
+ [FieldOffset(160)] public IMAGE_DATA_DIRECTORY GlobalPtr;
+
+ [FieldOffset(168)] public IMAGE_DATA_DIRECTORY TLSTable;
+
+ [FieldOffset(176)] public IMAGE_DATA_DIRECTORY LoadConfigTable;
+
+ [FieldOffset(184)] public IMAGE_DATA_DIRECTORY BoundImport;
+
+ [FieldOffset(192)] public IMAGE_DATA_DIRECTORY IAT;
+
+ [FieldOffset(200)] public IMAGE_DATA_DIRECTORY DelayImportDescriptor;
+
+ [FieldOffset(208)] public IMAGE_DATA_DIRECTORY CLRRuntimeHeader;
+
+ [FieldOffset(216)] public IMAGE_DATA_DIRECTORY Reserved;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct IMAGE_OPTIONAL_HEADER64 {
+ [FieldOffset(0)] public ushort Magic;
+
+ [FieldOffset(2)] public byte MajorLinkerVersion;
+
+ [FieldOffset(3)] public byte MinorLinkerVersion;
+
+ [FieldOffset(4)] public uint SizeOfCode;
+
+ [FieldOffset(8)] public uint SizeOfInitializedData;
+
+ [FieldOffset(12)] public uint SizeOfUninitializedData;
+
+ [FieldOffset(16)] public uint AddressOfEntryPoint;
+
+ [FieldOffset(20)] public uint BaseOfCode;
+
+ [FieldOffset(24)] public ulong ImageBase;
+
+ [FieldOffset(32)] public uint SectionAlignment;
+
+ [FieldOffset(36)] public uint FileAlignment;
+
+ [FieldOffset(40)] public ushort MajorOperatingSystemVersion;
+
+ [FieldOffset(42)] public ushort MinorOperatingSystemVersion;
+
+ [FieldOffset(44)] public ushort MajorImageVersion;
+
+ [FieldOffset(46)] public ushort MinorImageVersion;
+
+ [FieldOffset(48)] public ushort MajorSubsystemVersion;
+
+ [FieldOffset(50)] public ushort MinorSubsystemVersion;
+
+ [FieldOffset(52)] public uint Win32VersionValue;
+
+ [FieldOffset(56)] public uint SizeOfImage;
+
+ [FieldOffset(60)] public uint SizeOfHeaders;
+
+ [FieldOffset(64)] public uint CheckSum;
+
+ [FieldOffset(68)] public SubSystemType Subsystem;
+
+ [FieldOffset(70)] public DllCharacteristicsType DllCharacteristics;
+
+ [FieldOffset(72)] public ulong SizeOfStackReserve;
+
+ [FieldOffset(80)] public ulong SizeOfStackCommit;
+
+ [FieldOffset(88)] public ulong SizeOfHeapReserve;
+
+ [FieldOffset(96)] public ulong SizeOfHeapCommit;
+
+ [FieldOffset(104)] public uint LoaderFlags;
+
+ [FieldOffset(108)] public uint NumberOfRvaAndSizes;
+
+ [FieldOffset(112)] public IMAGE_DATA_DIRECTORY ExportTable;
+
+ [FieldOffset(120)] public IMAGE_DATA_DIRECTORY ImportTable;
+
+ [FieldOffset(128)] public IMAGE_DATA_DIRECTORY ResourceTable;
+
+ [FieldOffset(136)] public IMAGE_DATA_DIRECTORY ExceptionTable;
+
+ [FieldOffset(144)] public IMAGE_DATA_DIRECTORY CertificateTable;
+
+ [FieldOffset(152)] public IMAGE_DATA_DIRECTORY BaseRelocationTable;
+
+ [FieldOffset(160)] public IMAGE_DATA_DIRECTORY Debug;
+
+ [FieldOffset(168)] public IMAGE_DATA_DIRECTORY Architecture;
+
+ [FieldOffset(176)] public IMAGE_DATA_DIRECTORY GlobalPtr;
+
+ [FieldOffset(184)] public IMAGE_DATA_DIRECTORY TLSTable;
+
+ [FieldOffset(192)] public IMAGE_DATA_DIRECTORY LoadConfigTable;
+
+ [FieldOffset(200)] public IMAGE_DATA_DIRECTORY BoundImport;
+
+ [FieldOffset(208)] public IMAGE_DATA_DIRECTORY IAT;
+
+ [FieldOffset(216)] public IMAGE_DATA_DIRECTORY DelayImportDescriptor;
+
+ [FieldOffset(224)] public IMAGE_DATA_DIRECTORY CLRRuntimeHeader;
+
+ [FieldOffset(232)] public IMAGE_DATA_DIRECTORY Reserved;
+ }
+
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct IMAGE_SECTION_HEADER {
+ //FROM: http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html
+ [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+ public char[] Name;
+
+ [FieldOffset(8)] public UInt32 VirtualSize;
+
+ [FieldOffset(12)] public UInt32 VirtualAddress;
+
+ [FieldOffset(16)] public UInt32 SizeOfRawData;
+
+ [FieldOffset(20)] public UInt32 PointerToRawData;
+
+ [FieldOffset(24)] public UInt32 PointerToRelocations;
+
+ [FieldOffset(28)] public UInt32 PointerToLinenumbers;
+
+ [FieldOffset(32)] public UInt16 NumberOfRelocations;
+
+ [FieldOffset(34)] public UInt16 NumberOfLinenumbers;
+
+ [FieldOffset(36)] public DataSectionFlags Characteristics;
+
+ public string Section {
+ get { return new string(Name); }
+ }
+ }
+
+ [Flags]
+ public enum DataSectionFlags : uint {
+ //FROM: http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html
+ ///
+ /// Reserved for future use.
+ ///
+ TypeReg = 0x00000000,
+
+ ///
+ /// Reserved for future use.
+ ///
+ TypeDsect = 0x00000001,
+
+ ///
+ /// Reserved for future use.
+ ///
+ TypeNoLoad = 0x00000002,
+
+ ///
+ /// Reserved for future use.
+ ///
+ TypeGroup = 0x00000004,
+
+ ///
+ /// The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files.
+ ///
+ TypeNoPadded = 0x00000008,
+
+ ///
+ /// Reserved for future use.
+ ///
+ TypeCopy = 0x00000010,
+
+ ///
+ /// The section contains executable code.
+ ///
+ ContentCode = 0x00000020,
+
+ ///
+ /// The section contains initialized data.
+ ///
+ ContentInitializedData = 0x00000040,
+
+ ///
+ /// The section contains uninitialized data.
+ ///
+ ContentUninitializedData = 0x00000080,
+
+ ///
+ /// Reserved for future use.
+ ///
+ LinkOther = 0x00000100,
+
+ ///
+ /// The section contains comments or other information. The .drectve section has this type. This is valid for object files only.
+ ///
+ LinkInfo = 0x00000200,
+
+ ///
+ /// Reserved for future use.
+ ///
+ TypeOver = 0x00000400,
+
+ ///
+ /// The section will not become part of the image. This is valid only for object files.
+ ///
+ LinkRemove = 0x00000800,
+
+ ///
+ /// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files.
+ ///
+ LinkComDat = 0x00001000,
+
+ ///
+ /// Reset speculative exceptions handling bits in the TLB entries for this section.
+ ///
+ NoDeferSpecExceptions = 0x00004000,
+
+ ///
+ /// The section contains data referenced through the global pointer (GP).
+ ///
+ RelativeGP = 0x00008000,
+
+ ///
+ /// Reserved for future use.
+ ///
+ MemPurgeable = 0x00020000,
+
+ ///
+ /// Reserved for future use.
+ ///
+ Memory16Bit = 0x00020000,
+
+ ///
+ /// Reserved for future use.
+ ///
+ MemoryLocked = 0x00040000,
+
+ ///
+ /// Reserved for future use.
+ ///
+ MemoryPreload = 0x00080000,
+
+ ///
+ /// Align data on a 1-byte boundary. Valid only for object files.
+ ///
+ Align1Bytes = 0x00100000,
+
+ ///
+ /// Align data on a 2-byte boundary. Valid only for object files.
+ ///
+ Align2Bytes = 0x00200000,
+
+ ///
+ /// Align data on a 4-byte boundary. Valid only for object files.
+ ///
+ Align4Bytes = 0x00300000,
+
+ ///
+ /// Align data on an 8-byte boundary. Valid only for object files.
+ ///
+ Align8Bytes = 0x00400000,
+
+ ///
+ /// Align data on a 16-byte boundary. Valid only for object files.
+ ///
+ Align16Bytes = 0x00500000,
+
+ ///
+ /// Align data on a 32-byte boundary. Valid only for object files.
+ ///
+ Align32Bytes = 0x00600000,
+
+ ///
+ /// Align data on a 64-byte boundary. Valid only for object files.
+ ///
+ Align64Bytes = 0x00700000,
+
+ ///
+ /// Align data on a 128-byte boundary. Valid only for object files.
+ ///
+ Align128Bytes = 0x00800000,
+
+ ///
+ /// Align data on a 256-byte boundary. Valid only for object files.
+ ///
+ Align256Bytes = 0x00900000,
+
+ ///
+ /// Align data on a 512-byte boundary. Valid only for object files.
+ ///
+ Align512Bytes = 0x00A00000,
+
+ ///
+ /// Align data on a 1024-byte boundary. Valid only for object files.
+ ///
+ Align1024Bytes = 0x00B00000,
+
+ ///
+ /// Align data on a 2048-byte boundary. Valid only for object files.
+ ///
+ Align2048Bytes = 0x00C00000,
+
+ ///
+ /// Align data on a 4096-byte boundary. Valid only for object files.
+ ///
+ Align4096Bytes = 0x00D00000,
+
+ ///
+ /// Align data on an 8192-byte boundary. Valid only for object files.
+ ///
+ Align8192Bytes = 0x00E00000,
+
+ ///
+ /// The section contains extended relocations.
+ ///
+ LinkExtendedRelocationOverflow = 0x01000000,
+
+ ///
+ /// The section can be discarded as needed.
+ ///
+ MemoryDiscardable = 0x02000000,
+
+ ///
+ /// The section cannot be cached.
+ ///
+ MemoryNotCached = 0x04000000,
+
+ ///
+ /// The section is not pageable.
+ ///
+ MemoryNotPaged = 0x08000000,
+
+ ///
+ /// The section can be shared in memory.
+ ///
+ MemoryShared = 0x10000000,
+
+ ///
+ /// The section can be executed as code.
+ ///
+ MemoryExecute = 0x20000000,
+
+ ///
+ /// The section can be read.
+ ///
+ MemoryRead = 0x40000000,
+
+ ///
+ /// The section can be written to.
+ ///
+ MemoryWrite = 0x80000000
+ }
+
+
+ public class ImageOptionalHeaderWrapper {
+ public IMAGE_OPTIONAL_HEADER32 h32;
+ public IMAGE_OPTIONAL_HEADER64 h64;
+ public bool IsPe;
+
+
+ public uint CertificateTableVirtualAddress {
+ get {
+ if (IsPe) {
+ return h32.CertificateTable.VirtualAddress;
+ } else {
+ return h64.CertificateTable.VirtualAddress;
+ }
+ }
+ }
+
+ public uint CertificateTableSize {
+ get {
+ if (IsPe) {
+ return h32.CertificateTable.Size;
+ } else {
+ return h64.CertificateTable.Size;
+ }
+ }
+ }
+
+ public uint SizeOfHeaders {
+ get {
+ if (IsPe) {
+ return h32.SizeOfHeaders;
+ } else {
+ return h64.SizeOfHeaders;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs
new file mode 100644
index 00000000..cb4ac576
--- /dev/null
+++ b/ZitiUpdateService/checkers/PeFile/SignedFileValidator.cs
@@ -0,0 +1,607 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Pkcs;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading.Tasks;
+using NLog;
+using VerifyingFiles.PInvoke;
+
+namespace ZitiUpdateService.Checkers.PeFile {
+ public class SignedFileValidator {
+ private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
+
+ public const int LengthOfChecksum = 4; //the checksum is 4 bytes
+ public const int LengthCertificateTable = 8; //the Certificate Table is 8 bytes
+
+ public HashPositions ImportantHashPositions { get; private set; }
+ public string FilePath { get; private set; }
+ public PeType Type { get; private set; }
+ public IMAGE_FILE_HEADER CoffHeader { get; private set; }
+ public IMAGE_OPTIONAL_HEADER32 StandardFieldsPe32 { get; private set; }
+ public IMAGE_OPTIONAL_HEADER64 StandardFieldsPe32Plus { get; private set; }
+
+ private readonly X509Certificate2 expectedRootCa = null;
+ private readonly X509Store empty = new X509Store();
+ private X509Store SystemCAs = new X509Store();
+ private const string oldKnownThumbprint = "39636E9F5E80308DE370C914CE8112876ECF4E0C";
+
+ public SignedFileValidator(string pathToFile) {
+ X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
+ store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
+ SystemCAs.Open(OpenFlags.ReadWrite);
+ SystemCAs.AddRange(store.Certificates);
+
+ expectedRootCa = certFromResource("ZitiUpdateService.checkers.PeFile.openziti.rootCA.rsa.pem.txt");
+ FilePath = pathToFile;
+ ImportantHashPositions = new HashPositions();
+
+ parse();
+ if (ImportantHashPositions.reorderNeeded) {
+ ImportantHashPositions.SectionTableHeaders.Sort((x, y) => x.PointerToRawData.CompareTo(y.PointerToRawData));
+ }
+ }
+
+ private X509Certificate2 certFromResource(string resourceName) {
+ var assembly = this.GetType().Assembly;
+ using (Stream stream = assembly.GetManifestResourceStream(resourceName))
+ using (BinaryReader reader = new BinaryReader(stream)) {
+ byte[] bytes = new byte[stream.Length];
+ stream.Read(bytes, 0, bytes.Length);
+ return new X509Certificate2(bytes);
+ }
+ }
+
+ // ReSharper disable once InconsistentNaming
+ private void parse() {
+ using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
+ using (BinaryReader reader = new BinaryReader(stream)) {
+
+ //seek to 0x3c to get right to the PE header location...
+ stream.Seek(0x3c, SeekOrigin.Begin);
+ uint startCoffHeader = BitConverter.ToUInt32(reader.ReadBytes(4), 0);
+ ImportantHashPositions.SetStartCoffHeader(startCoffHeader);
+ stream.Seek(startCoffHeader, SeekOrigin.Begin);
+
+ //parse COFF Header (28 or 24 bytes): https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image
+ byte[] coffHeaderBytes = reader.ReadBytes(24);
+ CoffHeader = StructHelper.FromBytes(coffHeaderBytes);
+
+ byte[] magicNumberBytes = reader.ReadBytes(2);
+ int magicNumber = BitConverter.ToInt16(magicNumberBytes, 0);
+ Type = magicNumber == 0x10b ? PeType.Pe32 : PeType.Pe32Plus;
+ stream.Seek(-2, SeekOrigin.Current); //skip back to before the magic number so the struct below is created properly
+
+ //parse Optional Header Standard and Windows-Specific fields (66 or 88 bytes)
+ //see: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-standard-fields-image-only
+ if (Type == PeType.Pe32) {
+ byte[] headerBytes = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER32)));
+ StandardFieldsPe32 = StructHelper.FromBytes(headerBytes);
+ } else {
+ byte[] headerBytes = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER64)));
+ StandardFieldsPe32Plus = StructHelper.FromBytes(headerBytes);
+ }
+
+ ImageOptionalHeaderWrapper wrapper = new ImageOptionalHeaderWrapper() {
+ h32 = StandardFieldsPe32,
+ h64 = StandardFieldsPe32Plus,
+ IsPe = Type == PeType.Pe32
+ };
+ ImportantHashPositions.SetCertificateDetails(Type, wrapper.SizeOfHeaders, wrapper.CertificateTableVirtualAddress, wrapper.CertificateTableSize);
+
+ for (int i = 0; i < CoffHeader.NumberOfSections; i++) {
+ byte[] nextSection = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER))); //should be 40 bytes...
+ IMAGE_SECTION_HEADER sectionTableHeader = StructHelper.FromBytes(nextSection);
+ ImportantHashPositions.AddSectionTableHeader(sectionTableHeader);
+ }
+ }
+ }
+
+ private void addCertToList(SignerInfo si, List list) {
+
+ X509Certificate2 cert = si.Certificate;
+ if (list.Find(x => x.Thumbprint == cert.Thumbprint) == null) {
+ list.Add(cert);
+ } else {
+ Logger.Debug("Certificate with Thumbprint {0} already in list. skipping.", cert.Thumbprint);
+ }
+ }
+
+ public List ExtractVerifiedSignatureCertificates() {
+ List list = new List();
+ using (FileStream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
+ using (BinaryReader reader = new BinaryReader(stream)) {
+ //verify all the embedded signatures
+ stream.Seek(this.ImportantHashPositions.CertificateTableStart, SeekOrigin.Begin);
+ int readCertLen = reader.ReadInt32();
+ int readRevision = reader.ReadInt16();
+ int readCertType = reader.ReadInt16();
+ byte[] pkcs7 = reader.ReadBytes(readCertLen);
+
+ SignedCms cms = new SignedCms();
+ cms.Decode(pkcs7);
+ cms.CheckHash();
+
+ //shout out to Scott MG: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/655f5c27-b049-4275-a8b3-cc1c0be2b4f2/retrieve-certificate-info-for-dualsigned-sha1sha256
+ //for indicating that the unsigned attributes contain the other SignerInfos
+ foreach (var cmsSi in cms.SignerInfos) {
+ cmsSi.CheckSignature(true);
+ if (VerifyTrust(SystemCAs, expectedRootCa, cmsSi.Certificate).Result) {
+ VerifyFileHash(cms, cmsSi);
+ addCertToList(cmsSi, list);
+ }
+ if (cmsSi.UnsignedAttributes.Count > 0) {
+ foreach (var unsignedAttr in cmsSi.UnsignedAttributes) {
+ foreach (AsnEncodedData asn in unsignedAttr.Values) {
+ SignedCms innerCms = new SignedCms();
+ innerCms.Decode(asn.RawData);
+ innerCms.CheckHash();
+ if (innerCms.SignerInfos.Count > 0) {
+ SignerInfo innerSignerInfo = innerCms.SignerInfos[0];
+ try {
+ innerSignerInfo.CheckSignature(false);
+ }
+ catch (CryptographicException) {
+ if (innerSignerInfo.Certificate.Thumbprint == oldKnownThumbprint) {
+ //special handling for the known 'old' NetFoundry signing certificate, now expired...
+ //TODO: remove this code after 2021
+ //just allow this one error
+ Logger.Warn("Ignoring timestamp validity issue for the existing code signing certificate. Subject: {0}, Thumbprint: {1}", innerSignerInfo.Certificate.Subject, innerSignerInfo.Certificate.Thumbprint);
+ } else {
+ if (IsCertificateOpenZitiVerifies(innerSignerInfo.Certificate)) {
+ Logger.Debug("Signature could not be verified. This is expected for the certificates issued by openziti/netfoundry.");
+ } else {
+ throw;
+ }
+ }
+ }
+
+ if (VerifyTrust(SystemCAs, expectedRootCa, innerSignerInfo.Certificate).Result) {
+ VerifyFileHash(innerCms, innerSignerInfo);
+ addCertToList(innerSignerInfo, list);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return list;
+ }
+
+ public bool IsCertificateOpenZitiVerifies(X509Certificate2 cert) {
+ bool yesOrNo = cert.Subject.ToLower().Contains("netfoundry") || cert.Subject.ToLower().Contains("openziti");
+ if (yesOrNo) {
+ Logger.Debug("Certificate does need verification: {0}", cert.Subject);
+ } else {
+ Logger.Debug("Certificate does not need verification: {0}", cert.Subject);
+ }
+ return yesOrNo;
+ }
+
+ public void Verify() {
+ Logger.Debug("extracting certificates from file: {0}", FilePath);
+ List list = ExtractVerifiedSignatureCertificates();
+ Logger.Info("Certificates extracted: {0}", list.Count);
+ foreach (X509Certificate2 cert in list) {
+ Logger.Info("Checking certificate: [{0}] {1}", cert.Thumbprint, cert);
+ if (IsCertificateOpenZitiVerifies(cert)) {
+ //verify this certificate was issued from the known CA
+ try {
+ Logger.Info("Verifying trust of certificate: [{0}] {1}", cert, cert.Thumbprint);
+ VerifyTrust(empty, expectedRootCa, cert, true).Wait();
+ Logger.Info("Certificate [{0}] {1} was verified signed by [{2}] {3}", cert.Thumbprint, cert.Subject, expectedRootCa.Thumbprint, expectedRootCa.Subject);
+ return; //yes!
+ }
+ catch {
+ //empty on purpose. keep checking...
+ }
+ }
+ }
+
+ throw new CryptographicException("Executable not signed by an appropriate certificate");
+ }
+
+ public void VerifyFileHash(SignedCms cms, SignerInfo signerInfo) {
+ if (!IsCertificateOpenZitiVerifies(signerInfo.Certificate)) {
+ return;
+ }
+
+ //calculate the hash using the first signed info
+ var hashAlg = IncrementalHash.CreateHash(new HashAlgorithmName(signerInfo.DigestAlgorithm.FriendlyName.ToUpper()));
+ byte[] calculatedHash = CalculatePeHashStreaming(hashAlg);
+ string hash = BitConverter.ToString(calculatedHash).Replace("-", "");
+
+ string content = BitConverter.ToString(cms.ContentInfo.Content).Replace("-", "");
+ if (!content.Contains(hash)) {
+ throw new CryptographicException("The expected hash and the actual hash did not match!");
+ }
+ }
+
+ public async static Task VerifyTrust(X509Store cas, X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate) {
+ return await VerifyTrust(cas, trustedRootCertificateAuthority, certificate, false);
+ }
+
+ public async static Task VerifyTrust(X509Store cas, X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate, bool verifyTrustedRoot) {
+ X509Chain chain = new X509Chain();
+ chain.ChainPolicy.ExtraStore.Add(trustedRootCertificateAuthority);
+ chain.ChainPolicy.ExtraStore.AddRange(cas.Certificates);
+ chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
+ chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
+ chain.Build(new X509Certificate2(certificate));
+ List exceptions = new List();
+ foreach (X509ChainStatus status in chain.ChainStatus) {
+ if (status.Status == X509ChainStatusFlags.NoError || status.Status == X509ChainStatusFlags.UntrustedRoot) {
+ //X509ChainStatusFlags.UntrustedRoot simply means it was not found in the computer/user's trust store.... that's fine...
+ } else {
+ if (status.Status == X509ChainStatusFlags.NotTimeValid && certificate.Thumbprint == oldKnownThumbprint) {
+ //X509ChainStatusFlags.NotTimeValid means the certificate has expired - we're allowing this for now (summer 2021) to allow older clients to update
+ Logger.Warn("Executable is signed using the old signing certificate. Allowing this certificate to report as expired");
+ } else {
+ exceptions.Add(new CryptographicException("Could not verify trust: " + status.StatusInformation));
+ }
+ }
+ }
+
+ //final check - make sure the last certificate matches the expected thumbprint
+ if (verifyTrustedRoot && chain.ChainElements[chain.ChainElements.Count - 1].Certificate.Thumbprint != trustedRootCertificateAuthority.Thumbprint) {
+ exceptions.Add(new Exception("Could not verify trust. The expected thumbprint was not found!"));
+ }
+
+ foreach (var e in certificate.Extensions) {
+ if (e.Oid.Value == "2.5.29.31") { //2.5.29.31 == CRL Distribution Points
+ try {
+ CrlDistributionPointParser t = new CrlDistributionPointParser(e.RawData);
+
+ if (t != null) {
+ foreach (string url in t.urls) {
+ try {
+ //good - things are going the way we want... keep going
+ //fetch the CRL from the url provided and parse it...
+ byte[] crlBytes = await new HttpClient().GetByteArrayAsync(url);
+
+ if (crlBytes != null && crlBytes.Length > 0) {
+ //fetch the crl
+ Win32Crypto.CrlInfo info = Win32Crypto.FromBlob(crlBytes);
+ if (info.RevokedSerialNumbers.Contains(certificate.SerialNumber)) {
+ exceptions.Add(new CryptographicException("Serial number " + certificate.SerialNumber + " has been revoked."));
+ }
+ } else {
+ exceptions.Add(new CryptographicException("Could not retrieve revocation list from " + url + "- cannot verify trust"));
+ }
+ }
+ catch (Exception innerException) {
+ exceptions.Add(new CryptographicException("crl at " + url + " could not be used", innerException));
+ }
+ }
+ }
+ }
+ catch (Exception ex) {
+ exceptions.Add(ex);
+ }
+ }
+ }
+
+ if (exceptions.Count > 0) {
+ throw new AggregateException(exceptions);
+ }
+
+ return true;
+ }
+
+ public byte[] CalculatePeHashStreaming(IncrementalHash alg) {
+ using (FileStream stream = new FileStream(FilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
+ using (BinaryReader reader = new BinaryReader(stream)) {
+ stream.Seek(ImportantHashPositions.Chunk1.Start, SeekOrigin.Begin);
+ alg.AppendData(reader.ReadBytes(ImportantHashPositions.Chunk1.Length));
+
+ stream.Seek(ImportantHashPositions.Chunk2.Start, SeekOrigin.Begin);
+ alg.AppendData(reader.ReadBytes(ImportantHashPositions.Chunk2.Length));
+
+ stream.Seek(ImportantHashPositions.Chunk3.Start, SeekOrigin.Begin);
+ alg.AppendData(reader.ReadBytes(ImportantHashPositions.Chunk3.Length));
+
+ int blockSize = 8192;
+ int read = 0;
+ byte[] buf = new byte[blockSize];
+
+ foreach (IMAGE_SECTION_HEADER h in ImportantHashPositions.SectionTableHeaders) {
+ stream.Seek(h.PointerToRawData, SeekOrigin.Begin);
+ if (h.SizeOfRawData <= blockSize) {
+ //just read the full block as one
+ alg.AppendData(reader.ReadBytes((int)h.SizeOfRawData));
+ } else {
+ long pos = stream.Position;
+ long end = h.PointerToRawData + h.SizeOfRawData - blockSize;
+ //process the data into blocks
+ while (pos < end) {
+ read = stream.Read(buf, 0, blockSize);
+ alg.AppendData(buf, 0, read);
+ pos += blockSize;
+ }
+
+ int remaining = (int)(end - pos + blockSize);
+ if (remaining > 0) {
+ read = stream.Read(buf, 0, remaining);
+ alg.AppendData(buf, 0, read);
+ }
+ }
+ }
+
+ /*
+ 14. Create a value called FILE_SIZE, which is not part of the signature. Set this value to the
+ image’s file size, acquired from the underlying file system. If FILE_SIZE is greater than
+ SUM_OF_BYTES_HASHED, the file contains extra data that must be added to the hash. This data
+ begins at the SUM_OF_BYTES_HASHED file offset, and its length is:
+ (File Size) – ((Size of AttributeCertificateTable) + SUM_OF_BYTES_HASHED)
+ Note: The size of Attribute Certificate Table is specified in the second ULONG value in the
+ Certificate Table entry (32 bit: offset 132, 64 bit: offset 148) in Optional Header Data
+ Directories.
+ */
+ long FILE_SIZE = new FileInfo(FilePath).Length;
+
+ long leftoverBytesToHash = FILE_SIZE - ImportantHashPositions.SumOfBytesHashed - ImportantHashPositions.CertificateTableSize;
+
+ if (leftoverBytesToHash > 0) {
+ //HashBlock(alg, reader, SUM_OF_BYTES_HASHED + certTableSize, FILE_SIZE);
+ stream.Seek(ImportantHashPositions.SumOfBytesHashed, SeekOrigin.Begin);
+ byte[] leftoverBytes = reader.ReadBytes((int)leftoverBytesToHash);
+ alg.AppendData(leftoverBytes);
+ } else {
+ //no more bytes to hash...
+ }
+ //alg.TransformFinalBlock(new byte[0], 0, 0);
+
+ byte[] result = alg.GetHashAndReset();
+
+ return result;
+ }
+ }
+
+ public class HashPositions {
+ internal HashPositions() {
+ Chunk1 = new Range();
+ Chunk2 = new Range();
+ Chunk3 = new Range();
+ SectionTableHeaders = new List();
+ }
+ /*
+ * These functions are all based from the document linked to from the bottom of:
+ * https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-standard-fields-image-only
+ *
+ * Link to the document:
+ * https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx
+ *
+ * Relevant section copied from that document:
+ * To calculate the hash value:
+ 1. Load the image header into memory.
+ 2. Initialize a hash algorithm context.
+ 3. Hash the image header from its base to immediately before the start of the
+ checksum address, as specified in Optional Header Windows-Specific Fields.
+ 4. Skip over the checksum, which is a 4-byte field.
+ 5. Hash everything from the end of the checksum field to immediately before the start of
+ the Certificate Table entry, as specified in Optional Header Data Directories.
+ 6. Get the Attribute Certificate Table address and size from the Certificate Table entry.
+ For details, see section 5.7 of the PE/COFF specification.
+ 7. Exclude the Certificate Table entry from the calculation and hash everything from the
+ end of the Certificate Table entry to the end of image header, including Section Table
+ (headers).The Certificate Table entry is 8 bytes long, as specified in Optional Header
+ Data Directories.
+ 8. Create a counter called SUM_OF_BYTES_HASHED, which is not part of the signature. Set
+ this counter to the SizeOfHeaders field, as specified in Optional Header Windows-Specific
+ Field.
+ 9. Build a temporary table of pointers to all of the section headers in the image. The
+ NumberOfSections field of COFF File Header indicates how big the table should be. Do not
+ include any section headers in the table whose SizeOfRawData field is zero.
+ 10. Using the PointerToRawData field (offset 20) in the referenced SectionHeader structure as
+ a key, arrange the table's elements in ascending order. In other words, sort the section
+ headers in ascending order according to the disk-file offset of the sections.
+ 11. Walk through the sorted table, load the corresponding section into memory, and hash the entire
+ section. Use the SizeOfRawData field in the SectionHeader structure to determine the amount
+ of data to hash.
+ 12. Add the section’s SizeOfRawData value to SUM_OF_BYTES_HASHED.
+ 13. Repeat steps 11 and 12 for all of the sections in the sorted table.
+ 14. Create a value called FILE_SIZE, which is not part of the signature. Set this value to the
+ image’s file size, acquired from the underlying file system. If FILE_SIZE is greater than
+ SUM_OF_BYTES_HASHED, the file contains extra data that must be added to the hash. This data
+ begins at the SUM_OF_BYTES_HASHED file offset, and its length is:
+ (File Size) – ((Size of AttributeCertificateTable) + SUM_OF_BYTES_HASHED)
+ Note: The size of Attribute Certificate Table is specified in the second ULONG value in the
+ Certificate Table entry (32 bit: offset 132, 64 bit: offset 148) in Optional Header Data
+ Directories.
+ 15. Finalize the hash algorithm context.
+ Note: This procedure uses offset values from the PE/COFF specification, version 8.1 .
+ For authoritative offset values, refer to the most recent version of the PE/COFF specification.
+ */
+
+ public List SectionTableHeaders { get; private set; }
+
+ ///
+ /// Chunk1 Start will set the position in the file which the hashing of the file should begin from
+ /// This is always the start of the file and thus is hardcoded to 0 and takes no parameters
+ ///
+ /// Chunk1 End is defined to be: "immediately before the start of the checksum address"
+ ///
+ /// Relevant step:
+ /// 3. Hash the image header from its base to immediately before the start of the
+ /// checksum address, as specified in Optional Header Windows-Specific Fields.
+ ///
+ public Range Chunk1 { get; private set; }
+
+ ///
+ /// Chunk2 Start is defined to be: "end of the checksum field"
+ /// Chunk2 End is defined to be: "immediately before the start of the Certificate Table entry"
+ ///
+ /// Relevant step:
+ /// 5. Hash everything from the end of the checksum field to immediately before the start of
+ /// the Certificate Table entry, as specified in Optional Header Data Directories.
+ ///
+ public Range Chunk2 { get; private set; }
+
+ ///
+ /// Chunk3 Start is defined to be: "the end of the Certificate Table entry"
+ /// Chunk3 End is defined to be: "the end of image header"
+ /// When testing/developing this solution - this was found to be the position specified by
+ /// the SizeOfHeaders field
+ ///
+ /// Relevant step:
+ /// 7. Exclude the Certificate Table entry from the calculation and hash everything from the
+ /// end of the Certificate Table entry to the end of image header, including Section Table
+ /// (headers).The Certificate Table entry is 8 bytes long, as specified in Optional Header
+ /// Data Directories.
+ ///
+ public Range Chunk3 { get; private set; }
+
+ public uint SizeOfHeaders { get; private set; }
+ public uint SumOfBytesHashed { get; private set; }
+ public long CertificateTableStart { get; private set; }
+ public long CertificateTableSize { get; private set; }
+
+ internal bool reorderNeeded = false;
+ private uint lastAddress = 0;
+ public void AddSectionTableHeader(IMAGE_SECTION_HEADER sectionTableHeader) {
+
+ if (sectionTableHeader.SizeOfRawData > 0) {
+ SectionTableHeaders.Add(sectionTableHeader);
+ }
+
+ if (lastAddress < sectionTableHeader.PointerToRawData) {
+ //good - it's in order...
+ } else {
+ reorderNeeded = true;
+ }
+ lastAddress = sectionTableHeader.PointerToRawData;
+ SumOfBytesHashed += sectionTableHeader.SizeOfRawData;
+ }
+
+ public void SetStartCoffHeader(uint peHeaderStart) {
+ Chunk1.Start = 0;
+ uint checksumStart = peHeaderStart + 88; //88 is the size of the COFF Header (24) + Standard Headers + Windows-Specific Fields up to checksum (offset 64)
+ Chunk1.End = checksumStart;
+ Chunk2.Start = checksumStart + LengthOfChecksum;
+
+ //and the four tables before the Certificate Table
+ //(ExportTable, ImportTable, ResourceTable, ExceptionTable)
+ uint certHeaderTableOffset = checksumStart + 64;
+
+ Chunk2.End = certHeaderTableOffset;
+ Chunk3.Start = certHeaderTableOffset + LengthCertificateTable;
+ }
+
+ public void SetCertificateDetails(PeType type, uint sizeOfHeaders, uint certificateTableStart,
+ uint certificateTableSize) {
+ if (type == PeType.Pe32Plus) {
+ uint winSpecificExtraBytes = 16; //need to account for the extra bytes in the Windows-Specific Fields (each field has 4 extra bytes)
+ Chunk2.End += winSpecificExtraBytes;
+ Chunk3.Start += winSpecificExtraBytes;
+ }
+
+ Chunk3.End = sizeOfHeaders;
+ SizeOfHeaders = sizeOfHeaders;
+ SumOfBytesHashed = sizeOfHeaders;
+ CertificateTableStart = certificateTableStart;
+ CertificateTableSize = certificateTableSize;
+ }
+ }
+
+ public class Range {
+ public long Start { get; internal set; }
+ public long End { get; internal set; }
+
+ public int Length => (int)(End - Start);
+ }
+ }
+
+ public enum PeType {
+ Pe32,
+ Pe32Plus
+ }
+
+ internal class CrlDistributionPointParser {
+ internal int offset;
+ internal int length;
+ internal List tags = new List();
+ private CrlDistributionPointParser parent = null;
+ private AsnTagType type = AsnTagType.NONE;
+ internal List urls = new List();
+ private int consumedBytes = 0;
+
+ internal CrlDistributionPointParser(byte[] asn1Data) : this(new MemoryStream(asn1Data)) {
+
+ }
+
+ internal CrlDistributionPointParser(MemoryStream asn1Data) : this(null, asn1Data/*new BinaryReader(asn1Data)*/, 0) {
+
+ }
+
+ internal CrlDistributionPointParser(CrlDistributionPointParser parent, MemoryStream memoryStream, /*BinaryReader asn1Reader,*/ int offset) {
+ BinaryReader asn1Reader = new BinaryReader(memoryStream);
+ this.offset = offset;
+ type = readTag(asn1Reader);
+ length = readTagLen(asn1Reader);
+ this.parent = parent;
+ int headerBytes = consumedBytes;
+ while (this.consumedBytes < length + headerBytes) {
+ switch (type) {
+ case AsnTagType.SEQUENCE:
+ case AsnTagType.ARRAY_ELEMENT:
+ var t = new CrlDistributionPointParser(this, memoryStream, (int) memoryStream.Position);
+ consumedBytes += t.consumedBytes;
+ tags.Add(t);
+ this.urls.AddRange(t.urls);
+ break;
+ case AsnTagType.STRING:
+ consumedBytes += length;
+ urls.Add(System.Text.Encoding.UTF8.GetString(asn1Reader.ReadBytes(length)));
+ break;
+ }
+ }
+ }
+
+ internal AsnTagType readTag(BinaryReader asn1Data) {
+ byte tagByte = asn1Data.ReadByte();
+ if (tagByte != 0x30 && tagByte != 0xA0 && tagByte != 0x86) {
+ throw new CryptographicException("Could not parse CRL Distribution Points. First byte not asn SEQUENCE|ARRAY|STRING marker");
+ }
+
+ consumedBytes++;
+ if (tagByte == 0x30) return AsnTagType.SEQUENCE;
+ if (tagByte == 0xA0) return AsnTagType.ARRAY_ELEMENT;
+ if (tagByte == 0x86) return AsnTagType.STRING;
+ return AsnTagType.NONE;
+ }
+
+ internal int readTagLen(BinaryReader asn1Data) {
+ int tagLen = asn1Data.ReadByte();
+ consumedBytes++;
+ if (tagLen > 128) {
+ //means there's more bytes to read that control the overall length of the next section
+ int moreBytes = tagLen - 128;
+ byte[] lenBytes = new byte[4];
+ byte[] d = asn1Data.ReadBytes(moreBytes);
+
+ int pos = 0;
+ for (int i = d.Length - 1; i >= 0; i--) {
+ lenBytes[pos++] = d[i];
+ }
+
+ tagLen = BitConverter.ToInt32(lenBytes, 0);
+ consumedBytes += moreBytes;
+ }
+
+ return tagLen;
+ }
+ }
+
+ internal enum AsnTagType {
+ SEQUENCE = 0x30,
+ ARRAY_ELEMENT = 0xA0,
+ STRING = 0x86,
+ NONE = 0,
+ }
+}
diff --git a/ZitiUpdateService/checkers/PeFile/StructHelper.cs b/ZitiUpdateService/checkers/PeFile/StructHelper.cs
new file mode 100644
index 00000000..60670764
--- /dev/null
+++ b/ZitiUpdateService/checkers/PeFile/StructHelper.cs
@@ -0,0 +1,15 @@
+using System.Runtime.InteropServices;
+
+namespace ZitiUpdateService.Checkers.PeFile {
+ public static class StructHelper {
+
+ public static T FromBytes(byte[] bytes) {
+ // Pin the managed memory while, copy it out the data, then unpin it
+ GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
+ T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
+ handle.Free();
+
+ return theStructure;
+ }
+ }
+}
diff --git a/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs b/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs
new file mode 100644
index 00000000..b84dd21e
--- /dev/null
+++ b/ZitiUpdateService/checkers/PeFile/Win32Crypto.cs
@@ -0,0 +1,249 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace VerifyingFiles.PInvoke {
+ public static class Win32Crypto {
+ //based on https://docs.microsoft.com/en-us/archive/blogs/alejacma/how-to-get-information-from-a-crl-net
+
+ #region APIs
+ [DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern Boolean CryptQueryObject(
+ Int32 dwObjectType,
+ [MarshalAs(UnmanagedType.LPWStr)] String pvObject,
+ Int32 dwExpectedContentTypeFlags,
+ Int32 dwExpectedFormatTypeFlags,
+ Int32 dwFlags,
+ IntPtr pdwMsgAndCertEncodingType,
+ IntPtr pdwContentType,
+ IntPtr pdwFormatType,
+ IntPtr phCertStore,
+ IntPtr phMsg,
+ ref IntPtr ppvContext
+ );
+ [DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern Boolean CryptQueryObject2(
+ Int32 dwObjectType,
+ IntPtr pvObject,
+ Int32 dwExpectedContentTypeFlags,
+ Int32 dwExpectedFormatTypeFlags,
+ Int32 dwFlags,
+ IntPtr pdwMsgAndCertEncodingType,
+ IntPtr pdwContentType,
+ IntPtr pdwFormatType,
+ ref IntPtr phCertStore,
+ IntPtr phMsg,
+ ref IntPtr ppvContext
+ );
+
+ [DllImport("CRYPT32.DLL", EntryPoint = "CertFreeCRLContext", SetLastError = true)]
+ public static extern Boolean CertFreeCRLContext(
+ IntPtr pCrlContext
+ );
+
+ [DllImport("CRYPT32.DLL", EntryPoint = "CertNameToStr", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern Int32 CertNameToStr(
+ Int32 dwCertEncodingType,
+ ref CRYPTOAPI_BLOB pName,
+ Int32 dwStrType,
+ StringBuilder psz,
+ Int32 csz
+ );
+
+ [DllImport("CRYPT32.DLL", EntryPoint = "CertFindExtension", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern IntPtr CertFindExtension(
+ [MarshalAs(UnmanagedType.LPStr)] String pszObjId,
+ Int32 cExtensions,
+ IntPtr rgExtensions
+ );
+
+ [DllImport("CRYPT32.DLL", EntryPoint = "CryptFormatObject", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern Boolean CryptFormatObject(
+ Int32 dwCertEncodingType,
+ Int32 dwFormatType,
+ Int32 dwFormatStrType,
+ IntPtr pFormatStruct,
+ [MarshalAs(UnmanagedType.LPStr)] String lpszStructType,
+ IntPtr pbEncoded,
+ Int32 cbEncoded,
+ StringBuilder pbFormat,
+ ref Int32 pcbFormat
+ );
+
+ #endregion APIs
+
+ #region Structs
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CRL_CONTEXT {
+ public Int32 dwCertEncodingType;
+ public IntPtr pbCrlEncoded;
+ public Int32 cbCrlEncoded;
+ public IntPtr pCrlInfo;
+ public IntPtr hCertStore;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CRL_INFO {
+ public Int32 dwVersion;
+ public CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
+ public CRYPTOAPI_BLOB Issuer;
+ public FILETIME ThisUpdate;
+ public FILETIME NextUpdate;
+ public Int32 cCRLEntry;
+ public IntPtr rgCRLEntry;
+ public Int32 cExtension;
+ public IntPtr rgExtension;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CRYPT_ALGORITHM_IDENTIFIER {
+ [MarshalAs(UnmanagedType.LPStr)] public String pszObjId;
+ public CRYPTOAPI_BLOB Parameters;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CRYPTOAPI_BLOB {
+ public Int32 cbData;
+ public IntPtr pbData;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct FILETIME {
+ public Int32 dwLowDateTime;
+ public Int32 dwHighDateTime;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CRL_ENTRY {
+ public CRYPTOAPI_BLOB SerialNumber;
+ public FILETIME RevocationDate;
+ public Int32 cExtension;
+ public IntPtr rgExtension;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CERT_EXTENSION {
+ [MarshalAs(UnmanagedType.LPStr)] public String pszObjId;
+ public Boolean fCritical;
+ public CRYPTOAPI_BLOB Value;
+ }
+
+ #endregion Structs
+
+ #region Consts
+
+ public const Int32 CERT_QUERY_OBJECT_BLOB = 0x00000002;
+ public const Int32 CERT_QUERY_CONTENT_CRL = 3;
+ public const Int32 CERT_QUERY_CONTENT_FLAG_CRL = 1 << CERT_QUERY_CONTENT_CRL;
+ public const Int32 CERT_QUERY_FORMAT_BINARY = 1;
+ public const Int32 CERT_QUERY_FORMAT_BASE64_ENCODED = 2;
+ public const Int32 CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED = 3;
+ public const Int32 CERT_QUERY_FORMAT_FLAG_BINARY = 1 << CERT_QUERY_FORMAT_BINARY;
+ public const Int32 CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = 1 << CERT_QUERY_FORMAT_BASE64_ENCODED;
+ public const Int32 CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED = 1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED;
+ public const Int32 CERT_QUERY_FORMAT_FLAG_ALL = CERT_QUERY_FORMAT_FLAG_BINARY | CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED | CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED;
+
+ public const Int32 X509_ASN_ENCODING = 0x00000001;
+ public const Int32 PKCS_7_ASN_ENCODING = 0x00010000;
+
+ public const Int32 X509_NAME = 7;
+
+ public const Int32 CERT_SIMPLE_NAME_STR = 1;
+ public const Int32 CERT_OID_NAME_STR = 2;
+ public const Int32 CERT_X500_NAME_STR = 3;
+
+ public const String szOID_CRL_REASON_CODE = "2.5.29.21";
+
+ #endregion
+
+ private static List GetRevokedSerialNumbers(CRL_INFO stCrlInfo) {
+ List rtn = new List();
+ var rgCrlEntry = stCrlInfo.rgCRLEntry;
+
+ for (var i = 0; i < stCrlInfo.cCRLEntry; i++) {
+ var serial = string.Empty;
+ var stCrlEntry = (CRL_ENTRY)Marshal.PtrToStructure(rgCrlEntry, typeof(CRL_ENTRY));
+
+ IntPtr pByte = stCrlEntry.SerialNumber.pbData;
+ for (var j = 0; j < stCrlEntry.SerialNumber.cbData; j++) {
+ Byte bByte = Marshal.ReadByte(pByte);
+ serial = bByte.ToString("X").PadLeft(2, '0') + serial;
+ pByte = pByte + Marshal.SizeOf(typeof(byte));
+
+ }
+ rtn.Add(serial);
+ rgCrlEntry = rgCrlEntry + Marshal.SizeOf(typeof(CRL_ENTRY));
+ }
+
+ return rtn;
+ }
+
+ public static DateTime FileTimeToDateTime(FILETIME fileTime) {
+ DateTime dateTime;
+ IntPtr int64Ptr = Marshal.AllocHGlobal(sizeof(Int64));
+ try {
+ Marshal.StructureToPtr(fileTime, int64Ptr, true);
+ dateTime = DateTime.FromFileTime(Marshal.ReadInt64(int64Ptr));
+ } finally {
+ Marshal.FreeHGlobal(int64Ptr);
+ }
+
+ return dateTime;
+ }
+
+ public static CrlInfo FromBlob(byte[] CrlDERBytes) {
+
+ var phCertStore = IntPtr.Zero;
+ var pvContext = IntPtr.Zero;
+ var hCrlData = new GCHandle();
+ var hCryptBlob = new GCHandle();
+ try {
+ hCrlData = GCHandle.Alloc(CrlDERBytes, GCHandleType.Pinned);
+ CRYPTOAPI_BLOB stCryptBlob;
+ stCryptBlob.cbData = CrlDERBytes.Length;
+ stCryptBlob.pbData = hCrlData.AddrOfPinnedObject();
+ hCryptBlob = GCHandle.Alloc(stCryptBlob, GCHandleType.Pinned);
+
+ if (!CryptQueryObject2(
+ CERT_QUERY_OBJECT_BLOB,
+ hCryptBlob.AddrOfPinnedObject(),
+ CERT_QUERY_CONTENT_FLAG_CRL,
+ CERT_QUERY_FORMAT_FLAG_BINARY,
+ 0,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ IntPtr.Zero,
+ ref phCertStore,
+ IntPtr.Zero,
+ ref pvContext
+ )) {
+ throw new Win32Exception(Marshal.GetLastWin32Error(), "CRL is Corrupted.");
+ }
+ var stCrlContext = (CRL_CONTEXT)Marshal.PtrToStructure(pvContext, typeof(CRL_CONTEXT));
+ var stCrlInfo = (CRL_INFO)Marshal.PtrToStructure(stCrlContext.pCrlInfo, typeof(CRL_INFO));
+
+ CrlInfo info = new CrlInfo();
+ info.validTo = FileTimeToDateTime(stCrlInfo.NextUpdate);
+ info.validFrom = FileTimeToDateTime(stCrlInfo.ThisUpdate);
+ info.RevokedSerialNumbers = GetRevokedSerialNumbers(stCrlInfo);
+
+ return info;
+ } finally {
+ if (hCrlData.IsAllocated) hCrlData.Free();
+ if (hCryptBlob.IsAllocated) hCryptBlob.Free();
+ if (!pvContext.Equals(IntPtr.Zero)) {
+ CertFreeCRLContext(pvContext);
+ }
+ }
+ }
+
+ public struct CrlInfo {
+ public DateTime validTo;
+ public DateTime validFrom;
+ public List RevokedSerialNumbers;
+ }
+ }
+}
diff --git a/ZitiUpdateService/checkers/PeFile/openziti.rootCA.rsa.pem.txt b/ZitiUpdateService/checkers/PeFile/openziti.rootCA.rsa.pem.txt
new file mode 100644
index 00000000..87464af1
--- /dev/null
+++ b/ZitiUpdateService/checkers/PeFile/openziti.rootCA.rsa.pem.txt
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFozCCA4ugAwIBAgIUdhfTn8Eu4pU39MIPpoerTLqbLRkwDQYJKoZIhvcNAQEN
+BQAwYTEYMBYGA1UEAwwPUm9vdCBTaWduaW5nIENBMRkwFwYDVQQKDBBvcGVueml0
+aS5vcmcgSW5jMRAwDgYDVQQLDAdhZHYtZGV2MQswCQYDVQQGEwJVUzELMAkGA1UE
+CAwCTkMwHhcNMjEwNjAyMTcyNDMzWhcNNDEwNjE3MTcyNDMzWjBhMRgwFgYDVQQD
+DA9Sb290IFNpZ25pbmcgQ0ExGTAXBgNVBAoMEG9wZW56aXRpLm9yZyBJbmMxEDAO
+BgNVBAsMB2Fkdi1kZXYxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBALcLp/jh0zYqDQw6Gxzoxnib08QLwBo7
+AHLdJSOxdFy3K0a7xSEZjkhJgWQWw/c/HArw5PiHS4jdsh7o8mSJGLqBj+s/Gj4M
+tM4k3A5jJHcgAbDKPAaAZAXQ2aEDp8eA65S1mRqNnYHbmTm3Q3t3U7dqI1lISzjz
+k5jBQnXS3Qtlj2SyMXRYPpPJIQlXgZXdvLmL7wG1FpmCFBSEEdOVMIhEr5hJ/5rc
+HpTRSueLPpdJUSv3B3uuPdo5rI/pJJ6gAJUtmMw/G8CAlMwXlUbSLt5IvNriyjqT
+pisTjkQR0Vs8pvAN0CKfOqwoPWDwuJkump+LRTTQ0PLaNCnlU96mpoX9D7yhQAGP
+C5h0N0jPV2MpnEFNVPaGE43Wj1zejVP1S5Ep3i/Xu/aSUGmIK5MQ95z/J927LBKe
+AOyQDdQC0WkCBpCzpe0G9c+M0u7lYUHWXExWB22nQT2rM3aG5ZdOJRu42PXFNUr4
+bgkZq56PiCCgwDENOlln6/h8wD2a2o5n638BCwW3Qq4w8u2e7dKIAjNroQ053iyw
+ZmSIwImf9N+0631d8LDoVLllry+geP1bSlI1UCYcVjJWGSDwEmK9h+kwUsMX+yl7
+TAzgrnAkBS+C62sDvGHRjhp82IDklqsBQV7SsRG5ZqHAipbIa0NfCURsqmQ6iyNR
+a3/9oiE9ARdHAgMBAAGjUzBRMB0GA1UdDgQWBBS0v5OYBy16UEw7k7nMLg7Y3Fs7
+uDAfBgNVHSMEGDAWgBS0v5OYBy16UEw7k7nMLg7Y3Fs7uDAPBgNVHRMBAf8EBTAD
+AQH/MA0GCSqGSIb3DQEBDQUAA4ICAQBPtyCaKyk+WhXI1Gu83h6ePKuevwwnBygr
+xOEJM7b0kAxtcgK46B9wPgvLuUw1znmDzAuAqU52k00q1xLGZ+N5JNhlzzNWW0n2
+8Kj0gFLmu3tO7XXjQfQPKiZp/iiwOG9XPVVDTF2HVqkOsvdGYD2XH+wI4v/4m3wa
+xTvliEz9NCei7/NOhuTbJvbvcSmqAvs3+gH55bzBvvr+Hx/krx8HMcBikx92S5nF
+NKBkHIUQVqGoCHxGY/uM0xou5797faVEzLkaDle+oisMiy061pYI0bb9CRdHAU0r
+UoemoY3UOKlyK57wdXKRizDxmEyND2zxPYrktZu0SybjZmaFOCHLLp1WrcTlcp3C
+7Lsuj9Vzui2mjJuQzSTEyd1R04AT3xwb9slN+iHuN1dAGexVsENahW2q9ZAjEtbu
+E6B+gOrSJ7WaL8R7AAz/E3tfU2bYm99JoGCPjI2/kP7udw7EqvoKYfJO1gbRo45z
+37xJmDrxb2SJFEtn5mqPO63HjGXGiC6xI52Olmv4Csk3WOseO3j9/SwazDAMqZSh
+dY0BN0DADO8kiohyF+Cq7nPR/tX17DpQiWFgJ4xoYHRFYCyb6QZ5jOzMhrZJa7cM
+v7BIbBcUe1vcKJ+gD6dXMT77KakdkRSfuiuEAeh6GmjPC130ni1stn+chw0pymax
+ZSpzkClEkQ==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/ZitiUpdateService/checkers/UpdateCheckers.cs b/ZitiUpdateService/checkers/UpdateCheckers.cs
index 582a15d8..374f1113 100644
--- a/ZitiUpdateService/checkers/UpdateCheckers.cs
+++ b/ZitiUpdateService/checkers/UpdateCheckers.cs
@@ -3,7 +3,7 @@ using System.IO;
using NLog;
-namespace ZitiUpdateService.checkers {
+namespace ZitiUpdateService.Checkers {
abstract class UpdateCheck {
public abstract int IsUpdateAvailable(Version current);
diff --git a/ZitiUpdateService/packages.config b/ZitiUpdateService/packages.config
index 2acb7a14..4e54d782 100644
--- a/ZitiUpdateService/packages.config
+++ b/ZitiUpdateService/packages.config
@@ -5,4 +5,9 @@
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/release-notes.md b/release-notes.md
index 611c4a40..0a509425 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -1,3 +1,17 @@
+# Release 1.9.7
+
+## What's New
+* none
+
+## Other changes:
+* none
+
+## Bugs fixed:
+* none
+
+## Dependency Updates
+* tsdk is updated to v0.15.7, c-sdk 0.23.3
+
# Release 1.9.6
## What's New
diff --git a/service/build.bat b/service/build.bat
index e1b6b27a..3c390e0c 100644
--- a/service/build.bat
+++ b/service/build.bat
@@ -14,7 +14,7 @@ REM See the License for the specific language governing permissions and
REM limitations under the License.
REM
SET REPO_URL=https://github.com/openziti/ziti-tunnel-sdk-c.git
-SET ZITI_TUNNEL_REPO_BRANCH=v0.15.6
+SET ZITI_TUNNEL_REPO_BRANCH=v0.15.7
REM override the c sdk used in the build - leave blank for the same as specified in the tunneler sdk
SET ZITI_SDK_C_BRANCH=
REM the number of TCP connections the tunneler sdk can have at any one time
diff --git a/service/ziti-tunnel/version.go b/service/ziti-tunnel/version.go
index 5305d2a9..2307c033 100644
--- a/service/ziti-tunnel/version.go
+++ b/service/ziti-tunnel/version.go
@@ -20,9 +20,9 @@
package main
const (
- Version = "1.9.6"
- Revision = "ed9a214d5c33"
+ Version = "1.9.8"
+ Revision = "a763f2acac36"
Branch = "main"
BuildUser = ""
- BuildDate = "2021-05-31 14:36:25"
+ BuildDate = "2021-06-07 18:26:10"
)
diff --git a/version b/version
index 7bc1c404..66beabb5 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-1.9.6
+1.9.8