Skip to content

Commit

Permalink
Fix pckcerts_with_fallback logic to retrieve TCB info and get PCK cer…
Browse files Browse the repository at this point in the history
…ts for each TCB level
  • Loading branch information
mzohreva committed Jan 29, 2025
1 parent ac783fa commit 9a493ef
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 7 deletions.
38 changes: 32 additions & 6 deletions intel-sgx/dcap-artifact-retrieval/src/provisioning_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::hash::{DefaultHasher, Hash, Hasher};
use std::io::Read;
Expand Down Expand Up @@ -534,26 +535,51 @@ pub trait ProvisioningClient {

fn qe_identity(&self) -> Result<QeIdentitySigned, Error>;

/// Retrieve PCK certificates using `pckcerts()` and fallback to `pckcert()`
/// if provisioning client does not support pckcerts. Note that in case of
/// fallback the returned PckCerts will only contain a single certificate
/// associated with the highest TCB level applied to the platform.
/// 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.
/// Note that PCK certs for some TCB levels may be missing.
fn pckcerts_with_fallback(&self, pck_id: &PckID) -> Result<PckCerts, Error> {
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),
)?;

pck_cert
let fmspc = pck_cert.sgx_extension()?.fmspc;
let tcb_info = self.tcbinfo(&fmspc)?;
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)
};
pcks.insert(p.platform_tcb()?.cpusvn, p);
}
let pcks: Vec<_> = pcks.into_iter().map(|(_, v)| v).collect();
pcks
.try_into()
.map_err(|e| Error::PCSDecodeError(format!("{}", e).into()))
}
Expand Down
36 changes: 36 additions & 0 deletions intel-sgx/dcap-artifact-retrieval/src/provisioning_client/pccs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,42 @@ mod tests {
}
}

#[test]
#[ignore = "needs a running PCCS service"] // FIXME
pub fn test_pckcerts_with_fallback() {
for api_version in [PcsVersion::V3, PcsVersion::V4] {
let client = make_client(api_version);

for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
.unwrap()
.iter()
{
let pckcerts = client.pckcerts_with_fallback(&pckid).unwrap();
println!("Found {} PCK certs.", pckcerts.as_pck_certs().len());

let tcb_info = client.tcbinfo(&pckcerts.fmspc().unwrap()).unwrap();
let tcb_data = tcb_info.data().unwrap();

let selected = pckcerts.select_pck(
&tcb_data,
&pckid.cpu_svn,
pckid.pce_isvsvn,
pckid.pce_id,
).unwrap();

let pck = client.pckcert(
Some(&pckid.enc_ppid),
&pckid.pce_id,
&pckid.cpu_svn,
pckid.pce_isvsvn,
Some(&pckid.qe_id),
).unwrap();

assert_eq!(format!("{:?}", selected.sgx_extension().unwrap()), format!("{:?}", pck.sgx_extension().unwrap()));
}
}
}

#[test]
#[ignore = "needs a running PCCS service"] // FIXME
pub fn tcb_info() {
Expand Down
25 changes: 25 additions & 0 deletions intel-sgx/pcs/src/pckcrt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::borrow::Cow;
use std::cmp::Ordering;
use std::convert::{TryFrom, TryInto};
use std::marker::PhantomData;
use std::mem;
use std::path::PathBuf;

use percent_encoding::percent_decode;
Expand Down Expand Up @@ -144,6 +145,10 @@ impl TcbComponents {
pub fn pce_svn(&self) -> u16 {
self.0.pcesvn
}

pub fn cpu_svn(&self) -> CpuSvn {
self.0.sgxtcbcomponents.each_ref().map(|c| c.svn)
}
}

impl PartialOrd for TcbComponents {
Expand Down Expand Up @@ -743,6 +748,26 @@ where
}
}

/// NOTE: This conversion is only correct if all PCK certs in the vec have the
/// same CA chain.
impl<V> TryFrom<Vec<PckCert<V>>> for PckCerts
where
V: VerificationType,
{
type Error = ASN1Error;

fn try_from(mut pcks: Vec<PckCert<V>>) -> Result<PckCerts, ASN1Error> {
let pck_data = pcks.iter().map(|pck| pck.as_pck_cert_body_item()).collect::<Result<Vec<_>, ASN1Error>>()?;
// NOTE: assuming that all PCK certs in the vec have the same CA chain,
// so we pick the ca_chain from the first one:
let ca_chain = match pcks.first_mut() {
Some(first) => mem::take(&mut first.ca_chain),
None => return Err(ASN1Error::new(ASN1ErrorKind::Eof)),
};
Ok(PckCerts { pck_data, ca_chain })
}
}

#[cfg(test)]
mod tests {
use dcap_ql::quote::{Qe3CertDataPckCertChain, Quote, Quote3SignatureEcdsaP256};
Expand Down
8 changes: 7 additions & 1 deletion intel-sgx/pcs/src/tcb_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use {
};

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

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Fmspc([u8; 6]);
Expand Down Expand Up @@ -221,6 +221,8 @@ impl TcbData<Unverified> {
}

impl<V: VerificationType> TcbData<V> {
// NOTE: don't make this publicly available. We want to prevent people from
// accessing the TCB levels without checking whether the TcbInfo is valid.
pub(crate) fn tcb_levels(&self) -> &Vec<TcbLevel> {
&self.tcb_levels
}
Expand All @@ -233,6 +235,10 @@ impl<V: VerificationType> TcbData<V> {
// TCB Type 0 simply copies cpu svn
Ok(TcbComponents::from_raw(*raw_cpusvn, pce_svn))
}

pub fn iter_tcb_components(&self) -> impl Iterator<Item = (CpuSvn, PceIsvsvn)> + '_ {
self.tcb_levels.iter().map(|tcb_level| (tcb_level.tcb.cpu_svn(), tcb_level.tcb.pce_svn()))
}
}

#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
Expand Down

0 comments on commit 9a493ef

Please sign in to comment.