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::FileInfoAllocated) => 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::FileInfoAllocated) => 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::FileInfoAllocated) => 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 = 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) // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
228}
229
230/// Write Key Protector Id (current Id) to the VMGS file.
231pub 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
241/// Read the guest secret key from VMGS file.
242pub 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                // Allow smaller buf by padding zero bytes
256                let mut data = data;
257                data.resize(GUEST_SECRET_KEY_MAX_SIZE, 0);
258                data
259            } else {
260                data
261            };
262
263            // read_from_prefix expects input bytes to be larger than or equal to size_of::<Self>()
264            Ok(GuestSecretKey::read_from_prefix(&data[..])
265                .map_err(|_| ReadFromVmgsError::InvalidFormat(file_id))?
266                .0) // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
267        }
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        // Ingress and egress KPs are assumed to be the only two KPs, therefore `NUMBER_KP` should be 2
321        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        // Read an undersized key protector
368        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        // Read an oversized key protector
384        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        // Read a key protector that is equal to the `dek_minimal_size` and smaller than the `KEY_PROTECTOR_SIZE`
395        // so that padding is added
396        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        // Try to read the `key_protector_by_id` from the VMGS file which doesn't have a `key_protector_by_id` entry
427        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        // Populate the VMGS file with `key_protector_by_id`
435        write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, true, kp_guid)
436            .await
437            .unwrap();
438
439        // Without using force, write the same `kp_guid` to the VMGS file and find that nothing changes
440        write_key_protector_by_id(&mut key_protector_by_id, &mut vmgs, false, kp_guid)
441            .await
442            .unwrap();
443        // `key_protector_by_id` should still hold `kp_guid`
444        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        // Without using force, write a new `Guid` to the VMGS file and find that the `key_protector_by_id` is updated
448        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        // `key_protector_by_id` should now hold `new_guid`
453        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        // Read a key protector by id from the VMGS file that is undersized
457        // ported and pad fields are expected to be zeroed
458        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        // When no security profile exists, a zeroed security profile will be written to the VMGS
480        assert_eq!(
481            found_security_profile.agent_data,
482            SecurityProfile::new_zeroed().agent_data
483        );
484
485        // Write a security profile to the VMGS
486        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        // Write a security profile larger than the maximum size to the VMGS
499        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        // Write a security profile smaller than the maximum size to the VMGS and observe that it is padded with zeros
511        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        // Write and then fail to read a hardware key protector larger than the expected size to the VMGS
551        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        // When no guest secret key exists, an error should be returned
568        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        // Write a guest secret key to the VMGS
576        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        // Write a guest secret key larger than the maximum size to the VMGS
589        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        // Write a guest secret smaller than the maximum size to the VMGS and observe that it is padded with zeros
601        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}