Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
38e80bb
First step towards integrating easy-tee/attest
ameba23 Jun 3, 2026
3227a16
Add test for measurement policy
ameba23 Jun 3, 2026
25ea83f
Also check rtmr0
ameba23 Jun 3, 2026
bced75d
Also check mrtd
ameba23 Jun 3, 2026
b25a42f
Fix to compile for azure feature
ameba23 Jun 3, 2026
9e0c263
Clippy
ameba23 Jun 3, 2026
737d790
Error handling
ameba23 Jun 3, 2026
5d20913
Typo, comments
ameba23 Jun 3, 2026
f245588
Use AttestationEvidence directly in AttestationExchangeMessage
ameba23 Jun 4, 2026
54dff24
Do not allow converting AttestationType::None to a attest-types::Atte…
ameba23 Jun 4, 2026
54e86c0
fmt
ameba23 Jun 5, 2026
c296367
Merge branch 'main' into peg/attest-integrate-incremental-00
ameba23 Jun 12, 2026
46fca45
Update to use ah/gcp-hobgen branch of the attest repo
ameba23 Jun 16, 2026
d75fda9
Update to use ah/gcp-hobgen branch of the attest repo
ameba23 Jun 16, 2026
52370b0
Logging, fix for attestation-provider-server
ameba23 Jun 16, 2026
bcc671f
Cache known GCP firmware indexed by MRTD
ameba23 Jun 16, 2026
ef5908d
Rm cache prewarm
ameba23 Jun 24, 2026
52579b8
Fmt
ameba23 Jun 24, 2026
bbabfff
Avoid api breaking change
ameba23 Jun 24, 2026
7d3f3ce
Rm unused test asset
ameba23 Jun 24, 2026
b61da02
Fixes following revert breaking api change
ameba23 Jun 24, 2026
6ae5aeb
Fmt
ameba23 Jun 24, 2026
fb070c8
Merge pull request #57 from flashbots/peg/gcp-known-firmware-cache
ameba23 Jun 24, 2026
870de13
Merge main
ameba23 Jun 24, 2026
4f53f7d
Fmt
ameba23 Jun 24, 2026
e99e89a
Add test demonstrating portable measurement policy with observed data…
ameba23 Jun 30, 2026
c78d660
Add test demonstrating portable measurement policy with observed data…
ameba23 Jun 30, 2026
bab2de8
Quote generator should not assume tdx-gcp
ameba23 Jun 30, 2026
59dd749
Switch to using pinned commit from head of main of attest repo now th…
ameba23 Jul 2, 2026
f002fa6
Never use placeholder platform metadata in production - drop support …
ameba23 Jul 2, 2026
bd68d39
Warn and bail when trying to match non-gcp measurements with portable…
ameba23 Jul 2, 2026
2024343
Update measurements JSON format and cover the new format in tests
ameba23 Jul 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
354 changes: 345 additions & 9 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/attestation-provider-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub async fn attestation_provider_client(
.await?;

let remote_attestation_message = AttestationExchangeMessage::decode(&mut &response[..])?;
let remote_attestation_type = remote_attestation_message.attestation_type;
let remote_attestation_type = remote_attestation_message.attestation_type();

println!("Remote attestation type: {remote_attestation_type}");

Expand Down
2 changes: 2 additions & 0 deletions crates/attestation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pccs = { workspace = true }
mock-tdx = { workspace = true, optional = true }
tokio = { workspace = true, features = ["fs"] }
tokio-rustls = { workspace = true, default-features = false }
attest-types = { git = "https://github.com/easy-tee/attest.git", rev = "a64f147362b8948e2288015e476c40d04b11b661" }
attest-measure = {git = "https://github.com/easy-tee/attest.git", rev = "a64f147362b8948e2288015e476c40d04b11b661" }

anyhow = "1.0.100"
pem-rfc7468 = { version = "0.7.0", features = ["std"] }
Expand Down
11 changes: 6 additions & 5 deletions crates/attestation/src/azure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ mod tests {
/// Verify a complete observed Azure attestation payload that includes
/// AK intermediates fetched from the leaf certificate's AIA URLs.
#[tokio::test]
async fn test_verify() {
async fn test_verify_attestation_with_ak_intermediates() {
// generated using [capture_azure_fixture] above.
let attestation_bytes: &'static [u8] =
include_bytes!("../../test-assets/azure-tdx-with-ak-intermediates-1780922561.yaml");
Expand Down Expand Up @@ -850,14 +850,15 @@ mod tests {
#[tokio::test]
async fn test_verify_fails_on_input_mismatch() {
let attestation_bytes: &'static [u8] =
include_bytes!("../../test-assets/azure-tdx-1764662251380464271.yaml");
let now = 1771423480;
include_bytes!("../../test-assets/azure-tdx-with-ak-intermediates-1780922561.yaml");
let now = 1_780_922_561;

let mut expected_input_data = input_data_from_attestation(attestation_bytes);
expected_input_data[63] ^= 0x01;

let collateral_bytes: &'static [u8] =
include_bytes!("../../test-assets/azure-collateral02.yaml");
let collateral_bytes: &'static [u8] = include_bytes!(
"../../test-assets/azure-collateral-with-ak-intermediates-1780922561.yaml"
);
let collateral = serde_saphyr::from_slice(collateral_bytes).unwrap();
let attestation_json = serde_json::to_vec(
&serde_saphyr::from_slice::<AttestationDocument>(attestation_bytes).unwrap(),
Expand Down
18 changes: 7 additions & 11 deletions crates/attestation/src/dcap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,14 @@ pub fn verify_dcap_attestation_sync(

/// Create a mock quote for testing on non-confidential hardware
#[cfg(any(test, feature = "mock"))]
fn generate_quote(input: [u8; 64]) -> Result<Vec<u8>, tdx_attest::TdxAttestError> {
generate_mock_tdx_quote(input).map_err(|error| {
tdx_attest::TdxAttestError::QuoteFailure(format!("mock-tdx quote generation: {error}"))
})
fn generate_quote(input: [u8; 64]) -> Result<Vec<u8>, AttestationError> {
generate_mock_tdx_quote(input).map_err(|error| AttestationError::Mock(format!("{error}")))
}

/// Create a quote
#[cfg(not(any(test, feature = "mock")))]
fn generate_quote(input: [u8; 64]) -> Result<Vec<u8>, tdx_attest::TdxAttestError> {
tdx_attest::get_quote(&input)
fn generate_quote(input: [u8; 64]) -> Result<Vec<u8>, AttestationError> {
Ok(tdx_attest::get_quote(&input)?)
}

/// Given a [Report] get the input data regardless of report type
Expand Down Expand Up @@ -363,7 +361,7 @@ mod tests {
.unwrap();

assert_eq!(async_measurements, sync_measurements);
measurement_policy.check_measurement(&async_measurements).unwrap();
measurement_policy.check_measurement(&async_measurements, None).unwrap();
}

// This specifically tests a quote which has outdated TCB level from Azure
Expand Down Expand Up @@ -407,12 +405,10 @@ mod tests {
.unwrap();
let pccs = Pccs::new(Some(mock_pcs.base_url.clone()));
let expected_input_data = [0xA5; 64];
let attestation_bytes = create_dcap_attestation(expected_input_data).unwrap();
let quote = create_dcap_attestation(expected_input_data).unwrap();

let measurements =
verify_dcap_attestation(attestation_bytes, expected_input_data, Some(pccs))
.await
.unwrap();
verify_dcap_attestation(quote, expected_input_data, Some(pccs)).await.unwrap();

assert_eq!(measurements, crate::measurements::mock_dcap_measurements());
assert_eq!(mock_pcs.tcb_call_count(), 1);
Expand Down
166 changes: 166 additions & 0 deletions crates/attestation/src/gcp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use std::{
collections::HashMap,
sync::{Arc, RwLock},
};

use attest_measure::dcap::DcapFirmware;
use thiserror::Error;

/// Maps MRTD values to GCP firmware to avoid re-fetching on subsequent
/// verification
#[derive(Clone, Debug, Default)]
pub(crate) struct GcpFirmwareCache {
cache: Arc<RwLock<HashMap<[u8; 48], attest_measure::dcap::DcapFirmware>>>,
}

impl GcpFirmwareCache {
pub(crate) fn new() -> Self {
Self { cache: Default::default() }
}

/// Retrieve firmware from cache or fetch if not present
pub(crate) fn get_or_fetch(
&self,
mrtd: [u8; 48],
) -> Result<DcapFirmware, GcpFirmwareCacheError> {
if let Some(firmware) =
self.cache.read().map_err(|_| GcpFirmwareCacheError::CacheLock)?.get(&mrtd).cloned()
{
return Ok(firmware);
}

let firmware = DcapFirmware::from_google(mrtd)?;
self.cache
.write()
.map_err(|_| GcpFirmwareCacheError::CacheLock)?
.insert(mrtd, firmware.clone());
Ok(firmware)
}
}

#[derive(Debug, Error)]
pub(crate) enum GcpFirmwareCacheError {
#[error("Cache lock poisoned")]
CacheLock,
#[error("Firmware fetch: {0}")]
Firmware(#[from] attest_measure::dcap::GoogleError),
}

#[cfg(test)]
mod tests {
use attest_measure::dcap::DcapFirmware;
use attest_types::{AcpiHashes, DcapImageHashes};
use dcap_qvl::quote::Quote;

use crate::{
PlatformMetadata,
dcap::{get_quote_input_data, verify_dcap_attestation_with_given_timestamp},
gcp::GcpFirmwareCache,
measurements::{ExpectedMeasurements, MeasurementPolicy, MeasurementRecord},
};

/// Timestamp used with test fixture
const GCP_TDX_PORTABLE_FIXTURE_TIMESTAMP: u64 = 1_782_809_233;

/// Create a firmware cache with given firmware loaded
fn create_cache_with_firmware(firmware: DcapFirmware) -> GcpFirmwareCache {
let cache = GcpFirmwareCache::new();
cache.cache.write().unwrap().insert(firmware.mrtd, firmware);
cache
}

fn decode_dcap_hash(input: &str) -> [u8; 48] {
hex::decode(input).unwrap().try_into().unwrap()
}

/// Image hashes associated with test fixture
fn gcp_portable_image_hashes() -> DcapImageHashes {
DcapImageHashes {
uki_authenticode: decode_dcap_hash(
"82500f977e16a1e3fd47db792ac9c9fdd69caa73d8e719fe4489416355f23f5d0863ad796febfc1241bc3e868c3649a6",
),
kernel_authenticode: decode_dcap_hash(
"b2a6076ae199d325e553a5102cf1f4a18b5e67e36b33261ef20352052199ec5853b5133c0231b16f1198bb086f1cbfac",
),
cmdline_hash: decode_dcap_hash(
"e03b89abf354a38976537b7a9138fd312e4cbf73b61eebc44086491701b1d167b9f6cb97a922325866c93e0834723d87",
),
initrd_hash: decode_dcap_hash(
"99251a9997f552ce98364e3f7311ca47471e299b6fdb31226d738a10577959ab741cc2e7b8c268236153de568265d3f2",
),
gpt_disk_guid_hash: decode_dcap_hash(
"488fa3f08aae01c1a46b497319e8a7d3b7335c9ff4f4d7fe6a3dd62c844b03de22157c0303be58f10e3152687778e68d",
),
}
}

/// Platform metadata associated with test fixture
fn gcp_portable_platform_metadata() -> PlatformMetadata {
PlatformMetadata {
attestation_type: attest_types::AttestationType::GcpTdx,
ram_bytes: 17_179_869_184,
num_disks: 1,
acpi: Some(AcpiHashes {
loader: [
246, 12, 53, 229, 59, 178, 27, 70, 117, 207, 168, 219, 49, 14, 200, 142, 56,
205, 54, 157, 141, 70, 58, 205, 222, 129, 81, 34, 250, 139, 137, 59, 136, 150,
165, 120, 59, 83, 136, 86, 105, 62, 215, 100, 93, 219, 137, 126,
],
rsdp: [
80, 157, 207, 225, 11, 235, 93, 71, 12, 64, 242, 94, 48, 137, 83, 112, 148,
136, 49, 185, 207, 121, 219, 21, 217, 119, 231, 187, 168, 235, 66, 247, 32, 2,
18, 7, 26, 216, 177, 157, 96, 17, 117, 151, 121, 236, 237, 90,
],
tables: [
11, 176, 175, 160, 8, 135, 59, 220, 32, 222, 224, 247, 65, 218, 120, 150, 194,
191, 238, 233, 74, 229, 46, 155, 219, 249, 75, 200, 124, 50, 208, 74, 75, 31,
29, 130, 68, 144, 241, 218, 229, 116, 255, 109, 78, 75, 176, 179,
],
}),
}
}

#[tokio::test]
async fn test_gcp_tdx_portable_policy_with_stored_collateral() {
let attestation_bytes: &'static [u8] =
include_bytes!("../test-assets/gcp-tdx-1782809233226668671");
let collateral_bytes: &'static [u8] =
include_bytes!("../test-assets/gcp-tdx-collateral-1782809233226668671.yaml");
let firmware_bytes: &'static [u8] =
include_bytes!("../test-assets/gcp-tdx-firmware-1782809233226668671.yaml");

let expected_input_data = {
let quote = Quote::parse(attestation_bytes).unwrap();
get_quote_input_data(quote.report)
};

let collateral = serde_saphyr::from_slice(collateral_bytes).unwrap();
let firmware = serde_saphyr::from_slice(firmware_bytes).unwrap();
let measurements = verify_dcap_attestation_with_given_timestamp(
attestation_bytes.to_vec(),
expected_input_data,
None,
Some(collateral),
GCP_TDX_PORTABLE_FIXTURE_TIMESTAMP,
false,
)
.await
.unwrap();

let measurement_policy = MeasurementPolicy {
accepted_measurements: vec![MeasurementRecord {
measurement_id: "gcp-tdx-portable-image-hashes".to_string(),
measurements: ExpectedMeasurements::Image(gcp_portable_image_hashes()),
}],
};
let gcp_firmware_cache = create_cache_with_firmware(firmware);

measurement_policy
.check_measurement_with_gcp_cache(
&measurements,
Some(gcp_portable_platform_metadata()),
Some(&gcp_firmware_cache),
)
.unwrap();
}
}
Loading
Loading