Skip to content
Draft
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
1 change: 1 addition & 0 deletions .changelog/6331.breaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/common/sgx/pcs/policy: Add FMSPC whitelist to quote policy
Empty file added .changelog/6336.trivial.md
Empty file.
4 changes: 4 additions & 0 deletions go/common/sgx/pcs/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ type QuotePolicy struct {
// valid. TCB bundles containing smaller values will be invalid.
MinTCBEvaluationDataNumber uint32 `json:"min_tcb_evaluation_data_number" yaml:"min_tcb_evaluation_data_number"`

// FMSPCWhitelist is a list of hexadecimal encoded FMSPCs specifying which processor
// packages and platform instances are allowed.
FMSPCWhitelist []string `json:"fmspc_whitelist,omitempty" yaml:"fmspc_whitelist,omitempty"`

// FMSPCBlacklist is a list of hexadecimal encoded FMSPCs specifying which processor
// packages and platform instances are blocked.
FMSPCBlacklist []string `json:"fmspc_blacklist,omitempty" yaml:"fmspc_blacklist,omitempty"`
Expand Down
3 changes: 2 additions & 1 deletion go/common/sgx/pcs/quote.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ func (q *Quote) Verify(policy *QuotePolicy, ts time.Time, tcb *TCBBundle) (*sgx.
policy = &QuotePolicy{
TCBValidityPeriod: 30,
MinTCBEvaluationDataNumber: DefaultMinTCBEvaluationDataNumber,
FMSPCBlacklist: []string{},
FMSPCWhitelist: make([]string, 0),
FMSPCBlacklist: make([]string, 0),
}
}

Expand Down
25 changes: 24 additions & 1 deletion go/common/sgx/pcs/quote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,37 @@ func TestQuoteV3_ECDSA_P256_PCK_CertificateChain(t *testing.T) {
require.Error(err, "Quote verification should fail for invalid TCB evaluation data number")
require.ErrorContains(err, "pcs/quote: failed to verify TCB bundle: pcs/tcb: failed to verify QE identity: pcs/tcb: invalid QE identity: pcs/tcb: invalid QE evaluation data number")

// Test whitelisted FMSPC.
quotePolicy = &QuotePolicy{
TCBValidityPeriod: 90,
FMSPCWhitelist: []string{},
}
_, err = quote.Verify(quotePolicy, now, &tcbBundle)
require.NoError(err, "Quote verification should succeed for whitelisted FMSPCs")

quotePolicy = &QuotePolicy{
TCBValidityPeriod: 90,
FMSPCWhitelist: []string{"00606A000000"},
}
_, err = quote.Verify(quotePolicy, now, &tcbBundle)
require.NoError(err, "Quote verification should succeed for whitelisted FMSPCs")

quotePolicy = &QuotePolicy{
TCBValidityPeriod: 90,
FMSPCWhitelist: []string{"00606A000001"},
}
_, err = quote.Verify(quotePolicy, now, &tcbBundle)
require.Error(err, "Quote verification should fail for non-whitelisted FMSPCs")
require.ErrorContains(err, "pcs/quote: failed to verify TCB bundle: pcs/tcb: failed to verify TCB info: pcs/tcb: invalid TCB info: pcs/tcb: FMSPC is not whitelisted")

// Test blacklisted FMSPC.
quotePolicy = &QuotePolicy{
TCBValidityPeriod: 90,
FMSPCBlacklist: []string{"00606A000000"},
}
_, err = quote.Verify(quotePolicy, now, &tcbBundle)
require.Error(err, "Quote verification should fail for blacklisted FMSPCs")
require.ErrorContains(err, "pcs/quote: failed to verify TCB bundle: pcs/tcb: failed to verify TCB info: pcs/tcb: invalid TCB info: pcs/tcb: blacklisted FMSPC")
require.ErrorContains(err, "pcs/quote: failed to verify TCB bundle: pcs/tcb: failed to verify TCB info: pcs/tcb: invalid TCB info: pcs/tcb: FMSPC is blacklisted")

// Test TCB info certificates missing.
tcbBundle2 := TCBBundle{
Expand Down
13 changes: 8 additions & 5 deletions go/common/sgx/pcs/tcb.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,14 @@ func (ti *TCBInfo) validate(teeType TeeType, ts time.Time, policy *QuotePolicy)
return fmt.Errorf("pcs/tcb: invalid TCB evaluation data number")
}

// Validate FMSPC not blacklisted.
for _, blocked := range policy.FMSPCBlacklist {
if blocked == ti.FMSPC {
return fmt.Errorf("pcs/tcb: blacklisted FMSPC")
}
// Validate FMSPC is whitelisted.
if len(policy.FMSPCWhitelist) > 0 && !slices.Contains(policy.FMSPCWhitelist, ti.FMSPC) {
return fmt.Errorf("pcs/tcb: FMSPC is not whitelisted")
}

// Validate FMSPC is not blacklisted.
if slices.Contains(policy.FMSPCBlacklist, ti.FMSPC) {
return fmt.Errorf("pcs/tcb: FMSPC is blacklisted")
}

return nil
Expand Down
12 changes: 9 additions & 3 deletions go/common/sgx/quote/quote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ pcs:
disabled: false
tcb_validity_period: 30
min_tcb_evaluation_data_number: 17
fmspc_blacklist:
fmspc_whitelist:
- "000000000000"
- "00606A000000"
fmspc_blacklist:
- "000000000001"
- "00606A000001"
`
var dec Policy
err := yaml.Unmarshal([]byte(testCase1), &dec)
Expand All @@ -27,7 +30,10 @@ pcs:
require.EqualValues(false, dec.PCS.Disabled)
require.EqualValues(30, dec.PCS.TCBValidityPeriod)
require.EqualValues(17, dec.PCS.MinTCBEvaluationDataNumber)
require.Len(dec.PCS.FMSPCWhitelist, 2)
require.EqualValues("000000000000", dec.PCS.FMSPCWhitelist[0])
require.EqualValues("00606A000000", dec.PCS.FMSPCWhitelist[1])
require.Len(dec.PCS.FMSPCBlacklist, 2)
require.EqualValues("000000000000", dec.PCS.FMSPCBlacklist[0])
require.EqualValues("00606A000000", dec.PCS.FMSPCBlacklist[1])
require.EqualValues("000000000001", dec.PCS.FMSPCBlacklist[0])
require.EqualValues("00606A000001", dec.PCS.FMSPCBlacklist[1])
}
4 changes: 3 additions & 1 deletion go/upgrade/migrations/consensus_242.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
// Consensus242 is the name of the upgrade that enables features introduced in Oasis Core 24.2.
//
// This upgrade includes:
// - The `MayQuery field in the CHURP SGX policy, which defines which enclave identities
// - The `MayQuery` field in the CHURP SGX policy, which defines which enclave identities
// are allowed to query runtime key shares.
// - The `FMSPCWhitelist` field in the quote policy, which defines which processor packages
// and platform instances are allowed.
// - An updated key manager policy update transaction that applies a new policy at the epoch
// boundary.
const Consensus242 = "consensus242"
Expand Down
35 changes: 33 additions & 2 deletions runtime/src/common/sgx/pcs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub enum Error {
TCBMismatch,
#[error("TCB evaluation data number is invalid")]
TCBEvaluationDataNumberInvalid,
#[error("FMSPC is not whitelisted")]
NotWhitelistedFMSPC,
#[error("FMSPC is blacklisted")]
BlacklistedFMSPC,
#[error("QE report is malformed")]
Expand Down Expand Up @@ -217,19 +219,48 @@ mod tests {
}

#[test]
fn test_quote_blacklisted_fmscp() {
fn test_quote_whitelisted_fmscp() {
// From Go implementation.
const RAW_QUOTE_BUNDLE: &[u8] =
include_bytes!("../../../../testdata/pcs_quote_bundle.cbor");

let qb: QuoteBundle = cbor::from_slice(RAW_QUOTE_BUNDLE).unwrap();
let now = Utc.timestamp_opt(1671497404, 0).unwrap();

let policy = &QuotePolicy {
..Default::default()
};
qb.verify(policy, now)
.expect("quote verification should succeed for whitelisted FMSPCs");

let policy = &QuotePolicy {
fmspc_whitelist: vec!["00606A000000".to_string()],
..Default::default()
};
qb.verify(policy, now)
.expect("quote verification should succeed for whitelisted FMSPCs");

let policy: &QuotePolicy = &QuotePolicy {
fmspc_whitelist: vec!["00606A000001".to_string()],
..Default::default()
};
qb.verify(policy, now)
.expect_err("quote verification should fail for non-whitelisted FMSPCs");
}

#[test]
fn test_quote_blacklisted_fmscp() {
// From Go implementation.
const RAW_QUOTE_BUNDLE: &[u8] =
include_bytes!("../../../../testdata/pcs_quote_bundle.cbor");

let qb: QuoteBundle = cbor::from_slice(RAW_QUOTE_BUNDLE).unwrap();
let now = Utc.timestamp_opt(1671497404, 0).unwrap();

let policy = &QuotePolicy {
fmspc_blacklist: vec!["00606A000000".to_string()],
..Default::default()
};

qb.verify(policy, now)
.expect_err("quote verification should fail for blacklisted FMSPCs");
}
Expand Down
6 changes: 6 additions & 0 deletions runtime/src/common/sgx/pcs/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ pub struct QuotePolicy {
/// smaller values will be invalid.
pub min_tcb_evaluation_data_number: u32,

/// A list of hexadecimal encoded FMSPCs specifying which processor packages and platform
/// instances are allowed.
#[cbor(optional)]
pub fmspc_whitelist: Vec<String>,

/// A list of hexadecimal encoded FMSPCs specifying which processor packages and platform
/// instances are blocked.
#[cbor(optional)]
Expand All @@ -31,6 +36,7 @@ impl Default for QuotePolicy {
disabled: false,
tcb_validity_period: 30,
min_tcb_evaluation_data_number: DEFAULT_MIN_TCB_EVALUATION_DATA_NUMBER,
fmspc_whitelist: Vec::new(),
fmspc_blacklist: Vec::new(),
tdx: None,
}
Expand Down
13 changes: 7 additions & 6 deletions runtime/src/common/sgx/pcs/tcb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,13 @@ impl TCBInfo {
return Err(Error::TCBEvaluationDataNumberInvalid);
}

// Validate FMSPC not blacklisted.
let blocked = policy
.fmspc_blacklist
.iter()
.any(|blocked| blocked == &self.fmspc);
if blocked {
// Validate FMSPC is whitelisted.
if !policy.fmspc_whitelist.is_empty() && !policy.fmspc_whitelist.contains(&self.fmspc) {
return Err(Error::NotWhitelistedFMSPC);
}

// Validate FMSPC is not blacklisted.
if policy.fmspc_blacklist.contains(&self.fmspc) {
return Err(Error::BlacklistedFMSPC);
}

Expand Down
Loading