docs(pdftract-3wrx): add release signing strategy note

Resolves OQ-10: document v1.0.0 stance on binary signing.
- Linux: GPG-signed (implemented)
- macOS: Deferred to v1.1+ ($99/yr Apple Developer Program)
- Windows: Deferred to v1.1+ ($200-400/yr Authenticode cert)
- All platforms: SLSA Level 2 attestation (already committed)

Closes: pdftract-3wrx

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-05-24 11:12:56 -04:00
parent 6ffeccc26e
commit eb025f7b1a

View file

@ -0,0 +1,258 @@
# Release Binary Signing Strategy
**Status:** RESOLVED (OQ-10)
**Date:** 2026-05-24
**Bead:** pdftract-3wrx
## Open Question OQ-10
> What is the v1.0.0 stance on signed binaries — code-signed macOS releases, Authenticode-signed Windows binaries, GPG-signed Linux releases? Each adds CI complexity.
## Resolution Decision
**v1.0.0 Stance:** GPG-sign Linux artifacts only. Defer macOS and Windows code-signing to v1.1+.
| Platform | Code Signing | v1.0.0 | Rationale |
|----------|--------------|--------|-----------|
| Linux | GPG signature | ✅ Yes | Low cost; strong trust chain; distro compatibility |
| macOS | Developer ID + notarization | ❌ Defer | $99/yr Apple Developer Program cost |
| Windows | Authenticode | ❌ Defer | $200-400/yr cert cost; cross-platform CI complexity |
## Background: Code-Signing vs Attestation
Code-signing and SLSA provenance answer **different questions**:
- **Code-signing** (macOS Developer ID, Windows Authenticode): *Who built this?* Binds a binary to an identity certificate. End-of-chain trust anchors are Apple/Microsoft root CAs.
- **SLSA provenance** (`provenance.intoto.jsonl`): *How was this built?* Cryptographic attestation of the build process (materials, inputs, builder identity). Trust anchor is the build infrastructure's signing key.
Both are complementary. SLSA Level 2 (already committed per plan line 895) provides supply-chain transparency. Code-signing provides platform-native trust integration (Gatekeeper, SmartScreen).
## SLSA Level 2 Attestation (Already Implemented)
Per plan line 895, every GitHub Release includes `provenance.intoto.jsonl` generated by the Argo runner on `iad-ci`. This attestation contains:
- **Builder identity:** Argo Workflow execution on `iad-ci`
- **Materials:** Git commit SHA, dependency crate versions (from `Cargo.lock`)
- **Recipe:** Build command invocation
- **Metadata:** Timestamp, workflow template
The attestation is signed by the CI fleet's root key. Consumers verify using `slsa-verifier`:
```bash
slsa-verifier verify-artifact \
--provenance-path provenance.intoto.jsonl \
--source-uri github.com/jedarden/pdftract \
--source-tag v1.0.0
```
This provides supply-chain integrity **without** platform-specific code-signing.
## Platform-Specific Analysis
### Linux: GPG Signing
**Status:** ✅ **Implemented in v1.0.0**
**Implementation:**
- Release tarball (`pdftract-$VERSION-x86_64-unknown-linux-gnu.tar.gz`) and cargo registry crate are signed with a maintainer-controlled GPG key
- Public key is checked into the repo at `docs/signing/pubkey.asc`
- Signature files (`*.tar.gz.asc`) are uploaded alongside artifacts on GitHub Releases
- `Cargo.toml` includes `metadata.signing` key for cargo verification
**Rationale for v1.0.0:**
- **Zero cost:** GPG is free; key generation is local
- **Strong trust:** GPG web-of-trust is well-understood by Linux users
- **Distro compatibility:** Many Linux distributions require GPG signatures for third-party packages
- **Low CI complexity:** `gpg --detach-sign` is a single command; key lives in OpenBao as a Kubernetes Secret
**Key Management:**
- **Private key:** Stored in OpenBao, synced to Kubernetes Secret `signing-gpg-private` in the `iad-ci` namespace
- **Passphrase:** Separate OpenBao entry, injected as env var `GPG_PASSPHRASE` during signing step
- **Public key:** Checked into repo at `docs/signing/pubkey.asc` (NOT secret)
- **Rotation:** Annually, or immediately on suspected compromise. Rotate = generate new key, update `pubkey.asc`, re-sign latest release, publish new fingerprint.
**CI Integration:**
```yaml
# Argo WorkflowTemplate: pdftract-release
- name: sign-linux-artifacts
container:
image: debian:bookworm-slim
command:
- sh
- -c
- |
echo "$GPG_PRIVATE_KEY" | gpg --import --batch --passphrase "$GPG_PASSPHRASE"
for artifact in pdftract-*.tar.gz; do
gpg --detach-sign --armor --passphrase "$GPG_PASSPHRASE" "$artifact"
done
env:
- name: GPG_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: signing-gpg-private
key: key
- name: GPG_PASSPHRASE
valueFrom:
secretKeyRef:
name: signing-gpg-passphrase
key: passphrase
```
**Verification:**
```bash
# Download and verify release
wget https://github.com/jedarden/pdftract/releases/download/v1.0.0/pdftract-1.0.0-x86_64-unknown-linux-gnu.tar.gz{,.asc}
gpg --verify pdftract-1.0.0-x86_64-unknown-linux-gnu.tar.gz.asc
gpg --import docs/signing/pubkey.asc # First time only
```
### macOS: Code Signing + Notarization
**Status:** ❌ **Deferred to v1.1+**
**What would be required:**
1. **Apple Developer Program account** ($99/year)
2. **Developer ID Application certificate** (issued by Apple)
3. **Notarization** (post-sign submission to Apple's notarization service)
4. **Stapling** (attach notarization ticket to binary)
**Implementation cost:**
- **Financial:** $99/year recurring (Apple Developer Program)
- **Tooling:** `codesign` CLI (built into Xcode), `xcrun notarytool submit` (requires Apple ID auth)
- **CI complexity:**
- Certificates stored as `.p12` in OpenBao
- Notarization requires Apple ID app-specific password (separate secret)
- Two-step signing+notarization workflow
**Consequences of NOT signing (current state):**
- **Gatekeeper quarantine:** macOS quarantines unsigned downloads; users see "cannot be opened because the developer cannot be verified"
- **User friction:** Users must right-click → Open, or use `xattr -d com.apple.quarantine pdftract`
- **No SmartScreen integration:** macOS doesn't block execution, but warns on first run
**Why defer to v1.1+:**
- **Funding question:** $99/year is non-trivial for a v1.0.0 release with uncertain adoption
- **ADR-009 constraint:** `iad-ci` is Linux-only; cannot run `codesign` natively. Would need macOS runner or cross-compilation with remote signing
- **Alternative:** Community-sponsored Apple Developer Program account (e.g., "pdftract is signed by Ardenone LLC under their developer account")
**v1.1+ trigger:**
- Sustained macOS user base (>10% of downloads)
- Funding allocation for Apple Developer Program
- OR community contribution: "I will sign pdftract binaries under my Apple Developer account"
### Windows: Authenticode Signing
**Status:** ❌ **Deferred to v1.1+**
**What would be required:**
1. **Code signing certificate** from a Windows-trusted CA (DigiCert, Sectigo, GlobalSign)
2. **Certificate cost:** $200-400/year (standard), ~$1000/year (EV certificate)
3. **Signing tool:** `signtool` (Windows SDK) or `osslsigncode` (cross-platform)
**Implementation cost:**
- **Financial:** $200-400/year minimum
- **Tooling:**
- **Native:** `signtool sign /f cert.pfx /p password ...` (requires Windows)
- **Cross-platform:** `osslsigncode` (Linux-compatible, but less battle-tested)
- **CI complexity:** `iad-ci` is Linux-only per ADR-009. `signtool` doesn't run on Linux. Options:
1. **Cross-compile + remote signing:** Build on Linux, transfer binary to Windows VM for signing (complex)
2. **osslsigncode:** Open-source alternative, but unclear if Windows SmartScreen trusts it equally
3. **Azure Trusted Signing:** Microsoft's cloud signing service (requires Azure AD tenancy, per-certificate cost)
**Consequences of NOT signing (current state):**
- **SmartScreen warning:** Users see "Windows protected your PC" warning on first run
- **User friction:** Users must click "More info" → "Run anyway"
- **No Microsoft Store compatibility:** Cannot publish to Microsoft Store without signing
**Why defer to v1.1+:**
- **Funding question:** $200-400/year is non-trivial
- **CI-platform mismatch:** ADR-009 mandates Linux-only CI; Authenticode signing is fundamentally Windows-centric
- **osslsigncode uncertainty:** Unclear if cross-platform signing produces identical SmartScreen behavior
**v1.1+ trigger:**
- Sustained Windows user base (>10% of downloads)
- Funding allocation for certificate
- OR community contribution: "I will sign pdftract binaries under my certificate"
## Failure Modes and Mitigations
### macOS Notarization Rejection
**Failure:** `xcrun notarytool` returns `Status: Invalid`
**Mitigation:**
- Do not ship unsigned binary with failed notarization
- CI step aborts release; diagnostic logged to Argo Workflow
- Maintainer must investigate (malware fingerprint, entitlement issue, Apple ID problem)
### SmartScreen False Positive (Windows)
**Failure:** Unsigned binary triggers SmartScreen "unrecognized app" warning
**Mitigation (v1.0.0, unsigned):**
- Document in README: "Click 'More info' → 'Run anyway'"
- Encourage users to verify GPG signature on Linux subsystem or WSL
- Track SmartScreen false-positive rate via user feedback
**Mitigation (v1.1+, signed):**
- Timestamped Authenticode signature prevents post-signage tampering
- Certificate reputation improves over time (fewer warnings as adoption grows)
### GPG Key Compromise
**Failure:** Maintainer's GPG private key is exfiltrated from OpenBao
**Mitigation:**
- **Immediate:** Rotate compromised key (generate new key, revoke old on keyserver)
- **Re-sign:** Re-sign latest release with new key
- **Communicate:** Publish security advisory with new fingerprint
- **Postmortem:** Audit OpenBao access logs; implement stricter RBAC
**Prevention:**
- OpenBao's transit encryption (key never leaves Vault in plaintext)
- Kubernetes Secret with strict RBAC (only `pdftract-release` workflow can read)
- Quarterly key rotation (planned, not implemented)
## Version Policy
### v1.0.0 (Current Release)
- **Linux:** GPG-signed (`*.tar.gz.asc`)
- **macOS:** Unsigned (quarantine warning expected)
- **Windows:** Unsigned (SmartScreen warning expected)
- **All platforms:** SLSA Level 2 attestation (`provenance.intoto.jsonl`)
### v1.1.0 (Future Release, TBD)
- **Linux:** GPG-signed (no change)
- **macOS:** Developer ID signed + notarized (funding dependent)
- **Windows:** Authenticode-signed (funding dependent)
- **All platforms:** SLSA Level 2 attestation (no change)
## Release Checklist (Per KU-12)
The `docs/operations/manual-platform-smoke.md` runbook (KU-12) includes signature verification:
1. **Download release artifacts** from GitHub Releases
2. **Verify GPG signature** (Linux): `gpg --verify *.asc`
3. **Verify SLSA provenance** (all platforms): `slsa-verifier verify-artifact`
4. **Run smoke test** (binary execution, extraction sanity check)
5. **Check platform-specific behavior**:
- macOS: Does Gatekeeper quarantine the binary? (Expected v1.0.0, unexpected v1.1+)
- Windows: Does SmartScreen warn? (Expected v1.0.0, unexpected v1.1+)
- Linux: Does GPG verify successfully? (Expected always)
## References
- Plan Open Question OQ-10 (line 521)
- Plan Supply Chain Considerations (line 895): SLSA Level 2 commitment
- Plan ADR-009 (line 495): Argo Workflows on `iad-ci` (Linux-only CI)
- Plan Known Unknown KU-12 (line 608): Manual quarterly smoke test
- Apple Developer Documentation: [Code Signing](https://developer.apple.com/support/code-signing/)
- Microsoft Docs: [Authenticode](https://docs.microsoft.com/en-us/windows/win32/seccrypto/cryptography-tools)
- SLSA: [SLSA Level 2](https://slsa.dev/spec/v0.1/levels#level-2)
## Revision History
| Date | Change |
|------|--------|
| 2026-05-24 | Initial resolution; v1.0.0 stance (GPG Linux only) |