feat(capsule-cli): add verify subcommand for PCR attestation check#1
feat(capsule-cli): add verify subcommand for PCR attestation check#1mvanhorn wants to merge 2 commits into
Conversation
The verify subcommand reads a build-attestation.json file and compares its
PCR0/1/2 (and optionally PCR8) against the actual EIF measurements via
nitro-cli describe-eif. This closes the trust loop App Hub opens: builds
emit signed attestations, but until now there was no client-side tool to
confirm a release image's measurements actually match its attestation.
The attestation parser is permissive about JSON shape:
- {"PCR0": ...} (uppercase, used in some workflows)
- {"pcr0": ...} (lowercase, used in app-hub build-on-merge.yml)
- {"measurements": {"PCR0": ...}} (nested, default describe-eif output)
Hex comparison is case-insensitive and tolerates a leading 0x prefix.
Exit codes: 0 on full match, 1 on mismatch, 2 on file/parse errors.
PR #1 Review:
|
|
cc @mvanhorn |
Adds enclave: Option<NestedMeasurements> to RawAttestation so capsule-cli verify accepts the build-attestation.json shape emitted by app-hub/.github/workflows/build-on-merge.yml. Enclave is preferred over measurements; top-level PCR fields remain a fallback. Adds a unit test covering the production fixture from the review.
|
@zfdang addressed B1 in 3277f1e: added The non-blocking notes (ANSI gating, exit-code refactor, PCR8 visibility, Thanks for the careful review. |
Summary
Adds a new
capsule-cli verifysubcommand that compares an EIF's PCR measurements against a build attestation. Closes the trust loop App Hub opens: builds emit a signedbuild-attestation.jsonwith measurements, but until now there was no client-side tool to confirm a downloaded release image actually matches its attestation.Why this matters
Today, a downstream consumer of a Nova release image has to trust App Hub's signature on the attestation file. There's no local way to re-derive measurements from the EIF and check them.
build-attestation.json. The values are consumed on-chain via App Registry, but no CLI surfaces them locally for inspection.edgebitio/enclaver#238("Reproducible Enclaver builds") was filed in the original codebase thatcapsule-cliforks from (seeTODO(russell_h)markers in src/build.rs#L152) and was never closed.oyster-cvm verifyand Phala dstack'sdstack-verify.capsule-cliwas the missing peer.Demo
The reel shows two things:
capsule-cli verify --help(subcommand surface with all three flags) andcargo test verify::tests --quiet(8 of 8 unit tests passing).A real-EIF happy path requires
nitro-cli describe-eifand a Nitro EC2 host, neither available in this environment. The wiring fromverifyto the existingNitroCLI::describe_eif()path is exercised through therun_verify()integration but the actual subprocess call is not in the demo. Maintainers with Nitro infrastructure can sanity-check the round trip on a real release image.Changes
New file:
enclave-capsule/capsule-cli/src/verify.rs(270 lines including tests). Permissive attestation parser (AttestationMeasurements::from_file), PCR comparison (compare), pretty-printed report, asyncrun_verifythat calls the existingNitroCLI::describe_eif().Modified:
enclave-capsule/capsule-cli/src/lib.rs:pub mod verify;.enclave-capsule/capsule-cli/src/bin/capsule-cli/main.rs: newVerifyclap subcommand variant and match arm. Exit 0 on full match, 1 on mismatch, 2 on file/parse errors.No new dependencies.
tempfile = "3.23"was already inCargo.toml; the verify tests use it.The attestation parser is permissive about JSON shape because App Hub emits
pcr0lowercase in some workflows (build-on-merge.yml#L562),PCR0uppercase in others, andnitro-cli describe-eifoutputs the nested{"measurements": {"PCR0": ...}}shape. The parser accepts all three. Hex comparison is case-insensitive and tolerates a leading0x.Testing
cargo fmt --check: clean.cargo clippy --all-targets -p capsule-cli -- -D warnings: clean (only the pre-existingnum-bigint-digfuture-incompat warning, unrelated).cargo test -p capsule-cli verify::tests: 8 of 8 pass. Coverage: uppercase / lowercase / nested attestation parsing, missing-PCR rejection, normalization (case +0xprefix), mismatch detection, PCR8 inclusion when present.What the tests do not cover end-to-end: the
nitro-cli describe-eifinvocation itself. That requires the AWS Nitro CLI plus a real EIF on disk. Happy to add a feature-gated integration test if you have a fixture path you'd like used.Open questions
.eif. Easy follow-up.--rebuildmode (clone source at the pinned ref, re-run the App Hub build, compare PCRs) as a follow-up PR? That's the harder half ofenclaver#238.I read through
enclave-capsule/capsule-cli/src/nitro_cli.rs:110-137to confirmdescribe_eifreturnsEIFInfo { measurements: EIFMeasurements }withpcr0/1/2/8asString, so the comparison stays in the existing type system rather than introducing a parallel one.