open-standards/sandbox/spec/attestation.md
Transaction Science df918bf256
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 5s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 50s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m53s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m12s
standards workspaces build + test / proof-rs (proof-conformance, proof/proof-rs, proof-rs, linux, true) (pull_request) Successful in 1m37s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m36s
standards workspaces build + test / joulecontract-rs (joulecontract/joulecontract-rs, joulecontract-rs, linux-arm64, true) (pull_request) Successful in 1m16s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m32s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 7m13s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 2m33s
sandbox: one report_data binding for a run, a relying-party challenge, and evidence a third party can verify
A confidential run's TEE report bound BLAKE3(unit) ‖ BLAKE3(receipt), with
no domain tag. The outcome sat only under the Supervisor's signature, the
only freshness input was a host-chosen nonce (every call site passed
[0; 8]), and the report was dropped once the host had checked it, so no
one else could. verify_eat compared no nonce, so any token the Supervisor
key signed verified, whichever run it was for.

sandbox-runtime (SPEC §7.6):
- run_report_data is one preimage: BLAKE3-XOF-64 in derive-key mode
  ("sandbox/1 run report_data v1") over length-prefixed unit_id,
  receipt_id and JCS(outcome), then 0x01 ‖ challenge, or 0x00 when there
  is none. The receipt binds the metered energy, so the report vouches for
  the joules through it. It replaces tee_report_data.
- TierRequest.challenge carries the relying party's 32 bytes into
  run_and_seal. seal_run_with_mechanism takes a challenge; seal_run and its
  callers are unchanged.
- The sealed TeeEvidence carries report_hex (the report itself) and
  challenge_hex.
- verify_run_evidence lets a party other than the host check:
  - the report's signature and policy;
  - that its report_data binds the attestation's unit, receipt and
    outcome and the verifier's OWN challenge, never the recorded one;
  - that the summary is the report's.
  Evidence without its report is refused.

sandbox-attest:
- Outcome::core is shared by the attestation core and the preimage.
- TeeEvidence.report_hex and challenge_hex are omitted when absent, so
  evidence sealed before them keeps its bytes.
- run_nonce is exposed.
- verify_eat takes expect_nonce and refuses a token whose nonce is not
  that run's. The CLI, MCP and build paths now pass the run's nonce. The
  MCP verify_eat tool takes an optional expected_nonce_hex and reports
  nonce_checked.

SPEC §7.6 gives the binding, the challenge and the third-party check;
§7.8, spec/eat.md and spec/attestation.md are updated to match.

Also: an orphaned doc comment in sandbox-conformance's vectors.rs that
clippy refused (it predates this change).

Tests:
- Binding and challenge.
- An unchallenged report is never taken for a challenged one.
- The preimage separates its inputs, including a length-delimitation
  collision.
- A requested challenge reaches the report through run_and_seal.
- An EAT is checked against the run the verifier holds.
Mutation testing: 12 mutants, all killed. They cover:
- the challenge, its presence tag, the outcome, the receipt and the
  length prefixes;
- the verifier trusting the recorded challenge, the summary check, and the
  report and challenge being carried;
- the seal and run_and_seal dropping the challenge;
- the EAT nonce check.

Verified on the build host:
- sandbox-rs: 421 tests (--locked); every conformance class passes.
- Clippy -D warnings clean on sandbox-runtime, -attest, -tee, -build,
  -mcp, -cli and -conformance.

Not in this change: running it on SEV-SNP hardware (a billable cloud cell),
a challenge slot in jcp-wire, and TDX on the run path, which still
verifies SEV-SNP only.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-21 07:41:40 -04:00

7.5 KiB

The attestation (signed claim + replay journal)

Normative companion to SPEC.md §7 and §8. RFC 2119 keywords apply.

An attestation is the Supervisor's signed statement of what a run was: which image ran, at which tier, under which grants, on which hardware, with which energy account, and — where recorded — replayable to a journal root. It is the artifact a third party verifies without trusting the host.

Core

Attestation {
  unit_id       : ContentId,
  image_id      : ContentId,
  grants_id     : ContentId,
  tier          : Tier,                 // S0..S7
  devices       : [DeviceIdentity],
  receipt_id    : ContentId,            // the energy receipt for this run (SPEC §6)
  journal_root  : Option<Hash>,         // BLAKE3 root of the replay journal (SPEC §8)
  outcome       : Outcome,              // Completed | Failed | Terminated{ breach }
  tee           : Option<TeeEvidence>,  // present iff tier >= S6 (SPEC §7.6)
  locality      : Option<Locality>,     // REQUIRED at S7 (SPEC §7.7)
  audit_prev    : Option<ContentId>,    // REQUIRED at S7: audit-chain head before this run
  mechanism     : Option<Mechanism>,    // how confinement was established; S1-S5 (SPEC §7.9)
  host_id       : String,
  started_at_us : u64,
  finished_at_us: u64,
}

DeviceIdentity { class : DeviceClass, model : String, driver : String }

Mechanism { runtime : String, profile_id : ContentId }
  // runtime    : enforcement runtime, name AND version ("wasmtime/43.0.2")
  // profile_id : BLAKE3 over the length-prefixed encoding of
  //              (runtime, policy profile applied, egress allowlist installed)

Outcome = Completed
        | Failed
        | Terminated { breach : Breach }     // EnergyBudget | CpuTime | Memory | Tamper | GrantMutation

TeeEvidence { kind : SevSnp | Tdx | ArmCca, measurement_hex : String, tcb : String,
              report_hex : String?, challenge_hex : String? }   // omitted when absent

Locality { region : String, jurisdiction : String }

Confidential and sovereign binding (S6/S7)

At S6, the host obtains a TEE report from the platform's attestation interface, binds it to the run through the report's report_data (one preimage over the unit, the receipt, the outcome and the relying party's challenge, SPEC §7.6), and verifies it — signature against the VCEK, and measurement / TCB / debug-state / report-data against policy — before sealing the TeeEvidence summary (SPEC §7.6). The reference sandbox-tee crate implements the full SEV-SNP verification path (parse, policy, ECDSA-P384/SHA-384); report generation is hardware-bound (configfs-tsm on real confidential hardware), with a tagged software-grounded provider for wiring.

An attestation carrying mechanism uses the profile tag sandbox.attestation/2; one without it remains sandbox.attestation/1 and canonicalizes to byte-identical JSON, so attestations sealed before §7.9 verify unchanged. A mechanism MUST NOT be present at S0 (unconfined by definition), and is not proof of isolation — a host that will misstate its tier can misstate its profile. It makes the claim checkable against a known-good profile rather than unfalsifiable. A relying party needing proof rather than assertion still requires S6+ (SPEC §7.5, §7.9).

At S7, the host additionally binds locality and links the run into a hash-chained audit log: audit_prev is the chain head before this run, and after signing the host appends the attestation's content address, advancing the head (head' = BLAKE3(head ‖ attestation_id)). The chain is tamper-evident, so the ordered sequence of sovereign runs is itself attestable (SPEC §7.7).

Device identity (REQUIRED — heterogeneity clause)

devices MUST name, for each device class the unit used, its { class, model, driver }. A run that dispatched accelerator work but does not record that accelerator's identity is not conformant (SPEC §7.2). The classes in the attestation's devices MUST match the classes in the receipt's devices (SPEC §6.2): the same silicon that was metered is the silicon that is named.

Binding, not duplication

The attestation does not restate the energy figures. It carries receipt_id, the content address of the receipt core, so the receipt is bound to the run by hash. A verifier checks the attestation signature, then checks that the receipt it holds addresses to receipt_id. The grant set is bound the same way through grants_id, and the executed artifact through image_id. None can be swapped without breaking the signature chain.

Signing (Ed25519 over JCS)

The attestation signs Ed25519 (RFC 8032) over the JCS-canonicalized JSON (RFC 8785) of its core, with SHA-256 backing the content addresses it references. This is the attestation primitive of the ARL Sandbox; a Sandbox attestation and an ARL-S session attestation verify under the same code path, so a measurement run (ARL-S) and a production run (Sandbox) speak one attestation language.

The reference sandbox-attest crate implements JCS canonicalization and the sign/verify pair, with vectors fixing the canonical bytes and a known-answer signature (vector class C4).

Why two crypto forms in one standard

The receipt seals as JCR-1 (det-CBOR + COSE) because it is a member of the family's receipt class and must verify under shared JCR-1 tooling. The attestation signs Ed25519-over-JCS because it is the generalization of ARL-S and must verify under shared ARL-S tooling. They cross-reference by content address (§ "Binding, not duplication"). This is deliberate interoperability with two existing surfaces, not redundancy.

Key custody

The Supervisor's signing key MUST be unreachable by the unit and by the host's in-boundary code — TPM, HSM, or secure enclave at production tiers (SPEC §7.4). The unit has no path to the telemetry stream or the signing operation. The Supervisor's read of physical telemetry occurs over a channel the unit cannot inspect or influence.

Anti-evasion (recorded in the attestation)

The host MUST normalize the environment the unit observes — hostname, paths, process tree, time, timezone, locale — to deployment-realistic values, and MUST record probing in the attestation: hardware enumeration, root filesystem walks, attempts to read or defeat the energy interface, timing-channel probing. A detected VM-escape, privilege escalation, or out-of-allocation write MUST set outcome = Terminated{ Tamper } and invalidate the run. Any mutation to the content-addressed grant set, image, or harness configuration during the run MUST set outcome = Terminated{ GrantMutation } (SPEC §1.2, §7.5).

Deterministic replay (SPEC §8)

Where a host records a replay journal, it logs the unit's nondeterministic inputs in order:

JournalEntry = HostCall   { name : String, args_hash : Hash, result_hash : Hash }
             | TimerFired  { at_us : u64 }
             | Random      { draw_hash : Hash }
             | Checkpoint  { state_hash : Hash }

The journal's BLAKE3 root MUST appear as journal_root. Given the recorded inputs and seeds, the host MUST be able to reconstruct the run to the identical sequence of Checkpoint state hashes. Replay is for audit, not re-execution: re-running a stochastic unit produces a new sample; replay reproduces the run that happened. A journal that does not replay to its recorded root invalidates the attestation. The reference sandbox-attest crate computes the journal root and verifies a Consistent | Diverged{at} | Truncated replay outcome.