From 1127fb7dc646ae8deba463a0ebcc1dcc70b3f7b4 Mon Sep 17 00:00:00 2001 From: Larry Dewey Date: Fri, 26 Jul 2024 09:27:20 -0500 Subject: [PATCH] verifier: Use the VirTEE CA Chain This introduces changes into the code to utilize the cert chain provided by the VirTEE sev crate, pushing resposibility for upkeep and maintainership back to the library itself. This also reduces duplication of code and expands functionality for future changes. Signed-off-by: Larry Dewey --- Cargo.lock | 12 ++- Cargo.toml | 13 +++- attestation-service/Makefile | 1 - attestation-service/verifier/Cargo.toml | 45 +++++++---- .../verifier/src/az_snp_vtpm/mod.rs | 23 +++--- attestation-service/verifier/src/lib.rs | 4 +- .../verifier/src/sgx/claims.rs | 1 + attestation-service/verifier/src/snp/mod.rs | 77 ++++++++++--------- .../verifier/src/tdx/eventlog.rs | 1 + 9 files changed, 101 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be3e4999de..54b41e1eeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -856,6 +856,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" +[[package]] +name = "bitfield" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c821a6e124197eb56d907ccc2188eab1038fb919c914f47976e64dd8dbc855d1" + [[package]] name = "bitflags" version = "1.3.2" @@ -4669,12 +4675,10 @@ dependencies = [ [[package]] name = "sev" version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2890179f8ef689340f441ba05f0b268bc14f672ae4b36d629cc2266d0d747ab" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bincode", - "bitfield 0.13.2", + "bitfield 0.15.0", "bitflags 1.3.2", "byteorder", "codicon", diff --git a/Cargo.toml b/Cargo.toml index c50b07651c..f396cd2ece 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,11 @@ kbs-types = "0.6.0" jsonwebtoken = { version = "9", default-features = false } log = "0.4.17" prost = "0.11.0" -regorus = { version = "0.1.5", default-features = false, features = ["regex", "base64", "time"] } +regorus = { version = "0.1.5", default-features = false, features = [ + "regex", + "base64", + "time", +] } rstest = "0.18.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.89" @@ -44,7 +48,10 @@ sha2 = "0.10" shadow-rs = "0.19.0" strum = { version = "0.25", features = ["derive"] } thiserror = "1.0" -tokio = { version = "1.23.0", features = ["full"] } +tokio = { version = "1.23.0", features = ["full"], default-features = false } tempfile = "3.4.0" tonic = "0.8.1" -tonic-build = "0.8.0" \ No newline at end of file +tonic-build = "0.8.0" + +[patch.crates-io] +sev = { version = "3.1.1", path = "/home/larry/git/larrydewey/virtee-sev" } diff --git a/attestation-service/Makefile b/attestation-service/Makefile index 60e285b12f..e5714a44e7 100644 --- a/attestation-service/Makefile +++ b/attestation-service/Makefile @@ -23,7 +23,6 @@ grpc-as: restful-as: cargo build --bin restful-as $(release) --features restful-bin - install: for bin_name in $(BIN_NAMES); do \ install -D -m0755 $(TARGET_DIR)/$$bin_name $(DESTDIR); \ diff --git a/attestation-service/verifier/Cargo.toml b/attestation-service/verifier/Cargo.toml index 774b10d51b..157aa1eb0c 100644 --- a/attestation-service/verifier/Cargo.toml +++ b/attestation-service/verifier/Cargo.toml @@ -4,31 +4,44 @@ version = "0.1.0" edition = "2021" [features] -default = [ "all-verifier" ] -all-verifier = [ "tdx-verifier", "sgx-verifier", "snp-verifier", "az-snp-vtpm-verifier", "az-tdx-vtpm-verifier", "csv-verifier", "cca-verifier", "se-verifier" ] -tdx-verifier = [ "eventlog-rs", "scroll", "intel-tee-quote-verification-rs" ] -sgx-verifier = [ "scroll", "intel-tee-quote-verification-rs" ] -az-snp-vtpm-verifier = [ "az-snp-vtpm", "sev", "snp-verifier" ] -az-tdx-vtpm-verifier = [ "az-tdx-vtpm", "openssl", "tdx-verifier" ] -snp-verifier = [ "asn1-rs", "openssl", "sev", "x509-parser" ] -csv-verifier = [ "openssl", "csv-rs", "codicon" ] -cca-verifier = [ "ear", "jsonwebtoken", "veraison-apiclient" ] -se-verifier = [ "openssl", "pv", "serde_with", "tokio/sync" ] +default = ["all-verifier"] +all-verifier = [ + "tdx-verifier", + "sgx-verifier", + "snp-verifier", + "az-snp-vtpm-verifier", + "az-tdx-vtpm-verifier", + "csv-verifier", + "cca-verifier", + "se-verifier", +] +tdx-verifier = ["eventlog-rs", "scroll", "intel-tee-quote-verification-rs"] +sgx-verifier = ["scroll", "intel-tee-quote-verification-rs"] +az-snp-vtpm-verifier = ["az-snp-vtpm", "sev", "snp-verifier"] +az-tdx-vtpm-verifier = ["az-tdx-vtpm", "openssl", "tdx-verifier"] +snp-verifier = ["asn1-rs", "openssl", "sev", "x509-parser"] +csv-verifier = ["openssl", "csv-rs", "codicon"] +cca-verifier = ["ear", "jsonwebtoken", "veraison-apiclient"] +se-verifier = ["openssl", "pv", "serde_with", "tokio/sync"] [dependencies] anyhow.workspace = true thiserror.workspace = true asn1-rs = { version = "0.5.1", optional = true } async-trait.workspace = true -az-snp-vtpm = { version = "0.5.3", default-features = false, features = ["verifier"], optional = true } -az-tdx-vtpm = { version = "0.5.3", default-features = false, features = ["verifier"], optional = true } +az-snp-vtpm = { version = "=0.5.3", default-features = false, features = [ + "verifier", +], optional = true } +az-tdx-vtpm = { version = "=0.5.3", default-features = false, features = [ + "verifier", +], optional = true } base64 = "0.21" bincode = "1.3.3" byteorder = "1" cfg-if = "1.0.0" codicon = { version = "3.0", optional = true } # TODO: change it to "0.1", once released. -csv-rs = { git = "https://github.com/openanolis/csv-rs", rev = "b74aa8c", optional = true } +csv-rs = { version = "=0.1.0", git = "https://github.com/openanolis/csv-rs", rev = "b74aa8c", optional = true } eventlog-rs = { version = "0.1.3", optional = true } hex.workspace = true jsonwebkey = "0.3.5" @@ -37,12 +50,14 @@ kbs-types.workspace = true log.workspace = true openssl = { version = "0.10.55", optional = true } pv = { version = "0.10.0", package = "s390_pv", optional = true } -scroll = { version = "0.11.0", default-features = false, features = ["derive"], optional = true } +scroll = { version = "0.11.0", default-features = false, features = [ + "derive", +], optional = true } serde.workspace = true serde_json.workspace = true serde_with = { workspace = true, optional = true } sev = { version = "3.1.1", features = ["openssl", "snp"], optional = true } -tokio = { workspace = true, optional = true, default-features = false } +tokio = { workspace = true, optional = true } intel-tee-quote-verification-rs = { git = "https://github.com/intel/SGXDataCenterAttestationPrimitives", tag = "DCAP_1.21", optional = true } strum.workspace = true veraison-apiclient = { git = "https://github.com/chendave/rust-apiclient", branch = "token", optional = true } diff --git a/attestation-service/verifier/src/az_snp_vtpm/mod.rs b/attestation-service/verifier/src/az_snp_vtpm/mod.rs index a1cced4cda..52557b7ce5 100644 --- a/attestation-service/verifier/src/az_snp_vtpm/mod.rs +++ b/attestation-service/verifier/src/az_snp_vtpm/mod.rs @@ -4,9 +4,7 @@ // use super::{TeeEvidenceParsedClaim, Verifier}; -use crate::snp::{ - load_milan_cert_chain, parse_tee_evidence, verify_report_signature, VendorCertificates, -}; +use crate::snp::{load_milan_cert_chain, parse_tee_evidence, verify_report_signature}; use crate::{InitDataHash, ReportData}; use anyhow::{bail, Context, Result}; use async_trait::async_trait; @@ -19,6 +17,7 @@ use log::{debug, warn}; use openssl::pkey::PKey; use serde::{Deserialize, Serialize}; use serde_json::Value; +use sev::certs::snp::Chain; use sev::firmware::host::{CertTableEntry, CertType}; use thiserror::Error; @@ -32,7 +31,7 @@ struct Evidence { } pub struct AzSnpVtpm { - vendor_certs: VendorCertificates, + vendor_certs: Chain, } #[derive(Error, Debug)] @@ -54,12 +53,10 @@ pub enum CertError { } impl AzSnpVtpm { - pub fn new() -> Result { - let Result::Ok(vendor_certs) = load_milan_cert_chain() else { - return Err(CertError::LoadMilanCert); - }; - let vendor_certs = vendor_certs.clone(); - Ok(Self { vendor_certs }) + pub fn new() -> Self { + Self { + vendor_certs: load_milan_cert_chain().clone(), + } } } @@ -171,7 +168,7 @@ fn verify_report_data( fn verify_snp_report( snp_report: &AttestationReport, vcek: &Vcek, - vendor_certs: &VendorCertificates, + vendor_certs: &Chain, ) -> Result<(), CertError> { let vcek_data = vcek.0.to_der().context("Failed to get raw VCEK data")?; let cert_chain = [CertTableEntry::new(CertType::VCEK, vcek_data)]; @@ -199,7 +196,7 @@ mod tests { let hcl_report = HclReport::new(REPORT.to_vec()).unwrap(); let snp_report = hcl_report.try_into().unwrap(); let vcek = Vcek::from_pem(include_str!("../../test_data/az-snp-vtpm/vcek.pem")).unwrap(); - let vendor_certs = load_milan_cert_chain().as_ref().unwrap(); + let vendor_certs = load_milan_cert_chain(); verify_snp_report(&snp_report, &vcek, vendor_certs).unwrap(); } @@ -211,7 +208,7 @@ mod tests { let hcl_report = HclReport::new(wrong_report.to_vec()).unwrap(); let snp_report = hcl_report.try_into().unwrap(); let vcek = Vcek::from_pem(include_str!("../../test_data/az-snp-vtpm/vcek.pem")).unwrap(); - let vendor_certs = load_milan_cert_chain().as_ref().unwrap(); + let vendor_certs = load_milan_cert_chain(); assert_eq!( verify_snp_report(&snp_report, &vcek, vendor_certs) .unwrap_err() diff --git a/attestation-service/verifier/src/lib.rs b/attestation-service/verifier/src/lib.rs index f0e2e7a289..0829430e55 100644 --- a/attestation-service/verifier/src/lib.rs +++ b/attestation-service/verifier/src/lib.rs @@ -37,7 +37,7 @@ pub fn to_verifier(tee: &Tee) -> Result> { Tee::AzSnpVtpm => { cfg_if::cfg_if! { if #[cfg(feature = "az-snp-vtpm-verifier")] { - let verifier = az_snp_vtpm::AzSnpVtpm::new()?; + let verifier = az_snp_vtpm::AzSnpVtpm::new(); Ok(Box::new(verifier) as Box) } else { bail!("feature `az-snp-vtpm-verifier` is not enabled for `verifier` crate.") @@ -65,7 +65,7 @@ pub fn to_verifier(tee: &Tee) -> Result> { Tee::Snp => { cfg_if::cfg_if! { if #[cfg(feature = "snp-verifier")] { - let verifier = snp::Snp::new()?; + let verifier = snp::Snp::new(); Ok(Box::new(verifier) as Box) } else { bail!("feature `snp-verifier` is not enabled for `verifier` crate.") diff --git a/attestation-service/verifier/src/sgx/claims.rs b/attestation-service/verifier/src/sgx/claims.rs index c47fa448ab..7abd6b023a 100644 --- a/attestation-service/verifier/src/sgx/claims.rs +++ b/attestation-service/verifier/src/sgx/claims.rs @@ -113,6 +113,7 @@ pub fn generate_parsed_claims(quote: sgx_quote3_t) -> Result { pub descriptor: [u8; 16], pub info_length: u32, diff --git a/attestation-service/verifier/src/snp/mod.rs b/attestation-service/verifier/src/snp/mod.rs index b5e4276b71..eff06868c2 100644 --- a/attestation-service/verifier/src/snp/mod.rs +++ b/attestation-service/verifier/src/snp/mod.rs @@ -15,6 +15,7 @@ use openssl::{ x509::{self, X509}, }; use serde_json::json; +use sev::certs::snp::{ca::Chain as CaChain, Certificate, Chain}; use sev::firmware::guest::AttestationReport; use sev::firmware::host::{CertTableEntry, CertType}; use std::sync::OnceLock; @@ -34,43 +35,43 @@ const LOADER_SPL_OID: Oid<'static> = oid!(1.3.6 .1 .4 .1 .3704 .1 .3 .1); #[derive(Debug)] pub struct Snp { - vendor_certs: VendorCertificates, + vendor_certs: Chain, } -pub(crate) fn load_milan_cert_chain() -> &'static Result { - static MILAN_CERT_CHAIN: OnceLock> = OnceLock::new(); - MILAN_CERT_CHAIN.get_or_init(|| { - let certs = X509::stack_from_pem(include_bytes!("milan_ask_ark_asvk.pem"))?; - if certs.len() != 3 { - bail!("Malformed Milan ASK/ARK/ASVK"); +fn read_certs_from_file() -> Vec { + match X509::stack_from_pem(include_bytes!("milan_ask_ark_asvk.pem")) { + Result::Ok(certs) => { + if certs.len() != 3 { + panic!("Malformed Milan ASK/ARK/ASVK"); + } + return certs; } + Result::Err(err) => panic!("Error reading certificates file: {err}"), + } +} - let vendor_certs = VendorCertificates { - ask: certs[0].clone(), - ark: certs[1].clone(), - asvk: certs[2].clone(), - }; - Ok(vendor_certs) +pub(crate) fn load_milan_cert_chain() -> &'static Chain { + static MILAN_CERT_CHAIN: OnceLock = OnceLock::new(); + MILAN_CERT_CHAIN.get_or_init(|| { + let certs = read_certs_from_file(); + Chain { + ca: CaChain { + ark: Certificate::from(certs[1].clone()), + ask: Certificate::from(certs[0].clone()), + }, + vek: Certificate::from(certs[2].clone()), + } }) } impl Snp { - pub fn new() -> Result { - let Result::Ok(vendor_certs) = load_milan_cert_chain() else { - bail!("Failed to load Milan cert chain"); - }; - let vendor_certs = vendor_certs.clone(); - Ok(Self { vendor_certs }) + pub fn new() -> Self { + Self { + vendor_certs: load_milan_cert_chain().clone(), + } } } -#[derive(Clone, Debug)] -pub(crate) struct VendorCertificates { - ask: X509, - ark: X509, - asvk: X509, -} - #[async_trait] impl Verifier for Snp { async fn evaluate( @@ -165,10 +166,10 @@ fn get_oid_int(cert: &x509_parser::certificate::TbsCertificate, oid: Oid) -> Res pub(crate) fn verify_report_signature( report: &AttestationReport, cert_chain: &[CertTableEntry], - vendor_certs: &VendorCertificates, + vendor_certs: &Chain, ) -> Result<()> { // check cert chain - let VendorCertificates { ask, ark, asvk } = vendor_certs; + let (ask, ark, asvk): (&X509, &X509, &X509) = vendor_certs.into(); // verify VCEK or VLEK cert chain // the key can be either VCEK or VLEK @@ -322,7 +323,7 @@ mod tests { #[test] fn check_milan_certificates() { - let VendorCertificates { ask, ark, asvk } = load_milan_cert_chain().as_ref().unwrap(); + let (ask, ark, asvk) = load_milan_cert_chain().into(); assert_eq!(get_common_name(ark).unwrap(), "ARK-Milan"); assert_eq!(get_common_name(ask).unwrap(), "SEV-Milan"); assert_eq!(get_common_name(asvk).unwrap(), "SEV-VLEK-Milan"); @@ -393,7 +394,7 @@ mod tests { #[test] fn check_vcek_signature_verification() { let cert_table = vec![CertTableEntry::new(CertType::VCEK, VCEK.to_vec())]; - let VendorCertificates { ask, ark, asvk } = load_milan_cert_chain().as_ref().unwrap(); + let (ask, ark, asvk) = load_milan_cert_chain().into(); verify_cert_chain(&cert_table, ask, ark, asvk).unwrap(); } @@ -406,14 +407,14 @@ mod tests { X509::from_der(&vcek).expect("failed to parse der"); let cert_table = vec![CertTableEntry::new(CertType::VCEK, vcek.to_vec())]; - let VendorCertificates { ask, ark, asvk } = load_milan_cert_chain().as_ref().unwrap(); + let (ask, ark, asvk) = load_milan_cert_chain().into(); verify_cert_chain(&cert_table, ask, ark, asvk).unwrap_err(); } #[test] fn check_vlek_signature_verification() { let cert_table = vec![CertTableEntry::new(CertType::VLEK, VLEK.to_vec())]; - let VendorCertificates { ask, ark, asvk } = load_milan_cert_chain().as_ref().unwrap(); + let (ask, ark, asvk) = load_milan_cert_chain().into(); verify_cert_chain(&cert_table, ask, ark, asvk).unwrap(); } @@ -426,14 +427,14 @@ mod tests { X509::from_der(&vlek).expect("failed to parse der"); let cert_table = vec![CertTableEntry::new(CertType::VLEK, vlek.to_vec())]; - let VendorCertificates { ask, ark, asvk } = load_milan_cert_chain().as_ref().unwrap(); + let (ask, ark, asvk) = load_milan_cert_chain().into(); verify_cert_chain(&cert_table, ask, ark, asvk).unwrap_err(); } #[test] fn check_milan_chain_signature_failure() { let cert_table = vec![CertTableEntry::new(CertType::VCEK, VCEK.to_vec())]; - let VendorCertificates { ask, ark, asvk } = load_milan_cert_chain().as_ref().unwrap(); + let (ask, ark, asvk) = load_milan_cert_chain().into(); // toggle ark <=> ask verify_cert_chain(&cert_table, ark, ask, asvk).unwrap_err(); @@ -444,7 +445,7 @@ mod tests { let attestation_report = bincode::deserialize::(VCEK_REPORT.as_slice()).unwrap(); let cert_chain = vec![CertTableEntry::new(CertType::VCEK, VCEK.to_vec())]; - let vendor_certs = load_milan_cert_chain().as_ref().unwrap(); + let vendor_certs = load_milan_cert_chain(); verify_report_signature(&attestation_report, &cert_chain, vendor_certs).unwrap(); } @@ -453,7 +454,7 @@ mod tests { let attestation_report = bincode::deserialize::(VLEK_REPORT.as_slice()).unwrap(); let cert_chain = vec![CertTableEntry::new(CertType::VLEK, VLEK.to_vec())]; - let vendor_certs = load_milan_cert_chain().as_ref().unwrap(); + let vendor_certs = load_milan_cert_chain(); verify_report_signature(&attestation_report, &cert_chain, vendor_certs).unwrap(); } @@ -466,7 +467,7 @@ mod tests { let attestation_report = bincode::deserialize::(&bytes).unwrap(); let cert_chain = vec![CertTableEntry::new(CertType::VCEK, VCEK.to_vec())]; - let vendor_certs = load_milan_cert_chain().as_ref().unwrap(); + let vendor_certs = load_milan_cert_chain(); verify_report_signature(&attestation_report, &cert_chain, vendor_certs).unwrap_err(); } @@ -479,7 +480,7 @@ mod tests { let attestation_report = bincode::deserialize::(&bytes).unwrap(); let cert_chain = vec![CertTableEntry::new(CertType::VLEK, VLEK.to_vec())]; - let vendor_certs = load_milan_cert_chain().as_ref().unwrap(); + let vendor_certs = load_milan_cert_chain(); verify_report_signature(&attestation_report, &cert_chain, vendor_certs).unwrap_err(); } } diff --git a/attestation-service/verifier/src/tdx/eventlog.rs b/attestation-service/verifier/src/tdx/eventlog.rs index fb9fdca333..265e432415 100644 --- a/attestation-service/verifier/src/tdx/eventlog.rs +++ b/attestation-service/verifier/src/tdx/eventlog.rs @@ -127,6 +127,7 @@ impl CcEventLog { /// Defined in TCG PC Client Platform Firmware Profile Specification section /// 'UEFI_PLATFORM_FIRMWARE_BLOB Structure Definition' +#[allow(dead_code)] pub struct ParsedUefiPlatformFirmwareBlob2 { pub desc_len: u8, pub desc: Vec,