High-Level API
Livy, AttestBuilder, and Attestation — the primary integration point for application developers.
The high-level API wraps quote generation, ITA nonce fetching, and verification into three types: Livy (the client), AttestBuilder (the builder), and Attestation (the result). It requires the ita-verify feature flag.
livy-tee = { path = "../livy-tee", features = ["ita-verify"] }Livy — the client
Livy reads your ITA API key and vends AttestBuilder instances.
use livy_tee::Livy;
// From environment variable ITA_API_KEY
let livy = Livy::from_env()?;
// Or explicit key
let livy = Livy::new("your-ita-api-key");
// Or from a full ItaConfig
use livy_tee::ItaConfig;
let livy = Livy::with_config(ItaConfig {
api_key: "your-key".to_string(),
..ItaConfig::default()
});AttestBuilder — committing values
livy.attest() returns an AttestBuilder. Use it to commit public values, set a nonce, and then finalize.
let mut builder = livy.attest();
// Commit public typed values in any order.
// Values are readable by anyone who receives the attestation.
builder.commit(&content_hash); // [u8; 32]
builder.commit(&identity_pubkey); // String
builder.commit(&capture_timestamp); // i64
// For sensitive data, commit a hash instead of the raw value.
builder.commit_hashed(&private_input); // commits SHA-256(serialized(value))
// Replay protection — use a monotonically increasing counter.
builder.nonce(request_counter);
// Generate the TDX quote and get an ITA attestation.
let attestation = builder.finalize().await?;commit and nonce return &mut Self, so you can chain them:
let attestation = livy.attest()
.commit(&content_hash)
.commit(&identity_pubkey)
.nonce(counter)
.finalize()
.await?;Commit vs commit_hashed
| Method | Stores | Verifier can see |
|---|---|---|
commit(&value) | JSON-serialized value in plain text | Full value |
commit_hashed(&value) | SHA-256 of JSON-serialized value (32 bytes) | Hash only |
commit_raw(bytes) | Raw bytes | Raw bytes |
Only commit data you intend to make public. For sensitive inputs, use commit_hashed — the verifier confirms the hash matches their copy of the data without you revealing the data itself.
Attestation — the result
finalize().await returns an Attestation containing all fields needed for verification.
let attestation = livy.attest()
.commit(&my_input)
.commit(&my_output)
.nonce(42)
.finalize()
.await?;
// Fields available on every Attestation
println!("ita_token: {}", attestation.ita_token);
println!("mrtd: {}", attestation.mrtd);
println!("tcb_status: {}", attestation.tcb_status);
println!("payload_hash: {}", attestation.payload_hash_hex());Attestation fields
| Field | Type | Description |
|---|---|---|
ita_token | String | ITA-signed JWT. Verifiable against Intel's JWKS endpoint. Contains MRTD, REPORTDATA hash, and TCB status. |
mrtd | String | Hex-encoded 48-byte measurement of the TEE binary (96 hex chars). |
tcb_status | String | "UpToDate" / "OutOfDate" / "Revoked". OutOfDate still attests correctly. |
raw_quote | String | Base64-encoded raw DCAP quote (~8 KB). |
runtime_data | String | Base64-encoded 64-byte ReportData struct. |
verifier_nonce_val | String | Base64-encoded ITA verifier nonce value. |
verifier_nonce_iat | String | Base64-encoded ITA verifier nonce issued-at. |
report_data | ReportData | Structured REPORTDATA parsed from runtime_data. |
public_values | PublicValues | All committed values — read with .public_values.read::<T>(). |
Reading committed values back
The verifier reads values from public_values in commit order:
// TEE side committed these in order:
// builder.commit(&content_hash).commit(&identity_pubkey)
// Verifier reads them back in the same order:
let content_hash: [u8; 32] = attestation.public_values.read();
let pubkey: String = attestation.public_values.read();
assert_eq!(content_hash, sha256(&original_file));Verification
Full chain verification
// Checks:
// 1. SHA-512(nonce_val ‖ nonce_iat ‖ runtime_data) == REPORTDATA in the TDX quote
// 2. SHA-256(public_values buffer) == report_data.payload_hash
//
// No TDX hardware. No network. Pure software.
let ok = attestation.verify()?;
assert!(ok);Local self-consistency check only
// Only checks: SHA-256(public_values) == report_data.payload_hash
// Does NOT verify the TDX quote binding.
// Use verify() for full chain verification.
let ok = attestation.verify_public_values_commitment();Standalone verify functions
If you have the raw fields (e.g. received over an API) rather than an Attestation struct:
use livy_tee::{verify_quote, verify_quote_with_public_values};
// With a pre-computed payload_hash
let ok = verify_quote(
&raw_quote_b64,
&runtime_data_b64,
&nonce_val_b64,
&nonce_iat_b64,
&expected_payload_hash,
)?;
// With a PublicValues buffer (hash computed automatically)
let ok = verify_quote_with_public_values(
&raw_quote_b64,
&runtime_data_b64,
&nonce_val_b64,
&nonce_iat_b64,
&public_values,
)?;Complete example
use livy_tee::{Livy, PublicValues};
use sha2::{Digest, Sha256};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let livy = Livy::from_env()?;
// Your program's inputs and outputs.
let content_hash: [u8; 32] = Sha256::digest(b"photo bytes").into();
let identity_pubkey = "did:key:z6MkjoRhExample".to_string();
let capture_ts: i64 = 1_717_171_717_000;
let counter: u64 = 42;
// Commit and attest.
let attestation = livy.attest()
.commit(&content_hash)
.commit(&identity_pubkey)
.commit(&capture_ts)
.nonce(counter)
.finalize()
.await?;
// Verify locally.
assert!(attestation.verify()?);
println!("tcb_status: {}", attestation.tcb_status);
println!("payload_hash: {}", attestation.payload_hash_hex());
// On the verifier side — read values back.
let hash: [u8; 32] = attestation.public_values.read();
let pubkey: String = attestation.public_values.read();
let ts: i64 = attestation.public_values.read();
assert_eq!(hash, content_hash);
assert_eq!(pubkey, identity_pubkey);
assert_eq!(ts, capture_ts);
Ok(())
}