Skip to content

mamoreau-devolutions/psign

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

psign

psign is a Rust port of the Windows SDK signtool.exe behavior (sign, verify, timestamp, remove, and related Authenticode flows), validated with differential parity tests against the native tool where CI fixtures allow.

Current command coverage

  • verify: WinVerifyTrust-backed implementation with policy modes (default, pa, pg).
  • sign: Rust mssign32 core (SignerSignEx3) with PFX/system-store cert selection, RFC3161 sign-time timestamping, and decoupled-digest bridge flow (--dlib or --trusted-signing-dlib-root + --dmdf) for MSIX parity and Azure Artifact Signing / Trusted Signing.
  • inspect-signature: JSON dump of PKCS#7 signers, timestamp OIDs, and nested signatures (1.3.6.1.4.1.311.2.4.1) — same parser as psign-tool portable inspect-authenticode (docs/psa-interoperability.md).
  • timestamp: Rust mssign32 core (SignerTimeStampEx3/SignerTimeStampEx2) plus AppX restrictions.
  • rdp: Rust port of rdpsign.exe for .rdp files (SignScope / Signature records, detached PKCS#7 over the secure-settings blob).

MSIX parity notes

  • MSIX/AppX signing requires --timestamp-url in the current parity profile.
  • Sign-time digest controls now distinguish file digest (--digest, native /fd) and RFC3161 timestamp digest (--timestamp-digest, native /td).
  • Decoupled digest inputs (--dlib + --dmdf) are executed via a native-signature bridge path and parity-gated in CI scenarios.

Build

cargo build

At the repo root, cargo build targets default-members, including the unified psign-tool executable from src\main.rs plus the portable digest / trust / package crates. On Windows, cargo build -p psign --bin psign-tool remains the explicit way to build only that executable. Optional Cargo features: azure-kv-sign (Key Vault digest callback), artifact-signing-rest (artifact-signing-submit LRO against *.codesigning.azure.net).

Dotnet tool package (.NET 10+)

psign-tool can be distributed as a RID-specific dotnet tool package:

dotnet tool install -g Devolutions.Psign.Tool
psign-tool --help

One-shot execution:

dotnet tool exec Devolutions.Psign.Tool -- --help
dnx Devolutions.Psign.Tool --help

Create local dotnet tool packages from prebuilt release artifacts:

pwsh ./nuget/pack-psign-dotnet-tool.ps1 -Version 0.1.0 -ArtifactsRoot ./dist -OutputDir ./dist/nuget

The package is built from native psign-tool artifacts for win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, and osx-arm64, plus an any fallback package for unsupported runtimes.

Linux / portable digest tooling

The canonical psign-tool CLI (package psign) supports an optional backend selector: --mode auto|windows|portable. When omitted, auto is used; PSIGN_TOOL_MODE can set the same default for parity automation. Windows mode uses Win32 APIs and registered SIP DLLs. Portable mode and the psign-tool portable ... namespace use the cross-platform Rust implementations from psign-sip-digest, psign-authenticode-trust, and psign-opc-sign without WinVerifyTrust.

Feature gaps vs native signtool, AzureSignTool, and Azure Artifact Signing: docs/gap-analysis-signing-platforms.md. Linux workflows (verify, REST hash sign, hybrid embed): docs/linux-signing-pipelines.md. For Key Vault RS256 over CMS authenticated attributes (not the PE image hash), use psign-tool portable pe-signer-rs256-prehash — see docs/migration-azuresigntool.md.

From the repo root (see docs/roadmap-authenticode-linux.md):

cargo build -p psign --bin psign-tool --locked
# Portable RDP signing:
# psign-tool portable rdp --cert cert.der --key key.pk8 file.rdp
# Portable package inspection helpers:
# psign-tool portable nupkg-signature-info package.nupkg
# psign-tool portable nupkg-digest package.nupkg --algorithm sha256
# psign-tool portable vsix-signature-info extension.vsix
# Optional portable REST helpers (Linux/macOS):
# cargo build -p psign --bin psign-tool --locked --features artifact-signing-rest
# cargo build -p psign --bin psign-tool --locked --features azure-kv-sign
cargo test -p psign-sip-digest -p psign-authenticode-trust -p psign-codesigning-rest -p psign-azure-kv-rest -p psign-digest-cli -p psign --locked
cargo check -p psign-sip-digest -p psign-digest-cli -p psign-authenticode-trust -p psign-codesigning-rest -p psign-azure-kv-rest --locked

Unix CI (ci-unix) runs cargo fmt, strict clippy -D warnings on those crates plus the psign library, and the digest CLI tests. Local mirror (bash): scripts/linux-portable-validation.sh from the repo root.

Generate binary manifest and dependency graph

cargo run -p psign --bin psign-depgraph -- --signtool "C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe"

Output files (gitignored parity-output/):

  • parity-output/binary-manifest.json
  • parity-output/dependency-graph.json

Component reference (exe/DLL roles, SIP map, relationship diagram): docs/windows-signing-components.md.

Optional local copies of inbox signing binaries

./scripts/copy-windows-signing-binaries.ps1
# Optional: amd64 + WOW64 crypt32.dll (large).
./scripts/copy-windows-signing-binaries.ps1 -IncludeCrypt32

Writes parity-output/vendor-binaries/ (WOW64 under syswow64/): inbox SIP DLLs, imagehlp.dll, optional crypt32.dll, Office mso.dll / VBE7.DLL when found, plus SDK mssign32.dll and signtool.exe when Windows Kits\10\bin is installed.

Run tests

cargo test --workspace
cargo test --test parity_signtool -- --ignored --nocapture
./scripts/run-parity-diff.ps1 -FailOnSemantic

-FailOnSemantic requires PSIGN_UNSIGNED_FIXTURE and PSIGN_TEST_PFX. Add -FailOnSemanticExhaustive when timestamp, MSIX package, and detached PKCS#7 env vars are also set (see docs/ci-parity.md).

CI parity (GitHub Actions)

The windows workflow builds the repo, bootstraps the public Devolutions test CA/PFX (pinned raw URLs — no signing secrets), derives signed/detached fixtures, packs a minimal unsigned MSIX, and runs ./scripts/ci/run-exhaustive-parity-ci.ps1. Details and extension workflows live in docs/ci-parity.md. The workflow fails only on semanticMismatchCount in the generated parity-output/parity-report.json (that directory is gitignored; the JSON is a CI artifact or local output); rows classified documented_* (for example UTF-16 response files native cannot parse) do not fail the gate.

Local mirror of the CI orchestrator:

cargo build -p psign --bin psign-tool
./scripts/ci/run-exhaustive-parity-ci.ps1

MSIX parity signing script

Use the dedicated local parity runner to sign the same unsigned MSIX with native signtool.exe and psign-tool, then compare verification outcomes:

$env:PSIGN_MSIX_UNSIGNED_FIXTURE="D:\path\unsigned.msix"
$env:PSIGN_MSIX_TEST_PFX="D:\path\authenticode-test-cert.pfx"
$env:PSIGN_MSIX_TEST_PFX_PASSWORD="CodeSign123!"
$env:PSIGN_MSIX_TIMESTAMP_URL="http://timestamp.digicert.com"
./scripts/msix-parity-sign.ps1 -FailOnSemantic

If you already imported the Devolutions test cert into CurrentUser\\My, you can use thumbprint mode instead of a PFX:

$env:PSIGN_MSIX_UNSIGNED_FIXTURE="D:\path\unsigned.msix"
$env:PSIGN_MSIX_TEST_CERT_SHA1="A9FDF3593E91689CC93B1CEBED5E8FFC1F6FEE38"
$env:PSIGN_MSIX_TIMESTAMP_URL="http://timestamp.digicert.com"
./scripts/msix-parity-sign.ps1 -FailOnSemantic

Optional decoupled digest parity:

$env:PSIGN_MSIX_DLIB="D:\path\provider.dll"
$env:PSIGN_MSIX_DMDF="D:\path\metadata.json"
./scripts/msix-parity-sign.ps1 -UseDecoupledDigest -FailOnSemantic

Report artifact:

  • parity-output/msix-parity-sign-report.json

You can also invoke the focused path through the main harness:

./scripts/run-parity-diff.ps1 -MsixOnly -FailOnSemantic

About

Windows signtool.exe Rust port

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages