Replace AuthenticodeTools with Microsoft.Security.Extensions

This commit is contained in:
Caelan Sayler
2024-12-30 09:43:09 +00:00
committed by Caelan
parent da27736195
commit 3e645b823a
4 changed files with 22 additions and 245 deletions

View File

@@ -1,241 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Velopack.Packaging.Windows;
[SupportedOSPlatform("windows")]
[ExcludeFromCodeCoverage]
public static class AuthenticodeTools
{
[DllImport("Wintrust.dll", PreserveSig = true, SetLastError = false)]
static extern uint WinVerifyTrust(IntPtr hWnd, IntPtr pgActionID, IntPtr pWinTrustData);
static uint winVerifyTrust(string fileName)
{
Guid wintrust_action_generic_verify_v2 = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}");
uint result = 0;
using (WINTRUST_FILE_INFO fileInfo = new WINTRUST_FILE_INFO(fileName, Guid.Empty))
using (UnmanagedPointer guidPtr = new UnmanagedPointer(Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))), AllocMethod.HGlobal))
using (UnmanagedPointer wvtDataPtr = new UnmanagedPointer(Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_DATA))), AllocMethod.HGlobal)) {
WINTRUST_DATA data = new WINTRUST_DATA(fileInfo);
IntPtr pGuid = guidPtr;
IntPtr pData = wvtDataPtr;
Marshal.StructureToPtr(wintrust_action_generic_verify_v2, pGuid, true);
Marshal.StructureToPtr(data, pData, true);
result = WinVerifyTrust(IntPtr.Zero, pGuid, pData);
}
return result;
}
public static bool IsTrusted(string fileName)
{
return winVerifyTrust(fileName) == 0;
}
}
[ExcludeFromCodeCoverage]
internal struct WINTRUST_FILE_INFO : IDisposable
{
public WINTRUST_FILE_INFO(string fileName, Guid subject)
{
cbStruct = (uint) Marshal.SizeOf(typeof(WINTRUST_FILE_INFO));
pcwszFilePath = fileName;
if (subject != Guid.Empty) {
pgKnownSubject = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid)));
Marshal.StructureToPtr(subject, pgKnownSubject, true);
} else {
pgKnownSubject = IntPtr.Zero;
}
hFile = IntPtr.Zero;
}
public uint cbStruct;
[MarshalAs(UnmanagedType.LPTStr)]
public string pcwszFilePath;
public IntPtr hFile;
public IntPtr pgKnownSubject;
public void Dispose()
{
Dispose(true);
}
void Dispose(bool disposing)
{
if (pgKnownSubject != IntPtr.Zero) {
Marshal.DestroyStructure(this.pgKnownSubject, typeof(Guid));
Marshal.FreeHGlobal(this.pgKnownSubject);
}
}
}
enum AllocMethod
{
HGlobal, CoTaskMem
};
enum UnionChoice
{
File = 1,
Catalog,
Blob,
Signer,
Cert
};
enum UiChoice
{
All = 1,
NoUI,
NoBad,
NoGood
};
enum RevocationCheckFlags
{
None = 0,
WholeChain
};
enum StateAction
{
Ignore = 0,
Verify,
Close,
AutoCache,
AutoCacheFlush
};
enum TrustProviderFlags
{
UseIE4Trust = 1,
NoIE4Chain = 2,
NoPolicyUsage = 4,
RevocationCheckNone = 16,
RevocationCheckEndCert = 32,
RevocationCheckChain = 64,
RecovationCheckChainExcludeRoot = 128,
Safer = 256,
HashOnly = 512,
UseDefaultOSVerCheck = 1024,
LifetimeSigning = 2048
};
enum UIContext
{
Execute = 0,
Install
};
[StructLayout(LayoutKind.Sequential)]
[ExcludeFromCodeCoverage]
internal struct WINTRUST_DATA : IDisposable
{
public WINTRUST_DATA(WINTRUST_FILE_INFO fileInfo)
{
this.cbStruct = (uint) Marshal.SizeOf(typeof(WINTRUST_DATA));
pInfoStruct = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)));
Marshal.StructureToPtr(fileInfo, pInfoStruct, false);
this.dwUnionChoice = UnionChoice.File;
pPolicyCallbackData = IntPtr.Zero;
pSIPCallbackData = IntPtr.Zero;
dwUIChoice = UiChoice.NoUI;
fdwRevocationChecks = RevocationCheckFlags.None;
dwStateAction = StateAction.Ignore;
hWVTStateData = IntPtr.Zero;
pwszURLReference = IntPtr.Zero;
dwProvFlags = TrustProviderFlags.Safer;
dwUIContext = UIContext.Execute;
}
public uint cbStruct;
public IntPtr pPolicyCallbackData;
public IntPtr pSIPCallbackData;
public UiChoice dwUIChoice;
public RevocationCheckFlags fdwRevocationChecks;
public UnionChoice dwUnionChoice;
public IntPtr pInfoStruct;
public StateAction dwStateAction;
public IntPtr hWVTStateData;
public TrustProviderFlags dwProvFlags;
public UIContext dwUIContext;
IntPtr pwszURLReference;
public void Dispose()
{
Dispose(true);
}
void Dispose(bool disposing)
{
if (dwUnionChoice == UnionChoice.File) {
WINTRUST_FILE_INFO info = new WINTRUST_FILE_INFO();
Marshal.PtrToStructure(pInfoStruct, info);
info.Dispose();
Marshal.DestroyStructure(pInfoStruct, typeof(WINTRUST_FILE_INFO));
}
Marshal.FreeHGlobal(pInfoStruct);
}
}
[ExcludeFromCodeCoverage]
internal sealed class UnmanagedPointer : IDisposable
{
IntPtr m_ptr;
AllocMethod m_meth;
internal UnmanagedPointer(IntPtr ptr, AllocMethod method)
{
m_meth = method;
m_ptr = ptr;
}
~UnmanagedPointer()
{
Dispose(false);
}
void Dispose(bool disposing)
{
if (m_ptr != IntPtr.Zero) {
if (m_meth == AllocMethod.HGlobal) {
Marshal.FreeHGlobal(m_ptr);
} else if (m_meth == AllocMethod.CoTaskMem) {
Marshal.FreeCoTaskMem(m_ptr);
}
m_ptr = IntPtr.Zero;
}
if (disposing) {
GC.SuppressFinalize(this);
}
}
public void Dispose()
{
Dispose(true);
}
public static implicit operator IntPtr(UnmanagedPointer ptr)
{
return ptr.m_ptr;
}
}

View File

@@ -1,6 +1,7 @@
using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using Microsoft.Security.Extensions;
using Velopack.Core;
using Velopack.Util;
@@ -15,6 +16,13 @@ public class CodeSign
Log = logger;
}
private bool IsTrusted(string filePath)
{
using var fileStream = File.OpenRead(filePath);
var targetPackageSignatureInfo = FileSignatureInfo.GetFromFileStream(fileStream);
return targetPackageSignatureInfo.State == SignatureState.SignedAndTrusted;
}
private bool ShouldSign(string filePath)
{
if (String.IsNullOrWhiteSpace(filePath)) return true;
@@ -25,7 +33,7 @@ public class CodeSign
}
try {
if (VelopackRuntimeInfo.IsWindows && AuthenticodeTools.IsTrusted(filePath)) {
if (VelopackRuntimeInfo.IsWindows && IsTrusted(filePath)) {
Log.Debug($"'{filePath}' is already signed, skipping...");
return false;
}
@@ -57,7 +65,8 @@ public class CodeSign
Log.Info($"Preparing to codesign using a single file signing template, with a parallelism of {parallelism}.");
signArguments = signArguments.Replace("{{file...}}", "{{file}}");
} else {
throw new UserInfoException("The sign template must contain '{{{file}}}' or '{{{file...}}}', " +
throw new UserInfoException(
"The sign template must contain '{{{file}}}' or '{{{file...}}}', " +
"which will be substituted by one, or many files, respectively.");
}
} else {

View File

@@ -15,6 +15,7 @@
<ItemGroup>
<PackageReference Include="AsmResolver.DotNet" Version="5.5.1" />
<PackageReference Include="AsmResolver.PE.Win32Resources" Version="5.5.1" />
<PackageReference Include="Microsoft.Security.Extensions" Version="1.3.0" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,6 @@
using Azure.Core;
using Azure.Identity;
using Microsoft.Security.Extensions;
using Velopack.Packaging.Windows;
using Velopack.Util;
@@ -71,7 +72,14 @@ public class TrustedSigningTests
Assert.NotEmpty(files);
#pragma warning disable CA1416 // Validate platform compatibility, this test only executes on Windows
Assert.All(files, x => Assert.True(AuthenticodeTools.IsTrusted(x)));
Assert.All(files, x => Assert.True(IsTrusted(x)));
#pragma warning restore CA1416 // Validate platform compatibility
}
private bool IsTrusted(string filePath)
{
using var fileStream = File.OpenRead(filePath);
var targetPackageSignatureInfo = FileSignatureInfo.GetFromFileStream(fileStream);
return targetPackageSignatureInfo.State == SignatureState.SignedAndTrusted;
}
}