diff --git a/Cargo.lock b/Cargo.lock index 3083b90..f30b0c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -503,6 +503,38 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "attestation" +version = "0.0.1" +dependencies = [ + "anyhow", + "az-tdx-vtpm", + "base64 0.22.1", + "configfs-tsm", + "dcap-qvl", + "hex", + "http", + "num-bigint", + "once_cell", + "openssl", + "parity-scale-codec", + "pem-rfc7468", + "rand_core 0.6.4", + "reqwest", + "rustls-webpki", + "serde", + "serde_json", + "tdx-quote", + "tempfile", + "thiserror 2.0.17", + "time", + "tokio", + "tokio-rustls", + "tracing", + "tss-esapi", + "x509-parser", +] + [[package]] name = "attestation-provider-server" version = "0.1.0" @@ -525,40 +557,24 @@ version = "0.0.1" dependencies = [ "alloy-rpc-client", "alloy-transport-http", - "anyhow", - "az-tdx-vtpm", - "base64 0.22.1", + "attestation", "bytes", - "configfs-tsm", - "dcap-qvl", "futures-util", - "hex", "http", "http-body-util", "hyper", "hyper-util", - "num-bigint", - "once_cell", - "openssl", "parity-scale-codec", - "pem-rfc7468", - "rand_core 0.6.4", "rcgen", - "reqwest", - "rustls-webpki", - "serde", "serde_json", "sha2", - "tdx-quote", "tempfile", "thiserror 2.0.17", - "time", "tokio", "tokio-rustls", "tokio-tungstenite", "tower-service", "tracing", - "tss-esapi", "url", "webpki-roots", "x509-parser", diff --git a/Cargo.toml b/Cargo.toml index 900260f..305efca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [".", "attestation-provider-server", "attested-tls"] +members = [".", "attestation-provider-server", "attested-tls", "attestation"] [package] name = "attested-tls-proxy" diff --git a/attestation/Cargo.toml b/attestation/Cargo.toml new file mode 100644 index 0000000..2b6dc8b --- /dev/null +++ b/attestation/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "attestation" +version = "0.0.1" +edition = "2024" +license = "MIT" +description = "Attestation generation and verification, and measurement policy handling" +repository = "https://github.com/flashbots/attested-tls-proxy" +keywords = ["attestation", "CVM", "TDX"] + +[dependencies] +tokio = { version = "1.48.0", features = ["fs"] } +tokio-rustls = { version = "0.26.4", default-features = false, features = [ + "ring", +] } +x509-parser = "0.18.0" +thiserror = "2.0.17" +anyhow = "1.0.100" +pem-rfc7468 = { version = "0.7.0", features = ["std"] } +configfs-tsm = "0.0.2" +rand_core = { version = "0.6.4", features = ["getrandom"] } +dcap-qvl = { git = "https://github.com/flashbots/dcap-qvl.git", branch = "peg/azure-outdated-tcp-override", features = ["danger-allow-tcb-override"] } +hex = "0.4.3" +http = "1.3.1" +serde_json = "1.0.145" +serde = "1.0.228" +base64 = "0.22.1" +reqwest = { version = "0.12.23", default-features = false, features = [ + "rustls-tls-webpki-roots-no-provider", +] } +tracing = "0.1.41" +parity-scale-codec = "3.7.5" +openssl = "0.10.75" +num-bigint = "0.4.6" +webpki = { package = "rustls-webpki", version = "0.103.8" } +time = "0.3.47" +once_cell = "1.21.3" + +# Used for azure vTPM attestation support +az-tdx-vtpm = { version = "0.7.4", optional = true } +tss-esapi = { version = "7.6.0", optional = true } + +# Used by test helpers +tdx-quote = { version = "0.0.5", features = ["mock"], optional = true } + +[dev-dependencies] +tempfile = "3.23.0" +tdx-quote = { version = "0.0.5", features = ["mock"] } + +[features] +default = [] + +# Adds support for Microsoft Azure attestation generation and verification +azure = ["tss-esapi", "az-tdx-vtpm"] + +# Exposes helper functions for testing - do not enable in production as this allows dangerous configuration +test-helpers = [] + +# Allows mock quotes used in tests +mock = ["tdx-quote"] diff --git a/attested-tls/src/attestation/azure/ak_certificate.rs b/attestation/src/azure/ak_certificate.rs similarity index 99% rename from attested-tls/src/attestation/azure/ak_certificate.rs rename to attestation/src/azure/ak_certificate.rs index ae2bb55..0e4d258 100644 --- a/attested-tls/src/attestation/azure/ak_certificate.rs +++ b/attestation/src/azure/ak_certificate.rs @@ -1,5 +1,5 @@ //! Generation and verification of AK certificates from the vTPM -use crate::attestation::azure::{MaaError, nv_index}; +use crate::azure::{MaaError, nv_index}; use once_cell::sync::Lazy; use std::time::Duration; use tokio_rustls::rustls::pki_types::{CertificateDer, TrustAnchor, UnixTime}; diff --git a/attested-tls/src/attestation/azure/mod.rs b/attestation/src/azure/mod.rs similarity index 95% rename from attested-tls/src/attestation/azure/mod.rs rename to attestation/src/azure/mod.rs index 63486b1..78e45f6 100644 --- a/attested-tls/src/attestation/azure/mod.rs +++ b/attestation/src/azure/mod.rs @@ -12,9 +12,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use x509_parser::prelude::*; -use crate::attestation::{ - dcap::verify_dcap_attestation_with_given_timestamp, measurements::MultiMeasurements, -}; +use crate::{dcap::verify_dcap_attestation_with_given_timestamp, measurements::MultiMeasurements}; /// The attestation evidence payload that gets sent over the channel #[derive(Debug, Serialize, Deserialize)] @@ -339,12 +337,12 @@ pub enum MaaError { #[error("HCL runtime claims user-data does not match expected report input data")] ClaimsUserDataInputMismatch, #[error("DCAP verification: {0}")] - DcapVerification(#[from] crate::attestation::dcap::DcapVerificationError), + DcapVerification(#[from] crate::dcap::DcapVerificationError), } #[cfg(test)] mod tests { - use crate::attestation::measurements::MeasurementPolicy; + use crate::measurements::MeasurementPolicy; use super::*; @@ -363,7 +361,7 @@ mod tests { #[tokio::test] async fn test_decode_hcl() { // From cvm-reverse-proxy/internal/attestation/azure/tdx/testdata/hclreport.bin - let hcl_bytes: &'static [u8] = include_bytes!("../../../test-assets/hclreport.bin"); + let hcl_bytes: &'static [u8] = include_bytes!("../../test-assets/hclreport.bin"); let hcl_report = hcl::HclReport::new(hcl_bytes.to_vec()).unwrap(); let hcl_var_data = hcl_report.var_data(); @@ -382,7 +380,7 @@ mod tests { #[tokio::test] async fn test_verify() { let attestation_bytes: &'static [u8] = - include_bytes!("../../../test-assets/azure-tdx-1764662251380464271"); + include_bytes!("../../test-assets/azure-tdx-1764662251380464271"); // To avoid this test stopping working when the certificate is no longer valid we pass in a // timestamp @@ -411,7 +409,7 @@ mod tests { .unwrap(); let collateral_bytes: &'static [u8] = - include_bytes!("../../../test-assets/azure-collateral02.json"); + include_bytes!("../../test-assets/azure-collateral02.json"); let collateral = serde_json::from_slice(collateral_bytes).unwrap(); @@ -432,14 +430,14 @@ mod tests { #[tokio::test] async fn test_verify_fails_on_input_mismatch() { let attestation_bytes: &'static [u8] = - include_bytes!("../../../test-assets/azure-tdx-1764662251380464271"); + include_bytes!("../../test-assets/azure-tdx-1764662251380464271"); let now = 1771423480; 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.json"); + include_bytes!("../../test-assets/azure-collateral02.json"); let collateral = serde_json::from_slice(collateral_bytes).unwrap(); let err = verify_azure_attestation_with_given_timestamp( diff --git a/attested-tls/src/attestation/azure/nv_index.rs b/attestation/src/azure/nv_index.rs similarity index 100% rename from attested-tls/src/attestation/azure/nv_index.rs rename to attestation/src/azure/nv_index.rs diff --git a/attested-tls/src/attestation/dcap.rs b/attestation/src/dcap.rs similarity index 95% rename from attested-tls/src/attestation/dcap.rs rename to attestation/src/dcap.rs index 2632557..c1b70c6 100644 --- a/attested-tls/src/attestation/dcap.rs +++ b/attestation/src/dcap.rs @@ -1,5 +1,5 @@ //! Data Center Attestation Primitives (DCAP) evidence generation and verification -use crate::attestation::{AttestationError, measurements::MultiMeasurements}; +use crate::{AttestationError, measurements::MultiMeasurements}; use configfs_tsm::QuoteGenerationError; use dcap_qvl::{ @@ -168,13 +168,13 @@ pub enum DcapVerificationError { #[cfg(test)] mod tests { - use crate::attestation::measurements::MeasurementPolicy; + use crate::measurements::MeasurementPolicy; use super::*; #[tokio::test] async fn test_dcap_verify() { let attestation_bytes: &'static [u8] = - include_bytes!("../../test-assets/dcap-tdx-1766059550570652607"); + include_bytes!("../test-assets/dcap-tdx-1766059550570652607"); // To avoid this test stopping working when the certificate is no longer valid we pass in a // timestamp @@ -199,7 +199,7 @@ mod tests { .unwrap(); let collateral_bytes: &'static [u8] = - include_bytes!("../../test-assets/dcap-quote-collateral-00.json"); + include_bytes!("../test-assets/dcap-quote-collateral-00.json"); let collateral = serde_json::from_slice(collateral_bytes).unwrap(); @@ -226,14 +226,14 @@ mod tests { #[tokio::test] async fn test_dcap_verify_azure_override() { let attestation_bytes: &'static [u8] = - include_bytes!("../../test-assets/azure_failed_dcap_quote_10.bin"); + include_bytes!("../test-assets/azure_failed_dcap_quote_10.bin"); // To avoid this test stopping working when the certificate is no longer valid we pass in a // timestamp let now = 1771414156; let collateral_bytes: &'static [u8] = - include_bytes!("../../test-assets/azure-collateral.json"); + include_bytes!("../test-assets/azure-collateral.json"); let collateral = serde_json::from_slice(collateral_bytes).unwrap(); diff --git a/attested-tls/src/attestation/mod.rs b/attestation/src/lib.rs similarity index 99% rename from attested-tls/src/attestation/mod.rs rename to attestation/src/lib.rs index f017f2b..a6496cb 100644 --- a/attested-tls/src/attestation/mod.rs +++ b/attestation/src/lib.rs @@ -5,6 +5,9 @@ pub mod azure; pub mod dcap; pub mod measurements; +#[cfg(any(test, feature = "test-helpers"))] +pub mod test_helpers; + use measurements::MultiMeasurements; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -16,7 +19,7 @@ use std::{ use thiserror::Error; -use crate::attestation::{dcap::DcapVerificationError, measurements::MeasurementPolicy}; +use crate::{dcap::DcapVerificationError, measurements::MeasurementPolicy}; const GCP_METADATA_API: &str = "http://metadata.google.internal/computeMetadata/v1/project/project-id"; diff --git a/attested-tls/src/attestation/measurements.rs b/attestation/src/measurements.rs similarity index 99% rename from attested-tls/src/attestation/measurements.rs rename to attestation/src/measurements.rs index 9c0300c..798bf47 100644 --- a/attested-tls/src/attestation/measurements.rs +++ b/attestation/src/measurements.rs @@ -1,5 +1,5 @@ //! Measurements and policy for enforcing them when validating a remote attestation -use crate::attestation::{AttestationError, AttestationType, dcap::DcapVerificationError}; +use crate::{AttestationError, AttestationType, dcap::DcapVerificationError}; use std::{collections::HashMap, path::PathBuf}; use std::{fmt, fmt::Formatter}; diff --git a/attestation/src/test_helpers.rs b/attestation/src/test_helpers.rs new file mode 100644 index 0000000..706e08e --- /dev/null +++ b/attestation/src/test_helpers.rs @@ -0,0 +1,13 @@ +use crate::{MultiMeasurements, measurements::DcapMeasurementRegister}; +use std::collections::HashMap; + +/// All-zero measurment values used in some tests +pub fn mock_dcap_measurements() -> MultiMeasurements { + MultiMeasurements::Dcap(HashMap::from([ + (DcapMeasurementRegister::MRTD, [0u8; 48]), + (DcapMeasurementRegister::RTMR0, [0u8; 48]), + (DcapMeasurementRegister::RTMR1, [0u8; 48]), + (DcapMeasurementRegister::RTMR2, [0u8; 48]), + (DcapMeasurementRegister::RTMR3, [0u8; 48]), + ])) +} diff --git a/attested-tls/test-assets/azure-collateral.json b/attestation/test-assets/azure-collateral.json similarity index 100% rename from attested-tls/test-assets/azure-collateral.json rename to attestation/test-assets/azure-collateral.json diff --git a/attested-tls/test-assets/azure-collateral02.json b/attestation/test-assets/azure-collateral02.json similarity index 100% rename from attested-tls/test-assets/azure-collateral02.json rename to attestation/test-assets/azure-collateral02.json diff --git a/attested-tls/test-assets/azure-tdx-1764662251380464271 b/attestation/test-assets/azure-tdx-1764662251380464271 similarity index 100% rename from attested-tls/test-assets/azure-tdx-1764662251380464271 rename to attestation/test-assets/azure-tdx-1764662251380464271 diff --git a/attested-tls/test-assets/azure_failed_dcap_quote_10.bin b/attestation/test-assets/azure_failed_dcap_quote_10.bin similarity index 100% rename from attested-tls/test-assets/azure_failed_dcap_quote_10.bin rename to attestation/test-assets/azure_failed_dcap_quote_10.bin diff --git a/attested-tls/test-assets/dcap-quote-collateral-00.json b/attestation/test-assets/dcap-quote-collateral-00.json similarity index 100% rename from attested-tls/test-assets/dcap-quote-collateral-00.json rename to attestation/test-assets/dcap-quote-collateral-00.json diff --git a/attested-tls/test-assets/dcap-tdx-1766059550570652607 b/attestation/test-assets/dcap-tdx-1766059550570652607 similarity index 100% rename from attested-tls/test-assets/dcap-tdx-1766059550570652607 rename to attestation/test-assets/dcap-tdx-1766059550570652607 diff --git a/attested-tls/test-assets/hclreport.bin b/attestation/test-assets/hclreport.bin similarity index 100% rename from attested-tls/test-assets/hclreport.bin rename to attestation/test-assets/hclreport.bin diff --git a/attested-tls/test-assets/measurements.json b/attestation/test-assets/measurements.json similarity index 100% rename from attested-tls/test-assets/measurements.json rename to attestation/test-assets/measurements.json diff --git a/attested-tls/test-assets/measurements_2.json b/attestation/test-assets/measurements_2.json similarity index 100% rename from attested-tls/test-assets/measurements_2.json rename to attestation/test-assets/measurements_2.json diff --git a/attested-tls/Cargo.toml b/attested-tls/Cargo.toml index 58d3c11..f7a722d 100644 --- a/attested-tls/Cargo.toml +++ b/attested-tls/Cargo.toml @@ -16,30 +16,11 @@ sha2 = "0.10.9" x509-parser = "0.18.0" thiserror = "2.0.17" webpki-roots = "1.0.4" -anyhow = "1.0.100" -pem-rfc7468 = { version = "0.7.0", features = ["std"] } -configfs-tsm = "0.0.2" -rand_core = { version = "0.6.4", features = ["getrandom"] } -dcap-qvl = { git = "https://github.com/flashbots/dcap-qvl.git", branch = "peg/azure-outdated-tcp-override", features = ["danger-allow-tcb-override"] } -hex = "0.4.3" http = "1.3.1" serde_json = "1.0.145" -serde = "1.0.228" -base64 = "0.22.1" -reqwest = { version = "0.12.23", default-features = false, features = [ - "rustls-tls-webpki-roots-no-provider", -] } tracing = "0.1.41" parity-scale-codec = "3.7.5" -openssl = "0.10.75" -num-bigint = "0.4.6" -webpki = { package = "rustls-webpki", version = "0.103.8" } -time = "0.3.47" -once_cell = "1.21.3" - -# Used for azure vTPM attestation support -az-tdx-vtpm = { version = "0.7.4", optional = true } -tss-esapi = { version = "7.6.0", optional = true } +attestation = { path = "../attestation" } # Used for websocket support tokio-tungstenite = { version = "0.28.0", optional = true } @@ -57,18 +38,17 @@ http-body-util = { version = "0.1.3", optional = true } # Used by test helpers rcgen = { version = "0.14.5", optional = true } -tdx-quote = { version = "0.0.5", features = ["mock"], optional = true } [dev-dependencies] rcgen = "0.14.5" tempfile = "3.23.0" -tdx-quote = { version = "0.0.5", features = ["mock"] } +attestation = { path = "../attestation", features = ["test-helpers", "mock"] } [features] default = ["ws", "rpc"] # Adds support for Microsoft Azure attestation generation and verification -azure = ["tss-esapi", "az-tdx-vtpm"] +azure = ["attestation/azure"] # Adds websocket support ws = ["tokio-tungstenite", "futures-util"] @@ -87,6 +67,6 @@ rpc = [ ] # Exposes helper functions for testing - do not enable in production as this allows dangerous configuration -test-helpers = ["rcgen"] +test-helpers = ["rcgen", "attestation/test-helpers"] -mock = ["tdx-quote"] +mock = ["attestation/mock"] diff --git a/attested-tls/src/lib.rs b/attested-tls/src/lib.rs index dbd08a2..7578b3a 100644 --- a/attested-tls/src/lib.rs +++ b/attested-tls/src/lib.rs @@ -1,6 +1,4 @@ //! Attested TLS protocol server and client -pub mod attestation; - #[cfg(feature = "ws")] pub mod websockets; @@ -10,7 +8,9 @@ pub mod attested_rpc; #[cfg(any(test, feature = "test-helpers"))] pub mod test_helpers; -use crate::attestation::{ +pub use attestation; + +use attestation::{ AttestationError, AttestationExchangeMessage, AttestationGenerator, AttestationType, AttestationVerifier, measurements::MultiMeasurements, }; @@ -621,10 +621,9 @@ fn map_alpn_protocols(existing_protocols: Vec>) -> Vec> { #[cfg(test)] mod tests { use super::*; - use crate::{ - attestation::measurements::MeasurementPolicy, - test_helpers::{generate_certificate_chain, generate_tls_config}, - }; + + use crate::test_helpers::{generate_certificate_chain, generate_tls_config}; + use attestation::measurements::MeasurementPolicy; use tokio::net::TcpListener; #[tokio::test] diff --git a/attested-tls/src/test_helpers.rs b/attested-tls/src/test_helpers.rs index 365629e..4cd8ec7 100644 --- a/attested-tls/src/test_helpers.rs +++ b/attested-tls/src/test_helpers.rs @@ -1,15 +1,14 @@ //! Helper functions used in tests -use std::{collections::HashMap, net::IpAddr, sync::Arc}; +use std::{net::IpAddr, sync::Arc}; use tokio_rustls::rustls::{ ClientConfig, RootCertStore, ServerConfig, pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}, server::{WebPkiClientVerifier, danger::ClientCertVerifier}, }; -use crate::{ - SUPPORTED_ALPN_PROTOCOL_VERSIONS, - attestation::measurements::{DcapMeasurementRegister, MultiMeasurements}, -}; +use crate::SUPPORTED_ALPN_PROTOCOL_VERSIONS; + +pub use attestation::test_helpers::mock_dcap_measurements; /// Helper to generate a self-signed certificate for testing pub fn generate_certificate_chain( @@ -127,14 +126,3 @@ fn client_verifier_from_remote_cert( root_store, ) } - -/// All-zero measurment values used in some tests -pub fn mock_dcap_measurements() -> MultiMeasurements { - MultiMeasurements::Dcap(HashMap::from([ - (DcapMeasurementRegister::MRTD, [0u8; 48]), - (DcapMeasurementRegister::RTMR0, [0u8; 48]), - (DcapMeasurementRegister::RTMR1, [0u8; 48]), - (DcapMeasurementRegister::RTMR2, [0u8; 48]), - (DcapMeasurementRegister::RTMR3, [0u8; 48]), - ])) -}