Merge branch 'main' into dns-requests-delay-fix

This commit is contained in:
Mary Lincy Dcouto
2021-06-08 17:37:15 +05:30
25 changed files with 1545 additions and 66 deletions
+5 -1
View File
@@ -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"
+1 -1
View File
@@ -268,7 +268,7 @@ service/libuv.dll
Installer/build/
Installer/ziti-tunnel-service.zip
ziti-tunnel-service.zip
WinSign.p12
WinSign.*
*.p12
*.pfx
*.pem
+2 -1
View File
@@ -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
}
}
+3 -3
View File
@@ -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")]
+2 -2
View File
@@ -23,10 +23,10 @@
<ROW Property="CTRLS" Value="2"/>
<ROW Property="Manufacturer" Value="NetFoundry, Inc."/>
<ROW Property="MsiLogging" MultiBuildValue="AnyCPU_Release:vp"/>
<ROW Property="ProductCode" Value="1033:{831C7D48-901A-4B7D-A2F5-60BFDA7A6859} " Type="16"/>
<ROW Property="ProductCode" Value="1033:{AE5F8F1C-44E1-4BA6-8F72-BA58D2641742} " Type="16"/>
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="Ziti Desktop Edge"/>
<ROW Property="ProductVersion" Value="1.9.6" Type="32"/>
<ROW Property="ProductVersion" Value="1.9.8" Type="32"/>
<ROW Property="REBOOT" MultiBuildValue="AnyCPU_Release:Suppress"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
<ROW Property="UpgradeCode" Value="{CB7E173A-663D-4A23-BB22-D1E8D885C2F3}"/>
+7 -1
View File
@@ -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 =========================="
@@ -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;
+3
View File
@@ -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}"
+10 -10
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<!--
@@ -10,32 +10,32 @@
This operation is should probably remain high such as "1:0:0" (every hour) or "1:0:0:0" (every day)
-->
<add key="UpdateTimer" value="0:10:0"/>
<add key="UpdateTimer" value="0:10:0" />
<!--default to look for updates once every ten minutes-->
<add key="Version" value="release"/>
<add key="ClientSettingsProvider.ServiceUri" value=""/>
<add key="UseBetaReleases" value="false"/>
<add key="Version" value="release" />
<add key="ClientSettingsProvider.ServiceUri" value="" />
<add key="UseBetaReleases" value="false" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<system.web>
<membership defaultProvider="ClientAuthenticationMembershipProvider">
<providers>
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri=""/>
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
</providers>
</membership>
<roleManager defaultProvider="ClientRoleProvider" enabled="true">
<providers>
<add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400"/>
<add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400" />
</providers>
</roleManager>
</system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
+3 -3
View File
@@ -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")]
+10 -27
View File
@@ -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();
+24 -1
View File
@@ -13,6 +13,7 @@
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<IsWebBootstrapper>false</IsWebBootstrapper>
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
@@ -28,7 +29,6 @@
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
@@ -98,9 +98,26 @@
<Reference Include="System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.PowerShell.5.ReferenceAssemblies.1.1.0\lib\net4\System.Management.Automation.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Security" />
<Reference Include="System.Security.Cryptography.Pkcs, Version=5.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Pkcs.5.0.1\lib\net461\System.Security.Cryptography.Pkcs.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -113,6 +130,10 @@
<ItemGroup>
<Compile Include="checkers\FilesystemCheck.cs" />
<Compile Include="checkers\GithubCheck.cs" />
<Compile Include="checkers\PeFile\SignedFileStructs.cs" />
<Compile Include="checkers\PeFile\SignedFileValidator.cs" />
<Compile Include="checkers\PeFile\StructHelper.cs" />
<Compile Include="checkers\PeFile\Win32Crypto.cs" />
<Compile Include="Program.cs" />
<Compile Include="ProjectInstaller.cs">
<SubType>Component</SubType>
@@ -150,6 +171,7 @@
</ItemGroup>
<ItemGroup>
<Resource Include="ziti-green.ico" />
<EmbeddedResource Include="checkers\PeFile\openziti.rootCA.rsa.pem.txt" />
<Content Include="ziti-white.ico" />
</ItemGroup>
<ItemGroup>
@@ -170,5 +192,6 @@
<Name>ZitiDesktopEdge.Client</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -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;
+3 -2
View File
@@ -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;
@@ -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
/// <summary>
/// Reserved for future use.
/// </summary>
TypeReg = 0x00000000,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeDsect = 0x00000001,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeNoLoad = 0x00000002,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeGroup = 0x00000004,
/// <summary>
/// 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.
/// </summary>
TypeNoPadded = 0x00000008,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeCopy = 0x00000010,
/// <summary>
/// The section contains executable code.
/// </summary>
ContentCode = 0x00000020,
/// <summary>
/// The section contains initialized data.
/// </summary>
ContentInitializedData = 0x00000040,
/// <summary>
/// The section contains uninitialized data.
/// </summary>
ContentUninitializedData = 0x00000080,
/// <summary>
/// Reserved for future use.
/// </summary>
LinkOther = 0x00000100,
/// <summary>
/// The section contains comments or other information. The .drectve section has this type. This is valid for object files only.
/// </summary>
LinkInfo = 0x00000200,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeOver = 0x00000400,
/// <summary>
/// The section will not become part of the image. This is valid only for object files.
/// </summary>
LinkRemove = 0x00000800,
/// <summary>
/// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files.
/// </summary>
LinkComDat = 0x00001000,
/// <summary>
/// Reset speculative exceptions handling bits in the TLB entries for this section.
/// </summary>
NoDeferSpecExceptions = 0x00004000,
/// <summary>
/// The section contains data referenced through the global pointer (GP).
/// </summary>
RelativeGP = 0x00008000,
/// <summary>
/// Reserved for future use.
/// </summary>
MemPurgeable = 0x00020000,
/// <summary>
/// Reserved for future use.
/// </summary>
Memory16Bit = 0x00020000,
/// <summary>
/// Reserved for future use.
/// </summary>
MemoryLocked = 0x00040000,
/// <summary>
/// Reserved for future use.
/// </summary>
MemoryPreload = 0x00080000,
/// <summary>
/// Align data on a 1-byte boundary. Valid only for object files.
/// </summary>
Align1Bytes = 0x00100000,
/// <summary>
/// Align data on a 2-byte boundary. Valid only for object files.
/// </summary>
Align2Bytes = 0x00200000,
/// <summary>
/// Align data on a 4-byte boundary. Valid only for object files.
/// </summary>
Align4Bytes = 0x00300000,
/// <summary>
/// Align data on an 8-byte boundary. Valid only for object files.
/// </summary>
Align8Bytes = 0x00400000,
/// <summary>
/// Align data on a 16-byte boundary. Valid only for object files.
/// </summary>
Align16Bytes = 0x00500000,
/// <summary>
/// Align data on a 32-byte boundary. Valid only for object files.
/// </summary>
Align32Bytes = 0x00600000,
/// <summary>
/// Align data on a 64-byte boundary. Valid only for object files.
/// </summary>
Align64Bytes = 0x00700000,
/// <summary>
/// Align data on a 128-byte boundary. Valid only for object files.
/// </summary>
Align128Bytes = 0x00800000,
/// <summary>
/// Align data on a 256-byte boundary. Valid only for object files.
/// </summary>
Align256Bytes = 0x00900000,
/// <summary>
/// Align data on a 512-byte boundary. Valid only for object files.
/// </summary>
Align512Bytes = 0x00A00000,
/// <summary>
/// Align data on a 1024-byte boundary. Valid only for object files.
/// </summary>
Align1024Bytes = 0x00B00000,
/// <summary>
/// Align data on a 2048-byte boundary. Valid only for object files.
/// </summary>
Align2048Bytes = 0x00C00000,
/// <summary>
/// Align data on a 4096-byte boundary. Valid only for object files.
/// </summary>
Align4096Bytes = 0x00D00000,
/// <summary>
/// Align data on an 8192-byte boundary. Valid only for object files.
/// </summary>
Align8192Bytes = 0x00E00000,
/// <summary>
/// The section contains extended relocations.
/// </summary>
LinkExtendedRelocationOverflow = 0x01000000,
/// <summary>
/// The section can be discarded as needed.
/// </summary>
MemoryDiscardable = 0x02000000,
/// <summary>
/// The section cannot be cached.
/// </summary>
MemoryNotCached = 0x04000000,
/// <summary>
/// The section is not pageable.
/// </summary>
MemoryNotPaged = 0x08000000,
/// <summary>
/// The section can be shared in memory.
/// </summary>
MemoryShared = 0x10000000,
/// <summary>
/// The section can be executed as code.
/// </summary>
MemoryExecute = 0x20000000,
/// <summary>
/// The section can be read.
/// </summary>
MemoryRead = 0x40000000,
/// <summary>
/// The section can be written to.
/// </summary>
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;
}
}
}
}
}
@@ -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<IMAGE_FILE_HEADER>(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<IMAGE_OPTIONAL_HEADER32>(headerBytes);
} else {
byte[] headerBytes = reader.ReadBytes(Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER64)));
StandardFieldsPe32Plus = StructHelper.FromBytes<IMAGE_OPTIONAL_HEADER64>(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<IMAGE_SECTION_HEADER>(nextSection);
ImportantHashPositions.AddSectionTableHeader(sectionTableHeader);
}
}
}
private void addCertToList(SignerInfo si, List<X509Certificate2> 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<X509Certificate2> ExtractVerifiedSignatureCertificates() {
List<X509Certificate2> list = new List<X509Certificate2>();
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<X509Certificate2> 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<bool> VerifyTrust(X509Store cas, X509Certificate2 trustedRootCertificateAuthority, X509Certificate2 certificate) {
return await VerifyTrust(cas, trustedRootCertificateAuthority, certificate, false);
}
public async static Task<bool> 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<Exception> exceptions = new List<Exception>();
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<IMAGE_SECTION_HEADER>();
}
/*
* 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<IMAGE_SECTION_HEADER> SectionTableHeaders { get; private set; }
/// <summary>
/// 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.
/// </summary>
public Range Chunk1 { get; private set; }
/// <summary>
/// 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.
/// </summary>
public Range Chunk2 { get; private set; }
/// <summary>
/// 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.
/// </summary>
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<CrlDistributionPointParser> tags = new List<CrlDistributionPointParser>();
private CrlDistributionPointParser parent = null;
private AsnTagType type = AsnTagType.NONE;
internal List<string> urls = new List<string>();
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,
}
}
@@ -0,0 +1,15 @@
using System.Runtime.InteropServices;
namespace ZitiUpdateService.Checkers.PeFile {
public static class StructHelper {
public static T FromBytes<T>(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;
}
}
}
@@ -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<string> GetRevokedSerialNumbers(CRL_INFO stCrlInfo) {
List<string> rtn = new List<string>();
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<string> RevokedSerialNumbers;
}
}
}
@@ -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-----
+1 -1
View File
@@ -3,7 +3,7 @@ using System.IO;
using NLog;
namespace ZitiUpdateService.checkers {
namespace ZitiUpdateService.Checkers {
abstract class UpdateCheck {
public abstract int IsUpdateAvailable(Version current);
+6 -1
View File
@@ -5,4 +5,9 @@
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net48" />
<package id="NLog" version="4.7.8" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
</packages>
<package id="System.Memory" version="4.5.4" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net48" />
<package id="System.Security.Cryptography.Pkcs" version="5.0.1" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
</packages>
+14
View File
@@ -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
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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"
)
+1 -1
View File
@@ -1 +1 @@
1.9.6
1.9.8