attest-pccs 0.0.1

Provisioning Certificate Caching Service with pre-emptive fetching
Documentation
use tokio::time::Duration;

use super::{
    mock_pcs::{MockPcsConfig, spawn_mock_pcs_server},
    *,
};

#[tokio::test]
async fn test_mock_pcs_server_helper_with_get_collateral() {
    let mock = spawn_mock_pcs_server(MockPcsConfig {
        fmspc: "00806F050000".to_string(),
        include_fmspcs_listing: false,
        tcb_next_update: "2999-01-01T00:00:00Z".to_string(),
        qe_next_update: "2999-01-01T00:00:00Z".to_string(),
        refreshed_tcb_next_update: None,
        refreshed_qe_next_update: None,
    })
    .await;

    let pccs = Pccs::new(Some(mock.base_url.clone()));
    let now = 1_700_000_000_u64;
    let (_, is_fresh) =
        pccs.get_collateral("00806F050000".to_string(), "processor", now).await.unwrap();
    assert!(is_fresh);
}

#[test]
fn test_extract_next_update_includes_crl_expiry() {
    let mut collateral: QuoteCollateralV3 =
        serde_saphyr::from_slice(include_bytes!("test-assets/dcap-quote-collateral-00.yaml"))
            .unwrap();

    let mut tcb_info: serde_json::Value = serde_json::from_str(&collateral.tcb_info).unwrap();
    tcb_info["nextUpdate"] = serde_json::Value::String("2999-01-01T00:00:00Z".to_string());
    collateral.tcb_info = serde_json::to_string(&tcb_info).unwrap();

    let mut qe_identity: serde_json::Value = serde_json::from_str(&collateral.qe_identity).unwrap();
    qe_identity["nextUpdate"] = serde_json::Value::String("2999-01-01T00:00:00Z".to_string());
    collateral.qe_identity = serde_json::to_string(&qe_identity).unwrap();

    let expected = parse_crl_next_update("root_ca_crl.nextUpdate", &collateral.root_ca_crl)
        .unwrap()
        .min(parse_crl_next_update("pck_crl.nextUpdate", &collateral.pck_crl).unwrap());

    assert_eq!(extract_next_update(&collateral, 0).unwrap(), expected);
}

#[tokio::test]
async fn test_proactive_refresh_updates_cached_entry() {
    let initial_now = unix_now().unwrap();
    let initial_next_update =
        OffsetDateTime::from_unix_timestamp(initial_now + 2).unwrap().format(&Rfc3339).unwrap();
    let refreshed_next_update =
        OffsetDateTime::from_unix_timestamp(initial_now + 3600).unwrap().format(&Rfc3339).unwrap();

    let mock = spawn_mock_pcs_server(MockPcsConfig {
        fmspc: "00806F050000".to_string(),
        include_fmspcs_listing: false,
        tcb_next_update: initial_next_update.clone(),
        qe_next_update: initial_next_update,
        refreshed_tcb_next_update: Some(refreshed_next_update.clone()),
        refreshed_qe_next_update: Some(refreshed_next_update),
    })
    .await;

    let pccs = Pccs::new(Some(mock.base_url.clone()));
    let (_, is_fresh) = pccs
        .get_collateral("00806F050000".to_string(), "processor", initial_now as u64)
        .await
        .unwrap();
    assert!(is_fresh);
    assert_eq!(mock.tcb_call_count(), 1);
    assert_eq!(mock.qe_call_count(), 1);

    let (_, is_fresh_second) = pccs
        .get_collateral("00806F050000".to_string(), "processor", initial_now as u64)
        .await
        .unwrap();
    assert!(!is_fresh_second);
    assert_eq!(mock.tcb_call_count(), 1);
    assert_eq!(mock.qe_call_count(), 1);

    for _ in 0..60 {
        if mock.tcb_call_count() >= 2 && mock.qe_call_count() >= 2 {
            break;
        }
        tokio::time::sleep(Duration::from_millis(100)).await;
    }

    assert!(mock.tcb_call_count() >= 2, "expected proactive TCB refresh to run");
    assert!(mock.qe_call_count() >= 2, "expected proactive QE identity refresh to run");

    let before_check_calls = mock.tcb_call_count();
    let now_after_background = unix_now().unwrap();
    let (_, is_fresh_again) = pccs
        .get_collateral("00806F050000".to_string(), "processor", now_after_background as u64)
        .await
        .unwrap();
    assert!(!is_fresh_again);
    assert_eq!(mock.tcb_call_count(), before_check_calls);
}

#[tokio::test]
async fn test_ready_waits_for_startup_prewarm() {
    let mock = spawn_mock_pcs_server(MockPcsConfig {
        fmspc: "00806F050000".to_string(),
        include_fmspcs_listing: true,
        tcb_next_update: "2999-01-01T00:00:00Z".to_string(),
        qe_next_update: "2999-01-01T00:00:00Z".to_string(),
        refreshed_tcb_next_update: None,
        refreshed_qe_next_update: None,
    })
    .await;
    let pccs = Pccs::new(Some(mock.base_url.clone()));
    let summary =
        tokio::time::timeout(Duration::from_secs(5), pccs.ready()).await.unwrap().unwrap();
    assert_eq!(summary.discovered_fmspcs, 1);
    assert_eq!(summary.attempted, 2);
    assert_eq!(summary.successes, 2);
    assert_eq!(summary.failures, 0);

    let (total_entries, fmspc, ca) = {
        let cache_guard = pccs.cache.read().unwrap();
        let total_entries = cache_guard.len();
        let (fmspc, ca) = cache_guard
            .keys()
            .next()
            .map(|k| (k.fmspc.clone(), k.ca.clone()))
            .expect("expected startup pre-provision to populate PCCS cache");
        (total_entries, fmspc, ca)
    };
    assert_eq!(total_entries, 2, "expected startup pre-provision to cache processor+platform");
    let ca_static = ca_as_static(&ca).expect("unexpected CA value in warmed cache entry");
    let now = unix_now().unwrap();
    let (_, is_fresh) = pccs.get_collateral(fmspc, ca_static, now as u64).await.unwrap();
    assert!(!is_fresh);
}

#[tokio::test]
async fn test_ready_supports_multiple_waiters() {
    let mock = spawn_mock_pcs_server(MockPcsConfig {
        fmspc: "00806F050000".to_string(),
        include_fmspcs_listing: true,
        tcb_next_update: "2999-01-01T00:00:00Z".to_string(),
        qe_next_update: "2999-01-01T00:00:00Z".to_string(),
        refreshed_tcb_next_update: None,
        refreshed_qe_next_update: None,
    })
    .await;
    let pccs = Pccs::new(Some(mock.base_url.clone()));
    let pccs_clone = pccs.clone();

    let (first, second) = tokio::join!(pccs.ready(), pccs_clone.ready());
    let first = first.unwrap();
    let second = second.unwrap();
    assert_eq!(first, second);
    assert_eq!(first.discovered_fmspcs, 1);
}

#[tokio::test]
async fn test_ready_returns_error_when_prewarm_bootstrap_fails() {
    let pccs = Pccs::new(Some("http://127.0.0.1:1".to_string()));
    let ready_result = tokio::time::timeout(Duration::from_secs(2), pccs.ready()).await.unwrap();
    assert!(matches!(ready_result, Err(PccsError::PrewarmFailed(_))));
}

#[tokio::test]
async fn test_ready_returns_error_when_prewarm_disabled() {
    let pccs = Pccs::new_without_prewarm(None);
    let ready_result = pccs.ready().await;
    assert!(matches!(ready_result, Err(PccsError::PrewarmDisabled)));
}

#[tokio::test]
async fn test_get_collateral_sync_repairs_cache_miss_in_background() {
    let mock = spawn_mock_pcs_server(MockPcsConfig {
        fmspc: "00806F050000".to_string(),
        include_fmspcs_listing: false,
        tcb_next_update: "2999-01-01T00:00:00Z".to_string(),
        qe_next_update: "2999-01-01T00:00:00Z".to_string(),
        refreshed_tcb_next_update: None,
        refreshed_qe_next_update: None,
    })
    .await;

    let pccs = Pccs::new_without_prewarm(Some(mock.base_url.clone()));
    let now = unix_now().unwrap() as u64;

    let err = pccs.get_collateral_sync("00806F050000".to_string(), "processor", now);
    assert!(matches!(err, Err(PccsError::NoCollateralForFmspc(_))));

    for _ in 0..50 {
        if pccs.get_collateral_sync("00806F050000".to_string(), "processor", now).is_ok() {
            break;
        }
        tokio::time::sleep(Duration::from_millis(20)).await;
    }

    let collateral = pccs.get_collateral_sync("00806F050000".to_string(), "processor", now);
    assert!(collateral.is_ok(), "expected sync miss repair to populate cache");
    assert_eq!(mock.tcb_call_count(), 1);
    assert_eq!(mock.qe_call_count(), 1);
}

#[tokio::test]
async fn test_get_collateral_sync_repairs_expired_cache_entry_in_background() {
    let initial_now = unix_now().unwrap();
    let initial_next_update =
        OffsetDateTime::from_unix_timestamp(initial_now + 1).unwrap().format(&Rfc3339).unwrap();
    let refreshed_next_update =
        OffsetDateTime::from_unix_timestamp(initial_now + 3600).unwrap().format(&Rfc3339).unwrap();

    let mock = spawn_mock_pcs_server(MockPcsConfig {
        fmspc: "00806F050000".to_string(),
        include_fmspcs_listing: false,
        tcb_next_update: initial_next_update.clone(),
        qe_next_update: initial_next_update,
        refreshed_tcb_next_update: Some(refreshed_next_update.clone()),
        refreshed_qe_next_update: Some(refreshed_next_update),
    })
    .await;

    let pccs = Pccs::new_without_prewarm(Some(mock.base_url.clone()));
    let (_, is_fresh) = pccs
        .get_collateral("00806F050000".to_string(), "processor", initial_now as u64)
        .await
        .unwrap();
    assert!(is_fresh);

    {
        let mut cache = pccs.cache.write().unwrap();
        let entry = cache
            .get_mut(&PccsInput::new("00806F050000".to_string(), "processor"))
            .expect("expected cached collateral entry");
        entry.next_update = initial_now - 1;
        entry.refresh_task = None;
    }

    let stale_collateral =
        pccs.get_collateral_sync("00806F050000".to_string(), "processor", initial_now as u64);
    assert!(stale_collateral.is_ok(), "expected stale collateral to be returned");

    for _ in 0..50 {
        if mock.tcb_call_count() >= 2 && mock.qe_call_count() >= 2 {
            break;
        }
        tokio::time::sleep(Duration::from_millis(20)).await;
    }

    assert!(mock.tcb_call_count() >= 2, "expected background refresh after sync expired hit");
    assert!(mock.qe_call_count() >= 2, "expected background refresh after sync expired hit");
}