Hacktoberfest 2026: as issues que os mantenedores marcaram para outubro, abertas e boas para iniciantes. Ver issues do Hacktoberfest

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

Aberta
#1,294 0 comentários 0 reações 0 responsáveis Ver no GitHub

Mantenedores costumam responder em até 1 dia

Ninguém assumiu esta issue ainda.

Avaliação

Dificuldade
5/5
Tempo estimado
Mais de uma semana
Facilidade para iniciantes
35/100
Tipo de issue
Bug
Clareza
Claramente especificada
Status de atividade
Ativa
Stack de tecnologia
rust
Domínio
security

Direção de pesquisa

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.

Escrita pelo modelo de indexação a partir do texto da issue.

Descrição

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.

Linguagem predominante
Rust
Estrelas
551
Forks
97
Merge médio
1d 3h
PRs com merge (30d)
199

Preparar o ambiente

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de Dstack-TEE/dstack

Todas as issues de Dstack-TEE/dstack

Issues semelhantes

Mais issues de Rust

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.