Skip to content

calibration: the attestation envelope's reportData is 32 bytes for a 64-byte TEE field, and its binding has no freshness component — a valid report replays #9140

Description

@JSONbored

Problem

packages/loopover-engine/src/calibration/attestation-envelope.ts (#8541) is the typed seam the attested-
evaluation epic will build on. Two gaps in it will block an independent verifier, and both are cheaper to
fix now — before #8537 persists real envelopes — than after.

1. reportData is 32 bytes; SEV-SNP and TDX both bind 64

buildAttestationReportData returns a hex SHA-256 — 32 bytes — and the validator pins it at exactly 64
hex chars (REPORT_DATA_HEX = 64). The doc comment calls it "the 32-byte payload a TEE binds into its
attestation report."

But the SEV-SNP attestation report's REPORT_DATA field is 64 bytes of guest-supplied data, and
TDX's REPORTDATA is likewise 64 bytes. So the digest must be placed into a field twice its width, and
the envelope specifies no placement convention — left-aligned zero-padded, right-aligned, or
double-hashed. #8538's verification walkthrough asks a skeptic to "recompute reportData … and check it
against the attestation report's user-data binding."
Without a stated convention that check is
unspecified, and two honest implementers will disagree.

2. No freshness binding — a valid attestation report replays

reportData commits to corpusChecksum:headSha:baseSha and nothing else. That triple is deterministic
across runs
: the same corpus at the same SHAs always yields the same reportData. So one genuine
attested run's report can be presented for any later run with an identical binding, and no field in the
envelope distinguishes them — there is no nonce, no run id, and no timestamp inside the signed payload
(verifiedAt is on the verifier's record, outside the report).

The unused 32 bytes from gap (1) are exactly where a nonce or run id belongs. Fixing both together is
one change.

Why now

Both are pure-schema concerns in a module that is explicitly infrastructure-free, so they are fixable
before any hardware exists. Once #8537 persists envelopes under audit_events metadata, changing the
binding means a schemaVersion bump and a mixed-vintage corpus of evidence.

Requirements

  • Define and document the exact 64-byte layout, e.g. sha256(binding) || nonce or
    sha256(binding) || 32 zero bytes, and encode it in buildAttestationReportData's return (or add a
    sibling helper returning the full 64-byte field). Publish it as a written spec with test vectors — a
    non-JS verifier must be able to reimplement it.
  • Add a freshness component (nonce or run id) to the binding, and a matching envelope field so a verifier
    can tie the report to one specific run.
  • Validate the binding inputs. buildAttestationReportData currently accepts arbitrary strings and joins
    them with :; a corpusChecksum containing a colon makes the commitment ambiguous. Constrain the
    three inputs to their expected hex shapes, or length-prefix the concatenation.
  • Minor: BASE64 = /^[A-Za-z0-9+/]+={0,2}$/ accepts strings whose length is not a multiple of 4, which
    are not valid base64.
  • Update orb: run the backtest replay inside the CoCo runtime class and persist its attestation envelope #8537 and docs+ui: extend verify-this-review with the attested-run verification path #8538 to reference the finalized layout so neither re-derives it.

Test Coverage Requirements

99%+ patch coverage, branch-counted (this package is graded by node:test — see #9064 for the coverage-
evidence caveat). Property test that the binding is injective over its input triple; published test
vectors asserted.

Links & Resources

Boundaries

Schema, binding, and spec only. No verification policy, no certificate-chain checking, no infrastructure —
those remain the parent epic's maintainer work, exactly as #8541 scoped it.

maintainer-only — trust-architecture schema.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions