Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pckcerts_with_fallback logic to retrieve TCB info and get PCK certs for each TCB level #686

Merged
merged 1 commit into from
Jan 29, 2025
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
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> {
raoulstrackx marked this conversation as resolved.
Show resolved Hide resolved
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
Taowyoo marked this conversation as resolved.
Show resolved Hide resolved
.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() {
Taowyoo marked this conversation as resolved.
Show resolved Hide resolved
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:
mzohreva marked this conversation as resolved.
Show resolved Hide resolved
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