398 lines
16 KiB
Rust
398 lines
16 KiB
Rust
//! # TEE Attestation and Confidential Computing
|
|
//!
|
|
//! Category: **Network & Security**
|
|
//!
|
|
//! **What this demonstrates:**
|
|
//! - Full hardware TEE attestation lifecycle using RVF's attestation module
|
|
//! - Encode/decode `AttestationHeader` and attestation records
|
|
//! - Build attestation witness payloads with multiple TEE platforms
|
|
//! - TEE-bound key creation, encoding, and verification
|
|
//! - Tamper detection: modified attestation records are rejected
|
|
//! - Multi-platform attestation: SGX, SEV-SNP, TDX in one witness chain
|
|
//!
|
|
//! **RVF segments used:** VEC, WITNESS, CRYPTO
|
|
//!
|
|
//! **Context:**
|
|
//! In confidential computing, vectors may be generated inside a TEE (Trusted
|
|
//! Execution Environment). RVF records TEE attestation quotes alongside the
|
|
//! vectors so consumers can verify provenance. This example demonstrates the
|
|
//! full attestation flow using RVF's real attestation API.
|
|
//!
|
|
//! **Run:** `cargo run --example tee_attestation`
|
|
|
|
use rvf_crypto::{
|
|
attestation_witness_entry, build_attestation_witness_payload,
|
|
decode_attestation_header, decode_attestation_record, decode_tee_bound_key,
|
|
encode_attestation_header, encode_attestation_record, encode_tee_bound_key,
|
|
verify_attestation_witness_payload, verify_key_binding,
|
|
shake256_256,
|
|
};
|
|
use rvf_crypto::hash::shake256_128;
|
|
use rvf_runtime::options::DistanceMetric;
|
|
use rvf_runtime::{QueryOptions, RvfOptions, RvfStore};
|
|
use rvf_types::{
|
|
AttestationHeader, AttestationWitnessType, TeePlatform, KEY_TYPE_TEE_BOUND,
|
|
};
|
|
use rvf_crypto::TeeBoundKeyRecord;
|
|
use tempfile::TempDir;
|
|
|
|
/// Simple LCG-based pseudo-random vector generator for deterministic results.
|
|
fn random_vector(dim: usize, seed: u64) -> Vec<f32> {
|
|
let mut v = Vec::with_capacity(dim);
|
|
let mut x = seed.wrapping_add(1);
|
|
for _ in 0..dim {
|
|
x = x.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
|
|
v.push(((x >> 33) as f32) / (u32::MAX as f32) - 0.5);
|
|
}
|
|
v
|
|
}
|
|
|
|
/// Format bytes as a hex string.
|
|
fn hex_string(bytes: &[u8]) -> String {
|
|
bytes.iter().map(|b| format!("{:02x}", b)).collect()
|
|
}
|
|
|
|
/// Create a TEE measurement hash from a name string.
|
|
fn make_measurement(name: &str) -> [u8; 32] {
|
|
shake256_256(name.as_bytes())
|
|
}
|
|
|
|
/// Create a signer ID hash.
|
|
fn make_signer(name: &str) -> [u8; 32] {
|
|
shake256_256(format!("signer:{}", name).as_bytes())
|
|
}
|
|
|
|
/// Create a nonce from a seed value.
|
|
fn make_nonce(seed: u64) -> [u8; 16] {
|
|
shake256_128(&seed.to_le_bytes())
|
|
}
|
|
|
|
fn main() {
|
|
println!("=== TEE Attestation & Confidential Computing Example ===\n");
|
|
|
|
let dim = 128;
|
|
let tmp = TempDir::new().expect("temp dir");
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Phase 1: Create AttestationHeaders for different TEE platforms
|
|
// ──────────────────────────────────────────────
|
|
println!("--- Phase 1: Create Attestation Headers ---\n");
|
|
|
|
let platforms = [
|
|
("SGX Enclave", TeePlatform::Sgx, "enclave-v1.2.3"),
|
|
("SEV-SNP VM", TeePlatform::SevSnp, "sev-vm-prod-0"),
|
|
("TDX Trust Domain", TeePlatform::Tdx, "tdx-domain-alpha"),
|
|
];
|
|
|
|
let base_ts = 1_700_000_000_000_000_000u64;
|
|
|
|
let mut headers = Vec::new();
|
|
for (i, (label, platform, enclave_name)) in platforms.iter().enumerate() {
|
|
let measurement = make_measurement(enclave_name);
|
|
let signer_id = make_signer(enclave_name);
|
|
let nonce = make_nonce(i as u64);
|
|
|
|
let header = AttestationHeader {
|
|
platform: *platform as u8,
|
|
attestation_type: AttestationWitnessType::PlatformAttestation as u8,
|
|
quote_length: 64,
|
|
reserved_0: 0,
|
|
measurement,
|
|
signer_id,
|
|
timestamp_ns: base_ts + (i as u64) * 1_000_000_000,
|
|
nonce,
|
|
svn: (i as u16) + 1,
|
|
sig_algo: 1, // Ed25519
|
|
flags: AttestationHeader::FLAG_HAS_REPORT_DATA,
|
|
reserved_1: [0u8; 3],
|
|
report_data_len: 32,
|
|
};
|
|
|
|
println!(" [{}] {}", i, label);
|
|
println!(" Platform: {:?} (0x{:02x})", platform, *platform as u8);
|
|
println!(" Measurement: {}...", hex_string(&measurement[..8]));
|
|
println!(" Signer: {}...", hex_string(&signer_id[..8]));
|
|
println!(" Nonce: {}...", hex_string(&nonce[..8]));
|
|
println!(" SVN: {}", header.svn);
|
|
println!(" Record size: {} bytes", header.total_record_length());
|
|
|
|
headers.push(header);
|
|
}
|
|
println!();
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Phase 2: Encode/decode attestation records
|
|
// ──────────────────────────────────────────────
|
|
println!("--- Phase 2: Attestation Record Codec ---\n");
|
|
|
|
let mut records = Vec::new();
|
|
for (i, header) in headers.iter().enumerate() {
|
|
// Report data: hash of vectors generated in this TEE
|
|
let report_data = shake256_256(
|
|
format!("vectors-generated-in-tee-{}", i).as_bytes(),
|
|
);
|
|
let report_data_slice = &report_data[..header.report_data_len as usize];
|
|
|
|
// Quote blob (opaque platform-specific attestation data)
|
|
let quote: Vec<u8> = (0..header.quote_length as usize)
|
|
.map(|j| ((j + i * 37) & 0xFF) as u8)
|
|
.collect();
|
|
|
|
let encoded = encode_attestation_record(header, report_data_slice, "e);
|
|
records.push(encoded.clone());
|
|
|
|
// Verify round-trip
|
|
let (dec_header, dec_rd, dec_quote) =
|
|
decode_attestation_record(&encoded).expect("decode record");
|
|
|
|
assert_eq!(dec_header.platform, header.platform);
|
|
assert_eq!(dec_header.quote_length, header.quote_length);
|
|
assert_eq!(dec_header.report_data_len, header.report_data_len);
|
|
assert_eq!(dec_rd, report_data_slice);
|
|
assert_eq!(dec_quote, quote);
|
|
|
|
println!(
|
|
" Record {}: {} bytes, codec round-trip OK",
|
|
i, encoded.len()
|
|
);
|
|
}
|
|
|
|
// Also verify header-only codec
|
|
let hdr_encoded = encode_attestation_header(&headers[0]);
|
|
let hdr_decoded = decode_attestation_header(&hdr_encoded).expect("decode header");
|
|
assert_eq!(hdr_decoded.platform, headers[0].platform);
|
|
assert_eq!(hdr_decoded.measurement, headers[0].measurement);
|
|
println!(" Header-only codec: 112 bytes, round-trip OK");
|
|
println!();
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Phase 3: Build attestation witness payload
|
|
// ──────────────────────────────────────────────
|
|
println!("--- Phase 3: Attestation Witness Payload ---\n");
|
|
|
|
let timestamps: Vec<u64> = (0..records.len())
|
|
.map(|i| base_ts + (i as u64) * 2_000_000_000)
|
|
.collect();
|
|
let witness_types = vec![
|
|
AttestationWitnessType::PlatformAttestation,
|
|
AttestationWitnessType::ComputationProof,
|
|
AttestationWitnessType::DataProvenance,
|
|
];
|
|
|
|
let payload =
|
|
build_attestation_witness_payload(&records, ×tamps, &witness_types)
|
|
.expect("build payload");
|
|
|
|
println!(" Payload size: {} bytes", payload.len());
|
|
println!(" Records: {}", records.len());
|
|
|
|
// Verify the payload
|
|
let verified_entries =
|
|
verify_attestation_witness_payload(&payload).expect("verify payload");
|
|
|
|
println!(" Verified: {} entries OK\n", verified_entries.len());
|
|
|
|
println!(
|
|
" {:>5} {:>16} {:>18} {:>10} {:>8}",
|
|
"Entry", "Witness Type", "Platform", "Quote Len", "RD Len"
|
|
);
|
|
println!(
|
|
" {:->5} {:->16} {:->18} {:->10} {:->8}",
|
|
"", "", "", "", ""
|
|
);
|
|
for (i, (entry, header, rd, quote)) in verified_entries.iter().enumerate() {
|
|
let wtype_name = match AttestationWitnessType::try_from(entry.witness_type) {
|
|
Ok(AttestationWitnessType::PlatformAttestation) => "PLATFORM_ATT",
|
|
Ok(AttestationWitnessType::ComputationProof) => "COMP_PROOF",
|
|
Ok(AttestationWitnessType::DataProvenance) => "DATA_PROV",
|
|
Ok(AttestationWitnessType::KeyBinding) => "KEY_BIND",
|
|
Err(_) => "UNKNOWN",
|
|
};
|
|
let platform_name = match TeePlatform::try_from(header.platform) {
|
|
Ok(TeePlatform::Sgx) => "SGX",
|
|
Ok(TeePlatform::SevSnp) => "SEV-SNP",
|
|
Ok(TeePlatform::Tdx) => "TDX",
|
|
Ok(TeePlatform::ArmCca) => "ARM-CCA",
|
|
Ok(TeePlatform::SoftwareTee) => "SW-TEE",
|
|
Err(_) => "UNKNOWN",
|
|
};
|
|
println!(
|
|
" {:>5} {:>16} {:>18} {:>10} {:>8}",
|
|
i, wtype_name, platform_name, quote.len(), rd.len()
|
|
);
|
|
}
|
|
println!();
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Phase 4: Tamper detection
|
|
// ──────────────────────────────────────────────
|
|
println!("--- Phase 4: Tamper Detection ---\n");
|
|
|
|
let mut tampered = payload.clone();
|
|
// Flip a byte in the records section (near the end)
|
|
let tamper_offset = tampered.len() - 10;
|
|
tampered[tamper_offset] ^= 0xFF;
|
|
|
|
match verify_attestation_witness_payload(&tampered) {
|
|
Ok(_) => println!(" Tampered payload: VALID (unexpected!)"),
|
|
Err(e) => println!(" Tampered payload: REJECTED ({:?})", e),
|
|
}
|
|
|
|
// Truncation detection
|
|
let truncated = &payload[..payload.len() / 2];
|
|
match verify_attestation_witness_payload(truncated) {
|
|
Ok(_) => println!(" Truncated payload: VALID (unexpected!)"),
|
|
Err(e) => println!(" Truncated payload: REJECTED ({:?})", e),
|
|
}
|
|
println!();
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Phase 5: TEE-bound key record
|
|
// ──────────────────────────────────────────────
|
|
println!("--- Phase 5: TEE-Bound Key Record ---\n");
|
|
|
|
let measurement = make_measurement("enclave-v1.2.3");
|
|
let sealed_key = vec![0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80];
|
|
let public_key = b"ruvector-attestation-public-key";
|
|
let key_id = shake256_128(public_key);
|
|
|
|
let key_record = TeeBoundKeyRecord {
|
|
key_type: KEY_TYPE_TEE_BOUND,
|
|
algorithm: 1, // Ed25519
|
|
sealed_key_length: sealed_key.len() as u16,
|
|
key_id,
|
|
measurement,
|
|
platform: TeePlatform::Sgx as u8,
|
|
reserved: [0u8; 3],
|
|
valid_from: base_ts,
|
|
valid_until: base_ts + 86_400_000_000_000, // 24h
|
|
sealed_key: sealed_key.clone(),
|
|
};
|
|
|
|
let encoded_key = encode_tee_bound_key(&key_record);
|
|
let decoded_key = decode_tee_bound_key(&encoded_key).expect("decode key");
|
|
|
|
assert_eq!(decoded_key.key_type, KEY_TYPE_TEE_BOUND);
|
|
assert_eq!(decoded_key.algorithm, 1);
|
|
assert_eq!(decoded_key.key_id, key_id);
|
|
assert_eq!(decoded_key.measurement, measurement);
|
|
assert_eq!(decoded_key.sealed_key, sealed_key);
|
|
|
|
println!(" Key type: TEE_BOUND ({})", KEY_TYPE_TEE_BOUND);
|
|
println!(" Algorithm: Ed25519");
|
|
println!(" Key ID: {}...", hex_string(&key_id[..8]));
|
|
println!(" Measurement: {}...", hex_string(&measurement[..8]));
|
|
println!(" Platform: SGX");
|
|
println!(" Sealed key: {} bytes", sealed_key.len());
|
|
println!(" Wire size: {} bytes", encoded_key.len());
|
|
println!(" Codec: round-trip OK");
|
|
|
|
// Verify key binding in matching environment
|
|
let binding_result = verify_key_binding(
|
|
&decoded_key,
|
|
TeePlatform::Sgx,
|
|
&measurement,
|
|
base_ts + 1_000_000_000, // within validity window
|
|
);
|
|
println!(" Binding check: {}", if binding_result.is_ok() { "VALID" } else { "FAILED" });
|
|
assert!(binding_result.is_ok());
|
|
|
|
// Wrong platform → rejection
|
|
let wrong_platform = verify_key_binding(
|
|
&decoded_key,
|
|
TeePlatform::SevSnp,
|
|
&measurement,
|
|
base_ts + 1_000_000_000,
|
|
);
|
|
println!(" Wrong platform: {}", if wrong_platform.is_err() { "REJECTED (correct)" } else { "VALID (bad)" });
|
|
assert!(wrong_platform.is_err());
|
|
|
|
// Wrong measurement → rejection
|
|
let wrong_meas = verify_key_binding(
|
|
&decoded_key,
|
|
TeePlatform::Sgx,
|
|
&[0xFF; 32],
|
|
base_ts + 1_000_000_000,
|
|
);
|
|
println!(" Wrong measurement: {}", if wrong_meas.is_err() { "REJECTED (correct)" } else { "VALID (bad)" });
|
|
assert!(wrong_meas.is_err());
|
|
|
|
// Expired → rejection
|
|
let expired = verify_key_binding(
|
|
&decoded_key,
|
|
TeePlatform::Sgx,
|
|
&measurement,
|
|
base_ts + 100_000_000_000_000, // way past valid_until
|
|
);
|
|
println!(" Expired key: {}", if expired.is_err() { "REJECTED (correct)" } else { "VALID (bad)" });
|
|
assert!(expired.is_err());
|
|
println!();
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Phase 6: Store vectors with attestation context
|
|
// ──────────────────────────────────────────────
|
|
println!("--- Phase 6: Attested Vector Store ---\n");
|
|
|
|
let store_path = tmp.path().join("attested_vectors.rvf");
|
|
let options = RvfOptions {
|
|
dimension: dim as u16,
|
|
metric: DistanceMetric::L2,
|
|
..Default::default()
|
|
};
|
|
let mut store = RvfStore::create(&store_path, options).expect("create store");
|
|
|
|
let vectors: Vec<Vec<f32>> = (0..100)
|
|
.map(|i| random_vector(dim, i))
|
|
.collect();
|
|
let vec_refs: Vec<&[f32]> = vectors.iter().map(|v| v.as_slice()).collect();
|
|
let ids: Vec<u64> = (0..100).collect();
|
|
|
|
let ingest = store.ingest_batch(&vec_refs, &ids, None).expect("ingest");
|
|
println!(" Ingested {} vectors into attested store", ingest.accepted);
|
|
|
|
// Query
|
|
let query = random_vector(dim, 999);
|
|
let results = store
|
|
.query(&query, 5, &QueryOptions::default())
|
|
.expect("query");
|
|
|
|
println!(" Top-5 nearest neighbors:");
|
|
for (i, r) in results.iter().enumerate() {
|
|
println!(" #{}: id={}, dist={:.6}", i + 1, r.id, r.distance);
|
|
}
|
|
|
|
// Create a single witness entry for the attestation
|
|
let entry = attestation_witness_entry(
|
|
&records[0],
|
|
base_ts,
|
|
AttestationWitnessType::PlatformAttestation,
|
|
);
|
|
assert_eq!(entry.witness_type, AttestationWitnessType::PlatformAttestation as u8);
|
|
println!("\n Attestation witness entry:");
|
|
println!(" Type: PLATFORM_ATTESTATION");
|
|
println!(" Action hash: {}...", hex_string(&entry.action_hash[..8]));
|
|
println!(" Timestamp: {} ns", entry.timestamp_ns);
|
|
|
|
store.close().expect("close store");
|
|
println!();
|
|
|
|
// ──────────────────────────────────────────────
|
|
// Summary
|
|
// ──────────────────────────────────────────────
|
|
println!("=== TEE Attestation Summary ===\n");
|
|
println!(" Platforms demonstrated: SGX, SEV-SNP, TDX");
|
|
println!(" Attestation records: {} (all codec round-trip OK)", records.len());
|
|
println!(" Witness payload: {} entries, verified", verified_entries.len());
|
|
println!(" Tamper detection: payload + truncation → rejected");
|
|
println!(" TEE-bound key: {} bytes, binding verified", encoded_key.len());
|
|
println!(" Key binding checks: valid / wrong-platform / wrong-measurement / expired");
|
|
println!(" Attested vectors: {} stored + queried", ingest.accepted);
|
|
println!(" Segments used: VEC, WITNESS, CRYPTO");
|
|
println!();
|
|
println!(" Key insight: RVF binds attestation quotes to vectors,");
|
|
println!(" so consumers can verify that embeddings were generated");
|
|
println!(" inside a specific TEE before trusting them.");
|
|
|
|
println!("\n=== Done ===");
|
|
}
|