Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

validate_v2_preimages is documented as binding RTMR replay to displayed fields, but inspects the event list that is not replayed

Đang mở
#1,294 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức phù hợp với người mới
35/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
rust
Lĩnh vực
security

Hướng nghiên cứu

Start with validate_v2_preimages in cc-eventlog/src/tdx.rs and the event handling in dstack-attest/src/attestation.rs, v1.rs, and VersionedAttestation::from_bytes. Compare platform event_log with runtime_events, review Attestation::from_tdx_quote and into_stripped, and run or add a divergent-pair test; done means the replayed and validated event representations cannot disagree and the misleading doc comment is corrected.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Label: DESIGN. The API shape is wrong, not a live decision. The demonstration below is of the shape, not of an exploit.

Base: origin/next @ 030fbb2183.

What the chain claims

cc-eventlog/src/tdx.rs:170-175, the doc comment on validate_v2_preimages:

Validate the externally supplied digest preimage of every V2 runtime event.
The preimage must be present, valid hex, hash to the advertised digest, and equal the canonical representation reconstructed from the public event fields. This binds RTMR replay and displayed fields to the same bytes.

What it enforces

validate_v2_preimages is called on q.event_log / q.tdx_quote.event_log — dstack-attest/src/attestation.rs:2389-2399.

The RTMR3 replay uses a different field: self.runtime_events — dstack-attest/src/attestation.rs:2521 (verify_tdx) and :1807 (verify_tdx_quote_with_events).

These are two independent fields on the wire:

  • SCALE/V0: Attestation { quote: AttestationQuote::DstackTdx(TdxQuote { event_log }), runtime_events, .. } — dstack-attest/src/attestation.rs:1449-1465.
  • msgpack/V1: PlatformEvidence::Tdx { quote, event_log } (dstack-attest/src/v1.rs:66-70) versus StackEvidence::Dstack { report_data, runtime_events, config } (dstack-attest/src/v1.rs:199-206).

Nothing checks that they agree. from_msgpack (v1.rs:~305) and VersionedAttestation::from_bytes (attestation.rs:742-770) do not; neither does verify_with_time.

So the function documented as binding replay to displayed fields is applied to the list that is not replayed, and the list that is replayed carries no supplied digest for the function to check.

Demonstrated

Scratch crate outside the repo, path-depending on ra-tls and cc-eventlog, driving the checked-in sdk/simulator/attestation.bin:

platform.event_log entries   = 33
stack.runtime_events entries = 9
app_id unmodified                        = 5bb4ff9a3837357f19dc176407a5709c62eb6c56
app_id with platform.event_log emptied   = 5bb4ff9a3837357f19dc176407a5709c62eb6c56
app_id with stack.runtime_events emptied = "" (empty = true)

Emptying the list validate_v2_preimages inspects changes nothing about the decoded identity; emptying the other one empties it.

Reachability

  • Who can trigger it: anyone who can submit an attestation blob — POST /verify with attestation (verifier/src/main.rs:95, no auth fairing), a SignCertRequest CSR, or an RA-TLS certificate extension.
  • What credential it needs: none for POST /verify.
  • Who controls frequency: the submitter.

What it costs, concretely

The event log a consumer displays or exports and the event log a verifier replays can disagree, and the stated safety property is attached to the wrong one.

Today the blast radius is small and I want to be accurate about that: VerificationResponse does not echo the log (verifier/src/types.rs:80-100), and ct_monitor derives both lists from one blob via Attestation::from_tdx_quote (ct_monitor/src/main.rs:173, attestation.rs:2137-2161), so its two copies are consistent by construction. The exposure is to any consumer that reads platform.event_log out of an attestation blob it also verified — which the type invites, because both fields are public.

The one place platform.event_log does feed a check is TDX-lite ACPI (verifier/src/verification.rs:1059-1072), and that is safe: the reported digests are compared against recomputed ones and then discarded, and RTMR0 is rebuilt from the recomputed values (verification.rs:1074-1081).

Steelman

validate_v2_preimages is doing real work where it is: it is what lets a relying party reading the serialized TdxEvent list check a V2 event's digest field, and it rejects a non-canonical preimage that happens to hash to the advertised digest — pinned by rejects_noncanonical_v2_preimage_with_matching_digest (cc-eventlog/src/tdx.rs:397-404). Carrying two representations is a compatibility artefact rather than a design choice: RuntimeEvent is the semantic type and TdxEvent is the TCG-shaped one, and both were already on the wire when the V1 msgpack schema was written, so the schema recorded both.

It is also worth saying that the replay itself is sound: TdxEvent::digest() recomputes runtime-event digests from (name, payload, version) rather than trusting the supplied field (cc-eventlog/src/tdx.rs:121-126), and replay_events hashes those. There is no forgeable digest in the replay path — which is exactly why validate_v2_preimages has nothing to do there.

Improvement directions

  1. Cheap, closes it, wire-compatible. At decode time, derive runtime_events from platform.event_log's imr == 3 entries instead of accepting it as an independent field — which is precisely what Attestation::from_tdx_quote already does (attestation.rs:2137-2161). One field leaves the trusted surface; existing encoders keep working because they emit both consistently. Cost: one read-side change plus a test that a divergent pair no longer decodes two ways.
  2. Cheaper, weaker. Add an equality check in verify_with_time and reject an attestation whose two lists disagree on their imr == 3 entries. Keeps the wire format exactly; costs one comparison per verification. Risk: any historical encoder that legitimately stripped one list differently would start failing — into_stripped (attestation.rs:804-830, v1.rs:20-62) strips event_log but not runtime_events, so this option needs that asymmetry resolved first, which is why (1) is cleaner.
  3. Minimum. Correct the doc comment at cc-eventlog/src/tdx.rs:170-175 to say what the function actually validates, so the next reader does not inherit the claim.

(1) is the one worth doing; (3) should happen regardless.

Ngôn ngữ chính
Rust
Star
551
Fork
97
Merge trung bình
1 ngày 8 giờ
Pull request đã merge (30 ngày)
182

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của Dstack-TEE/dstack

Tất cả issue của Dstack-TEE/dstack

Issue tương tự

Thêm issue về Rust

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.