ReportData
The 64-byte REPORTDATA field embedded in every TDX quote — wire layout, fields, and verification.
ReportData is the 64-byte field that Livy embeds inside every TDX DCAP quote. It is the structured commit from your application to the hardware: the quote's REPORTDATA field contains exactly these bytes (wrapped in a SHA-512 hash together with the ITA nonce).
Wire layout
All multi-byte integers are big-endian.
Bytes Size Field Description
───── ──── ───── ───────────
00..32 32 payload_hash SHA-256(public_values buffer).
Commits to all values your program chose to make public.
32..40 8 build_id SHA-256(server binary)[0..8].
Short, human-verifiable build fingerprint.
Anyone who can reproduce the build can verify this.
40..44 4 version_code u32 BE — schema version (currently 1).
Increment when the wire layout changes.
44..48 4 build_number u32 BE — CI build counter.
0 in development, CI-assigned in production.
48..56 8 nonce u64 BE — monotonic request counter.
Replay protection: prevents a valid proof from being
reused across different requests.
56..64 8 reserved Zero-filled. Reserved for future fields.The REPORTDATA field sits at bytes [568..632] of the DCAP quote, but it is not stored there directly — the ITA protocol replaces it with:
REPORTDATA in quote = SHA-512( nonce.val ‖ nonce.iat ‖ runtime_data )where runtime_data is the 64-byte ReportData struct above, and nonce.val / nonce.iat are the ITA verifier nonce fields. The original runtime_data travels alongside the quote so verifiers can reconstruct this hash.
Using ReportData directly
For the high-level API, ReportData is constructed automatically by AttestBuilder::finalize. Use it directly when you need the low-level API:
use livy_tee::report::{ReportData, REPORT_DATA_VERSION, build_id_from_hash_hex};
use livy_tee::generate::binary_hash;
use sha2::{Digest, Sha256};
// Compute payload_hash from your inputs manually.
let input_hash: [u8; 32] = Sha256::digest(b"input bytes").into();
let output_hash: [u8; 32] = Sha256::digest(b"output bytes").into();
let mut combined = Sha256::new();
combined.update(input_hash);
combined.update(output_hash);
let payload_hash: [u8; 32] = combined.finalize().into();
// Derive build_id from the running binary.
let binary_hash_hex = binary_hash()?;
let build_id = build_id_from_hash_hex(&binary_hash_hex);
// Construct ReportData.
let rd = ReportData::new(
payload_hash,
build_id,
REPORT_DATA_VERSION, // currently 1
0, // build_number (0 in development)
nonce_counter, // u64 monotonic counter
);
// Serialize to 64-byte wire format.
let bytes: [u8; 64] = rd.to_bytes();Serialization and parsing
use livy_tee::report::ReportData;
// Serialize to 64-byte canonical wire format.
let bytes: [u8; 64] = rd.to_bytes();
// Deserialize from 64-byte canonical wire format.
let rd2 = ReportData::from_bytes(&bytes);
// Hex-encode the full 64 bytes (128 hex characters).
let hex_str = rd.to_hex();Verification
use livy_tee::report::{ReportData, build_id_from_hash_hex};
// Parse from the 64-byte runtime_data field (received alongside a quote).
let rd = ReportData::from_bytes(&runtime_data_bytes);
// 1. Verify payload_hash matches the expected value.
let expected: [u8; 32] = sha256(sha256(input) || sha256(output));
assert!(rd.verify_payload(&expected));
// 2. Verify build_id matches the published binary.
let expected_build_id = build_id_from_hash_hex(&published_tee_binary_hash);
assert_eq!(rd.build_id, expected_build_id);
// 3. Verify nonce matches the expected counter value for this request.
assert_eq!(rd.nonce, expected_nonce);
// 4. Verify schema version is supported.
assert_eq!(rd.version_code, livy_tee::report::REPORT_DATA_VERSION);Build ID helpers
build_id is the first 8 bytes of SHA-256(binary). Two helpers derive it:
use livy_tee::report::{build_id_from_binary, build_id_from_hash_hex};
// From raw binary bytes (e.g. from fs::read)
let id: [u8; 8] = build_id_from_binary(&binary_bytes);
// From an already-computed SHA-256 hex string
let id: [u8; 8] = build_id_from_hash_hex(&binary_hash_hex);
// Both produce identical results for the same binary.The full binary hash is available from livy_tee::generate::binary_hash() which hashes the currently running executable.
Fields reference
| Field | Bytes | Type | Description |
|---|---|---|---|
payload_hash | [0..32] | [u8; 32] | SHA-256 of the public_values buffer |
build_id | [32..40] | [u8; 8] | First 8 bytes of SHA-256(binary) |
version_code | [40..44] | u32 BE | Schema version — currently 1 |
build_number | [44..48] | u32 BE | CI build counter — 0 in dev |
nonce | [48..56] | u64 BE | Monotonic counter for replay protection |
| (reserved) | [56..64] | — | Zero-filled, reserved for future fields |