1use crate::crypto;
9use cvm_tracing::CVM_ALLOWED;
10use openhcl_attestation_protocol::igvm_attest;
11use openhcl_attestation_protocol::vmgs;
12use openhcl_attestation_protocol::vmgs::HardwareKeyProtector;
13use openssl_kdf::kdf::Kbkdf;
14use thiserror::Error;
15use zerocopy::IntoBytes;
16
17#[derive(Debug, Error)]
18pub(crate) enum HardwareDerivedKeysError {
19 #[error("failed to initialize hardware secret")]
20 InitializeHardwareSecret(#[source] tee_call::Error),
21 #[error("KDF derivation with hardware secret failed")]
22 KdfWithHardwareSecret(#[source] openssl_kdf::kdf::KdfError),
23}
24
25#[derive(Debug, Error)]
26pub(crate) enum HardwareKeySealingError {
27 #[error("failed to encrypt the egress key")]
28 EncryptEgressKey(#[source] crypto::Aes256CbcError),
29 #[error("invalid egress key encryption size {0}, expected {1}")]
30 InvalidEgressKeyEncryptionSize(usize, usize),
31 #[error("HMAC-SHA-256 after encryption failed")]
32 HmacAfterEncrypt(#[source] crypto::HmacSha256Error),
33 #[error("HMAC-SHA-256 before ecryption failed")]
34 HmacBeforeDecrypt(#[source] crypto::HmacSha256Error),
35 #[error("Hardware key protector HMAC verification failed")]
36 HardwareKeyProtectorHmacVerificationFailed,
37 #[error("failed to decrypt the ingress key")]
38 DecryptIngressKey(#[source] crypto::Aes256CbcError),
39 #[error("invalid ingress key decryption size {0}, expected {1}")]
40 InvalidIngressKeyDecryptionSize(usize, usize),
41}
42
43pub struct HardwareDerivedKeys {
45 tcb_version: u64,
46 aes_key: [u8; vmgs::AES_CBC_KEY_LENGTH],
47 hmac_key: [u8; vmgs::HMAC_SHA_256_KEY_LENGTH],
48}
49
50impl HardwareDerivedKeys {
51 pub fn derive_key(
53 tee_call: &dyn tee_call::TeeCallGetDerivedKey,
54 vm_config: &igvm_attest::get::runtime_claims::AttestationVmConfig,
55 tcb_version: u64,
56 ) -> Result<Self, HardwareDerivedKeysError> {
57 let hardware_secret = tee_call
58 .get_derived_key(tcb_version)
59 .map_err(HardwareDerivedKeysError::InitializeHardwareSecret)?;
60 let label = b"ISOHWKEY";
61
62 let vm_config = serde_json::to_string(vm_config).expect("JSON serialization failed");
63
64 let mut kdf = Kbkdf::new(
65 openssl::hash::MessageDigest::sha256(),
66 label.to_vec(),
67 hardware_secret.to_vec(),
68 );
69 kdf.set_context(vm_config.as_bytes().to_vec());
70
71 let mut output = [0u8; vmgs::AES_CBC_KEY_LENGTH + vmgs::HMAC_SHA_256_KEY_LENGTH];
72 openssl_kdf::kdf::derive(kdf, &mut output)
73 .map_err(HardwareDerivedKeysError::KdfWithHardwareSecret)?;
74
75 let mut aes_key = [0u8; vmgs::AES_CBC_KEY_LENGTH];
76 let mut hmac_key = [0u8; vmgs::HMAC_SHA_256_KEY_LENGTH];
77
78 aes_key.copy_from_slice(&output[..vmgs::AES_CBC_KEY_LENGTH]);
79 hmac_key.copy_from_slice(&output[vmgs::AES_CBC_KEY_LENGTH..]);
80
81 Ok(Self {
82 tcb_version,
83 aes_key,
84 hmac_key,
85 })
86 }
87}
88
89pub trait HardwareKeyProtectorExt: Sized {
91 fn seal_key(
93 hardware_derived_keys: &HardwareDerivedKeys,
94 egress_key: &[u8],
95 ) -> Result<Self, HardwareKeySealingError>;
96
97 fn unseal_key(
99 &self,
100 hardware_derived_keys: &HardwareDerivedKeys,
101 ) -> Result<[u8; vmgs::AES_CBC_KEY_LENGTH], HardwareKeySealingError>;
102}
103
104impl HardwareKeyProtectorExt for HardwareKeyProtector {
105 fn seal_key(
106 hardware_derived_keys: &HardwareDerivedKeys,
107 egress_key: &[u8],
108 ) -> Result<Self, HardwareKeySealingError> {
109 let header = vmgs::HardwareKeyProtectorHeader::new(
110 vmgs::HW_KEY_VERSION,
111 vmgs::HW_KEY_PROTECTOR_SIZE as u32,
112 hardware_derived_keys.tcb_version,
113 );
114
115 let mut iv = [0u8; vmgs::AES_CBC_IV_LENGTH];
116 getrandom::fill(&mut iv).expect("rng failure");
117
118 let mut encrypted_egress_key = [0u8; vmgs::AES_GCM_KEY_LENGTH];
119 let output = crypto::aes_256_cbc_encrypt(&hardware_derived_keys.aes_key, egress_key, &iv)
120 .map_err(HardwareKeySealingError::EncryptEgressKey)?;
121 if output.len() != vmgs::AES_GCM_KEY_LENGTH {
122 Err(HardwareKeySealingError::InvalidEgressKeyEncryptionSize(
123 output.len(),
124 vmgs::AES_GCM_KEY_LENGTH,
125 ))?
126 }
127 encrypted_egress_key.copy_from_slice(&output[..vmgs::AES_GCM_KEY_LENGTH]);
128
129 let mut hardware_key_protector = Self {
130 header,
131 iv,
132 ciphertext: encrypted_egress_key,
133 hmac: [0u8; vmgs::HMAC_SHA_256_KEY_LENGTH],
134 };
135 let offset = std::mem::offset_of!(Self, hmac);
136 hardware_key_protector.hmac = crypto::hmac_sha_256(
137 &hardware_derived_keys.hmac_key,
138 &hardware_key_protector.as_bytes()[..offset],
139 )
140 .map_err(HardwareKeySealingError::HmacAfterEncrypt)?;
141
142 tracing::info!(CVM_ALLOWED, "encrypt egress_key using hardware derived key");
143
144 Ok(hardware_key_protector)
145 }
146
147 fn unseal_key(
148 &self,
149 hardware_derived_keys: &HardwareDerivedKeys,
150 ) -> Result<[u8; vmgs::AES_CBC_KEY_LENGTH], HardwareKeySealingError> {
151 let offset = std::mem::offset_of!(HardwareKeyProtector, hmac);
152 let hmac =
153 crypto::hmac_sha_256(&hardware_derived_keys.hmac_key, &self.as_bytes()[..offset])
154 .map_err(HardwareKeySealingError::HmacBeforeDecrypt)?;
155
156 if hmac != self.hmac {
157 Err(HardwareKeySealingError::HardwareKeyProtectorHmacVerificationFailed)?
158 }
159
160 let mut decrypted_ingress_key = [0u8; vmgs::AES_GCM_KEY_LENGTH];
161 let output =
162 crypto::aes_256_cbc_decrypt(&hardware_derived_keys.aes_key, &self.ciphertext, &self.iv)
163 .map_err(HardwareKeySealingError::DecryptIngressKey)?;
164 if output.len() != vmgs::AES_GCM_KEY_LENGTH {
165 Err(HardwareKeySealingError::InvalidIngressKeyDecryptionSize(
166 output.len(),
167 vmgs::AES_GCM_KEY_LENGTH,
168 ))?
169 }
170 decrypted_ingress_key.copy_from_slice(&output[..vmgs::AES_GCM_KEY_LENGTH]);
171
172 tracing::info!(
173 CVM_ALLOWED,
174 "decrypt ingress_key using hardware derived key"
175 );
176
177 Ok(decrypted_ingress_key)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use zerocopy::FromBytes;
185
186 struct MockTeeCall;
187
188 impl tee_call::TeeCall for MockTeeCall {
189 fn get_attestation_report(
190 &self,
191 _report_data: &[u8; 64],
192 ) -> Result<tee_call::GetAttestationReportResult, tee_call::Error> {
193 Ok(tee_call::GetAttestationReportResult {
194 report: vec![],
195 tcb_version: None,
196 })
197 }
198
199 fn supports_get_derived_key(&self) -> Option<&dyn tee_call::TeeCallGetDerivedKey> {
200 Some(self)
201 }
202
203 fn tee_type(&self) -> tee_call::TeeType {
204 tee_call::TeeType::Snp
205 }
206 }
207
208 impl tee_call::TeeCallGetDerivedKey for MockTeeCall {
209 fn get_derived_key(&self, _tcb_version: u64) -> Result<[u8; 32], tee_call::Error> {
210 const TEST_HW_DERIVED_KEY: [u8; tee_call::HW_DERIVED_KEY_LENGTH] = [
211 0xe0, 0xd8, 0x29, 0x04, 0xd6, 0x19, 0xd8, 0xdb, 0xd5, 0xd3, 0xba, 0x1c, 0x3c, 0x07,
212 0x2f, 0xaa, 0x56, 0x90, 0xa8, 0x95, 0x3e, 0x66, 0x69, 0x2e, 0xb9, 0xe7, 0xb4, 0xca,
213 0xaa, 0x3a, 0x92, 0x47,
214 ];
215
216 Ok(TEST_HW_DERIVED_KEY)
217 }
218 }
219
220 #[test]
221 fn hardware_derived_keys() {
222 const PLAINTEXT: [u8; 32] = [
223 0x5e, 0xd7, 0xf3, 0xd4, 0x9e, 0xcf, 0xb5, 0x6c, 0x05, 0x54, 0x7c, 0x87, 0xe7, 0x30,
224 0x59, 0xb1, 0x91, 0xcb, 0xa6, 0xc4, 0x0e, 0x4e, 0x30, 0x77, 0x65, 0x19, 0x71, 0xf5,
225 0x20, 0x83, 0x2a, 0xc0,
226 ];
227
228 let vm_config = igvm_attest::get::runtime_claims::AttestationVmConfig {
229 current_time: None,
230 root_cert_thumbprint: "".to_string(),
231 console_enabled: false,
232 secure_boot: false,
233 tpm_enabled: false,
234 tpm_persisted: false,
235 filtered_vpci_devices_allowed: true,
236 vm_unique_id: "".to_string(),
237 };
238 let mock_call = Box::new(MockTeeCall {}) as Box<dyn tee_call::TeeCall>;
239 let mock_get_derived_key_call = mock_call.supports_get_derived_key().unwrap();
240 let result = HardwareDerivedKeys::derive_key(
241 mock_get_derived_key_call,
242 &vm_config,
243 0x7308000000000003,
244 );
245 assert!(result.is_ok());
246 let hardware_derived_keys = result.unwrap();
247
248 let result = HardwareKeyProtector::seal_key(&hardware_derived_keys, &PLAINTEXT);
249 assert!(result.is_ok());
250 let output = result.unwrap();
251
252 let result = HardwareKeyProtector::read_from_prefix(output.as_bytes());
253 assert!(result.is_ok());
254 let hardware_key_protector = result.unwrap().0;
255
256 let result = hardware_key_protector.unseal_key(&hardware_derived_keys);
257 assert!(result.is_ok());
258 let plaintext = result.unwrap();
259 assert_eq!(plaintext, PLAINTEXT);
260 }
261}