mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-21 07:03:56 +02:00

- Add mock tests using real Intel API response data (25 tests) - Create fetch_test_data tool to retrieve real API responses for testing - Add integration_test example covering 17 API endpoints - Add common_usage example demonstrating attestation verification patterns - Add issuer chain validation checks to ensure signature verification is possible - Add comprehensive documentation in CLAUDE.md The test suite now covers all major Intel DCAP API functionality including TCB info, enclave identities, PCK CRLs, FMSPCs, and evaluation data numbers for both SGX and TDX platforms across API v3 and v4. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
495 lines
18 KiB
Rust
495 lines
18 KiB
Rust
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright (c) 2025 Matter Labs
|
|
|
|
use intel_dcap_api::{
|
|
ApiClient, ApiVersion, CaType, CrlEncoding, IntelApiError, PlatformFilter, UpdateType,
|
|
};
|
|
use std::time::Duration;
|
|
use tokio::time::sleep;
|
|
|
|
/// Comprehensive integration test example demonstrating most Intel DCAP API client functions
|
|
///
|
|
/// This example shows how to use various endpoints of the Intel Trusted Services API.
|
|
/// Note: Some operations may fail with 404 or 400 errors if the data doesn't exist on Intel's servers.
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Intel DCAP API Integration Test Example ===\n");
|
|
|
|
// Create clients for both V3 and V4 APIs
|
|
let v4_client = ApiClient::new()?;
|
|
let v3_client =
|
|
ApiClient::new_with_options("https://api.trustedservices.intel.com", ApiVersion::V3)?;
|
|
|
|
// Track successes and failures
|
|
let mut results = Vec::new();
|
|
|
|
// Test FMSPC - commonly used for TCB lookups
|
|
let test_fmspc = "00906ED50000";
|
|
let test_fmspc_tdx = "00806F050000";
|
|
|
|
println!("1. Testing TCB Info Endpoints...");
|
|
println!("================================");
|
|
|
|
// 1.1 SGX TCB Info (V4)
|
|
print!(" - SGX TCB Info (V4): ");
|
|
match v4_client.get_sgx_tcb_info(test_fmspc, None, None).await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("SGX TCB Info (V4)", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" FMSPC: {}", test_fmspc);
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
let tcb_info: serde_json::Value = serde_json::from_str(&response.tcb_info_json)?;
|
|
if let Some(version) = tcb_info["tcbInfo"]["version"].as_u64() {
|
|
println!(" TCB Info Version: {}", version);
|
|
}
|
|
results.push(("SGX TCB Info (V4)", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("SGX TCB Info (V4)", false));
|
|
}
|
|
}
|
|
|
|
// Add small delay between requests to be nice to the API
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
// 1.2 SGX TCB Info (V3)
|
|
print!(" - SGX TCB Info (V3): ");
|
|
match v3_client.get_sgx_tcb_info(test_fmspc, None, None).await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("SGX TCB Info (V3)", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
results.push(("SGX TCB Info (V3)", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("SGX TCB Info (V3)", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
// 1.3 TDX TCB Info
|
|
print!(" - TDX TCB Info: ");
|
|
match v4_client.get_tdx_tcb_info(test_fmspc_tdx, None, None).await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("TDX TCB Info", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
let tcb_info: serde_json::Value = serde_json::from_str(&response.tcb_info_json)?;
|
|
if let Some(id) = tcb_info["tcbInfo"]["id"].as_str() {
|
|
println!(" Platform: {}", id);
|
|
}
|
|
results.push(("TDX TCB Info", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("TDX TCB Info", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
// 1.4 SGX TCB Info with Early Update
|
|
print!(" - SGX TCB Info (Early Update): ");
|
|
match v4_client
|
|
.get_sgx_tcb_info(test_fmspc, Some(UpdateType::Early), None)
|
|
.await
|
|
{
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("SGX TCB Info (Early)", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
results.push(("SGX TCB Info (Early)", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("SGX TCB Info (Early)", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
println!("\n2. Testing Enclave Identity Endpoints...");
|
|
println!("========================================");
|
|
|
|
// 2.1 SGX QE Identity
|
|
print!(" - SGX QE Identity: ");
|
|
match v4_client.get_sgx_qe_identity(None, None).await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("SGX QE Identity", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
let identity: serde_json::Value =
|
|
serde_json::from_str(&response.enclave_identity_json)?;
|
|
if let Some(id) = identity["enclaveIdentity"]["id"].as_str() {
|
|
println!(" Enclave ID: {}", id);
|
|
}
|
|
results.push(("SGX QE Identity", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("SGX QE Identity", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
// 2.2 SGX QVE Identity
|
|
print!(" - SGX QVE Identity: ");
|
|
match v4_client.get_sgx_qve_identity(None, None).await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("SGX QVE Identity", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
results.push(("SGX QVE Identity", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("SGX QVE Identity", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
// 2.3 SGX QAE Identity
|
|
print!(" - SGX QAE Identity: ");
|
|
match v4_client.get_sgx_qae_identity(None, None).await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("SGX QAE Identity", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
results.push(("SGX QAE Identity", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("SGX QAE Identity", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
// 2.4 TDX QE Identity (V4 only)
|
|
print!(" - TDX QE Identity: ");
|
|
match v4_client.get_tdx_qe_identity(None, None).await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("TDX QE Identity", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
results.push(("TDX QE Identity", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("TDX QE Identity", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
println!("\n3. Testing PCK CRL Endpoints...");
|
|
println!("================================");
|
|
|
|
// 3.1 PCK CRL - Processor (PEM)
|
|
print!(" - PCK CRL (Processor, PEM): ");
|
|
match v4_client.get_pck_crl(CaType::Processor, None).await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("PCK CRL (Processor)", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
let crl_str = String::from_utf8_lossy(&response.crl_data);
|
|
if crl_str.contains("BEGIN X509 CRL") {
|
|
println!(" Format: PEM");
|
|
}
|
|
results.push(("PCK CRL (Processor)", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("PCK CRL (Processor)", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
// 3.2 PCK CRL - Platform (DER)
|
|
print!(" - PCK CRL (Platform, DER): ");
|
|
match v4_client
|
|
.get_pck_crl(CaType::Platform, Some(CrlEncoding::Der))
|
|
.await
|
|
{
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("PCK CRL (Platform, DER)", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
println!(" CRL size: {} bytes", response.crl_data.len());
|
|
results.push(("PCK CRL (Platform, DER)", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("PCK CRL (Platform, DER)", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
println!("\n4. Testing FMSPC Endpoints (V4 only)...");
|
|
println!("=======================================");
|
|
|
|
// 4.1 Get FMSPCs (no filter)
|
|
print!(" - Get FMSPCs (no filter): ");
|
|
match v4_client.get_fmspcs(None).await {
|
|
Ok(fmspcs_json) => {
|
|
println!("✓ Success");
|
|
let fmspcs: serde_json::Value = serde_json::from_str(&fmspcs_json)?;
|
|
if let Some(arr) = fmspcs.as_array() {
|
|
println!(" Total FMSPCs: {}", arr.len());
|
|
// Show first few FMSPCs
|
|
for (i, fmspc) in arr.iter().take(3).enumerate() {
|
|
if let (Some(fmspc_val), Some(platform)) =
|
|
(fmspc["fmspc"].as_str(), fmspc["platform"].as_str())
|
|
{
|
|
println!(" [{}] {} - {}", i + 1, fmspc_val, platform);
|
|
}
|
|
}
|
|
if arr.len() > 3 {
|
|
println!(" ... and {} more", arr.len() - 3);
|
|
}
|
|
}
|
|
results.push(("Get FMSPCs", true));
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("Get FMSPCs", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
// 4.2 Get FMSPCs with platform filter
|
|
print!(" - Get FMSPCs (All platforms): ");
|
|
match v4_client.get_fmspcs(Some(PlatformFilter::All)).await {
|
|
Ok(_) => {
|
|
println!("✓ Success");
|
|
results.push(("Get FMSPCs (filtered)", true));
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("Get FMSPCs (filtered)", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
println!("\n5. Testing TCB Evaluation Data Numbers (V4 only)...");
|
|
println!("===================================================");
|
|
|
|
// 5.1 SGX TCB Evaluation Data Numbers
|
|
print!(" - SGX TCB Evaluation Data Numbers: ");
|
|
match v4_client.get_sgx_tcb_evaluation_data_numbers().await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("SGX TCB Eval Numbers", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
let data: serde_json::Value =
|
|
serde_json::from_str(&response.tcb_evaluation_data_numbers_json)?;
|
|
if let Some(sgx_data) = data.get("sgx") {
|
|
println!(
|
|
" SGX entries: {}",
|
|
sgx_data.as_array().map(|a| a.len()).unwrap_or(0)
|
|
);
|
|
}
|
|
results.push(("SGX TCB Eval Numbers", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("SGX TCB Eval Numbers", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
// 5.2 TDX TCB Evaluation Data Numbers
|
|
print!(" - TDX TCB Evaluation Data Numbers: ");
|
|
match v4_client.get_tdx_tcb_evaluation_data_numbers().await {
|
|
Ok(response) => {
|
|
if response.issuer_chain.is_empty() {
|
|
println!("✗ Failed: Empty issuer chain");
|
|
results.push(("TDX TCB Eval Numbers", false));
|
|
} else {
|
|
println!("✓ Success");
|
|
println!(" Issuer chain: {} bytes", response.issuer_chain.len());
|
|
let data: serde_json::Value =
|
|
serde_json::from_str(&response.tcb_evaluation_data_numbers_json)?;
|
|
if let Some(tdx_data) = data.get("tdx") {
|
|
println!(
|
|
" TDX entries: {}",
|
|
tdx_data.as_array().map(|a| a.len()).unwrap_or(0)
|
|
);
|
|
}
|
|
results.push(("TDX TCB Eval Numbers", true));
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Failed: {:?}", e);
|
|
results.push(("TDX TCB Eval Numbers", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
|
|
println!("\n6. Testing PCK Certificate Endpoints...");
|
|
println!("=======================================");
|
|
|
|
/* // 6.1 PCK Certificate by PPID (usually requires valid data)
|
|
print!(" - PCK Certificate by PPID: ");
|
|
let test_ppid = "0000000000000000000000000000000000000000000000000000000000000000";
|
|
let test_cpusvn = "00000000000000000000000000000000";
|
|
let test_pcesvn = "0000";
|
|
let test_pceid = "0000";
|
|
|
|
match v4_client
|
|
.get_pck_certificate_by_ppid(test_ppid, test_cpusvn, test_pcesvn, test_pceid, None, None)
|
|
.await
|
|
{
|
|
Ok(_) => {
|
|
println!("✓ Success");
|
|
results.push(("PCK Certificate", true));
|
|
}
|
|
Err(e) => {
|
|
// Expected to fail with test data
|
|
match &e {
|
|
IntelApiError::ApiError { status, .. } => {
|
|
println!("✗ Failed (Expected): HTTP {}", status);
|
|
}
|
|
_ => println!("✗ Failed: {:?}", e),
|
|
}
|
|
results.push(("PCK Certificate", false));
|
|
}
|
|
}
|
|
|
|
sleep(Duration::from_millis(100)).await;
|
|
*/
|
|
println!("\n7. Testing API Version Compatibility...");
|
|
println!("=======================================");
|
|
|
|
// 7.1 Try V4-only endpoint on V3
|
|
print!(" - V4-only endpoint on V3 (should fail): ");
|
|
match v3_client.get_fmspcs(None).await {
|
|
Ok(_) => {
|
|
println!("✗ Unexpected success!");
|
|
results.push(("V3/V4 compatibility check", false));
|
|
}
|
|
Err(IntelApiError::UnsupportedApiVersion(_)) => {
|
|
println!("✓ Correctly rejected");
|
|
results.push(("V3/V4 compatibility check", true));
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Wrong error: {:?}", e);
|
|
results.push(("V3/V4 compatibility check", false));
|
|
}
|
|
}
|
|
|
|
println!("\n8. Testing Error Handling...");
|
|
println!("============================");
|
|
|
|
// 8.1 Invalid FMSPC
|
|
print!(" - Invalid FMSPC format: ");
|
|
match v4_client.get_sgx_tcb_info("invalid", None, None).await {
|
|
Ok(_) => {
|
|
println!("✗ Unexpected success!");
|
|
results.push(("Error handling", false));
|
|
}
|
|
Err(IntelApiError::ApiError {
|
|
status,
|
|
error_code,
|
|
error_message,
|
|
..
|
|
}) => {
|
|
println!("✓ Correctly handled");
|
|
println!(" Status: {}", status);
|
|
if let Some(code) = error_code {
|
|
println!(" Error Code: {}", code);
|
|
}
|
|
if let Some(msg) = error_message {
|
|
println!(" Error Message: {}", msg);
|
|
}
|
|
results.push(("Error handling", true));
|
|
}
|
|
Err(e) => {
|
|
println!("✗ Unexpected error: {:?}", e);
|
|
results.push(("Error handling", false));
|
|
}
|
|
}
|
|
|
|
// Summary
|
|
println!("\n\n=== Summary ===");
|
|
println!("===============");
|
|
|
|
let total = results.len();
|
|
let successful = results.iter().filter(|(_, success)| *success).count();
|
|
let failed = total - successful;
|
|
|
|
println!("Total tests: {}", total);
|
|
println!(
|
|
"Successful: {} ({}%)",
|
|
successful,
|
|
(successful * 100) / total
|
|
);
|
|
println!("Failed: {} ({}%)", failed, (failed * 100) / total);
|
|
|
|
println!("\nDetailed Results:");
|
|
for (test, success) in &results {
|
|
println!(" {} {}", if *success { "✓" } else { "✗" }, test);
|
|
}
|
|
|
|
println!("\nNote: Some failures are expected due to:");
|
|
println!("- Test data not existing on Intel servers");
|
|
println!("- PCK operations requiring valid platform data");
|
|
println!("- Subscription key requirements for certain endpoints");
|
|
|
|
Ok(())
|
|
}
|