feat(tee-verifier): stateless dcap_qvl::verify wrapper contract#3237
feat(tee-verifier): stateless dcap_qvl::verify wrapper contract#3237pbeza wants to merge 5 commits into
dcap_qvl::verify wrapper contract#3237Conversation
b3acde8 to
820497e
Compare
eb25314 to
7766451
Compare
820497e to
2e70aa9
Compare
7766451 to
43413b5
Compare
…-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.
2e70aa9 to
dde7dd2
Compare
43413b5 to
5fae5d5
Compare
dcap_qvl::verify wrapper contract
d1e513b to
54fc11c
Compare
b731b70 to
c3f7c04
Compare
56f6430 to
d69203b
Compare
a288dbd to
b2ff62d
Compare
5edeb86 to
7f576f3
Compare
d392d5c to
198b297
Compare
Adds the `tee-verifier` contract — a stateless NEAR contract exposing
one method:
verify_quote(quote: QuoteBytes, collateral: Collateral) -> VerifiedReport
The method reads the current block timestamp inside the contract and
calls `dcap_qvl::verify::verify(quote, collateral, now)`. On success,
the parsed report is converted to the Borsh mirror types from
`tee-verifier-interface` and returned via Borsh. On verification
failure, the method panics with the upstream error rendered as a
string; callers handle this as `PromiseResult::Failed` in their
callback.
The contract has no state and no admin. All policy (allowlists,
report-data binding, RTMR3 replay, app-compose validation, etc.) is
the caller's responsibility — only the cryptographic dcap-qvl part
lives here.
Includes:
- `tee_verifier::TeeVerifier` (empty state struct, one method).
- `tee_verifier::conversions` (free functions converting between
`dcap_qvl` types and the mirror types in `tee-verifier-interface`;
free functions rather than `From` impls because of the orphan rule).
- An integration test (`tests/verify_quote.rs`) that calls
`verify_quote` directly against the real Dstack quote+collateral
fixture from `test-utils`, asserting the returned `VerifiedReport`
has status `UpToDate`, no advisory IDs, and a TD10 report.
WASM size (non-reproducible, default release): ~518 KiB.
This is the v1 verifier from
`docs/design/attestation-verifier-contract.md` (#3160). A follow-up
PR will wire `mpc-contract`'s `submit_participant_info` into it via
Promise + callback.
Stacked on #3235.
…utils feature The workspace near-sdk dependency no longer enables `unit-testing` by default, so `tests/verify_quote.rs`'s use of `testing_env!` fails to compile after rebasing onto main. Add a `test-utils` feature that turns on `near-sdk/unit-testing` and enable it through a path self dev-dependency, mirroring how `crates/contract` does it.
…r out of the wire crate verify_quote now returns Result<VerifiedReport, VerifierError> with directly. Behaviour is unchanged (an Err still panics, surfacing to a cross-contract caller as PromiseError::Failed), but the fallible-method shape matches the rest of the contract surface and the error message now comes from the error's Display. VerifierError moves from tee-verifier-interface into tee-verifier: it never crosses the wire (failures panic rather than serialize a returned error), so it does not belong in the DTO crate. tee-verifier-interface drops its thiserror dependency and keeps only the genuine wire types; tee-verifier picks thiserror up and implements FunctionError on the local error.
Most internal workspace crates omit `repository`; only the published/SDK-style crates set it. Drop it here to match the common convention.
198b297 to
77b14bb
Compare
7f576f3 to
6d043fe
Compare
|
@claude review |
There was a problem hiding this comment.
Pull request overview
Introduces a new stateless NEAR contract crate (tee-verifier) that centralizes Intel TDX quote verification by wrapping dcap_qvl::verify::verify behind a single verify_quote entry point, so other contracts can delegate DCAP verification over a cross-contract call without linking the heavy dcap-qvl dependency set themselves.
Changes:
- Added the
tee-verifiercontract crate with a single Borsh-serializedverify_quotemethod returningVerifiedReport. - Added conversion glue (and layout-pinning tests) between
dcap_qvltypes andtee-verifier-interfaceDTOs. - Wired the new crate into the workspace and added an integration test using real fixtures from
test-utils.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| crates/tee-verifier/src/lib.rs | Implements the stateless TeeVerifier contract and the verify_quote wrapper around dcap_qvl::verify::verify. |
| crates/tee-verifier/src/conversions.rs | Adds dcap_qvl ↔ interface DTO conversions plus tests that pin Borsh wire layout to upstream types. |
| crates/tee-verifier/tests/verify_quote.rs | Integration test exercising verify_quote with real quote+collateral fixtures and asserting expected report shape/status. |
| crates/tee-verifier/Cargo.toml | Defines the new contract crate, features (ABI + unit-testing), and reproducible build metadata. |
| Cargo.toml | Adds crates/tee-verifier to workspace members and wires tee-verifier-interface in workspace deps. |
| Cargo.lock | Locks dependencies for the newly added tee-verifier crate. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #[test] | ||
| fn verify_quote__should_return_up_to_date_td10_report_for_valid_fixture() { | ||
| // Given | ||
| let block_timestamp_ns = Duration::from_secs(VALID_ATTESTATION_TIMESTAMP).as_nanos() as u64; |
Pull request overviewAdds a new Changes:
Reviewed changesPer-file summary
FindingsNo blocking issues found. A few non-blocking observations: Non-blocking (suggestions/nits):
✅ Approved |
|
test review body |
|
Pull request overview Adds a stateless tee-verifier smart-contract crate that wraps dcap_qvl::verify::verify in a Borsh-serialized verify_quote method. A conversions module maps dcap_qvl types to/from the tee-verifier-interface mirrors. A no-op getrandom is registered for wasm32. An integration test calls verify_quote directly with the existing test-utils Dstack fixture and asserts UpToDate/TD10. Changes:
Reviewed changes Cargo.toml - Adds the new tee-verifier member and a tee-verifier-interface workspace-dep entry. Findings BLOCKING (must fix or explicitly resolve before merge):
NON-BLOCKING (nits, follow-ups, suggestions):
Issues found. |
…ia local traits Add a conformance test module to conversions.rs that asserts each tee-verifier-interface mirror type encodes to the same Borsh bytes as its upstream dcap_qvl counterpart, paired by field/variant name. This catches a silent same-name reorder upstream (which the field-by-field conversions and self-consistent round-trip tests cannot), failing CI on the next dcap-qvl bump rather than at runtime on the wire. Replace the free conversion functions with local IntoDcapType / IntoInterfaceType traits, since tee-verifier owns neither type family and cannot host From/Into impls; the dcap-qvl -> interface direction would be orphan-legal only in the no_std, dcap-qvl-free interface crate. Also fold in self-review fixes: render the dcap failure via Display rather than Debug, drop the unused Clone on VerifierError, and remove tautological length assertions on fixed-size arrays in the integration test.
6d043fe to
cf72a5a
Compare
Closes #3266
Overview
Adds
tee-verifier: a stateless TEE attestation verifier contract.It wraps
dcap_qvl::verify::verifyin a singleverify_quotemethod. The contract holds no state and has no admin — verifier-internal policy (thedcap-qvlversion, 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-certclosure 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.mdfor the design.