The primary psign-tool binary is unified: Windows mode depends on windows, WinVerifyTrust, SignerSignEx3, and OS CryptSIP registration, while portable mode uses Rust digest/trust implementations. A practical Linux story is phased: keep Windows as the reference implementation while carving out portable pieces.
Cross-tool comparison (native signtool vs AzureSignTool vs Artifact Signing vs this repo): gap-analysis-signing-platforms.md. Linux cookbook (verify / REST / hybrid): linux-signing-pipelines.md.
ci-unix:cargo fmt --check,cargo metadata --locked,cargo clippyon portable digest + trust crates +psignlib (-D warnings),cargo test -p psign-sip-digest --lib,cargo test -p psign-authenticode-trust --lib,cargo check -p psign,cargo test -p psign --lib(see.github/workflows/ci-unix.yml).- Workspace
default-members(rootCargo.toml): a barecargo build/cargo testat the repo root includes the rootpsignpackage, socargo buildemitspsign-toolfromsrc/main.rs. Portable commands are available throughpsign-tool portable ...; there is no separatepsign-tool-portableexecutable. - Local Linux/macOS mirror of CI (from repo root,
--lockedoptional but matches CI):
cargo test -p psign-sip-digest --lib --locked,cargo test -p psign-authenticode-trust --lib --locked, andcargo test -p psign --test cli_pe_digest --locked. Build the unified CLI:cargo build -p psign --bin psign-tool --locked; usepsign-tool portable ...for portable-only diagnostics. - The
psignCLI binary on non-Windows dispatches to portable Rust paths where available;winis behind#[cfg(windows)]sowindowsis not a dependency on Linux.
crates/psign-sip-digestholds portable digest modules (nowindowsdependency). The Win32 binary re-exports them fromsrc/win/sip_rust/mod.rsand keeps thinsign_*helpers that needGlobalOpts.ci-unixrunscargo test -p psign-sip-digest --lib --locked(see.github/workflows/ci-unix.yml).- CLI:
psign-tool portable ...(runner incrates/psign-digest-cli) —pe-digest,verify-pe(digest-only PKCS#7 consistency),trust-verify-pe/trust-verify-cab/trust-verify-catalog/trust-verify-detached(explicit-anchor trust + picky chain),sign-pe(portable PE Authenticode CMS +WIN_CERTIFICATEembed with local RSA/SHA-2 keys),sign-cab(unsigned single-volume CAB reserve-header + tail PKCS#7 signing),sign-msi(MSI/MSPDigitalSignaturestream signing),sign-catalog(portable generic CTL/catalog authoring + CMS signing),timestamp-pe-rfc3161(attach RFC3161timeStampToken/ grantedTimeStampRespto PESignedData),pe-has-page-hashes,pe-page-hash-info,verify-pe-page-hashes,pe-authenticode-ranges(Authenticode digest file segments),verify-cab,verify-msi,verify-esd,verify-msix,verify-catalog,verify-script,cab-digest,extract-cab-pkcs7,cab-signer-rs256-prehash,extract-msi-pkcs7,msi-signer-rs256-prehash,catalog-signer-rs256-prehash,inspect-pkcs7/extract-pkcx-pkcs7(standalone PKCS#7 and AppXPKCXP7X helpers),inspect-pe-spc-indirect,extract-pe-pkcs7,list-pe-pkcs7,pe-signer-rs256-prehash(KVRS256CMS prehash;--signer-indexfor multi-SignerInfoSignedData),pkcs7-signer-rs256-prehash,append-pe-pkcs7(low-level append helper). Runs on Linux/macOS; doesnotcallWinVerifyTrust. - Trust library:
psign-authenticode-trust— see authenticode-trust-stack.md and authroot-linux-verify.md. - Remaining Linux work: optional revocation, PinRules, and full CryptoAPI policy parity still need dedicated design; digest CLI extras (JSON output, stdin) remain optional.
Portable mode is staged by lifecycle capability rather than by native signtool.exe verb parity:
| Stage | Current portable support | Boundary |
|---|---|---|
| Digest / consistency verification | verify-*, pe-digest, cab-digest, page-hash diagnostics |
No WinVerifyTrust policy decision unless a later Windows parity check is run |
| Explicit-anchor trust | trust-verify-* with --trusted-ca, --anchor-dir, optional AIA/OCSP/CRL, and fixed --as-of dates |
No OS root/intermediate stores, enterprise TrustedPublisher, or PinRules |
| Remote hash signing | Artifact Signing :sign, Key Vault keys/sign, and Authenticode signer prehash helpers |
Returns signatures over supplied digests only; no Authenticode embed |
| Local signing | Portable RDP, PE sign-pe, unsigned single-volume CAB sign-cab, MSI/MSP sign-msi, and generic catalog sign-catalog with RSA/SHA-2 |
Cleartext MSIX Authenticode signing and CAB replacement/multivolume signing remain backlog |
| CMS creation / embedding | PE, CAB, MSI, and catalog SignedData creation; remote RSA signature injection helpers; PE WIN_CERTIFICATE, CAB reserve-header/tail PKCS#7, MSI DigitalSignature stream embedding, and CTL eContent authoring |
MSIX production embedding remains backlog |
| Timestamping | RFC3161 request/response construction, POST, inspection helpers, PE sign-time timestamping through sign-pe --timestamp-url --timestamp-digest, and PE timestamp-pe-rfc3161 token embedding |
Non-PE timestamp embedding and full SignerTimeStampEx3 policy parity remain backlog |
| Mutation / removal | None for Authenticode subjects | Requires format-specific embedders before safe remove/update support |
| Catalog workflows | Generic catalog sign-catalog, CMS/catalog consistency checks, and explicit verify-catalog-member --catalog file.cat subject for MakeCat-style or psign-authored catalogs |
No OS catalog database search, driver package policy, INF metadata, or MakeCat byte-for-byte output |
Top-level --mode portable should only route native-looking verbs when the requested stage is supported. Otherwise it should fail explicitly and point to the closest psign-tool portable helper.
- Verify-only paths that parse Embedded PKCS#7, SPC_PE_IMAGE_DATA, indirect data, and RFC3161 timestamps can live behind
ring/cms/x509-parser(already partially aligned viacms/authenticodecrates). - Signing on Linux without a hardware CSP or Windows SIP remains non-parity unless integrating OpenSSL / Azure Key Vault / pkcs11 — document as optional backends, not drop-in
signtool.exereplacement.
Order-of-effort sketch (each step needs tests + fixtures):
- Portable CMS producer — Finish
SignedData/SignerInfoassembly for PE (SpcIndirectData+WIN_CERTIFICATEembed) using existingpe_digest/ helpers inpkcs7.rs.encode_pkcs7_content_info_signed_data_deralready wraps an existingSignedDataas PKCS#7ContentInfoDER (fixture round-trip tests).pe_embedwrap/append PKCS#7 rows and recomputesCheckSum(pe_compute_image_checksum); remaining work is full CMS signer encode and unsigned→signed parity. - Remote signing adapters on Unix — Small
reqwestclients mirroringazure_kv_sign.rs(KVkeys/sign) andartifact_signing_rest.rs(codesigning:signLRO) behind Cargo features, emitting raw signature bytes for step (1). - Additional embedders — CAB replacement/multivolume cases and MSIX ZIP manipulation (hardest:
AppxSipCreateIndirectData-equivalent APPX blob + publisher binding rules). - RFC3161 request/sign — PE token embedding exists through
timestamp-pe-rfc3161; remaining work is TSA policy parity, automatic sign-time request/POST integration, and CAB/MSI/MSIX timestamp mutation.
Until Phase 2 completes, verify-first Linux CI remains the supported story; production signing stays on psign-tool (or native signtool.exe).
Already aligned in Rust for cleartext subjects:
- MSIX / APPX / bundles (ZIP layout) —
sip_rust::msix_digest(encrypted Eappx stays out of scope without Windows crypto).psign-tool portable verify-msixexercises the same portable ZIP/hash path on Linux; manifest publisher vs PKCS#7 signer enforcement remainsAppxSip/SignerSignEx*on Windows. - VSIX / NuGet packages — these are portable package-signing formats, not Windows SIP formats.
psign-opc-signstarts the dedicated OPC/NuGet layer with signature marker inspection and unsigned NuGet package digests (psign-tool portable nupkg-signature-info,nupkg-digest,vsix-signature-info). Full VSIX XMLDSig anddotnet nuget sign-compatible CMS author-signature creation remain separate milestones from Authenticode SIP parity. - MSI OLE tree —
sip_rust::msi_digest. - ESD / WIM prefix hash —
sip_rust::esd_digest. - PE / CAB / catalog digests — pure byte/layout algorithms; deployment policy still differs without WinTrust.
| Surface | PSIGN_* / related |
Notes |
|---|---|---|
psign-tool portable (Linux/macOS) |
PSIGN_NO_AUTO_TRUST, PSIGN_AUTHROOT_MAX_AGE_DAYS, PSIGN_AUTHROOT_CACHE_DIR, PSIGN_AUTHROOT_URL optional |
Trust subcommands can run with paths only by auto-caching Microsoft authrootstl.cab; explicit --anchor-dir / --authroot-cab inputs override auto trust. Digest-only commands such as verify-pe remain available. |
psign-tool --mode portable (non-Windows) |
PSIGN_TOOL_MODE=portable optional |
Uses portable Rust paths where implemented; Win32-only commands fail explicitly. |
psign-tool --mode windows (Windows) |
PSIGN_TOOL_MODE=windows, PSIGN_RUST_SIP, SIGNTOOL_PAGE_HASHES (via --no-page-hashes) |
Win32 backend, post-sign Rust SIP digest gates, and SignerSignEx3 page-hash hint — see psign-cli-matrix.json, rust-sip-architecture.md. |
| Parity scripts / CI | SIGNTOOL_EXE, PSIGN_TEST_PFX, PSIGN_MSIX_*, … |
Full matrix and semantics in ci-parity.md. |
- VBA /
mso.dllSIP. - Encrypted MSIX (
.eappx,.emsix, encrypted bundles). - ExtensionsSip third-party DLL chains.
- Standalone P7X/PKCX container signing outside extracted AppX/MSIX package signatures.
- ClickOnce / VSTO XML manifest signing.
- Full
signtool.exeargv parity on Linux without a Windows ABI shim. - NuGet / VSIX package signing remains package-native OPC/CMS/XMLDSig work in
psign-opc-sign, not Authenticode SIP parity.
Short term: Unix CI for fmt + lockfile + document this split. Medium term: extract digest library + Linux cargo test for parity-heavy code. Long term: optional verify-focused CLI on Linux (done) plus explicitly-scoped portable signing helpers for PE, CAB, and MSI; broad native-shaped sign and provider-dependent SIPs remain Windows-first until each portable path has dedicated fixtures and policy coverage.