underhill_attestation/
vmgs.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Implementation of the helper functions for accessing VMGS entries.
5
6use 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/// Error while writing to vmgs
54#[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
62/// Read Key Protector data from the VMGS file. If [`FileId::KEY_PROTECTOR`] doesn't exist yet,
63/// locally initialize a key_protector instance that can be written to.
64pub 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                // Allow smaller buf by padding zero bytes
91                let mut data = data;
92                data.resize(KEY_PROTECTOR_SIZE, 0);
93                data
94            } else {
95                data
96            };
97
98            // read_from_prefix expects input bytes to be larger than or equal to size_of::<Self>()
99            KeyProtector::read_from_prefix(&data[..])
100                .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))
101                .map(|k| k.0) // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
102        }
103        Err(vmgs::Error::FileInfoNotAllocated) => Ok(KeyProtector::new_zeroed()),
104        Err(vmgs_err) => Err(ReadFromVmgsError::ReadFromVmgs { vmgs_err, file_id }),
105    }
106}
107
108/// Write Key Protector data to the VMGS file.
109pub 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
119/// Read Key Protector ID from the VMGS file.
120pub async fn read_key_protector_by_id(
121    vmgs: &mut Vmgs,
122) -> Result<KeyProtectorById, ReadFromVmgsError> {
123    // This file could include state data following the GUID.
124    // File contents vary with what paravisor previously wrote this file,
125    // but a GUID must be present.
126    // It is safe to write the file out with full structure, downlevel OS support this pattern.
127
128    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() // TODO: zerocopy: ok (https://github.com/microsoft/openvmm/issues/759)
132            .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))? // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
138                    .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
151/// Write Key Protector Id (current Id) to the VMGS file.
152///
153/// Write if `bios_guid` is different from the one held in `key_protector_by_id` (which
154/// will be set to `bios_guid` before write) or `force_write` is `true`.
155pub 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
172/// Read the security profile from the VMGS file. If [`FileId::ATTEST`] doesn't exist yet,
173/// return an empty vector.
174pub 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                // Allow smaller buf by padding zero bytes
188                let mut data = data;
189                data.resize(AGENT_DATA_MAX_SIZE, 0);
190                data
191            } else {
192                data
193            };
194
195            // read_from_prefix expects input bytes to be larger than or equal to size_of::<Self>()
196            Ok(SecurityProfile::read_from_prefix(&data[..])
197                .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))?
198                .0) // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
199        }
200        Err(vmgs::Error::FileInfoNotAllocated) => Ok(SecurityProfile::new_zeroed()),
201        Err(vmgs_err) => Err(ReadFromVmgsError::ReadFromVmgs { file_id, vmgs_err })?,
202    }
203}
204
205/// Read the hardware key protector from the VMGS file.
206pub 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) // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
233}
234
235/// Write Key Protector Id (current Id) to the VMGS file.
236pub 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
246/// Read the guest secret key from VMGS file.
247pub 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                // Allow smaller buf by padding zero bytes
261                let mut data = data;
262                data.resize(GUEST_SECRET_KEY_MAX_SIZE, 0);
263                data
264            } else {
265                data
266            };
267
268            // read_from_prefix expects input bytes to be larger than or equal to size_of::<Self>()
269            Ok(GuestSecretKey::read_from_prefix(&data[..])
270                .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))?
271                .0) // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
272        }
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        // Ingress and egress KPs are assumed to be the only two KPs, therefore `NUMBER_KP` should be 2
326        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        // Read an undersized key protector
373        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        // Read an oversized key protector
389        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        // Read a key protector that is equal to the `dek_minimal_size` and smaller than the `KEY_PROTECTOR_SIZE`
400        // so that padding is added
401        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        // Try to read the `key_protector_by_id` from the VMGS file which doesn't have a `key_protector_by_id` entry
432        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        // Populate the VMGS file with `key_protector_by_id`
440        write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, true, kp_guid)
441            .await
442            .unwrap();
443
444        // Without using force, write the same `kp_guid` to the VMGS file and find that nothing changes
445        write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, false, kp_guid)
446            .await
447            .unwrap();
448        // `key_protector_by_id` should still hold `kp_guid`
449        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        // Without using force, write a new `Guid` to the VMGS file and find that the `key_protector_by_id` is updated
453        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        // `key_protector_by_id` should now hold `new_guid`
458        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        // Read a key protector by id from the VMGS file that is undersized
462        // ported and pad fields are expected to be zeroed
463        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        // When no security profile exists, a zeroed security profile will be written to the VMGS
485        assert_eq!(
486            found_security_profile.agent_data,
487            SecurityProfile::new_zeroed().agent_data
488        );
489
490        // Write a security profile to the VMGS
491        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        // Write a security profile larger than the maximum size to the VMGS
504        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        // Write a security profile smaller than the maximum size to the VMGS and observe that it is padded with zeros
516        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        // Write and then fail to read a hardware key protector larger than the expected size to the VMGS
556        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        // When no guest secret key exists, an error should be returned
573        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        // Write a guest secret key to the VMGS
581        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        // Write a guest secret key larger than the maximum size to the VMGS
594        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        // Write a guest secret smaller than the maximum size to the VMGS and observe that it is padded with zeros
606        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}