Skip to content

feat(tee-verifier): stateless dcap_qvl::verify wrapper contract - #3237

Merged
pbeza merged 11 commits into
mainfrom
feat/tee-verifier-contract
Jun 10, 2026
Merged

feat(tee-verifier): stateless dcap_qvl::verify wrapper contract#3237
pbeza merged 11 commits into
mainfrom
feat/tee-verifier-contract

Conversation

@pbeza

@pbeza pbeza commented May 14, 2026

Copy link
Copy Markdown
Contributor

Closes #3266

Overview

Adds tee-verifier: a stateless TEE attestation verifier contract.

It wraps dcap_qvl::verify::verify in a single verify_quote method. The contract holds no state and has no admin — verifier-internal policy (the dcap-qvl version, Intel root certs, etc.) is bound to the deployed code hash. Per-team allowlists, report-data binding, and other post-DCAP checks live in the caller, not here.

This moves the heavy dcap-qvl / ring / webpki / x509-cert closure out of any contract that needs quote verification and into this one contract, reached over a cross-contract call using the DTOs from #3235.

Builds on #3235 (PR #3235); part of the stack tracked by #3264. See docs/design/attestation-verifier-contract.md for the design.

@pbeza
pbeza force-pushed the feat/tee-verifier-interface branch from b3acde8 to 820497e Compare May 15, 2026 09:18
@pbeza
pbeza force-pushed the feat/tee-verifier-contract branch from eb25314 to 7766451 Compare May 15, 2026 09:29
@pbeza
pbeza force-pushed the feat/tee-verifier-interface branch from 820497e to 2e70aa9 Compare May 15, 2026 10:20
@pbeza
pbeza force-pushed the feat/tee-verifier-contract branch from 7766451 to 43413b5 Compare May 15, 2026 10:23
pbeza added a commit that referenced this pull request May 15, 2026
…-qvl

Pure crate split, no behaviour change. Sets up the WASM-size win that
the follow-up PR (mpc-contract Promise refactor) will land.

What moves:
- `app_compose`, `measurements`, `report_data`, `tcb_info` modules and
  their assets — none of these touched `dcap-qvl` to begin with.
- The post-DCAP verification helpers (`verify_tcb_status`,
  `verify_report_data`, `verify_rtmr3`, `verify_app_compose`,
  `verify_any_measurements`, `verify_static_rtmrs`,
  `verify_key_provider_digest`, `verify_event_log_rtmr3`,
  `validate_app_compose_config`, `compare_hashes`, `compare_hex_hashes`,
  plus the `OrErr` and `GetSingleEvent` traits and `VerificationError`)
  — refactored from private methods on `DstackAttestation` to free
  functions in `attestation_types::verify_post_dcap`, taking the
  `tee_verifier_interface::VerifiedReport` mirror instead of the
  `dcap_qvl` type.

What stays in `attestation`:
- `DstackAttestation::verify` (the one and only `dcap_qvl::verify::verify`
  call site). It now converts the dcap-qvl-returned report to the
  Borsh mirror once and calls the free functions on it.
- The `Collateral` newtype and `QuoteBytes` newtype. Their dedup with
  the `tee-verifier-interface` mirrors is deferred to a later PR.

What this enables (next PR):
- `mpc-contract` can depend on `attestation-types` alone (no `dcap-qvl`)
  and call the post-DCAP helpers from a Promise callback that receives
  the verifier-returned `VerifiedReport`. That is the actual WASM-size
  win for NEP-509.

Drops the dead `TryFrom<dcap_qvl::verify::VerifiedReport> for Measurements`
impl (no callers anywhere in the codebase).

Re-exports the moved modules from `attestation::lib.rs` so existing
consumers (`mpc-attestation`, `tee-authority`, `attestation-cli`, etc.)
keep their import paths unchanged.

Part of the verifier breakout from
docs/design/attestation-verifier-contract.md (#3160). Stacked on #3237.
@pbeza
pbeza force-pushed the feat/tee-verifier-interface branch from 2e70aa9 to dde7dd2 Compare June 3, 2026 09:08
@pbeza
pbeza force-pushed the feat/tee-verifier-contract branch from 43413b5 to 5fae5d5 Compare June 3, 2026 09:08
@pbeza pbeza changed the title feat(tee-verifier): stateless dcap_qvl::verify wrapper contract feat(tee-verifier): stateless dcap_qvl::verify wrapper contract Jun 3, 2026
@pbeza
pbeza force-pushed the feat/tee-verifier-contract branch from d1e513b to 54fc11c Compare June 3, 2026 10:37
@pbeza
pbeza force-pushed the feat/tee-verifier-interface branch from b731b70 to c3f7c04 Compare June 3, 2026 10:42
@pbeza
pbeza force-pushed the feat/tee-verifier-contract branch 3 times, most recently from 56f6430 to d69203b Compare June 3, 2026 12:05
@pbeza
pbeza force-pushed the feat/tee-verifier-interface branch from a288dbd to b2ff62d Compare June 3, 2026 12:05
@pbeza
pbeza force-pushed the feat/tee-verifier-contract branch 2 times, most recently from 5edeb86 to 7f576f3 Compare June 3, 2026 13:01
@pbeza
pbeza force-pushed the feat/tee-verifier-interface branch 2 times, most recently from 198b297 to 77b14bb Compare June 3, 2026 14:35
@pbeza
pbeza force-pushed the feat/tee-verifier-contract branch from 7f576f3 to 6d043fe Compare June 3, 2026 14:35
@pbeza
pbeza marked this pull request as ready for review June 3, 2026 14:36
Copilot AI review requested due to automatic review settings June 3, 2026 14:36

This comment was marked as outdated.

@pbeza
pbeza force-pushed the feat/tee-verifier-contract branch from 6d043fe to cf72a5a Compare June 3, 2026 17:03
@pbeza
pbeza requested a review from gilcu3 June 3, 2026 17:15
@pbeza
pbeza requested a review from Copilot June 9, 2026 17:57
@near near deleted a comment from claude Bot Jun 9, 2026
@near near deleted a comment from claude Bot Jun 9, 2026
@near near deleted a comment from claude Bot Jun 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Comment thread crates/tee-verifier/Cargo.toml
Comment thread crates/tee-verifier/tests/verify_quote.rs Outdated
Comment thread crates/tee-verifier/src/lib.rs
pbeza added 2 commits June 9, 2026 20:04
…n tests

Matches the repo convention (and `conversions.rs` in this crate), so the
attribute warns if the lint stops being triggered.
- Bump the NEP-330 reproducible-build image to 0.21.1-rust-1.93.0 to match
  `rust-toolchain.toml` and `crates/contract` (was 0.17.0-rust-1.86.0).
- Convert the fixture timestamp with `u64::try_from(...).expect(...)` instead
  of a silently-truncating `as u64` cast.
- Use `err.to_string()` instead of `format!("{err}")`.
@pbeza
pbeza requested a review from barakeinav1 June 9, 2026 18:22
barakeinav1
barakeinav1 previously approved these changes Jun 10, 2026

@barakeinav1 barakeinav1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, aligned to the changes we agreed upon in #3235. verify_quote returning VerificationResult resolves the failure-distinguishability concern, conversions are field-for-field correct and pinned against dcap-qvl, and the negative-path tests are a good add. 👍

One nit inline.

Comment thread crates/tee-verifier/Cargo.toml Outdated
The crate uses `borsh::` directly in the `conversions.rs` layout-pinning tests,
so cargo-shear sees `borsh` as used — unlike the contract template this was
copied from, which doesn't touch borsh. The ignore was redundant.
@pbeza
pbeza requested a review from barakeinav1 June 10, 2026 08:53
barakeinav1
barakeinav1 previously approved these changes Jun 10, 2026

@gilcu3 gilcu3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Comment on lines +23 to +44
fn make_collateral() -> Collateral {
// `test_utils::attestation::collateral()` returns a `serde_json::Value`
// matching `attestation::Collateral`'s JSON shape. We re-parse it
// into the interface crate's mirror type by extracting the same
// field names that `dcap_qvl::QuoteCollateralV3` uses.
let v = collateral_json();
Collateral {
pck_crl_issuer_chain: v["pck_crl_issuer_chain"].as_str().unwrap().to_string(),
root_ca_crl: hex::decode(v["root_ca_crl"].as_str().unwrap()).unwrap(),
pck_crl: hex::decode(v["pck_crl"].as_str().unwrap()).unwrap(),
tcb_info_issuer_chain: v["tcb_info_issuer_chain"].as_str().unwrap().to_string(),
tcb_info: v["tcb_info"].as_str().unwrap().to_string(),
tcb_info_signature: hex::decode(v["tcb_info_signature"].as_str().unwrap()).unwrap(),
qe_identity_issuer_chain: v["qe_identity_issuer_chain"].as_str().unwrap().to_string(),
qe_identity: v["qe_identity"].as_str().unwrap().to_string(),
qe_identity_signature: hex::decode(v["qe_identity_signature"].as_str().unwrap()).unwrap(),
pck_certificate_chain: v
.get("pck_certificate_chain")
.and_then(|s| s.as_str())
.map(str::to_string),
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am surprised we need such manual conversion here. I would expect we should have that conversion code somewhere already

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, looks like there are no existing conversions for this, but we can easily add one (see demo: #3521).

The issue is that we don’t want to add a dependency on MPC-specific crates to tee-verifier, since it’s supposed to stay MPC-agnostic and potentially be used by external teams too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, maybe in a future refactor this will become easier, thank you for giving it a try

@gilcu3 gilcu3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ups found a small blocker

Comment thread crates/tee-verifier/Cargo.toml
…-contract

Align the verifier contract's reproducible-build command with mpc-contract; it
was missing the release-contract profile.
@pbeza
pbeza enabled auto-merge June 10, 2026 10:54
@pbeza
pbeza added this pull request to the merge queue Jun 10, 2026
Merged via the queue into main with commit c371628 Jun 10, 2026
15 checks passed
@pbeza
pbeza deleted the feat/tee-verifier-contract branch June 10, 2026 11:03
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.

Introduce tee-verifier contract (stateless dcap_qvl::verify wrapper)

4 participants