Skip to content

feat(airgapped): real offline keyless verification (#219, REQ-23) - #229

Merged
avrabe merged 1 commit into
mainfrom
feat/offline-keyless-verify
Aug 8, 2026
Merged

feat(airgapped): real offline keyless verification (#219, REQ-23)#229
avrabe merged 1 commit into
mainfrom
feat/offline-keyless-verify

Conversation

@avrabe

@avrabe avrabe commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Closes #219 (REQ-23). Replaces the AirGappedVerifier::verify_crypto stub with real offline Sigstore keyless verification, delegating to the existing tested online primitives anchored to the provisioned TrustBundle.

The stub was broken and unsound

  • ed25519 over the full SPKI DER (spki.raw) → fails for any real Fulcio cert (they're ECDSA P-256)
  • no cert-chain anchoring, no Rekor SET verification

Real verification (mirrors online order, signer.rs:592-674)

  1. hash-binding · 2. cert chain (from_pem_authoritiesverify_pem_cert at Rekor integrated_time, RFC-5280 + codeSigning EKU) · 3. Rekor SET (from_pem_logsverify_set) · 4. P-256 sig over module_hash via verify_prehash (self-binding) · 5. Rekor body-binding (wsc verify --keyless accepts tampered WASM (signed-payload byte flip not detected) #135 UCA-2) · 6. revocation

Soundness note

Review caught that the first cut omitted Rekor body-binding — the online path verifies it (signer.rs:664) precisely because without it "a holder of a legitimate Fulcio cert can staple an unrelated public Rekor entry to a malicious module." Added it + a test_offline_rekor_body_mismatch regression test. Clean-room confirmed it's load-bearing.

Explicitly NOT verified offline (stated, not hidden — DD-11/DD-12)

Oracle

11 offline tests, each negative asserting its own failure step (wrong root→chain, expired→time, wrong rekor key→SET, tampered→P-256, body mismatch→body-binding, revoked→revocation, hash→step 1); positive path builds a genuine hashedrekord body. cargo test -p wsc: 619 passed / 0 failed. Clean-room: all claims CONFIRMED. SigstoreBundle::verify deferred to its own requirement (was optional).

🤖 Generated with Claude Code

AirGappedVerifier::verify_crypto was a stub — ed25519 over the full SPKI DER
(fails for any real Fulcio cert, which are ECDSA P-256), no cert-chain anchoring,
no Rekor SET check. Replace it with real verification that DELEGATES to the
existing tested online primitives, anchored to the provisioned TrustBundle
(which already carries the Fulcio roots + Rekor keys — the stub just never used
them). Order mirrors the online path (signer.rs:592-674):

  1. hash-binding: module_hash == signature.module_hash (clear error)
  2. cert chain: CertificatePool::from_pem_authorities → verify_pem_cert at
     Rekor integrated_time (RFC-5280, validity, codeSigning EKU)
  3. Rekor SET: RekorKeyring::from_pem_logs → verify_set (MANDATORY)
  4. P-256 signature over module_hash via verify_prehash (self-binding)
  5. Rekor body binding: verify_rekor_body_binds_to_bundle (#135 UCA-2 — without
     it, a holder of a legit Fulcio cert can staple an unrelated Rekor entry to
     a malicious module; the online path checks this, so offline must too)
  6. revocation over the leaf DER fingerprint

New anchoring constructors CertificatePool::from_pem_authorities and
RekorKeyring::from_pem_logs mirror the tested from_trusted_root split (last cert
= root). Delete the broken extract_public_key_from_cert.

NOT verified offline, stated explicitly (VerificationResult.inclusion_verified
+ doc + DD-11/DD-12): Rekor Merkle inclusion (mirrors online skip, Rekor v2
shard bug / #137) and SCT (no CT-log key provisioned in the bundle).

Oracle: 11 offline tests, each negative test asserting its own failure step
(wrong root→chain, expired→time, wrong rekor key→SET, tampered→P-256,
body mismatch→body-binding, revoked→revocation, hash mismatch→step 1); positive
path builds a real hashedrekord body so body-binding is genuinely exercised.
wsc suite 619 passed / 0 failed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aR3Md1h46K9wAUWMQiESH
@avrabe
avrabe merged commit 255a597 into main Aug 8, 2026
20 checks passed
@avrabe
avrabe deleted the feat/offline-keyless-verify branch August 8, 2026 07:15
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.13043% with 55 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/lib/src/airgapped/verifier.rs 91.49% 37 Missing ⚠️
src/lib/src/signature/keyless/cert_verifier.rs 73.17% 11 Missing ⚠️
src/lib/src/signature/keyless/rekor_verifier.rs 76.66% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe avrabe mentioned this pull request Aug 8, 2026
avrabe added a commit that referenced this pull request Aug 8, 2026
…) (#230)

Merged to main as 255a597; oracle 619/0, clean-room all-confirmed, #219 closed.


Claude-Session: https://claude.ai/code/session_012aR3Md1h46K9wAUWMQiESH

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

airgapped: AirGappedVerifier::verify_crypto is a stub — offline keyless verification does not work end-to-end

1 participant