Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
95 changes: 63 additions & 32 deletions intel-sgx/dcap-artifact-retrieval/src/provisioning_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use std::collections::HashMap;
use std::collections::BTreeMap;
use std::convert::{TryFrom, TryInto};
use std::hash::{DefaultHasher, Hash, Hasher};
use std::io::Read;
Expand All @@ -16,7 +16,7 @@ use lru_cache::LruCache;
use num_enum::TryFromPrimitive;
use pcs::{
CpuSvn, DcapArtifactIssuer, EncPpid, Fmspc, PceId, PceIsvsvn, PckCert, PckCerts, PckCrl, PckID, QeId,
QeIdentitySigned, TcbInfo, RawTcbEvaluationDataNumbers, Unverified,
QeIdentitySigned, TcbComponent, TcbInfo, RawTcbEvaluationDataNumbers, Unverified,
};
#[cfg(feature = "reqwest")]
use reqwest::blocking::{Client as ReqwestClient, Response as ReqwestResponse};
Expand Down Expand Up @@ -570,50 +570,81 @@ pub trait ProvisioningClient {

/// Retrieve PCK certificates using `pckcerts()` and fallback to the
/// following method if that's not supported:
/// - Call `pckcert()` to find the FMSPC.
/// - Using the FMSPC value, call `tcbinfo()` to get TCB info.
/// - For each TCB level in the result of previous call:
/// - Call `pckcert()` to get the best available PCK cert for that TCB level.
/// 1. Call `pckcert()` with PCK ID to get best available PCK cert.
/// 2. Try to call `pckcert()` with PCK ID but with CPUSVN all 1's.
/// 3. Using the FMSPC value from PCK cert in step 1, call `tcbinfo()` to
/// get TCB info.
/// 4. For each TCB level in the result of previous call:
/// - Call `pckcert()` to get the best available PCK cert for that TCB
/// level.
/// - When late microcode value is higher than the early microcode
/// value, also try to get PCK cert with TCB level where the early
/// microcode value is set to the late microcode value.
///
/// Note that PCK certs for some TCB levels may be missing.
fn pckcerts_with_fallback(&self, pck_id: &PckID) -> Result<PckCerts, Error> {
let get_and_collect = |collection: &mut BTreeMap<([u8; 16], u16), PckCert<Unverified>>, cpu_svn: &[u8; 16], pce_svn: u16| -> Result<PckCert<Unverified>, Error> {
let pck_cert = self.pckcert(
Some(&pck_id.enc_ppid),
&pck_id.pce_id,
cpu_svn,
pce_svn,
Some(&pck_id.qe_id),
)?;

// Getting PCK cert using CPUSVN from PCKID
let ptcb = pck_cert.platform_tcb()?;
collection.insert((ptcb.cpusvn, ptcb.tcb_components.pce_svn()), pck_cert.clone());
Ok(pck_cert)
};

match self.pckcerts(&pck_id.enc_ppid, pck_id.pce_id) {
Ok(pck_certs) => return Ok(pck_certs),
Err(Error::RequestNotSupported) => {} // fallback below
Err(e) => return Err(e),
}
// fallback:

// NOTE: at least with PCCS, any call to `pckcert()` will return the
// "best available" PCK cert for the specified TCB level.
let pck_cert = self.pckcert(
Some(&pck_id.enc_ppid),
&pck_id.pce_id,
&pck_id.cpu_svn,
pck_id.pce_isvsvn,
Some(&pck_id.qe_id),
)?;
// Use BTreeMap to have an ordered PckCerts at the end
let mut pckcerts_map = BTreeMap::new();

// 1. Use PCK ID to get best available PCK Cert
let pck_cert = get_and_collect(&mut pckcerts_map, &pck_id.cpu_svn, pck_id.pce_isvsvn)?;

// 2. Getting PCK cert using CPUSVN all 1's
let _ign_err = get_and_collect(&mut pckcerts_map, &[u8::MAX; 16], pck_id.pce_isvsvn);

let fmspc = pck_cert.sgx_extension()?.fmspc;
let tcb_info = self.tcbinfo(&fmspc, None)?;
let tcb_data = tcb_info.data()?;
let mut pcks = HashMap::new();
for (cpu_svn, pce_isvsvn) in tcb_data.iter_tcb_components() {
let p = match self.pckcert(
Some(&pck_id.enc_ppid),
&pck_id.pce_id,
&cpu_svn,
pce_isvsvn,
Some(&pck_id.qe_id),
) {
Ok(cert) => cert,
Err(Error::PCSError(StatusCode::NotFound, _)) |
Err(Error::PCSError(StatusCode::NonStandard462, _)) => continue,
Err(other) => return Err(other)
};
let ptcb = p.platform_tcb()?;
pcks.insert((ptcb.cpusvn, ptcb.tcb_components.pce_svn()), p);
// 3. Get PCK based on TCB levels
let _ = get_and_collect(&mut pckcerts_map, &cpu_svn, pce_isvsvn)?;

// 4. If late loaded microcode version is higher than early loaded microcode,
// also try with highest microcode version of both components. We found cases where
// fetching the PCK Cert that exactly matched the TCB level, did not result in a PCK
// Cert for that level
const EARLY_UCODE_IDX: usize = 0;
const LATE_UCODE_IDX: usize = 1;
// Unfortunately the TCB Info does not populate the component type (e.g., curl -v -X GET
// "https://api.trustedservices.intel.com/sgx/certification/v4/tcb?fmspc=00906ED50000&tcbEvaluationDataNumber=20"
// ). We pick a default as backup, and ensure errors fetching these PckCerts are
// ignored.
let eary_ucode_idx = tcb_data.tcb_component_index(TcbComponent::EarlyMicrocodeUpdate).unwrap_or(EARLY_UCODE_IDX);
let late_ucode_idx = tcb_data.tcb_component_index(TcbComponent::LateMicrocodeUpdate).unwrap_or(LATE_UCODE_IDX);
let early_ucode = cpu_svn[eary_ucode_idx];
let late_ucode = cpu_svn[late_ucode_idx];
if early_ucode < late_ucode {
let mut cpu_svn = cpu_svn.clone();
cpu_svn[eary_ucode_idx] = late_ucode;
let _ign_err = get_and_collect(&mut pckcerts_map, &cpu_svn, pce_isvsvn);
}
}
let pcks: Vec<_> = pcks.into_iter().map(|(_, v)| v).collect();
pcks

// BTreeMap by default is Ascending
let pck_certs: Vec<_> = pckcerts_map.into_iter().rev().map(|(_, v)| v).collect();
pck_certs
.try_into()
.map_err(|e| Error::PCSDecodeError(format!("{}", e).into()))
}
Expand Down
46 changes: 37 additions & 9 deletions intel-sgx/dcap-artifact-retrieval/src/provisioning_client/pccs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,6 @@ mod tests {
.iter()
{
let pckcerts = client
//.pckcerts(&pckid.enc_ppid, pckid.pce_id.clone())
.pckcerts_with_fallback(&pckid)
.unwrap();

Expand All @@ -654,10 +653,24 @@ mod tests {
.unwrap();

for number in evaluation_data_numbers.numbers() {
assert!(client
.tcbinfo(&fmspc, Some(number.number()))
.and_then(|tcb| { Ok(tcb.store(OUTPUT_TEST_DIR).unwrap()) })
.is_ok());
// TODO(#811): Since PCCS is cache service and not able to cache the
// `Gone` response mentioned below from Intel PCS, We need to change
// the test behavior to call TCB INFO API with update=standard to get the
// smallest TcbEvaluationDataNumber that's still available.
//
// Here, we temporarily fix this be hardcoding.
if number.number() < 17 {
continue;
}
let tcb = match client.tcbinfo(&fmspc, Some(number.number())) {
Ok(tcb) => tcb,
// API query with update="standard" will return QE Identity with TCB Evaluation Data Number M.
// A 410 Gone response is returned when the inputted TCB Evaluation Data Number is < M,
// so we ignore these TCB Evaluation Data Numbers.
Err(super::Error::PCSError(status_code, _)) if status_code == super::StatusCode::Gone => continue,
res @Err(_) => res.unwrap(),
};
tcb.store(OUTPUT_TEST_DIR).unwrap();
}
}
}
Expand Down Expand Up @@ -818,12 +831,27 @@ mod tests {
let eval_numbers: TcbEvaluationDataNumbers =
eval_numbers.verify(&root_cas, Platform::SGX).unwrap();
for number in eval_numbers.numbers().map(|n| n.number()) {
let qe_id = client
.qe_identity(Some(number))
.unwrap()
// TODO(#811): Since PCCS is cache service and not able to cache the
// `Gone` response mentioned below from Intel PCS, We need to change
// the test behavior to call QE ID API with update=standard to get the
// smallest TcbEvaluationDataNumber that's still available.
//
// Here, we temporarily fix this be hardcoding.
if number < 17 {
continue;
}
let qe_identity = match client.qe_identity(Some(number)) {
Ok(id) => id,
// API query with update="standard" will return QE Identity with TCB Evaluation Data Number M.
// A 410 Gone response is returned when the inputted TCB Evaluation Data Number is < M,
// so we ignore these TCB Evaluation Data Numbers.
Err(super::Error::PCSError(status_code, _)) if status_code == super::StatusCode::Gone => continue,
res @Err(_) => res.unwrap(),
};
let verified_qe_id = qe_identity
.verify(&root_cas, EnclaveIdentity::QE)
.unwrap();
assert_eq!(qe_id.tcb_evaluation_data_number(), u64::from(number));
assert_eq!(verified_qe_id.tcb_evaluation_data_number(), u64::from(number));

let tcb_info = client
.tcbinfo(&fmspc, Some(number))
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5133c5451dff82456e83fd5f8b4402304bf7b8edf5ea93e23e33eb8874c8a1ff341c2d74fdc68412ab0e4b000d9c36b6a9f4b07def96fe0859a3f55220691b24ac27722cdef60aaa2338db10a431ec25e05514ff77fb513d8a8e5eb56ef199c6440d8a8b99ec0cafe2c85b096ea57ba2e18004cb4ec88a75ac6328aee95f89b0e632ee5d17559b233e37e6e1cb1fa1a7581ebce564879372e561a2c830e5bf1ffd4f2d741c1dad519e752706f98d73fc572192a495ae129fb72daa48919382bdafd368f5a70534000a6c9dff5d889702af005f658edc3878b3f556d73be902f5defb6dbb2e78837403589587adbccd47de140434afb8660b93a3415a47cb23711156d93c795d0678d0f8a27512364481416e91831e66513a6ca404d50b1f9b288fbfc353c656343ddd95f4210a043f233827daa2b9e6152df81bea2c8b90595ec630f4e0c872f75b0000f0ebc38fcbfba9cf9d4f3817d88283be6ff21051fd19d52ffef8fd066ceacee381962a8d1460429da6ef1ae8c79eba3707250f2989c4,0000,08080e0dffff01000000000000000000,0d00,4041a3c4d3af9f15e68513108e773a7f
5133c5451dff82456e83fd5f8b4402304bf7b8edf5ea93e23e33eb8874c8a1ff341c2d74fdc68412ab0e4b000d9c36b6a9f4b07def96fe0859a3f55220691b24ac27722cdef60aaa2338db10a431ec25e05514ff77fb513d8a8e5eb56ef199c6440d8a8b99ec0cafe2c85b096ea57ba2e18004cb4ec88a75ac6328aee95f89b0e632ee5d17559b233e37e6e1cb1fa1a7581ebce564879372e561a2c830e5bf1ffd4f2d741c1dad519e752706f98d73fc572192a495ae129fb72daa48919382bdafd368f5a70534000a6c9dff5d889702af005f658edc3878b3f556d73be902f5defb6dbb2e78837403589587adbccd47de140434afb8660b93a3415a47cb23711156d93c795d0678d0f8a27512364481416e91831e66513a6ca404d50b1f9b288fbfc353c656343ddd95f4210a043f233827daa2b9e6152df81bea2c8b90595ec630f4e0c872f75b0000f0ebc38fcbfba9cf9d4f3817d88283be6ff21051fd19d52ffef8fd066ceacee381962a8d1460429da6ef1ae8c79eba3707250f2989c4,0000,10100e0dffff01000000000000000000,0d00,4041a3c4d3af9f15e68513108e773a7f
2 changes: 1 addition & 1 deletion intel-sgx/pcs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use {
};

pub use crate::pckcrl::PckCrl;
pub use crate::pckcrt::{PckCert, PckCerts, SGXPCKCertificateExtension, SGXType};
pub use crate::pckcrt::{PckCert, PckCerts, SGXPCKCertificateExtension, SGXType, TcbComponent};
pub use crate::qe_identity::{EnclaveIdentity, QeIdentity, QeIdentitySigned};
pub use crate::tcb_info::{AdvisoryID, Fmspc, TcbInfo, TcbData, TcbLevel};
pub use crate::tcb_evaluation_data_numbers::{RawTcbEvaluationDataNumbers, TcbEvalNumber, TcbEvaluationDataNumbers, TcbPolicy};
Expand Down
31 changes: 29 additions & 2 deletions intel-sgx/pcs/src/pckcrt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,26 @@ enum IntelSgxTcbComponents {
#[serde(from = "IntelSgxTcbComponents")]
pub struct TcbComponents(IntelSgxTcbComponentsV4);

#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum TcbComponent {
EarlyMicrocodeUpdate,
LateMicrocodeUpdate,
}

impl TryFrom<&str> for TcbComponent {
type Error = ();

fn try_from(s: &str) -> Result<Self, Self::Error> {
if s == "Early Microcode Update" {
Ok(TcbComponent::EarlyMicrocodeUpdate)
} else if s == "SGX Late Microcode Update" {
Ok(TcbComponent::LateMicrocodeUpdate)
} else {
Err(())
}
}
}

impl TcbComponents {
pub fn from_raw(raw_cpusvn: [u8; 16], pcesvn: u16) -> Self {
TcbComponents(IntelSgxTcbComponentsV4 {
Expand Down Expand Up @@ -158,6 +178,13 @@ impl TcbComponents {
}
out
}

/// Returns the index of the TCB component
pub fn tcb_component_index(&self, comp: TcbComponent) -> Option<usize> {
self.0.sgxtcbcomponents
.iter()
.position(|c| TcbComponent::try_from(c.comp_type.as_str()) == Ok(comp))
}
}

impl PartialOrd for TcbComponents {
Expand Down Expand Up @@ -813,7 +840,7 @@ mod tests {

use super::*;
#[cfg(not(target_env = "sgx"))]
use crate::{TcbInfo, get_cert_subject, get_cert_subject_from_der};
use crate::{get_cert_subject, get_cert_subject_from_der};

fn decode_tcb_item<'a, 'b>(reader: &mut BERReaderSeq<'a, 'b>) -> ASN1Result<(ObjectIdentifier, u8)> {
let oid = reader.next().read_oid()?;
Expand Down Expand Up @@ -1262,7 +1289,7 @@ mod tests {
"./tests/data/",
&base16::decode("881c3086c0eef78f60f5702a7e379efe".as_bytes()).unwrap())
.unwrap();
let tcb_info = TcbInfo::restore("./tests/data/", &Fmspc::try_from("90806F000000").unwrap(), Some(19))
let tcb_info = crate::TcbInfo::restore("./tests/data/", &Fmspc::try_from("90806F000000").unwrap(), Some(19))
.unwrap();
// This TCB matches exactly with the first PCK cert in the list. This PCK cert must be
// selected
Expand Down
12 changes: 11 additions & 1 deletion intel-sgx/pcs/src/tcb_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use {
pkix::pem::PEM_CERTIFICATE, pkix::x509::GenericCertificate, pkix::FromBer, std::ops::Deref,
};

use crate::pckcrt::TcbComponents;
use crate::pckcrt::{TcbComponent, TcbComponents};
use crate::{io, CpuSvn, Error, PceIsvsvn, Platform, TcbStatus, Unverified, VerificationType, Verified};

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
Expand Down Expand Up @@ -301,6 +301,16 @@ impl TcbData<Unverified> {
}
Ok(data)
}

/// Returns the index of the TCB component
pub fn tcb_component_index(&self, comp: TcbComponent) -> Option<usize> {
if let Some(c) = self.tcb_levels.first() {
c.components().tcb_component_index(comp)
} else {
None
}
}

}

impl<V: VerificationType> TcbData<V> {
Expand Down