1use guid::Guid;
7use openhcl_attestation_protocol::vmgs::AGENT_DATA_MAX_SIZE;
8use openhcl_attestation_protocol::vmgs::GUEST_SECRET_KEY_MAX_SIZE;
9use openhcl_attestation_protocol::vmgs::GuestSecretKey;
10use openhcl_attestation_protocol::vmgs::HardwareKeyProtector;
11use openhcl_attestation_protocol::vmgs::KeyProtector;
12use openhcl_attestation_protocol::vmgs::KeyProtectorById;
13use openhcl_attestation_protocol::vmgs::SecurityProfile;
14use thiserror::Error;
15use vmgs::FileId;
16use vmgs::Vmgs;
17use zerocopy::FromBytes;
18use zerocopy::FromZeros;
19use zerocopy::IntoBytes;
20
21#[derive(Debug, Error)]
22pub(crate) enum ReadFromVmgsError {
23 #[error("failed to read {file_id:?} from vmgs")]
24 ReadFromVmgs {
25 #[source]
26 vmgs_err: vmgs::Error,
27 file_id: FileId,
28 },
29 #[error("invalid data format, file id: {0:?}")]
30 InvalidFormat(FileId),
31 #[error("entry does not exist, file id: {0:?}")]
32 EntryNotFound(FileId),
33 #[error("{file_id:?} valid bytes {size} smaller than the minimal size {minimal_size}")]
34 EntrySizeTooSmall {
35 file_id: FileId,
36 size: usize,
37 minimal_size: usize,
38 },
39 #[error("{file_id:?} valid bytes {size} larger than the maximum size {maximum_size}")]
40 EntrySizeTooLarge {
41 file_id: FileId,
42 size: usize,
43 maximum_size: usize,
44 },
45 #[error("{file_id:?} valid bytes {size}, expected {expected_size}")]
46 EntrySizeUnexpected {
47 file_id: FileId,
48 size: usize,
49 expected_size: usize,
50 },
51}
52
53#[derive(Debug, Error)]
55#[error("failed to write {file_id:?} to vmgs")]
56pub(crate) struct WriteToVmgsError {
57 #[source]
58 vmgs_err: vmgs::Error,
59 file_id: FileId,
60}
61
62pub async fn read_key_protector(
65 vmgs: &mut Vmgs,
66 dek_minimal_size: usize,
67) -> Result<KeyProtector, ReadFromVmgsError> {
68 use openhcl_attestation_protocol::vmgs::KEY_PROTECTOR_SIZE;
69
70 let file_id = FileId::KEY_PROTECTOR;
71 match vmgs.read_file(file_id).await {
72 Ok(data) => {
73 if data.len() < dek_minimal_size {
74 Err(ReadFromVmgsError::EntrySizeTooSmall {
75 file_id,
76 size: data.len(),
77 minimal_size: dek_minimal_size,
78 })?
79 }
80
81 if data.len() > KEY_PROTECTOR_SIZE {
82 Err(ReadFromVmgsError::EntrySizeTooLarge {
83 file_id,
84 size: data.len(),
85 maximum_size: KEY_PROTECTOR_SIZE,
86 })?
87 }
88
89 let data = if data.len() < KEY_PROTECTOR_SIZE {
90 let mut data = data;
92 data.resize(KEY_PROTECTOR_SIZE, 0);
93 data
94 } else {
95 data
96 };
97
98 KeyProtector::read_from_prefix(&data[..])
100 .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))
101 .map(|k| k.0) }
103 Err(vmgs::Error::FileInfoAllocated) => Ok(KeyProtector::new_zeroed()),
104 Err(vmgs_err) => Err(ReadFromVmgsError::ReadFromVmgs { vmgs_err, file_id }),
105 }
106}
107
108pub async fn write_key_protector(
110 key_protector: &KeyProtector,
111 vmgs: &mut Vmgs,
112) -> Result<(), WriteToVmgsError> {
113 let file_id = FileId::KEY_PROTECTOR;
114 vmgs.write_file(file_id, key_protector.as_bytes())
115 .await
116 .map_err(|vmgs_err| WriteToVmgsError { vmgs_err, file_id })
117}
118
119pub async fn read_key_protector_by_id(
121 vmgs: &mut Vmgs,
122) -> Result<KeyProtectorById, ReadFromVmgsError> {
123 let file_id = FileId::VM_UNIQUE_ID;
129 match vmgs.read_file(file_id).await {
130 Ok(data) => match KeyProtectorById::read_from_prefix(&data[..])
131 .ok() .map(|k| k.0)
133 {
134 Some(key_protector_by_id) => Ok(key_protector_by_id),
135 None => {
136 let id_guid = Guid::read_from_prefix(&data[..])
137 .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))? .0;
139
140 Ok(KeyProtectorById {
141 id_guid,
142 ..FromZeros::new_zeroed()
143 })
144 }
145 },
146 Err(vmgs::Error::FileInfoAllocated) => Err(ReadFromVmgsError::EntryNotFound(file_id)),
147 Err(vmgs_err) => Err(ReadFromVmgsError::ReadFromVmgs { vmgs_err, file_id }),
148 }
149}
150
151pub async fn write_key_protector_by_id(
156 key_protector_by_id: &mut KeyProtectorById,
157 vmgs: &mut Vmgs,
158 force_write: bool,
159 bios_guid: Guid,
160) -> Result<(), WriteToVmgsError> {
161 if force_write || bios_guid != key_protector_by_id.id_guid {
162 let file_id = FileId::VM_UNIQUE_ID;
163 key_protector_by_id.id_guid = bios_guid;
164 vmgs.write_file(file_id, key_protector_by_id.as_bytes())
165 .await
166 .map_err(|vmgs_err| WriteToVmgsError { vmgs_err, file_id })?
167 }
168
169 Ok(())
170}
171
172pub async fn read_security_profile(vmgs: &mut Vmgs) -> Result<SecurityProfile, ReadFromVmgsError> {
175 let file_id = FileId::ATTEST;
176 match vmgs.read_file(file_id).await {
177 Ok(data) => {
178 if data.len() > AGENT_DATA_MAX_SIZE {
179 Err(ReadFromVmgsError::EntrySizeTooLarge {
180 file_id,
181 size: data.len(),
182 maximum_size: AGENT_DATA_MAX_SIZE,
183 })?
184 }
185
186 let data = if data.len() < AGENT_DATA_MAX_SIZE {
187 let mut data = data;
189 data.resize(AGENT_DATA_MAX_SIZE, 0);
190 data
191 } else {
192 data
193 };
194
195 Ok(SecurityProfile::read_from_prefix(&data[..])
197 .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))?
198 .0) }
200 Err(vmgs::Error::FileInfoAllocated) => Ok(SecurityProfile::new_zeroed()),
201 Err(vmgs_err) => Err(ReadFromVmgsError::ReadFromVmgs { file_id, vmgs_err })?,
202 }
203}
204
205pub async fn read_hardware_key_protector(
207 vmgs: &mut Vmgs,
208) -> Result<HardwareKeyProtector, ReadFromVmgsError> {
209 use openhcl_attestation_protocol::vmgs::HW_KEY_PROTECTOR_SIZE;
210
211 let file_id = FileId::HW_KEY_PROTECTOR;
212 let data = vmgs
213 .read_file(file_id)
214 .await
215 .map_err(|vmgs_err| ReadFromVmgsError::ReadFromVmgs { vmgs_err, file_id })?;
216
217 if data.len() != HW_KEY_PROTECTOR_SIZE {
218 Err(ReadFromVmgsError::EntrySizeUnexpected {
219 file_id,
220 size: data.len(),
221 expected_size: HW_KEY_PROTECTOR_SIZE,
222 })?
223 }
224
225 HardwareKeyProtector::read_from_prefix(&data)
226 .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))
227 .map(|k| k.0) }
229
230pub async fn write_hardware_key_protector(
232 hardware_key_protector: &HardwareKeyProtector,
233 vmgs: &mut Vmgs,
234) -> Result<(), WriteToVmgsError> {
235 let file_id = FileId::HW_KEY_PROTECTOR;
236 vmgs.write_file(file_id, hardware_key_protector.as_bytes())
237 .await
238 .map_err(|vmgs_err| WriteToVmgsError { vmgs_err, file_id })
239}
240
241pub async fn read_guest_secret_key(vmgs: &mut Vmgs) -> Result<GuestSecretKey, ReadFromVmgsError> {
243 let file_id = FileId::GUEST_SECRET_KEY;
244 match vmgs.read_file(file_id).await {
245 Ok(data) => {
246 if data.len() > GUEST_SECRET_KEY_MAX_SIZE {
247 Err(ReadFromVmgsError::EntrySizeTooLarge {
248 file_id,
249 size: data.len(),
250 maximum_size: GUEST_SECRET_KEY_MAX_SIZE,
251 })?
252 }
253
254 let data = if data.len() < GUEST_SECRET_KEY_MAX_SIZE {
255 let mut data = data;
257 data.resize(GUEST_SECRET_KEY_MAX_SIZE, 0);
258 data
259 } else {
260 data
261 };
262
263 Ok(GuestSecretKey::read_from_prefix(&data[..])
265 .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))?
266 .0) }
268 Err(vmgs::Error::FileInfoAllocated) => Err(ReadFromVmgsError::EntryNotFound(file_id)),
269 Err(vmgs_err) => Err(ReadFromVmgsError::ReadFromVmgs { file_id, vmgs_err }),
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276 use disk_backend::Disk;
277 use disklayer_ram::ram_disk;
278 use openhcl_attestation_protocol::vmgs::AES_CBC_IV_LENGTH;
279 use openhcl_attestation_protocol::vmgs::AES_GCM_KEY_LENGTH;
280 use openhcl_attestation_protocol::vmgs::DEK_BUFFER_SIZE;
281 use openhcl_attestation_protocol::vmgs::DekKp;
282 use openhcl_attestation_protocol::vmgs::GSP_BUFFER_SIZE;
283 use openhcl_attestation_protocol::vmgs::GspKp;
284 use openhcl_attestation_protocol::vmgs::HMAC_SHA_256_KEY_LENGTH;
285 use openhcl_attestation_protocol::vmgs::HW_KEY_PROTECTOR_SIZE;
286 use openhcl_attestation_protocol::vmgs::HardwareKeyProtectorHeader;
287 use openhcl_attestation_protocol::vmgs::KEY_PROTECTOR_SIZE;
288 use openhcl_attestation_protocol::vmgs::KeyProtector;
289 use openhcl_attestation_protocol::vmgs::KeyProtectorById;
290 use openhcl_attestation_protocol::vmgs::NUMBER_KP;
291 use pal_async::async_test;
292
293 const ONE_MEGA_BYTE: u64 = 1024 * 1024;
294
295 fn new_test_file() -> Disk {
296 ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
297 }
298
299 async fn new_formatted_vmgs() -> Vmgs {
300 let disk = new_test_file();
301
302 Vmgs::format_new(disk, None).await.unwrap()
303 }
304
305 fn new_hardware_key_protector() -> HardwareKeyProtector {
306 let header = HardwareKeyProtectorHeader::new(1, HW_KEY_PROTECTOR_SIZE as u32, 2);
307 let iv = [3; AES_CBC_IV_LENGTH];
308 let ciphertext = [4; AES_GCM_KEY_LENGTH];
309 let hmac = [5; HMAC_SHA_256_KEY_LENGTH];
310
311 HardwareKeyProtector {
312 header,
313 iv,
314 ciphertext,
315 hmac,
316 }
317 }
318
319 fn new_key_protector() -> KeyProtector {
320 assert_eq!(NUMBER_KP, 2);
322
323 let ingress_dek = DekKp {
324 dek_buffer: [1; DEK_BUFFER_SIZE],
325 };
326 let egress_dek = DekKp {
327 dek_buffer: [2; DEK_BUFFER_SIZE],
328 };
329 let ingress_gsp = GspKp {
330 gsp_length: GSP_BUFFER_SIZE as u32,
331 gsp_buffer: [3; GSP_BUFFER_SIZE],
332 };
333 let egress_gsp = GspKp {
334 gsp_length: GSP_BUFFER_SIZE as u32,
335 gsp_buffer: [4; GSP_BUFFER_SIZE],
336 };
337 KeyProtector {
338 dek: [ingress_dek, egress_dek],
339 gsp: [ingress_gsp, egress_gsp],
340 active_kp: u32::MAX,
341 }
342 }
343
344 #[async_test]
345 async fn write_read_vmgs_key_protector() {
346 let mut vmgs = new_formatted_vmgs().await;
347 let key_protector = new_key_protector();
348 write_key_protector(&key_protector, &mut vmgs)
349 .await
350 .unwrap();
351
352 let key_protector = read_key_protector(&mut vmgs, KEY_PROTECTOR_SIZE)
353 .await
354 .unwrap();
355
356 assert!(key_protector.dek[0].dek_buffer.iter().all(|&x| x == 1));
357 assert!(key_protector.dek[1].dek_buffer.iter().all(|&x| x == 2));
358
359 assert_eq!(key_protector.gsp[0].gsp_length, GSP_BUFFER_SIZE as u32);
360 assert!(key_protector.gsp[0].gsp_buffer.iter().all(|&x| x == 3));
361
362 assert_eq!(key_protector.gsp[1].gsp_length, GSP_BUFFER_SIZE as u32);
363 assert!(key_protector.gsp[1].gsp_buffer.iter().all(|&x| x == 4));
364
365 assert_eq!(key_protector.active_kp, u32::MAX);
366
367 let key_protector_bytes = key_protector.as_bytes();
369 vmgs.write_file(
370 FileId::KEY_PROTECTOR,
371 &key_protector_bytes[..key_protector_bytes.len() - 1],
372 )
373 .await
374 .unwrap();
375 let found_key_protector_result =
376 read_key_protector(&mut vmgs, key_protector_bytes.len()).await;
377 assert!(found_key_protector_result.is_err());
378 assert_eq!(
379 found_key_protector_result.unwrap_err().to_string(),
380 "KEY_PROTECTOR valid bytes 2059 smaller than the minimal size 2060"
381 );
382
383 vmgs.write_file(FileId::KEY_PROTECTOR, &[1; KEY_PROTECTOR_SIZE + 1])
385 .await
386 .unwrap();
387 let found_key_protector_result = read_key_protector(&mut vmgs, KEY_PROTECTOR_SIZE).await;
388 assert!(found_key_protector_result.is_err());
389 assert_eq!(
390 found_key_protector_result.unwrap_err().to_string(),
391 "KEY_PROTECTOR valid bytes 2061 larger than the maximum size 2060"
392 );
393
394 vmgs.write_file(
397 FileId::KEY_PROTECTOR,
398 &key_protector_bytes[..(key_protector_bytes.len() - 10)],
399 )
400 .await
401 .unwrap();
402 let found_key_protector = read_key_protector(&mut vmgs, key_protector_bytes.len() - 10)
403 .await
404 .unwrap();
405 assert_eq!(
406 found_key_protector.as_bytes()[..(key_protector_bytes.len() - 10)],
407 key_protector_bytes[..(key_protector_bytes.len() - 10)]
408 );
409 assert_eq!(
410 found_key_protector.as_bytes()[key_protector_bytes.len() - 10..],
411 [0; 10]
412 );
413 }
414
415 #[async_test]
416 async fn write_vmgs_key_protector_by_id() {
417 let kp_guid = Guid::new_random();
418
419 let mut vmgs = new_formatted_vmgs().await;
420 let mut key_protector_by_id = KeyProtectorById {
421 id_guid: kp_guid,
422 ported: 1,
423 pad: [0; 3],
424 };
425
426 let found_key_protector_by_id_result = read_key_protector_by_id(&mut vmgs).await;
428 assert!(found_key_protector_by_id_result.is_err());
429 assert_eq!(
430 found_key_protector_by_id_result.unwrap_err().to_string(),
431 "entry does not exist, file id: VM_UNIQUE_ID"
432 );
433
434 write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, true, kp_guid)
436 .await
437 .unwrap();
438
439 write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, false, kp_guid)
441 .await
442 .unwrap();
443 let found_key_protector_by_id = read_key_protector_by_id(&mut vmgs).await.unwrap();
445 assert_eq!(found_key_protector_by_id.id_guid, kp_guid);
446
447 let bios_guid = Guid::new_random();
449 write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, false, bios_guid)
450 .await
451 .unwrap();
452 let found_key_protector_by_id = read_key_protector_by_id(&mut vmgs).await.unwrap();
454 assert_eq!(found_key_protector_by_id.id_guid, bios_guid);
455
456 let undersized_key_protector_by_id = key_protector_by_id.as_bytes();
459 let undersized_key_protector_by_id =
460 &undersized_key_protector_by_id[..undersized_key_protector_by_id.len() - 1];
461 vmgs.write_file(FileId::VM_UNIQUE_ID, undersized_key_protector_by_id)
462 .await
463 .unwrap();
464
465 let found_key_protector_by_id = read_key_protector_by_id(&mut vmgs).await.unwrap();
466 assert_eq!(
467 found_key_protector_by_id.id_guid,
468 key_protector_by_id.id_guid
469 );
470 assert_eq!(found_key_protector_by_id.ported, 0);
471 assert_eq!(found_key_protector_by_id.pad, [0, 0, 0]);
472 }
473
474 #[async_test]
475 async fn read_security_profile_from_vmgs() {
476 let mut vmgs = new_formatted_vmgs().await;
477 let found_security_profile = read_security_profile(&mut vmgs).await.unwrap();
478
479 assert_eq!(
481 found_security_profile.agent_data,
482 SecurityProfile::new_zeroed().agent_data
483 );
484
485 let security_profile = SecurityProfile {
487 agent_data: [5; AGENT_DATA_MAX_SIZE],
488 };
489 vmgs.write_file(FileId::ATTEST, security_profile.as_bytes())
490 .await
491 .unwrap();
492 let found_security_profile = read_security_profile(&mut vmgs).await.unwrap();
493 assert_eq!(
494 found_security_profile.agent_data,
495 security_profile.agent_data
496 );
497
498 let oversized_security_profile = [6u8; AGENT_DATA_MAX_SIZE + 1];
500 vmgs.write_file(FileId::ATTEST, oversized_security_profile.as_bytes())
501 .await
502 .unwrap();
503 let found_security_profile_result = read_security_profile(&mut vmgs).await;
504 assert!(found_security_profile_result.is_err());
505 assert_eq!(
506 found_security_profile_result.unwrap_err().to_string(),
507 "ATTEST valid bytes 2049 larger than the maximum size 2048"
508 );
509
510 let undersized_security_profile = [7u8; AGENT_DATA_MAX_SIZE - 10];
512 vmgs.write_file(FileId::ATTEST, undersized_security_profile.as_bytes())
513 .await
514 .unwrap();
515 let found_security_profile = read_security_profile(&mut vmgs).await.unwrap();
516 assert_eq!(
517 found_security_profile.agent_data[..AGENT_DATA_MAX_SIZE - 10],
518 undersized_security_profile[..]
519 );
520 assert_eq!(
521 found_security_profile.agent_data[AGENT_DATA_MAX_SIZE - 10..],
522 [0; 10]
523 );
524 }
525
526 #[async_test]
527 async fn write_read_hardware_key_protector() {
528 let mut vmgs = new_formatted_vmgs().await;
529 let hardware_key_protector = new_hardware_key_protector();
530 write_hardware_key_protector(&hardware_key_protector, &mut vmgs)
531 .await
532 .unwrap();
533
534 let found_hardware_key_protector = read_hardware_key_protector(&mut vmgs).await.unwrap();
535
536 assert_eq!(
537 found_hardware_key_protector.header.as_bytes(),
538 hardware_key_protector.header.as_bytes()
539 );
540 assert_eq!(found_hardware_key_protector.iv, hardware_key_protector.iv);
541 assert_eq!(
542 found_hardware_key_protector.ciphertext,
543 hardware_key_protector.ciphertext
544 );
545 assert_eq!(
546 found_hardware_key_protector.hmac,
547 hardware_key_protector.hmac
548 );
549
550 let oversized_hardware_key_protector = [8u8; HW_KEY_PROTECTOR_SIZE + 1];
552 vmgs.write_file(FileId::HW_KEY_PROTECTOR, &oversized_hardware_key_protector)
553 .await
554 .unwrap();
555 let found_hardware_key_protector_result = read_hardware_key_protector(&mut vmgs).await;
556 assert!(found_hardware_key_protector_result.is_err());
557 assert_eq!(
558 found_hardware_key_protector_result.unwrap_err().to_string(),
559 "HW_KEY_PROTECTOR valid bytes 105, expected 104"
560 );
561 }
562
563 #[async_test]
564 async fn read_guest_secret_key_from_vmgs() {
565 let mut vmgs = new_formatted_vmgs().await;
566
567 let found_guest_secret_key_result = read_guest_secret_key(&mut vmgs).await;
569 assert!(found_guest_secret_key_result.is_err());
570 assert_eq!(
571 found_guest_secret_key_result.unwrap_err().to_string(),
572 "entry does not exist, file id: GUEST_SECRET_KEY"
573 );
574
575 let guest_secret_key = GuestSecretKey {
577 guest_secret_key: [9; GUEST_SECRET_KEY_MAX_SIZE],
578 };
579 vmgs.write_file(FileId::GUEST_SECRET_KEY, guest_secret_key.as_bytes())
580 .await
581 .unwrap();
582 let found_guest_secret_key = read_guest_secret_key(&mut vmgs).await.unwrap();
583 assert_eq!(
584 found_guest_secret_key.guest_secret_key,
585 guest_secret_key.guest_secret_key
586 );
587
588 let oversized_guest_secret_key = [10u8; GUEST_SECRET_KEY_MAX_SIZE + 1];
590 vmgs.write_file(FileId::GUEST_SECRET_KEY, &oversized_guest_secret_key)
591 .await
592 .unwrap();
593 let found_guest_secret_key_result = read_guest_secret_key(&mut vmgs).await;
594 assert!(found_guest_secret_key_result.is_err());
595 assert_eq!(
596 found_guest_secret_key_result.unwrap_err().to_string(),
597 "GUEST_SECRET_KEY valid bytes 2049 larger than the maximum size 2048"
598 );
599
600 let undersized_guest_secret_key = [7u8; GUEST_SECRET_KEY_MAX_SIZE - 10];
602 vmgs.write_file(
603 FileId::GUEST_SECRET_KEY,
604 undersized_guest_secret_key.as_bytes(),
605 )
606 .await
607 .unwrap();
608 let found_guest_secret_key = read_guest_secret_key(&mut vmgs).await.unwrap();
609 assert_eq!(
610 found_guest_secret_key.guest_secret_key[..AGENT_DATA_MAX_SIZE - 10],
611 undersized_guest_secret_key[..]
612 );
613 assert_eq!(
614 found_guest_secret_key.guest_secret_key[AGENT_DATA_MAX_SIZE - 10..],
615 [0; 10]
616 );
617 }
618}