diff --git a/Cargo.lock b/Cargo.lock index 2810adc0..4b8b246e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2728,7 +2728,7 @@ dependencies = [ [[package]] name = "pcs" -version = "0.2.0" +version = "0.3.0" dependencies = [ "anyhow", "b64-ct", diff --git a/intel-sgx/dcap-artifact-retrieval/Cargo.toml b/intel-sgx/dcap-artifact-retrieval/Cargo.toml index 7f6d9cf9..919d97a1 100644 --- a/intel-sgx/dcap-artifact-retrieval/Cargo.toml +++ b/intel-sgx/dcap-artifact-retrieval/Cargo.toml @@ -28,7 +28,7 @@ mbedtls = { version = "0.12.3", features = [ "std", ], default-features = false } num_enum = { version = "0.7", features = ["complex-expressions"] } -pcs = { version = "0.2.0", path = "../pcs" } +pcs = { version = "0.3.0", path = "../pcs" } percent-encoding = "2.1.0" pkix = "0.2.0" quick-error = "1.1.0" @@ -43,7 +43,7 @@ default = ["clap", "reqwest"] [dev-dependencies] yasna = { version = "0.3", features = ["num-bigint", "bit-vec"] } -pcs = { version = "0.2.0", path = "../pcs", features = ["verify"] } +pcs = { version = "0.3.0", path = "../pcs", features = ["verify"] } [build-dependencies] mbedtls = { version = "0.12.3", features = ["ssl", "x509"] } diff --git a/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/intel.rs b/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/intel.rs index b7ccf306..a54fba83 100644 --- a/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/intel.rs +++ b/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/intel.rs @@ -12,8 +12,8 @@ //! - use pcs::{ - CpuSvn, EncPpid, PceId, PceIsvsvn, PckCert, PckCerts, PckCrl, QeId, QeIdentitySigned, TcbInfo, - Unverified, + CpuSvn, EncPpid, Fmspc, PceId, PceIsvsvn, PckCert, PckCerts, PckCrl, QeId, QeIdentitySigned, + TcbInfo, Unverified, }; use rustc_serialize::hex::ToHex; use std::time::Duration; @@ -336,7 +336,7 @@ impl TcbInfoApi { impl<'inp> TcbInfoService<'inp> for TcbInfoApi { fn build_input( &'inp self, - fmspc: &'inp Vec, + fmspc: &'inp Fmspc, ) -> >::Input { TcbInfoIn { api_version: self.api_version.clone(), @@ -353,7 +353,7 @@ impl<'inp> ProvisioningServiceApi<'inp> for TcbInfoApi { fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> { let api_version = input.api_version as u8; - let fmspc = input.fmspc.to_hex(); + let fmspc = input.fmspc.as_bytes().to_hex(); let url = format!( "{}/sgx/certification/v{}/tcb?fmspc={}", INTEL_BASE_URL, api_version, fmspc, @@ -610,6 +610,8 @@ mod tests { #[test] pub fn pck_cached() { for api_version in [PcsVersion::V3, PcsVersion::V4] { + let root_ca = include_bytes!("../../tests/data/root_SGX_CA_der.cert"); + let root_cas = [&root_ca[..]]; let mut intel_builder = IntelProvisioningClientBuilder::new(api_version) .set_retry_timeout(TIME_RETRY_TIMEOUT); if api_version == PcsVersion::V3 { @@ -629,6 +631,7 @@ mod tests { None, ) .unwrap(); + let pck = pck.verify(&root_cas).unwrap(); // The cache should be populated after initial service call { @@ -653,7 +656,15 @@ mod tests { .to_owned() }; - assert_eq!(pck.fmspc().unwrap(), cached_pck.fmspc().unwrap()); + assert_eq!( + pck.fmspc().unwrap(), + cached_pck + .clone() + .verify(&root_cas) + .unwrap() + .fmspc() + .unwrap() + ); assert_eq!(pck.ca_chain(), cached_pck.ca_chain()); } @@ -668,7 +679,15 @@ mod tests { ) .unwrap(); - assert_eq!(pck.fmspc().unwrap(), pck_from_service.fmspc().unwrap()); + assert_eq!( + pck.fmspc().unwrap(), + pck_from_service + .clone() + .verify(&root_cas) + .unwrap() + .fmspc() + .unwrap() + ); assert_eq!(pck.ca_chain(), pck_from_service.ca_chain()); } } diff --git a/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/mod.rs b/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/mod.rs index f2343561..a951e5b6 100644 --- a/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/mod.rs +++ b/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/mod.rs @@ -14,8 +14,8 @@ use std::time::{Duration, SystemTime}; use lru_cache::LruCache; use num_enum::TryFromPrimitive; use pcs::{ - CpuSvn, EncPpid, PceId, PceIsvsvn, PckCert, PckCerts, PckCrl, PckID, QeId, QeIdentitySigned, - TcbInfo, Unverified, + CpuSvn, EncPpid, Fmspc, PceId, PceIsvsvn, PckCert, PckCerts, PckCrl, PckID, QeId, + QeIdentitySigned, TcbInfo, Unverified, }; #[cfg(feature = "reqwest")] use reqwest::blocking::{Client as ReqwestClient, Response as ReqwestResponse}; @@ -224,7 +224,7 @@ pub trait QeIdService<'inp>: #[derive(Hash)] pub struct TcbInfoIn<'i> { pub(crate) api_version: PcsVersion, - pub(crate) fmspc: &'i Vec, + pub(crate) fmspc: &'i Fmspc, } impl WithApiVersion for TcbInfoIn<'_> { @@ -236,10 +236,8 @@ impl WithApiVersion for TcbInfoIn<'_> { pub trait TcbInfoService<'inp>: ProvisioningServiceApi<'inp, Input = TcbInfoIn<'inp>, Output = TcbInfo> { - fn build_input( - &'inp self, - fmspc: &'inp Vec, - ) -> >::Input; + fn build_input(&'inp self, fmspc: &'inp Fmspc) + -> >::Input; } pub struct ClientBuilder { @@ -530,7 +528,7 @@ pub trait ProvisioningClient { qe_id: Option<&QeId>, ) -> Result, Error>; - fn tcbinfo(&self, fmspc: &Vec) -> Result; + fn tcbinfo(&self, fmspc: &Fmspc) -> Result; fn pckcrl(&self) -> Result; @@ -588,7 +586,7 @@ impl Fetcher<'a>> ProvisioningClient for Client { self.pckcert_service.call_service(&self.fetcher, &input) } - fn tcbinfo(&self, fmspc: &Vec) -> Result { + fn tcbinfo(&self, fmspc: &Fmspc) -> Result { let input = self.tcbinfo_service.pcs_service().build_input(fmspc); self.tcbinfo_service.call_service(&self.fetcher, &input) } diff --git a/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/pccs.rs b/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/pccs.rs index 4f7eb530..f69d6a25 100644 --- a/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/pccs.rs +++ b/intel-sgx/dcap-artifact-retrieval/src/provisioning_client/pccs.rs @@ -14,7 +14,8 @@ use std::borrow::Cow; use std::time::Duration; use pcs::{ - CpuSvn, EncPpid, PceId, PceIsvsvn, PckCert, PckCrl, QeId, QeIdentitySigned, TcbInfo, Unverified, + CpuSvn, EncPpid, Fmspc, PceId, PceIsvsvn, PckCert, PckCrl, QeId, QeIdentitySigned, TcbInfo, + Unverified, }; use rustc_serialize::hex::{FromHex, ToHex}; @@ -260,7 +261,7 @@ impl TcbInfoApi { impl<'inp> TcbInfoService<'inp> for TcbInfoApi { fn build_input( &'inp self, - fmspc: &'inp Vec, + fmspc: &'inp Fmspc, ) -> >::Input { TcbInfoIn { api_version: self.api_version, @@ -278,7 +279,7 @@ impl<'inp> ProvisioningServiceApi<'inp> for TcbInfoApi { fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> { let api_version = input.api_version as u8; - let fmspc = input.fmspc.to_hex(); + let fmspc = input.fmspc.as_bytes().to_hex(); let url = format!( "{}/sgx/certification/v{}/tcb?fmspc={}", self.base_url, api_version, fmspc @@ -454,6 +455,8 @@ mod tests { #[test] #[ignore = "needs a running PCCS service"] // FIXME pub fn pck_cached() { + let root_ca = include_bytes!("../../tests/data/root_SGX_CA_der.cert"); + let root_cas = [&root_ca[..]]; for api_version in [PcsVersion::V3, PcsVersion::V4] { let client = make_client(api_version); @@ -471,6 +474,8 @@ mod tests { ) .unwrap(); + let pck = pck.verify(&root_cas).unwrap(); + // The cache should be populated after initial service call { let mut cache = client.pckcert_service.cache.lock().unwrap(); @@ -494,7 +499,15 @@ mod tests { .to_owned() }; - assert_eq!(pck.fmspc().unwrap(), cached_pck.fmspc().unwrap()); + assert_eq!( + pck.fmspc().unwrap(), + cached_pck + .clone() + .verify(&root_cas) + .unwrap() + .fmspc() + .unwrap() + ); assert_eq!(pck.ca_chain(), cached_pck.ca_chain()); } @@ -509,7 +522,15 @@ mod tests { ) .unwrap(); - assert_eq!(pck.fmspc().unwrap(), pck_from_service.fmspc().unwrap()); + assert_eq!( + pck.fmspc().unwrap(), + pck_from_service + .clone() + .verify(&root_cas) + .unwrap() + .fmspc() + .unwrap() + ); assert_eq!(pck.ca_chain(), pck_from_service.ca_chain()); } } diff --git a/intel-sgx/pcs/Cargo.toml b/intel-sgx/pcs/Cargo.toml index 483b83f9..a33045e2 100644 --- a/intel-sgx/pcs/Cargo.toml +++ b/intel-sgx/pcs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pcs" -version = "0.2.0" +version = "0.3.0" authors = ["Fortanix, Inc."] license = "MPL-2.0" edition = "2018" diff --git a/intel-sgx/pcs/src/pckcrt.rs b/intel-sgx/pcs/src/pckcrt.rs index 6b53296d..4e03335a 100644 --- a/intel-sgx/pcs/src/pckcrt.rs +++ b/intel-sgx/pcs/src/pckcrt.rs @@ -30,7 +30,7 @@ use { }; use crate::io::{self}; -use crate::tcb_info::{TcbData, TcbLevel}; +use crate::tcb_info::{Fmspc, TcbData, TcbLevel}; use crate::{CpuSvn, Error, Unverified, VerificationType, Verified}; /// [`SGXType`] is a rust enum representing the IntelĀ® SGX Type. @@ -295,7 +295,7 @@ impl PckCerts { Ok(pcks) } - pub fn fmspc(&self) -> Result, Error> { + pub fn fmspc(&self) -> Result { let pck = self.iter().nth(0).ok_or(Error::NoPckCertData)?; let sgx_extension = SGXPCKCertificateExtension::try_from(pck).map_err(|e| Error::InvalidPckFormat(e))?; Ok(sgx_extension.fmspc) @@ -458,6 +458,16 @@ impl PckCert { let pk = cert.public_key(); pk.ec_public() } + + pub fn ppid(&self) -> Result, ASN1Error> { + let extension = self.sgx_extension()?; + Ok(extension.ppid) + } + + pub fn fmspc(&self) -> Result { + let extension = self.sgx_extension()?; + Ok(extension.fmspc) + } } impl PckCert { @@ -536,11 +546,6 @@ impl PckCert { Err(Error::InvalidPck("PckCert isn't valid for provided TCB".into())) } } - - pub fn fmspc(&self) -> Result, Error> { - let sgx_extension = self.sgx_extension().map_err(|e| Error::InvalidPckFormat(e))?; - Ok(sgx_extension.fmspc) - } } #[derive(Default, Debug)] @@ -624,12 +629,12 @@ impl BERDecodable for SGXPlatformConfiguration { } } -#[derive(Default, Debug)] +#[derive(Debug)] pub struct SGXPCKCertificateExtension { pub ppid: Vec, pub tcb: PlatformTCB, pub pceid: u16, - pub fmspc: Vec, + pub fmspc: Fmspc, pub sgx_type: SGXType, pub platform_instance_id: Option>, pub configuration: Option, @@ -679,11 +684,8 @@ impl SGXPCKCertificateExtension { configuration = Some(reader.next(|reader| SGXPlatformConfiguration::decode_ber(reader))?); } - if ppid.len() != 16 - || pceid.len() != 2 - || fmspc.len() != 6 - || platform_instance_id.as_ref().map_or(false, |id| id.len() != 16) - { + let fmspc = Fmspc::try_from(&fmspc).map_err(|_| ASN1Error::new(ASN1ErrorKind::Invalid))?; + if ppid.len() != 16 || pceid.len() != 2 || platform_instance_id.as_ref().map_or(false, |id| id.len() != 16) { return Err(ASN1Error::new(ASN1ErrorKind::Invalid)); } @@ -921,7 +923,7 @@ mod tests { assert_eq!(sgx_extension.tcb.tcb_components.0.pcesvn, 9); assert_eq!(sgx_extension.tcb.cpusvn, [13, 13, 2, 4, 1, 128, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(sgx_extension.pceid, 0); - assert_eq!(sgx_extension.fmspc, [0, 144, 110, 161, 0, 0]); + assert_eq!(sgx_extension.fmspc, Fmspc::new([0, 144, 110, 161, 0, 0])); assert_eq!(sgx_extension.sgx_type, SGXType::Standard); assert!(sgx_extension.platform_instance_id.is_none()); assert!(sgx_extension.configuration.is_none()); @@ -957,7 +959,7 @@ mod tests { assert_eq!(sgx_extension.tcb.tcb_components.0.pcesvn, 10); assert_eq!(sgx_extension.tcb.cpusvn, [3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(sgx_extension.pceid, 0); - assert_eq!(sgx_extension.fmspc, [16, 96, 106, 0, 0, 0]); + assert_eq!(sgx_extension.fmspc, Fmspc::new([16, 96, 106, 0, 0, 0])); assert_eq!(sgx_extension.sgx_type, SGXType::Scalable); assert_eq!( sgx_extension.platform_instance_id.unwrap(), @@ -998,7 +1000,7 @@ mod tests { assert_eq!(sgx_extension.tcb.tcb_components.0.pcesvn, 9); assert_eq!(sgx_extension.tcb.cpusvn, [13, 13, 2, 4, 1, 128, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(sgx_extension.pceid, 0); - assert_eq!(sgx_extension.fmspc, [0, 144, 110, 161, 0, 0]); + assert_eq!(sgx_extension.fmspc, Fmspc::new([0, 144, 110, 161, 0, 0])); } } @@ -1010,7 +1012,7 @@ mod tests { let pck_chain: Qe3CertDataPckCertChain = sig.certification_data().unwrap(); let pck = PckCert::from_pck_chain(pck_chain.certs.into()).unwrap(); let sgx_extension = pck.sgx_extension().unwrap(); - assert_eq!(sgx_extension.fmspc, [0, 144, 110, 161, 0, 0]); + assert_eq!(sgx_extension.fmspc, Fmspc::new([0, 144, 110, 161, 0, 0])); } #[test] @@ -1041,7 +1043,7 @@ mod tests { assert_eq!(sgx_extension.tcb.tcb_components.0.pcesvn, 10); assert_eq!(sgx_extension.tcb.cpusvn, [14, 14, 2, 4, 1, 128, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(sgx_extension.pceid, 0); - assert_eq!(sgx_extension.fmspc, [0, 144, 110, 161, 0, 0]); + assert_eq!(sgx_extension.fmspc, Fmspc::new([0, 144, 110, 161, 0, 0])); } #[test] diff --git a/intel-sgx/pcs/src/tcb_info.rs b/intel-sgx/pcs/src/tcb_info.rs index 2466a09d..db213e45 100644 --- a/intel-sgx/pcs/src/tcb_info.rs +++ b/intel-sgx/pcs/src/tcb_info.rs @@ -9,9 +9,7 @@ use std::convert::TryFrom; use std::marker::PhantomData; use std::path::PathBuf; -use base16::DecodeError as Base16DecodeError; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde::de::Error as _; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde_json::value::RawValue; #[cfg(feature = "verify")] use { @@ -22,7 +20,7 @@ use { use crate::pckcrt::TcbComponents; use crate::{io, Error, TcbStatus, Unverified, VerificationType, Verified}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Fmspc([u8; 6]); #[derive(Debug)] @@ -31,8 +29,8 @@ pub enum FmspcDecodeError { InvalidFmspcLength, } -impl From for FmspcDecodeError { - fn from(_value: Base16DecodeError) -> FmspcDecodeError { +impl From for FmspcDecodeError { + fn from(_value: base16::DecodeError) -> FmspcDecodeError { FmspcDecodeError::InvalidHex } } @@ -62,6 +60,14 @@ impl TryFrom<&[u8]> for Fmspc { } } +impl TryFrom<&Vec> for Fmspc { + type Error = FmspcDecodeError; + + fn try_from(value: &Vec) -> Result { + Fmspc::try_from(value.as_slice()) + } +} + impl TryFrom<&str> for Fmspc { type Error = FmspcDecodeError; @@ -71,6 +77,14 @@ impl TryFrom<&str> for Fmspc { } } +impl TryFrom<&String> for Fmspc { + type Error = FmspcDecodeError; + + fn try_from(value: &String) -> Result { + Fmspc::try_from(value.as_str()) + } +} + impl ToString for Fmspc { fn to_string(&self) -> String { base16::encode_lower(&self.0) @@ -92,7 +106,7 @@ impl<'de> Deserialize<'de> for Fmspc { D: Deserializer<'de>, { let fmspc = <&str>::deserialize(deserializer)?; - Fmspc::try_from(fmspc).map_err(|_| D::Error::custom("Bad fmspc format")) + Fmspc::try_from(fmspc).map_err(|_| de::Error::custom("Bad fmspc format")) } } @@ -123,7 +137,7 @@ pub struct TcbData { version: u16, issue_date: String, next_update: String, - fmspc: String, + fmspc: Fmspc, pce_id: String, tcb_type: u16, tcb_evaluation_data_number: u64, @@ -144,7 +158,7 @@ impl<'de> Deserialize<'de> for TcbData { version: u16, issue_date: String, next_update: String, - fmspc: String, + fmspc: Fmspc, pce_id: String, tcb_type: u16, tcb_evaluation_data_number: u64, @@ -179,7 +193,7 @@ impl<'de> Deserialize<'de> for TcbData { } impl TcbData { - pub fn fmspc(&self) -> &str { + pub fn fmspc(&self) -> &Fmspc { &self.fmspc } } @@ -250,20 +264,19 @@ impl TcbInfo { pub fn store(&self, output_dir: &str) -> Result { let data = TcbData::::parse(&self.raw_tcb_info)?; - let filename = Self::create_filename(&data.fmspc); + let filename = Self::create_filename(&data.fmspc.to_string()); io::write_to_file(&self, output_dir, &filename)?; Ok(filename) } pub fn store_if_not_exist(&self, output_dir: &str) -> Result, Error> { let data = TcbData::::parse(&self.raw_tcb_info)?; - let filename = Self::create_filename(&data.fmspc); + let filename = Self::create_filename(&data.fmspc.to_string()); io::write_to_file_if_not_exist(&self, output_dir, &filename) } - pub fn restore(input_dir: &str, fmspc: &[u8]) -> Result { - let fmspc = &base16::encode_lower(&fmspc); - let filename = TcbInfo::create_filename(fmspc); + pub fn restore(input_dir: &str, fmspc: &Fmspc) -> Result { + let filename = TcbInfo::create_filename(&fmspc.to_string()); let info: TcbInfo = io::read_from_file(input_dir, &filename)?; Ok(info) } @@ -352,16 +365,16 @@ impl TcbInfo { #[cfg(test)] mod tests { #[cfg(not(target_env = "sgx"))] - use crate::tcb_info::TcbInfo; + use { + crate::tcb_info::{Fmspc, TcbInfo}, + std::convert::TryFrom, + }; #[test] #[cfg(not(target_env = "sgx"))] fn read_tcb_info() { - let info = TcbInfo::restore( - "./tests/data/", - &base16::decode("00906ea10000".as_bytes()).expect("validated"), - ) - .expect("validated"); + let info = + TcbInfo::restore("./tests/data/", &Fmspc::try_from("00906ea10000").expect("static fmspc")).expect("validated"); let root_certificate = include_bytes!("../tests/data/root_SGX_CA_der.cert"); let root_certificates = [&root_certificate[..]]; assert!(info.verify(&root_certificates).is_ok()); @@ -370,7 +383,7 @@ mod tests { #[test] #[cfg(not(target_env = "sgx"))] fn read_corrupt_tcb_info() { - let tcb_info = TcbInfo::restore("./tests/data/corrupted", &base16::decode("00906ea10000".as_bytes()).unwrap()).unwrap(); + let tcb_info = TcbInfo::restore("./tests/data/corrupted", &Fmspc::try_from("00906ea10000").unwrap()).unwrap(); let root_certificate = include_bytes!("../tests/data/root_SGX_CA_der.cert"); let root_certificates = [&root_certificate[..]]; assert!(tcb_info.verify(&root_certificates).is_err());