underhill_attestation/
runtime_sealing.rs1mod tcb_floor;
17
18pub(crate) use tcb_floor::BootTcbFloor;
19pub use tcb_floor::RuntimeTcbFloor;
20pub use tcb_floor::saved_state::SavedRuntimeTcbFloor;
21pub use tcb_floor::saved_state::SavedRuntimeTcbFloorSvn;
22
23use crate::hardware_key_sealing::HardwareDerivedKeys;
24use crate::hardware_key_sealing::HardwareDerivedKeysError;
25use crate::hardware_key_sealing::HardwareKeySealingError;
26use crate::hardware_key_sealing::HwKeyProtector;
27use crate::hardware_key_sealing::seal_key;
28use crate::vmgs::parse_hardware_key_protector;
29use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationVmConfig;
30use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::HardwareSealingPolicy;
31use openhcl_attestation_protocol::vmgs;
32use tee_call::KeyDerivationPolicy;
33use tee_call::KeyDerivationSvn;
34use tee_call::REPORT_DATA_SIZE;
35use tee_call::TeeCall;
36use tee_call::TeeCallGetDerivedKey;
37use tee_call::TeeType;
38use thiserror::Error;
39use zerocopy::IntoBytes;
40
41#[derive(Debug, Error)]
44#[error(transparent)]
45pub struct Error(ErrorInner);
46
47#[derive(Debug, Error)]
48enum ErrorInner {
49 #[error("hardware sealing policy is disabled")]
50 DisabledPolicy,
51 #[error("TDX signer-based hardware sealing is unsupported")]
52 TdxSignerPolicyUnsupported,
53 #[error("TEE does not support hardware sealing")]
54 UnsupportedTee,
55 #[error("failed to obtain a local TEE report")]
56 Report(#[source] tee_call::Error),
57 #[error("local TEE report has no key derivation SVN")]
58 MissingKeyDerivationSvn,
59 #[error("local report key derivation SVN does not match the TEE")]
60 ReportSvnMismatch,
61 #[error("local SNP report is malformed or truncated")]
62 MalformedReport,
63 #[error("local SNP report TCB does not match its key derivation SVN")]
64 ReportTcbMismatch,
65 #[error("local TCB has a component below the runtime floor")]
66 TcbLowered,
67 #[error("local TCB is incompatible with the runtime floor comparison domain")]
68 TcbIncompatible,
69 #[error("saved runtime TCB floor has an invalid version, variant, or comparison domain")]
70 InvalidSavedTcbFloor,
71 #[error("failed to freshly derive hardware sealing keys")]
72 Derive(#[source] HardwareDerivedKeysError),
73 #[error("failed to unseal the hardware protector")]
74 Unseal(#[source] HardwareKeySealingError),
75 #[error("failed to seal the active DEK")]
76 Seal(#[source] HardwareKeySealingError),
77}
78
79fn sealing_context<'a>(
82 tee: &'a dyn TeeCall,
83 config: &AttestationVmConfig,
84) -> Result<(&'a dyn TeeCallGetDerivedKey, bool), Error> {
85 let mix_measurement = match config.hardware_sealing_policy {
86 HardwareSealingPolicy::None => return Err(Error(ErrorInner::DisabledPolicy)),
87 HardwareSealingPolicy::Hash => true,
88 HardwareSealingPolicy::Signer => false,
89 };
90 match tee.tee_type() {
91 TeeType::Snp => {}
92 TeeType::Tdx if mix_measurement => {}
93 TeeType::Tdx => return Err(Error(ErrorInner::TdxSignerPolicyUnsupported)),
94 _ => return Err(Error(ErrorInner::UnsupportedTee)),
95 }
96 let hardware = tee
97 .supports_get_derived_key()
98 .ok_or(Error(ErrorInner::UnsupportedTee))?;
99 Ok((hardware, mix_measurement))
100}
101
102fn svn_matches_tee(svn: KeyDerivationSvn, tee_type: TeeType) -> bool {
103 matches!(
104 (svn, tee_type),
105 (KeyDerivationSvn::Snp { .. }, TeeType::Snp) | (KeyDerivationSvn::Tdx { .. }, TeeType::Tdx)
106 )
107}
108
109fn validated_policy(protector: &HwKeyProtector) -> Option<KeyDerivationPolicy> {
112 match protector {
113 HwKeyProtector::Legacy(p) => {
114 if p.header.version != vmgs::HW_KEY_PROTECTOR_VERSION_2
115 || p.header.length as usize != vmgs::HW_KEY_PROTECTOR_SIZE
116 || p.header.mix_measurement > 1
117 || p.header._reserved != [0; 7]
118 {
119 return None;
120 }
121 }
122 HwKeyProtector::V3(p) => {
123 if p.header.version != vmgs::HW_KEY_PROTECTOR_VERSION_3
124 || p.header.length as usize != vmgs::HW_KEY_PROTECTOR_V3_SIZE
125 || p.header.mix_measurement > 1
126 || p.header._reserved != [0; 3]
127 {
128 return None;
129 }
130 if p.header.tee_type == vmgs::HW_KEY_PROTECTOR_TEE_TYPE_SNP
131 && p.header.svn[8..].iter().any(|&byte| byte != 0)
132 {
133 return None;
134 }
135 }
136 }
137 protector.key_derivation_policy()
138}
139
140pub fn protector_matches(
151 tee: &dyn TeeCall,
152 config: &AttestationVmConfig,
153 protector_bytes: &[u8],
154 dek: &[u8; 32],
155) -> Result<bool, Error> {
156 let (hardware, mix_measurement) = sealing_context(tee, config)?;
157 let Ok(protector) = parse_hardware_key_protector(protector_bytes) else {
158 return Ok(false);
159 };
160 let Some(policy) = validated_policy(&protector) else {
161 return Ok(false);
162 };
163 if policy.mix_measurement != mix_measurement || !svn_matches_tee(policy.svn, tee.tee_type()) {
164 return Ok(false);
165 }
166
167 let keys = HardwareDerivedKeys::derive_key(hardware, config, policy)
168 .map_err(|err| Error(ErrorInner::Derive(err)))?;
169 let unsealed = match protector.unseal_key(&keys) {
170 Ok(unsealed) => unsealed,
171 Err(HardwareKeySealingError::HardwareKeyProtectorHmacVerificationFailed) => {
172 return Ok(false);
173 }
174 Err(err) => return Err(Error(ErrorInner::Unseal(err))),
175 };
176 Ok(constant_time_eq::constant_time_eq_32(&unsealed, dek))
177}
178
179pub fn create_protector(
190 tee: &dyn TeeCall,
191 config: &AttestationVmConfig,
192 dek: &[u8; 32],
193) -> Result<Vec<u8>, Error> {
194 let (hardware, mix_measurement) = sealing_context(tee, config)?;
195 let report = tee
196 .get_attestation_report(&[0; REPORT_DATA_SIZE])
197 .map_err(|err| Error(ErrorInner::Report(err)))?;
198 let svn = report
199 .key_derivation_svn
200 .ok_or(Error(ErrorInner::MissingKeyDerivationSvn))?;
201 if !svn_matches_tee(svn, tee.tee_type()) {
202 return Err(Error(ErrorInner::ReportSvnMismatch));
203 }
204 create_protector_with_svn(hardware, config, svn, mix_measurement, dek)
205}
206
207fn create_protector_with_svn(
210 hardware: &dyn TeeCallGetDerivedKey,
211 config: &AttestationVmConfig,
212 svn: KeyDerivationSvn,
213 mix_measurement: bool,
214 dek: &[u8; 32],
215) -> Result<Vec<u8>, Error> {
216 let keys = HardwareDerivedKeys::derive_key(
217 hardware,
218 config,
219 KeyDerivationPolicy {
220 svn,
221 mix_measurement,
222 },
223 )
224 .map_err(|err| Error(ErrorInner::Derive(err)))?;
225 let protector = seal_key(&keys, dek).map_err(|err| Error(ErrorInner::Seal(err)))?;
226 Ok(protector.as_bytes().to_vec())
227}
228
229#[cfg(test)]
230mod tests;