1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! Quote policy.
use super::{constants::*, report::TdReport, Error};

/// Quote validity policy.
#[derive(Clone, Debug, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct QuotePolicy {
    /// Whether PCS quotes are disabled and will always be rejected.
    #[cbor(optional)]
    pub disabled: bool,

    /// Validity (in days) of the TCB collateral.
    pub tcb_validity_period: u16,

    /// Minimum TCB evaluation data number that is considered to be valid. TCB bundles containing
    /// 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 blocked.
    #[cbor(optional)]
    pub fmspc_blacklist: Vec<String>,

    /// Optional TDX-specific policy. In case this is `None`, TDX quotes are disallowed.
    #[cbor(optional)]
    pub tdx: Option<TdxQuotePolicy>,
}

impl Default for QuotePolicy {
    fn default() -> Self {
        Self {
            disabled: false,
            tcb_validity_period: 30,
            min_tcb_evaluation_data_number: DEFAULT_MIN_TCB_EVALUATION_DATA_NUMBER,
            fmspc_blacklist: Vec::new(),
            tdx: None,
        }
    }
}

impl QuotePolicy {
    /// Whether the quote with timestamp `ts` is expired.
    pub fn is_expired(&self, now: i64, ts: i64) -> bool {
        if self.disabled {
            return true;
        }

        now.checked_sub(ts)
            .map(|d| d > 60 * 60 * 24 * (self.tcb_validity_period as i64))
            .expect("quote timestamp is in the future") // This should never happen.
    }
}

/// TDX-specific quote policy.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct TdxQuotePolicy {
    /// Allowed TDX modules. Empty to allow ANY Intel-signed module.
    pub allowed_tdx_modules: Vec<TdxModulePolicy>,
}

impl TdxQuotePolicy {
    /// Verify whether the TDX policy is satisfied for the given report.
    pub fn verify(&self, report: &TdReport) -> Result<(), Error> {
        self.verify_tdx_module(report)?;
        Ok(())
    }

    fn verify_tdx_module(&self, report: &TdReport) -> Result<(), Error> {
        // If at least one TDX Module matches, then we are good.
        for allowed_module in &self.allowed_tdx_modules {
            if allowed_module.matches(report) {
                return Ok(());
            }
        }

        // No module matched. Iff the list of modules is empty, allow ANY Intel-signed module.
        // As per the TDX specifications, MRSIGNER is all-zero for Intel.
        if self.allowed_tdx_modules.is_empty() && report.mr_signer_seam == TDX_MRSIGNER_INTEL {
            return Ok(());
        }

        Err(Error::TdxModuleNotAllowed)
    }
}

/// TDX module policy.
#[derive(Clone, Debug, PartialEq, Eq, cbor::Encode, cbor::Decode)]
#[cbor(no_default)]
pub struct TdxModulePolicy {
    /// Optional allowed measurement of the TDX Module. In case it is `None`, ANY measurement is
    /// allowed and only the signer is checked.
    pub mr_seam: Option<[u8; 48]>,

    /// Allowed signer of the TDX Module (zero for Intel).
    pub mr_signer_seam: [u8; 48],
}

impl TdxModulePolicy {
    /// Returns true iff the TDX module in the given report matches this module policy.
    pub fn matches(&self, report: &TdReport) -> bool {
        // Check MRSEAM if set.
        if let Some(mr_seam) = self.mr_seam {
            if mr_seam != report.mr_seam {
                return false;
            }
        }

        // Check MRSIGNER.
        if self.mr_signer_seam != report.mr_signer_seam {
            return false;
        }

        true
    }
}