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::FileInfoNotAllocated) => 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::FileInfoNotAllocated) => 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::FileInfoNotAllocated) => 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 = match vmgs.read_file(file_id).await {
213 Ok(data) => data,
214 Err(vmgs::Error::FileInfoNotAllocated) => {
215 return Err(ReadFromVmgsError::EntryNotFound(file_id));
216 }
217 Err(vmgs_err) => {
218 return Err(ReadFromVmgsError::ReadFromVmgs { vmgs_err, file_id });
219 }
220 };
221
222 if data.len() != HW_KEY_PROTECTOR_SIZE {
223 Err(ReadFromVmgsError::EntrySizeUnexpected {
224 file_id,
225 size: data.len(),
226 expected_size: HW_KEY_PROTECTOR_SIZE,
227 })?
228 }
229
230 HardwareKeyProtector::read_from_prefix(&data)
231 .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))
232 .map(|k| k.0) }
234
235pub async fn write_hardware_key_protector(
237 hardware_key_protector: &HardwareKeyProtector,
238 vmgs: &mut Vmgs,
239) -> Result<(), WriteToVmgsError> {
240 let file_id = FileId::HW_KEY_PROTECTOR;
241 vmgs.write_file(file_id, hardware_key_protector.as_bytes())
242 .await
243 .map_err(|vmgs_err| WriteToVmgsError { vmgs_err, file_id })
244}
245
246pub async fn read_guest_secret_key(vmgs: &mut Vmgs) -> Result<GuestSecretKey, ReadFromVmgsError> {
248 let file_id = FileId::GUEST_SECRET_KEY;
249 match vmgs.read_file(file_id).await {
250 Ok(data) => {
251 if data.len() > GUEST_SECRET_KEY_MAX_SIZE {
252 Err(ReadFromVmgsError::EntrySizeTooLarge {
253 file_id,
254 size: data.len(),
255 maximum_size: GUEST_SECRET_KEY_MAX_SIZE,
256 })?
257 }
258
259 let data = if data.len() < GUEST_SECRET_KEY_MAX_SIZE {
260 let mut data = data;
262 data.resize(GUEST_SECRET_KEY_MAX_SIZE, 0);
263 data
264 } else {
265 data
266 };
267
268 Ok(GuestSecretKey::read_from_prefix(&data[..])
270 .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))?
271 .0) }
273 Err(vmgs::Error::FileInfoNotAllocated) => Err(ReadFromVmgsError::EntryNotFound(file_id)),
274 Err(vmgs_err) => Err(ReadFromVmgsError::ReadFromVmgs { file_id, vmgs_err }),
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use disk_backend::Disk;
282 use disklayer_ram::ram_disk;
283 use openhcl_attestation_protocol::vmgs::AES_CBC_IV_LENGTH;
284 use openhcl_attestation_protocol::vmgs::AES_GCM_KEY_LENGTH;
285 use openhcl_attestation_protocol::vmgs::DEK_BUFFER_SIZE;
286 use openhcl_attestation_protocol::vmgs::DekKp;
287 use openhcl_attestation_protocol::vmgs::GSP_BUFFER_SIZE;
288 use openhcl_attestation_protocol::vmgs::GspKp;
289 use openhcl_attestation_protocol::vmgs::HMAC_SHA_256_KEY_LENGTH;
290 use openhcl_attestation_protocol::vmgs::HW_KEY_PROTECTOR_SIZE;
291 use openhcl_attestation_protocol::vmgs::HardwareKeyProtectorHeader;
292 use openhcl_attestation_protocol::vmgs::KEY_PROTECTOR_SIZE;
293 use openhcl_attestation_protocol::vmgs::KeyProtector;
294 use openhcl_attestation_protocol::vmgs::KeyProtectorById;
295 use openhcl_attestation_protocol::vmgs::NUMBER_KP;
296 use pal_async::async_test;
297
298 const ONE_MEGA_BYTE: u64 = 1024 * 1024;
299
300 fn new_test_file() -> Disk {
301 ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
302 }
303
304 async fn new_formatted_vmgs() -> Vmgs {
305 let disk = new_test_file();
306
307 Vmgs::format_new(disk, None).await.unwrap()
308 }
309
310 fn new_hardware_key_protector() -> HardwareKeyProtector {
311 let header = HardwareKeyProtectorHeader::new(1, HW_KEY_PROTECTOR_SIZE as u32, 2);
312 let iv = [3; AES_CBC_IV_LENGTH];
313 let ciphertext = [4; AES_GCM_KEY_LENGTH];
314 let hmac = [5; HMAC_SHA_256_KEY_LENGTH];
315
316 HardwareKeyProtector {
317 header,
318 iv,
319 ciphertext,
320 hmac,
321 }
322 }
323
324 fn new_key_protector() -> KeyProtector {
325 assert_eq!(NUMBER_KP, 2);
327
328 let ingress_dek = DekKp {
329 dek_buffer: [1; DEK_BUFFER_SIZE],
330 };
331 let egress_dek = DekKp {
332 dek_buffer: [2; DEK_BUFFER_SIZE],
333 };
334 let ingress_gsp = GspKp {
335 gsp_length: GSP_BUFFER_SIZE as u32,
336 gsp_buffer: [3; GSP_BUFFER_SIZE],
337 };
338 let egress_gsp = GspKp {
339 gsp_length: GSP_BUFFER_SIZE as u32,
340 gsp_buffer: [4; GSP_BUFFER_SIZE],
341 };
342 KeyProtector {
343 dek: [ingress_dek, egress_dek],
344 gsp: [ingress_gsp, egress_gsp],
345 active_kp: u32::MAX,
346 }
347 }
348
349 #[async_test]
350 async fn write_read_vmgs_key_protector() {
351 let mut vmgs = new_formatted_vmgs().await;
352 let key_protector = new_key_protector();
353 write_key_protector(&key_protector, &mut vmgs)
354 .await
355 .unwrap();
356
357 let key_protector = read_key_protector(&mut vmgs, KEY_PROTECTOR_SIZE)
358 .await
359 .unwrap();
360
361 assert!(key_protector.dek[0].dek_buffer.iter().all(|&x| x == 1));
362 assert!(key_protector.dek[1].dek_buffer.iter().all(|&x| x == 2));
363
364 assert_eq!(key_protector.gsp[0].gsp_length, GSP_BUFFER_SIZE as u32);
365 assert!(key_protector.gsp[0].gsp_buffer.iter().all(|&x| x == 3));
366
367 assert_eq!(key_protector.gsp[1].gsp_length, GSP_BUFFER_SIZE as u32);
368 assert!(key_protector.gsp[1].gsp_buffer.iter().all(|&x| x == 4));
369
370 assert_eq!(key_protector.active_kp, u32::MAX);
371
372 let key_protector_bytes = key_protector.as_bytes();
374 vmgs.write_file(
375 FileId::KEY_PROTECTOR,
376 &key_protector_bytes[..key_protector_bytes.len() - 1],
377 )
378 .await
379 .unwrap();
380 let found_key_protector_result =
381 read_key_protector(&mut vmgs, key_protector_bytes.len()).await;
382 assert!(found_key_protector_result.is_err());
383 assert_eq!(
384 found_key_protector_result.unwrap_err().to_string(),
385 "KEY_PROTECTOR valid bytes 2059 smaller than the minimal size 2060"
386 );
387
388 vmgs.write_file(FileId::KEY_PROTECTOR, &[1; KEY_PROTECTOR_SIZE + 1])
390 .await
391 .unwrap();
392 let found_key_protector_result = read_key_protector(&mut vmgs, KEY_PROTECTOR_SIZE).await;
393 assert!(found_key_protector_result.is_err());
394 assert_eq!(
395 found_key_protector_result.unwrap_err().to_string(),
396 "KEY_PROTECTOR valid bytes 2061 larger than the maximum size 2060"
397 );
398
399 vmgs.write_file(
402 FileId::KEY_PROTECTOR,
403 &key_protector_bytes[..(key_protector_bytes.len() - 10)],
404 )
405 .await
406 .unwrap();
407 let found_key_protector = read_key_protector(&mut vmgs, key_protector_bytes.len() - 10)
408 .await
409 .unwrap();
410 assert_eq!(
411 found_key_protector.as_bytes()[..(key_protector_bytes.len() - 10)],
412 key_protector_bytes[..(key_protector_bytes.len() - 10)]
413 );
414 assert_eq!(
415 found_key_protector.as_bytes()[key_protector_bytes.len() - 10..],
416 [0; 10]
417 );
418 }
419
420 #[async_test]
421 async fn write_vmgs_key_protector_by_id() {
422 let kp_guid = Guid::new_random();
423
424 let mut vmgs = new_formatted_vmgs().await;
425 let mut key_protector_by_id = KeyProtectorById {
426 id_guid: kp_guid,
427 ported: 1,
428 pad: [0; 3],
429 };
430
431 let found_key_protector_by_id_result = read_key_protector_by_id(&mut vmgs).await;
433 assert!(found_key_protector_by_id_result.is_err());
434 assert_eq!(
435 found_key_protector_by_id_result.unwrap_err().to_string(),
436 "entry does not exist, file id: VM_UNIQUE_ID"
437 );
438
439 write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, true, kp_guid)
441 .await
442 .unwrap();
443
444 write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, false, kp_guid)
446 .await
447 .unwrap();
448 let found_key_protector_by_id = read_key_protector_by_id(&mut vmgs).await.unwrap();
450 assert_eq!(found_key_protector_by_id.id_guid, kp_guid);
451
452 let bios_guid = Guid::new_random();
454 write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, false, bios_guid)
455 .await
456 .unwrap();
457 let found_key_protector_by_id = read_key_protector_by_id(&mut vmgs).await.unwrap();
459 assert_eq!(found_key_protector_by_id.id_guid, bios_guid);
460
461 let undersized_key_protector_by_id = key_protector_by_id.as_bytes();
464 let undersized_key_protector_by_id =
465 &undersized_key_protector_by_id[..undersized_key_protector_by_id.len() - 1];
466 vmgs.write_file(FileId::VM_UNIQUE_ID, undersized_key_protector_by_id)
467 .await
468 .unwrap();
469
470 let found_key_protector_by_id = read_key_protector_by_id(&mut vmgs).await.unwrap();
471 assert_eq!(
472 found_key_protector_by_id.id_guid,
473 key_protector_by_id.id_guid
474 );
475 assert_eq!(found_key_protector_by_id.ported, 0);
476 assert_eq!(found_key_protector_by_id.pad, [0, 0, 0]);
477 }
478
479 #[async_test]
480 async fn read_security_profile_from_vmgs() {
481 let mut vmgs = new_formatted_vmgs().await;
482 let found_security_profile = read_security_profile(&mut vmgs).await.unwrap();
483
484 assert_eq!(
486 found_security_profile.agent_data,
487 SecurityProfile::new_zeroed().agent_data
488 );
489
490 let security_profile = SecurityProfile {
492 agent_data: [5; AGENT_DATA_MAX_SIZE],
493 };
494 vmgs.write_file(FileId::ATTEST, security_profile.as_bytes())
495 .await
496 .unwrap();
497 let found_security_profile = read_security_profile(&mut vmgs).await.unwrap();
498 assert_eq!(
499 found_security_profile.agent_data,
500 security_profile.agent_data
501 );
502
503 let oversized_security_profile = [6u8; AGENT_DATA_MAX_SIZE + 1];
505 vmgs.write_file(FileId::ATTEST, oversized_security_profile.as_bytes())
506 .await
507 .unwrap();
508 let found_security_profile_result = read_security_profile(&mut vmgs).await;
509 assert!(found_security_profile_result.is_err());
510 assert_eq!(
511 found_security_profile_result.unwrap_err().to_string(),
512 "ATTEST valid bytes 2049 larger than the maximum size 2048"
513 );
514
515 let undersized_security_profile = [7u8; AGENT_DATA_MAX_SIZE - 10];
517 vmgs.write_file(FileId::ATTEST, undersized_security_profile.as_bytes())
518 .await
519 .unwrap();
520 let found_security_profile = read_security_profile(&mut vmgs).await.unwrap();
521 assert_eq!(
522 found_security_profile.agent_data[..AGENT_DATA_MAX_SIZE - 10],
523 undersized_security_profile[..]
524 );
525 assert_eq!(
526 found_security_profile.agent_data[AGENT_DATA_MAX_SIZE - 10..],
527 [0; 10]
528 );
529 }
530
531 #[async_test]
532 async fn write_read_hardware_key_protector() {
533 let mut vmgs = new_formatted_vmgs().await;
534 let hardware_key_protector = new_hardware_key_protector();
535 write_hardware_key_protector(&hardware_key_protector, &mut vmgs)
536 .await
537 .unwrap();
538
539 let found_hardware_key_protector = read_hardware_key_protector(&mut vmgs).await.unwrap();
540
541 assert_eq!(
542 found_hardware_key_protector.header.as_bytes(),
543 hardware_key_protector.header.as_bytes()
544 );
545 assert_eq!(found_hardware_key_protector.iv, hardware_key_protector.iv);
546 assert_eq!(
547 found_hardware_key_protector.ciphertext,
548 hardware_key_protector.ciphertext
549 );
550 assert_eq!(
551 found_hardware_key_protector.hmac,
552 hardware_key_protector.hmac
553 );
554
555 let oversized_hardware_key_protector = [8u8; HW_KEY_PROTECTOR_SIZE + 1];
557 vmgs.write_file(FileId::HW_KEY_PROTECTOR, &oversized_hardware_key_protector)
558 .await
559 .unwrap();
560 let found_hardware_key_protector_result = read_hardware_key_protector(&mut vmgs).await;
561 assert!(found_hardware_key_protector_result.is_err());
562 assert_eq!(
563 found_hardware_key_protector_result.unwrap_err().to_string(),
564 "HW_KEY_PROTECTOR valid bytes 105, expected 104"
565 );
566 }
567
568 #[async_test]
569 async fn read_guest_secret_key_from_vmgs() {
570 let mut vmgs = new_formatted_vmgs().await;
571
572 let found_guest_secret_key_result = read_guest_secret_key(&mut vmgs).await;
574 assert!(found_guest_secret_key_result.is_err());
575 assert_eq!(
576 found_guest_secret_key_result.unwrap_err().to_string(),
577 "entry does not exist, file id: GUEST_SECRET_KEY"
578 );
579
580 let guest_secret_key = GuestSecretKey {
582 guest_secret_key: [9; GUEST_SECRET_KEY_MAX_SIZE],
583 };
584 vmgs.write_file(FileId::GUEST_SECRET_KEY, guest_secret_key.as_bytes())
585 .await
586 .unwrap();
587 let found_guest_secret_key = read_guest_secret_key(&mut vmgs).await.unwrap();
588 assert_eq!(
589 found_guest_secret_key.guest_secret_key,
590 guest_secret_key.guest_secret_key
591 );
592
593 let oversized_guest_secret_key = [10u8; GUEST_SECRET_KEY_MAX_SIZE + 1];
595 vmgs.write_file(FileId::GUEST_SECRET_KEY, &oversized_guest_secret_key)
596 .await
597 .unwrap();
598 let found_guest_secret_key_result = read_guest_secret_key(&mut vmgs).await;
599 assert!(found_guest_secret_key_result.is_err());
600 assert_eq!(
601 found_guest_secret_key_result.unwrap_err().to_string(),
602 "GUEST_SECRET_KEY valid bytes 2049 larger than the maximum size 2048"
603 );
604
605 let undersized_guest_secret_key = [7u8; GUEST_SECRET_KEY_MAX_SIZE - 10];
607 vmgs.write_file(
608 FileId::GUEST_SECRET_KEY,
609 undersized_guest_secret_key.as_bytes(),
610 )
611 .await
612 .unwrap();
613 let found_guest_secret_key = read_guest_secret_key(&mut vmgs).await.unwrap();
614 assert_eq!(
615 found_guest_secret_key.guest_secret_key[..AGENT_DATA_MAX_SIZE - 10],
616 undersized_guest_secret_key[..]
617 );
618 assert_eq!(
619 found_guest_secret_key.guest_secret_key[AGENT_DATA_MAX_SIZE - 10..],
620 [0; 10]
621 );
622 }
623}