underhill_attestation/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! This modules implements attestation protocols for Underhill to support TVM
5//! and CVM, including getting a tenant key via secure key release (SKR) for
6//! unlocking VMGS and requesting an attestation key (AK) certificate for TPM.
7//! The module also implements the VMGS unlocking process based on SKR.
8
9#![cfg(target_os = "linux")]
10#![forbid(unsafe_code)]
11
12mod crypto;
13mod hardware_key_sealing;
14mod igvm_attest;
15mod key_protector;
16mod secure_key_release;
17mod vmgs;
18
19pub use igvm_attest::Error as IgvmAttestError;
20pub use igvm_attest::IgvmAttestRequestHelper;
21pub use igvm_attest::ak_cert::parse_response as parse_ak_cert_response;
22
23use ::vmgs::EncryptionAlgorithm;
24use ::vmgs::Vmgs;
25use cvm_tracing::CVM_ALLOWED;
26use get_protocol::dps_json::GuestStateEncryptionPolicy;
27use guest_emulation_transport::GuestEmulationTransportClient;
28use guest_emulation_transport::api::GspExtendedStatusFlags;
29use guest_emulation_transport::api::GuestStateProtection;
30use guest_emulation_transport::api::GuestStateProtectionById;
31use guid::Guid;
32use hardware_key_sealing::HardwareDerivedKeys;
33use hardware_key_sealing::HardwareKeyProtectorExt as _;
34use key_protector::GetKeysFromKeyProtectorError;
35use key_protector::KeyProtectorExt as _;
36use mesh::MeshPayload;
37use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationVmConfig;
38use openhcl_attestation_protocol::vmgs::AES_GCM_KEY_LENGTH;
39use openhcl_attestation_protocol::vmgs::HardwareKeyProtector;
40use openhcl_attestation_protocol::vmgs::KeyProtector;
41use openhcl_attestation_protocol::vmgs::SecurityProfile;
42use openssl::pkey::Private;
43use openssl::rsa::Rsa;
44use pal_async::local::LocalDriver;
45use secure_key_release::VmgsEncryptionKeys;
46use static_assertions::const_assert_eq;
47use std::fmt::Debug;
48use tee_call::TeeCall;
49use thiserror::Error;
50use zerocopy::FromZeros;
51use zerocopy::IntoBytes;
52
53/// An attestation error.
54#[derive(Debug, Error)]
55#[error(transparent)]
56pub struct Error(AttestationErrorInner);
57
58impl<T: Into<AttestationErrorInner>> From<T> for Error {
59    fn from(value: T) -> Self {
60        Self(value.into())
61    }
62}
63
64#[derive(Debug, Error)]
65enum AttestationErrorInner {
66    #[error("read security profile from vmgs")]
67    ReadSecurityProfile(#[source] vmgs::ReadFromVmgsError),
68    #[error("failed to request vmgs encryption keys")]
69    RequestVmgsEncryptionKeys(#[source] secure_key_release::RequestVmgsEncryptionKeysError),
70    #[error("failed to get derived keys")]
71    GetDerivedKeys(#[source] GetDerivedKeysError),
72    #[error("failed to read key protector from vmgs")]
73    ReadKeyProtector(#[source] vmgs::ReadFromVmgsError),
74    #[error("failed to read key protector by id from vmgs")]
75    ReadKeyProtectorById(#[source] vmgs::ReadFromVmgsError),
76    #[error("failed to unlock vmgs data store")]
77    UnlockVmgsDataStore(#[source] UnlockVmgsDataStoreError),
78    #[error("failed to read guest secret key from vmgs")]
79    ReadGuestSecretKey(#[source] vmgs::ReadFromVmgsError),
80}
81
82#[derive(Debug, Error)]
83enum GetDerivedKeysError {
84    #[error("failed to get ingress/egress keys from the the key protector")]
85    GetKeysFromKeyProtector(#[source] GetKeysFromKeyProtectorError),
86    #[error("failed to fetch GSP")]
87    FetchGuestStateProtectionById(
88        #[source] guest_emulation_transport::error::GuestStateProtectionByIdError,
89    ),
90    #[error("GSP By Id required, but no GSP By Id found")]
91    GspByIdRequiredButNotFound,
92    #[error("failed to unseal the ingress key using hardware derived keys")]
93    UnsealIngressKeyUsingHardwareDerivedKeys(
94        #[source] hardware_key_sealing::HardwareKeySealingError,
95    ),
96    #[error("failed to get an ingress key from key protector")]
97    GetIngressKeyFromKpFailed,
98    #[error("failed to get an ingress key from guest state protection")]
99    GetIngressKeyFromKGspFailed,
100    #[error("failed to get an ingress key from guest state protection by id")]
101    GetIngressKeyFromKGspByIdFailed,
102    #[error("Encryption cannot be disabled if VMGS was previously encrypted")]
103    DisableVmgsEncryptionFailed,
104    #[error("VMGS encryption is required, but no encryption sources were found")]
105    EncryptionRequiredButNotFound,
106    #[error("failed to seal the egress key using hardware derived keys")]
107    SealEgressKeyUsingHardwareDerivedKeys(#[source] hardware_key_sealing::HardwareKeySealingError),
108    #[error("failed to write to `FileId::HW_KEY_PROTECTOR` in vmgs")]
109    VmgsWriteHardwareKeyProtector(#[source] vmgs::WriteToVmgsError),
110    #[error("failed to get derived key by id")]
111    GetDerivedKeyById(#[source] GetDerivedKeysByIdError),
112    #[error("failed to derive an ingress key")]
113    DeriveIngressKey(#[source] crypto::KbkdfError),
114    #[error("failed to derive an egress key")]
115    DeriveEgressKey(#[source] crypto::KbkdfError),
116}
117
118#[derive(Debug, Error)]
119enum GetDerivedKeysByIdError {
120    #[error("failed to derive an egress key based on current vm bios guid")]
121    DeriveEgressKeyUsingCurrentVmId(#[source] crypto::KbkdfError),
122    #[error("invalid derived egress key size {key_size}, expected {expected_size}")]
123    InvalidDerivedEgressKeySize {
124        key_size: usize,
125        expected_size: usize,
126    },
127    #[error("failed to derive an ingress key based on key protector Id from vmgs")]
128    DeriveIngressKeyUsingKeyProtectorId(#[source] crypto::KbkdfError),
129    #[error("invalid derived egress key size {key_size}, expected {expected_size}")]
130    InvalidDerivedIngressKeySize {
131        key_size: usize,
132        expected_size: usize,
133    },
134}
135
136#[derive(Debug, Error)]
137enum UnlockVmgsDataStoreError {
138    #[error("failed to unlock vmgs with the existing egress key")]
139    VmgsUnlockUsingExistingEgressKey(#[source] ::vmgs::Error),
140    #[error("failed to unlock vmgs with the existing ingress key")]
141    VmgsUnlockUsingExistingIngressKey(#[source] ::vmgs::Error),
142    #[error("failed to write key protector to vmgs")]
143    WriteKeyProtector(#[source] vmgs::WriteToVmgsError),
144    #[error("failed to read key protector by id to vmgs")]
145    WriteKeyProtectorById(#[source] vmgs::WriteToVmgsError),
146    #[error("failed to remove the old vmgs encryption key")]
147    RemoveOldVmgsEncryptionKey(#[source] ::vmgs::Error),
148    #[error("failed to add a new vmgs encryption key after removing the old key")]
149    AddNewVmgsEncryptionKeyAfterRemoval(#[source] ::vmgs::Error),
150    #[error("failed to add a new vmgs encryption key")]
151    AddNewVmgsEncryptionKey(#[source] ::vmgs::Error),
152    #[error("failed to persist all key protectors")]
153    PersistAllKeyProtectors(#[source] PersistAllKeyProtectorsError),
154}
155
156#[derive(Debug, Error)]
157enum PersistAllKeyProtectorsError {
158    #[error("failed to write key protector to vmgs")]
159    WriteKeyProtector(#[source] vmgs::WriteToVmgsError),
160    #[error("failed to read key protector by id to vmgs")]
161    WriteKeyProtectorById(#[source] vmgs::WriteToVmgsError),
162}
163
164/// Label used by `derive_key`
165const VMGS_KEY_DERIVE_LABEL: &[u8; 7] = b"VMGSKEY";
166
167#[derive(Debug)]
168struct Keys {
169    ingress: [u8; AES_GCM_KEY_LENGTH],
170    decrypt_egress: Option<[u8; AES_GCM_KEY_LENGTH]>,
171    encrypt_egress: [u8; AES_GCM_KEY_LENGTH],
172}
173
174/// Key protector settings
175struct KeyProtectorSettings {
176    /// Whether to update key protector
177    should_write_kp: bool,
178    /// Whether GSP by id is used
179    use_gsp_by_id: bool,
180    /// Whether hardware key sealing is used
181    use_hardware_unlock: bool,
182}
183
184/// Helper struct for [`protocol::vmgs::KeyProtectorById`]
185struct KeyProtectorById {
186    /// The instance of [`protocol::vmgs::KeyProtectorById`].
187    pub inner: openhcl_attestation_protocol::vmgs::KeyProtectorById,
188    /// Indicate if the instance is read from the VMGS file.
189    pub found_id: bool,
190}
191
192/// Host attestation settings obtained via the GET GSP call-out.
193pub struct HostAttestationSettings {
194    /// Whether refreshing tpm seeds is needed.
195    pub refresh_tpm_seeds: bool,
196}
197
198/// The return values of [`get_derived_keys`].
199struct DerivedKeyResult {
200    /// Optional derived keys.
201    derived_keys: Option<Keys>,
202    /// The instance of [`KeyProtectorSettings`].
203    key_protector_settings: KeyProtectorSettings,
204    /// The instance of [`GspExtendedStatusFlags`] returned by GSP.
205    gsp_extended_status_flags: GspExtendedStatusFlags,
206}
207
208/// The return values of [`initialize_platform_security`].
209pub struct PlatformAttestationData {
210    /// The instance of [`HostAttestationSettings`].
211    pub host_attestation_settings: HostAttestationSettings,
212    /// The agent data used by an attestation request.
213    pub agent_data: Option<Vec<u8>>,
214    /// The guest secret key.
215    pub guest_secret_key: Option<Vec<u8>>,
216}
217
218/// The attestation type to use.
219// TODO: Support VBS
220#[derive(Debug, MeshPayload, Copy, Clone, PartialEq, Eq)]
221pub enum AttestationType {
222    /// Use the SEV-SNP TEE for attestation.
223    Snp,
224    /// Use the TDX TEE for attestation.
225    Tdx,
226    /// Use trusted host-based attestation.
227    Host,
228}
229
230/// If required, attest platform. Gets VMGS datastore key.
231///
232/// Returns `refresh_tpm_seeds` (the host side GSP service indicating
233/// whether certain state needs to be updated), along with the fully
234/// initialized VMGS client.
235pub async fn initialize_platform_security(
236    get: &GuestEmulationTransportClient,
237    bios_guid: Guid,
238    attestation_vm_config: &AttestationVmConfig,
239    vmgs: &mut Vmgs,
240    attestation_type: AttestationType,
241    suppress_attestation: bool,
242    driver: LocalDriver,
243    guest_state_encryption_policy: GuestStateEncryptionPolicy,
244    strict_encryption_policy: bool,
245) -> Result<PlatformAttestationData, Error> {
246    tracing::info!(CVM_ALLOWED,
247        attestation_type=?attestation_type,
248        secure_boot=attestation_vm_config.secure_boot,
249        tpm_enabled=attestation_vm_config.tpm_enabled,
250        tpm_persisted=attestation_vm_config.tpm_persisted,
251        "Reading security profile");
252
253    // Read Security Profile from VMGS
254    // Currently this only includes "Key Reference" data, which is not attested data, is opaque to the
255    // OpenHCL, and is passed to the IGVMm agent outside of the report contents.
256    let SecurityProfile { mut agent_data } = vmgs::read_security_profile(vmgs)
257        .await
258        .map_err(AttestationErrorInner::ReadSecurityProfile)?;
259
260    // If attestation is suppressed, return the `agent_data` that is required by
261    // TPM AK cert request.
262    if suppress_attestation {
263        tracing::info!(CVM_ALLOWED, "Suppressing attestation");
264
265        return Ok(PlatformAttestationData {
266            host_attestation_settings: HostAttestationSettings {
267                refresh_tpm_seeds: false,
268            },
269            agent_data: Some(agent_data.to_vec()),
270            guest_secret_key: None,
271        });
272    }
273
274    let tee_call: Option<Box<dyn TeeCall>> = match attestation_type {
275        AttestationType::Snp => Some(Box::new(tee_call::SnpCall)),
276        AttestationType::Tdx => Some(Box::new(tee_call::TdxCall)),
277        AttestationType::Host => None,
278    };
279
280    let VmgsEncryptionKeys {
281        ingress_rsa_kek,
282        wrapped_des_key,
283        tcb_version,
284    } = if let Some(tee_call) = tee_call.as_ref() {
285        tracing::info!(CVM_ALLOWED, "Retrieving key-encryption key");
286
287        // Retrieve the tenant key via attestation
288        secure_key_release::request_vmgs_encryption_keys(
289            get,
290            tee_call.as_ref(),
291            vmgs,
292            attestation_vm_config,
293            &mut agent_data,
294            driver,
295        )
296        .await
297        .map_err(AttestationErrorInner::RequestVmgsEncryptionKeys)?
298    } else {
299        tracing::info!(CVM_ALLOWED, "Key-encryption key retrieval not required");
300
301        // Attestation is unavailable, assume no tenant key
302        VmgsEncryptionKeys::default()
303    };
304
305    // Determine the minimal size of a DEK entry based on whether `wrapped_des_key` presents
306    let dek_minimal_size = if wrapped_des_key.is_some() {
307        key_protector::AES_WRAPPED_AES_KEY_LENGTH
308    } else {
309        key_protector::RSA_WRAPPED_AES_KEY_LENGTH
310    };
311
312    // Read Key Protector blob from VMGS
313    tracing::info!(
314        CVM_ALLOWED,
315        dek_minimal_size = dek_minimal_size,
316        "Reading key protector from VMGS"
317    );
318    let mut key_protector = vmgs::read_key_protector(vmgs, dek_minimal_size)
319        .await
320        .map_err(AttestationErrorInner::ReadKeyProtector)?;
321
322    // Read VM id from VMGS
323    tracing::info!(CVM_ALLOWED, "Reading VM ID from VMGS");
324    let mut key_protector_by_id = match vmgs::read_key_protector_by_id(vmgs).await {
325        Ok(key_protector_by_id) => KeyProtectorById {
326            inner: key_protector_by_id,
327            found_id: true,
328        },
329        Err(vmgs::ReadFromVmgsError::EntryNotFound(_)) => KeyProtectorById {
330            inner: openhcl_attestation_protocol::vmgs::KeyProtectorById::new_zeroed(),
331            found_id: false,
332        },
333        Err(e) => { Err(AttestationErrorInner::ReadKeyProtectorById(e)) }?,
334    };
335
336    // Check if the VM id has been changed since last boot with KP write
337    let vm_id_changed = if key_protector_by_id.found_id {
338        let changed = key_protector_by_id.inner.id_guid != bios_guid;
339        if changed {
340            tracing::info!("VM Id has changed since last boot");
341        };
342        changed
343    } else {
344        tracing::info!("First booting of the VM");
345        // Previous id in KP not found means this is the first boot,
346        // treat id as unchanged for this case.
347        false
348    };
349
350    let vmgs_encrypted: bool = vmgs.is_encrypted();
351
352    tracing::info!(tcb_version=?tcb_version, vmgs_encrypted = vmgs_encrypted, "Deriving keys");
353    let derived_keys_result = get_derived_keys(
354        get,
355        tee_call.as_deref(),
356        vmgs,
357        &mut key_protector,
358        &mut key_protector_by_id,
359        bios_guid,
360        attestation_vm_config,
361        vmgs_encrypted,
362        ingress_rsa_kek.as_ref(),
363        wrapped_des_key.as_deref(),
364        tcb_version,
365        guest_state_encryption_policy,
366        strict_encryption_policy,
367    )
368    .await
369    .map_err(AttestationErrorInner::GetDerivedKeys)?;
370
371    // All Underhill VMs use VMGS encryption
372    tracing::info!("Unlocking VMGS");
373    if let Err(e) = unlock_vmgs_data_store(
374        vmgs,
375        vmgs_encrypted,
376        &mut key_protector,
377        &mut key_protector_by_id,
378        derived_keys_result.derived_keys,
379        derived_keys_result.key_protector_settings,
380        bios_guid,
381    )
382    .await
383    {
384        get.event_log_fatal(guest_emulation_transport::api::EventLogId::ATTESTATION_FAILED)
385            .await;
386
387        Err(AttestationErrorInner::UnlockVmgsDataStore(e))?
388    }
389
390    let state_refresh_request_from_gsp = derived_keys_result
391        .gsp_extended_status_flags
392        .state_refresh_request();
393
394    let host_attestation_settings = HostAttestationSettings {
395        refresh_tpm_seeds: { state_refresh_request_from_gsp | vm_id_changed },
396    };
397
398    tracing::info!(
399        CVM_ALLOWED,
400        state_refresh_request_from_gsp = state_refresh_request_from_gsp,
401        vm_id_changed = vm_id_changed,
402        "determine if refreshing tpm seeds is needed"
403    );
404
405    // Read guest secret key from unlocked VMGS
406    let guest_secret_key = match vmgs::read_guest_secret_key(vmgs).await {
407        Ok(data) => Some(data.guest_secret_key.to_vec()),
408        Err(vmgs::ReadFromVmgsError::EntryNotFound(_)) => None,
409        Err(e) => return Err(AttestationErrorInner::ReadGuestSecretKey(e).into()),
410    };
411
412    Ok(PlatformAttestationData {
413        host_attestation_settings,
414        agent_data: Some(agent_data.to_vec()),
415        guest_secret_key,
416    })
417}
418
419/// Get ingress and egress keys for the VMGS, unlock VMGS,
420/// remove old key if necessary, and update KP.
421/// If key rolling did not complete successfully last time, there may be an
422/// old egress key in the VMGS, whose contents can be controlled by the host.
423/// This key can be used to attempt decryption but must not be used to
424/// re-encrypt the VMGS.
425async fn unlock_vmgs_data_store(
426    vmgs: &mut Vmgs,
427    vmgs_encrypted: bool,
428    key_protector: &mut KeyProtector,
429    key_protector_by_id: &mut KeyProtectorById,
430    derived_keys: Option<Keys>,
431    key_protector_settings: KeyProtectorSettings,
432    bios_guid: Guid,
433) -> Result<(), UnlockVmgsDataStoreError> {
434    let mut new_key = false; // Indicate if we need to add a new key after unlock
435
436    let Some(Keys {
437        ingress: new_ingress_key,
438        decrypt_egress: old_egress_key,
439        encrypt_egress: new_egress_key,
440    }) = derived_keys
441    else {
442        tracing::info!(
443            CVM_ALLOWED,
444            "Encryption disabled, skipping unlock vmgs data store"
445        );
446        return Ok(());
447    };
448
449    if !openssl::memcmp::eq(&new_ingress_key, &new_egress_key) {
450        tracing::trace!(CVM_ALLOWED, "EgressKey is different than IngressKey");
451        new_key = true;
452    }
453
454    // Call unlock_with_encryption_key using ingress_key if datastore is encrypted
455    let mut old_index = 2;
456    let mut provision = false;
457    if vmgs_encrypted {
458        tracing::info!(CVM_ALLOWED, "Decrypting vmgs file...");
459        match vmgs.unlock_with_encryption_key(&new_ingress_key).await {
460            Ok(index) => old_index = index,
461            Err(e) => {
462                if let Some(key) = old_egress_key {
463                    // Key rolling did not complete successfully last time and there's an old
464                    // egress key in the VMGS. It may be needed for decryption.
465                    tracing::trace!(CVM_ALLOWED, "Old EgressKey found");
466                    old_index = vmgs
467                        .unlock_with_encryption_key(&key)
468                        .await
469                        .map_err(UnlockVmgsDataStoreError::VmgsUnlockUsingExistingEgressKey)?;
470                } else {
471                    Err(UnlockVmgsDataStoreError::VmgsUnlockUsingExistingIngressKey(
472                        e,
473                    ))?
474                }
475            }
476        }
477    } else {
478        // The datastore is not encrypted which means it's during provision.
479        tracing::info!(
480            CVM_ALLOWED,
481            "vmgs data store is not encrypted, provisioning."
482        );
483        provision = true;
484    }
485
486    if key_protector_settings.should_write_kp {
487        // Update on disk KP with all seeds used, to allow for disaster recovery
488        vmgs::write_key_protector(key_protector, vmgs)
489            .await
490            .map_err(UnlockVmgsDataStoreError::WriteKeyProtector)?;
491
492        if key_protector_settings.use_gsp_by_id {
493            vmgs::write_key_protector_by_id(&mut key_protector_by_id.inner, vmgs, false, bios_guid)
494                .await
495                .map_err(UnlockVmgsDataStoreError::WriteKeyProtectorById)?;
496        }
497    }
498
499    // Call add_new_encryption_key adding egress_key if different with ingress_key or during provision
500    if provision || new_key {
501        let result = vmgs
502            .add_new_encryption_key(&new_egress_key, EncryptionAlgorithm::AES_GCM)
503            .await;
504
505        match result {
506            Ok(_new_index) => (),
507            Err(_) if old_index != 2 => {
508                // If last time we failed to remove old key then we'll come here.
509                // We have to remove old key before adding egress_key.
510                let key_index = if old_index == 0 { 1 } else { 0 };
511                tracing::trace!(CVM_ALLOWED, key_index = key_index, "Remove old key...");
512                vmgs.remove_encryption_key(key_index)
513                    .await
514                    .map_err(UnlockVmgsDataStoreError::RemoveOldVmgsEncryptionKey)?;
515
516                tracing::trace!(CVM_ALLOWED, "Add egress_key again...");
517                vmgs.add_new_encryption_key(&new_egress_key, EncryptionAlgorithm::AES_GCM)
518                    .await
519                    .map_err(UnlockVmgsDataStoreError::AddNewVmgsEncryptionKeyAfterRemoval)?;
520            }
521            Err(e) => Err(UnlockVmgsDataStoreError::AddNewVmgsEncryptionKey(e))?,
522        }
523    }
524
525    // Remove ingress_key if different with egress_key
526    if !provision && new_key {
527        vmgs.remove_encryption_key(old_index)
528            .await
529            .map_err(UnlockVmgsDataStoreError::RemoveOldVmgsEncryptionKey)?;
530    }
531
532    // Persist KP to VMGS
533    persist_all_key_protectors(
534        vmgs,
535        key_protector,
536        key_protector_by_id,
537        bios_guid,
538        key_protector_settings,
539    )
540    .await
541    .map_err(UnlockVmgsDataStoreError::PersistAllKeyProtectors)
542}
543
544/// Update data store keys with key protectors.
545///         VMGS encryption can come from combinations of three sources,
546///         a Tenant Key (KEK), GSP, and GSP By Id.
547///         There is an Ingress Key (previously used to lock the VMGS),
548///         and an Egress Key (new key for locking the VMGS), and these
549///         keys can be derived differently, where KEK is
550///         always used if available, and GSP is preferred to GSP By Id.
551///         Ingress                     Possible Egress in order of preference [Ingress]
552///         - No Encryption             - All
553///         - GSP By Id                 - KEK + GSP, KEK + GSP By Id, GSP, [GSP By Id]
554///         - GSP (v10 VM and later)    - KEK + GSP, [GSP]
555///         - KEK (IVM only)            - KEK + GSP, KEK + GSP By Id, [KEK]
556///         - KEK + GSP By Id           - KEK + GSP, [KEK + GSP By Id]
557///         - KEK + GSP                 - [KEK + GSP]
558///
559/// NOTE: for TVM parity, only None, Gsp By Id v9.1, and Gsp By Id / Gsp v10.0 are used.
560async fn get_derived_keys(
561    get: &GuestEmulationTransportClient,
562    tee_call: Option<&dyn TeeCall>,
563    vmgs: &mut Vmgs,
564    key_protector: &mut KeyProtector,
565    key_protector_by_id: &mut KeyProtectorById,
566    bios_guid: Guid,
567    attestation_vm_config: &AttestationVmConfig,
568    is_encrypted: bool,
569    ingress_rsa_kek: Option<&Rsa<Private>>,
570    wrapped_des_key: Option<&[u8]>,
571    tcb_version: Option<u64>,
572    guest_state_encryption_policy: GuestStateEncryptionPolicy,
573    strict_encryption_policy: bool,
574) -> Result<DerivedKeyResult, GetDerivedKeysError> {
575    tracing::info!(
576        CVM_ALLOWED,
577        ?guest_state_encryption_policy,
578        strict_encryption_policy,
579        "encryption policy"
580    );
581
582    // TODO: implement hardware sealing only
583    if matches!(
584        guest_state_encryption_policy,
585        GuestStateEncryptionPolicy::HardwareSealing
586    ) {
587        todo!("hardware sealing")
588    }
589
590    let mut key_protector_settings = KeyProtectorSettings {
591        should_write_kp: true,
592        use_gsp_by_id: false,
593        use_hardware_unlock: false,
594    };
595
596    let mut derived_keys = Keys {
597        ingress: [0u8; AES_GCM_KEY_LENGTH],
598        decrypt_egress: None,
599        encrypt_egress: [0u8; AES_GCM_KEY_LENGTH],
600    };
601
602    // Ingress / Egress seed values depend on what happened previously to the datastore
603    let ingress_idx = (key_protector.active_kp % 2) as usize;
604    let egress_idx = if ingress_idx == 0 { 1 } else { 0 } as usize;
605
606    let found_dek = !key_protector.dek[ingress_idx]
607        .dek_buffer
608        .iter()
609        .all(|&x| x == 0);
610
611    // Handle key released via attestation process (tenant key) to get keys from KeyProtector
612    let (ingress_key, mut decrypt_egress_key, encrypt_egress_key, no_kek) =
613        if let Some(ingress_kek) = ingress_rsa_kek {
614            let keys = match key_protector.unwrap_and_rotate_keys(
615                ingress_kek,
616                wrapped_des_key,
617                ingress_idx,
618                egress_idx,
619            ) {
620                Ok(keys) => keys,
621                Err(e)
622                    if matches!(
623                        e,
624                        GetKeysFromKeyProtectorError::DesKeyRsaUnwrap(_)
625                            | GetKeysFromKeyProtectorError::IngressDekRsaUnwrap(_)
626                    ) =>
627                {
628                    get.event_log_fatal(
629                        guest_emulation_transport::api::EventLogId::DEK_DECRYPTION_FAILED,
630                    )
631                    .await;
632
633                    return Err(GetDerivedKeysError::GetKeysFromKeyProtector(e));
634                }
635                Err(e) => return Err(GetDerivedKeysError::GetKeysFromKeyProtector(e)),
636            };
637            (
638                keys.ingress,
639                keys.decrypt_egress,
640                keys.encrypt_egress,
641                false,
642            )
643        } else {
644            (
645                [0u8; AES_GCM_KEY_LENGTH],
646                None,
647                [0u8; AES_GCM_KEY_LENGTH],
648                true,
649            )
650        };
651
652    // Handle various sources of Guest State Protection
653    let is_gsp_by_id = key_protector_by_id.found_id && key_protector_by_id.inner.ported != 1;
654    let is_gsp = key_protector.gsp[ingress_idx].gsp_length != 0;
655    tracing::info!(
656        CVM_ALLOWED,
657        is_encrypted,
658        is_gsp_by_id,
659        is_gsp,
660        found_dek,
661        "initial vmgs encryption state"
662    );
663    let mut requires_gsp_by_id = is_gsp_by_id;
664
665    // Attempt GSP
666    let (gsp_response, no_gsp, requires_gsp) = {
667        let response = get_gsp_data(get, key_protector).await;
668
669        tracing::info!(
670            CVM_ALLOWED,
671            request_data_length_in_vmgs = key_protector.gsp[ingress_idx].gsp_length,
672            no_rpc_server = response.extended_status_flags.no_rpc_server(),
673            requires_rpc_server = response.extended_status_flags.requires_rpc_server(),
674            encrypted_gsp_length = response.encrypted_gsp.length,
675            "GSP response"
676        );
677
678        let no_gsp = response.extended_status_flags.no_rpc_server()
679            || response.encrypted_gsp.length == 0
680            || (matches!(
681                guest_state_encryption_policy,
682                GuestStateEncryptionPolicy::GspById | GuestStateEncryptionPolicy::None
683            ) && (!is_gsp || strict_encryption_policy));
684
685        let requires_gsp = is_gsp
686            || response.extended_status_flags.requires_rpc_server()
687            || matches!(
688                guest_state_encryption_policy,
689                GuestStateEncryptionPolicy::GspKey
690            );
691
692        // If the VMGS is encrypted, but no key protection data is found,
693        // assume GspById encryption is enabled, but no ID file was written.
694        if is_encrypted && !requires_gsp_by_id && !requires_gsp && !found_dek {
695            requires_gsp_by_id = true;
696        }
697
698        (response, no_gsp, requires_gsp)
699    };
700
701    // Attempt GSP By Id protection if GSP is not available, when changing
702    // schemes, or as requested
703    let (gsp_response_by_id, no_gsp_by_id) = if no_gsp || requires_gsp_by_id {
704        let gsp_response_by_id = get
705            .guest_state_protection_data_by_id()
706            .await
707            .map_err(GetDerivedKeysError::FetchGuestStateProtectionById)?;
708
709        let no_gsp_by_id = gsp_response_by_id.extended_status_flags.no_registry_file()
710            || (matches!(
711                guest_state_encryption_policy,
712                GuestStateEncryptionPolicy::None
713            ) && (!requires_gsp_by_id || strict_encryption_policy));
714
715        if no_gsp_by_id && requires_gsp_by_id {
716            Err(GetDerivedKeysError::GspByIdRequiredButNotFound)?
717        }
718
719        (gsp_response_by_id, no_gsp_by_id)
720    } else {
721        (GuestStateProtectionById::new_zeroed(), true)
722    };
723
724    // If sources of encryption used last are missing, attempt to unseal VMGS key with hardware key
725    if (no_kek && found_dek) || (no_gsp && requires_gsp) || (no_gsp_by_id && requires_gsp_by_id) {
726        // If possible, get ingressKey from hardware sealed data
727        let (hardware_key_protector, hardware_derived_keys) = if let Some(tee_call) = tee_call {
728            let hardware_key_protector = match vmgs::read_hardware_key_protector(vmgs).await {
729                Ok(hardware_key_protector) => Some(hardware_key_protector),
730                Err(e) => {
731                    // non-fatal
732                    tracing::warn!(
733                        CVM_ALLOWED,
734                        error = &e as &dyn std::error::Error,
735                        "failed to read HW_KEY_PROTECTOR from Vmgs"
736                    );
737                    None
738                }
739            };
740
741            let hardware_derived_keys = tee_call.supports_get_derived_key().and_then(|tee_call| {
742                if let Some(hardware_key_protector) = &hardware_key_protector {
743                    match HardwareDerivedKeys::derive_key(
744                        tee_call,
745                        attestation_vm_config,
746                        hardware_key_protector.header.tcb_version,
747                    ) {
748                        Ok(hardware_derived_key) => Some(hardware_derived_key),
749                        Err(e) => {
750                            // non-fatal
751                            tracing::warn!(
752                                CVM_ALLOWED,
753                                error = &e as &dyn std::error::Error,
754                                "failed to derive hardware keys using HW_KEY_PROTECTOR",
755                            );
756                            None
757                        }
758                    }
759                } else {
760                    None
761                }
762            });
763
764            (hardware_key_protector, hardware_derived_keys)
765        } else {
766            (None, None)
767        };
768
769        if let (Some(hardware_key_protector), Some(hardware_derived_keys)) =
770            (hardware_key_protector, hardware_derived_keys)
771        {
772            derived_keys.ingress = hardware_key_protector
773                .unseal_key(&hardware_derived_keys)
774                .map_err(GetDerivedKeysError::UnsealIngressKeyUsingHardwareDerivedKeys)?;
775            derived_keys.decrypt_egress = None;
776            derived_keys.encrypt_egress = derived_keys.ingress;
777
778            key_protector_settings.should_write_kp = false;
779            key_protector_settings.use_hardware_unlock = true;
780
781            tracing::warn!(
782                CVM_ALLOWED,
783                "Using hardware-derived key to recover VMGS DEK"
784            );
785
786            return Ok(DerivedKeyResult {
787                derived_keys: Some(derived_keys),
788                key_protector_settings,
789                gsp_extended_status_flags: gsp_response.extended_status_flags,
790            });
791        } else {
792            if no_kek && found_dek {
793                Err(GetDerivedKeysError::GetIngressKeyFromKpFailed)?
794            } else if no_gsp && requires_gsp {
795                Err(GetDerivedKeysError::GetIngressKeyFromKGspFailed)?
796            } else {
797                // no_gsp_by_id && requires_gsp_by_id
798                Err(GetDerivedKeysError::GetIngressKeyFromKGspByIdFailed)?
799            }
800        }
801    }
802
803    tracing::info!(
804        CVM_ALLOWED,
805        kek = !no_kek,
806        gsp = !no_gsp,
807        gsp_by_id = !no_gsp_by_id,
808        "Encryption sources"
809    );
810
811    // Check if sources of encryption are available
812    if no_kek && no_gsp && no_gsp_by_id {
813        if is_encrypted {
814            Err(GetDerivedKeysError::DisableVmgsEncryptionFailed)?
815        }
816        match guest_state_encryption_policy {
817            // fail if some minimum level of encryption was required
818            GuestStateEncryptionPolicy::GspById
819            | GuestStateEncryptionPolicy::GspKey
820            | GuestStateEncryptionPolicy::HardwareSealing => {
821                Err(GetDerivedKeysError::EncryptionRequiredButNotFound)?
822            }
823            GuestStateEncryptionPolicy::Auto | GuestStateEncryptionPolicy::None => {
824                tracing::info!(CVM_ALLOWED, "No VMGS encryption used.");
825
826                return Ok(DerivedKeyResult {
827                    derived_keys: None,
828                    key_protector_settings,
829                    gsp_extended_status_flags: gsp_response.extended_status_flags,
830                });
831            }
832        }
833    }
834
835    // Attempt to get hardware derived keys
836    let hardware_derived_keys = tee_call
837        .and_then(|tee_call| tee_call.supports_get_derived_key())
838        .and_then(|tee_call| {
839            if let Some(tcb_version) = tcb_version {
840                match HardwareDerivedKeys::derive_key(tee_call, attestation_vm_config, tcb_version)
841                {
842                    Ok(keys) => Some(keys),
843                    Err(e) => {
844                        // non-fatal
845                        tracing::warn!(
846                            CVM_ALLOWED,
847                            error = &e as &dyn std::error::Error,
848                            "failed to derive hardware keys"
849                        );
850                        None
851                    }
852                }
853            } else {
854                None
855            }
856        });
857
858    // Use tenant key (KEK only)
859    if no_gsp && no_gsp_by_id {
860        tracing::info!(CVM_ALLOWED, "No GSP used with SKR");
861
862        derived_keys.ingress = ingress_key;
863        derived_keys.decrypt_egress = decrypt_egress_key;
864        derived_keys.encrypt_egress = encrypt_egress_key;
865
866        if let Some(hardware_derived_keys) = hardware_derived_keys {
867            let hardware_key_protector = HardwareKeyProtector::seal_key(
868                &hardware_derived_keys,
869                &derived_keys.encrypt_egress,
870            )
871            .map_err(GetDerivedKeysError::SealEgressKeyUsingHardwareDerivedKeys)?;
872            vmgs::write_hardware_key_protector(&hardware_key_protector, vmgs)
873                .await
874                .map_err(GetDerivedKeysError::VmgsWriteHardwareKeyProtector)?;
875
876            tracing::info!(CVM_ALLOWED, "hardware key protector updated (no GSP used)");
877        }
878
879        return Ok(DerivedKeyResult {
880            derived_keys: Some(derived_keys),
881            key_protector_settings,
882            gsp_extended_status_flags: gsp_response.extended_status_flags,
883        });
884    }
885
886    // GSP By Id derives keys differently,
887    // because key is shared across VMs different context must be used (Id GUID)
888    if (no_kek && no_gsp) || requires_gsp_by_id {
889        let derived_keys_by_id =
890            get_derived_keys_by_id(key_protector_by_id, bios_guid, gsp_response_by_id)
891                .map_err(GetDerivedKeysError::GetDerivedKeyById)?;
892
893        if no_kek && no_gsp {
894            if matches!(
895                guest_state_encryption_policy,
896                GuestStateEncryptionPolicy::None
897            ) {
898                // Log a warning here to indicate that the VMGS state is out of
899                // sync with the VM's configuration.
900                //
901                // This should only happen if the VM is configured to
902                // have no encryption, but it already has GspById encryption
903                // and strict encryption policy is disabled.
904                tracing::warn!(CVM_ALLOWED, "Allowing GspById");
905            } else {
906                tracing::info!(CVM_ALLOWED, "Using GspById");
907            }
908
909            // Not required for Id protection
910            key_protector_settings.should_write_kp = false;
911            key_protector_settings.use_gsp_by_id = true;
912
913            return Ok(DerivedKeyResult {
914                derived_keys: Some(derived_keys_by_id),
915                key_protector_settings,
916                gsp_extended_status_flags: gsp_response.extended_status_flags,
917            });
918        }
919
920        derived_keys.ingress = derived_keys_by_id.ingress;
921
922        tracing::info!(CVM_ALLOWED, "Converting GSP method.");
923    }
924
925    let egress_seed;
926    let mut ingress_seed = None;
927
928    // To get to this point, either KEK or GSP must be available
929    // Mix tenant key with GSP key to create data store encryption keys
930    // Covers possible egress combinations:
931    // GSP, GSP + KEK, GSP By Id + KEK
932
933    if requires_gsp_by_id || no_gsp {
934        // If DEK exists, ingress is either KEK or KEK + GSP By Id
935        // If no DEK, then ingress was Gsp By Id (derived above)
936        if found_dek {
937            if requires_gsp_by_id {
938                ingress_seed = Some(
939                    gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize]
940                        .to_vec(),
941                );
942            } else {
943                derived_keys.ingress = ingress_key;
944            }
945        }
946
947        // Choose best available egress seed
948        if no_gsp {
949            egress_seed =
950                gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize].to_vec();
951            key_protector_settings.use_gsp_by_id = true;
952        } else {
953            egress_seed =
954                gsp_response.new_gsp.buffer[..gsp_response.new_gsp.length as usize].to_vec();
955        }
956    } else {
957        // `no_gsp` is false, using `gsp_response`
958
959        if gsp_response.decrypted_gsp[ingress_idx].length == 0
960            && gsp_response.decrypted_gsp[egress_idx].length == 0
961        {
962            tracing::trace!(CVM_ALLOWED, "Applying GSP.");
963
964            // VMGS has never had any GSP applied.
965            // Leave ingress key untouched, derive egress key with new seed.
966            egress_seed =
967                gsp_response.new_gsp.buffer[..gsp_response.new_gsp.length as usize].to_vec();
968
969            // Ingress key is either zero or tenant only.
970            // Only copy in the case where a tenant key was released.
971            if !no_kek {
972                derived_keys.ingress = ingress_key;
973            }
974        } else {
975            tracing::trace!(CVM_ALLOWED, "Using GSP.");
976
977            ingress_seed = Some(
978                gsp_response.decrypted_gsp[ingress_idx].buffer
979                    [..gsp_response.decrypted_gsp[ingress_idx].length as usize]
980                    .to_vec(),
981            );
982
983            if gsp_response.decrypted_gsp[egress_idx].length == 0 {
984                // Derive ingress with saved seed, derive egress with new seed.
985                egress_seed =
986                    gsp_response.new_gsp.buffer[..gsp_response.new_gsp.length as usize].to_vec();
987            } else {
988                // System failed during data store unlock, and is in indeterminate state.
989                // The egress key might have been applied, or the ingress key might be valid.
990                // Use saved KP, derive ingress/egress keys to attempt recovery.
991                // Do not update the saved KP with new seed value.
992                egress_seed = gsp_response.decrypted_gsp[egress_idx].buffer
993                    [..gsp_response.decrypted_gsp[egress_idx].length as usize]
994                    .to_vec();
995                key_protector_settings.should_write_kp = false;
996                decrypt_egress_key = Some(encrypt_egress_key);
997            }
998        }
999    }
1000
1001    // Derive key used to lock data store previously
1002    if let Some(seed) = ingress_seed {
1003        derived_keys.ingress = crypto::derive_key(&ingress_key, &seed, VMGS_KEY_DERIVE_LABEL)
1004            .map_err(GetDerivedKeysError::DeriveIngressKey)?;
1005    }
1006
1007    // Always derive a new egress key using best available seed
1008    derived_keys.decrypt_egress = decrypt_egress_key
1009        .map(|key| crypto::derive_key(&key, &egress_seed, VMGS_KEY_DERIVE_LABEL))
1010        .transpose()
1011        .map_err(GetDerivedKeysError::DeriveEgressKey)?;
1012
1013    derived_keys.encrypt_egress =
1014        crypto::derive_key(&encrypt_egress_key, &egress_seed, VMGS_KEY_DERIVE_LABEL)
1015            .map_err(GetDerivedKeysError::DeriveEgressKey)?;
1016
1017    if key_protector_settings.should_write_kp {
1018        // Update with all seeds used, but do not write until data store is unlocked
1019        key_protector.gsp[egress_idx]
1020            .gsp_buffer
1021            .copy_from_slice(&gsp_response.encrypted_gsp.buffer);
1022        key_protector.gsp[egress_idx].gsp_length = gsp_response.encrypted_gsp.length;
1023
1024        if let Some(hardware_derived_keys) = hardware_derived_keys {
1025            let hardware_key_protector = HardwareKeyProtector::seal_key(
1026                &hardware_derived_keys,
1027                &derived_keys.encrypt_egress,
1028            )
1029            .map_err(GetDerivedKeysError::SealEgressKeyUsingHardwareDerivedKeys)?;
1030
1031            vmgs::write_hardware_key_protector(&hardware_key_protector, vmgs)
1032                .await
1033                .map_err(GetDerivedKeysError::VmgsWriteHardwareKeyProtector)?;
1034
1035            tracing::info!(CVM_ALLOWED, "hardware key protector updated");
1036        }
1037    }
1038
1039    if matches!(
1040        guest_state_encryption_policy,
1041        GuestStateEncryptionPolicy::None | GuestStateEncryptionPolicy::GspById
1042    ) {
1043        // Log a warning here to indicate that the VMGS state is out of
1044        // sync with the VM's configuration.
1045        //
1046        // This should only happen if the VM is configured to have no
1047        // encryption or GspById encryption, but it already has GspKey
1048        // encryption and strict encryption policy is disabled.
1049        tracing::warn!(CVM_ALLOWED, "Allowing Gsp");
1050    } else {
1051        tracing::info!(CVM_ALLOWED, "Using Gsp");
1052    }
1053
1054    Ok(DerivedKeyResult {
1055        derived_keys: Some(derived_keys),
1056        key_protector_settings,
1057        gsp_extended_status_flags: gsp_response.extended_status_flags,
1058    })
1059}
1060
1061/// Update data store keys with key protectors based on VmUniqueId & host seed.
1062fn get_derived_keys_by_id(
1063    key_protector_by_id: &mut KeyProtectorById,
1064    bios_guid: Guid,
1065    gsp_response_by_id: GuestStateProtectionById,
1066) -> Result<Keys, GetDerivedKeysByIdError> {
1067    // This does not handle tenant encrypted VMGS files or Isolated VM,
1068    // or the case where an unlock/relock fails and a snapshot is
1069    // made from that file (the Id cannot change in that failure path).
1070    // When converted to a later scheme, Egress Key will be overwritten.
1071
1072    // Always derive a new egress key from current VmUniqueId
1073    let new_egress_key = crypto::derive_key(
1074        &gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize],
1075        bios_guid.as_bytes(),
1076        VMGS_KEY_DERIVE_LABEL,
1077    )
1078    .map_err(GetDerivedKeysByIdError::DeriveEgressKeyUsingCurrentVmId)?;
1079
1080    if new_egress_key.len() != AES_GCM_KEY_LENGTH {
1081        Err(GetDerivedKeysByIdError::InvalidDerivedEgressKeySize {
1082            key_size: new_egress_key.len(),
1083            expected_size: AES_GCM_KEY_LENGTH,
1084        })?
1085    }
1086
1087    // Ingress values depend on what happened previously to the datastore.
1088    // If not previously encrypted (no saved Id), then Ingress Key not required.
1089    let new_ingress_key = if key_protector_by_id.inner.id_guid != Guid::default() {
1090        // Derive key used to lock data store previously
1091        crypto::derive_key(
1092            &gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize],
1093            key_protector_by_id.inner.id_guid.as_bytes(),
1094            VMGS_KEY_DERIVE_LABEL,
1095        )
1096        .map_err(GetDerivedKeysByIdError::DeriveIngressKeyUsingKeyProtectorId)?
1097    } else {
1098        // If data store is not encrypted, Ingress should equal Egress
1099        new_egress_key
1100    };
1101
1102    if new_ingress_key.len() != AES_GCM_KEY_LENGTH {
1103        Err(GetDerivedKeysByIdError::InvalidDerivedIngressKeySize {
1104            key_size: new_ingress_key.len(),
1105            expected_size: AES_GCM_KEY_LENGTH,
1106        })?
1107    }
1108
1109    Ok(Keys {
1110        ingress: new_ingress_key,
1111        decrypt_egress: None,
1112        encrypt_egress: new_egress_key,
1113    })
1114}
1115
1116/// Prepare the request payload and request GSP from the host via GET.
1117async fn get_gsp_data(
1118    get: &GuestEmulationTransportClient,
1119    key_protector: &mut KeyProtector,
1120) -> GuestStateProtection {
1121    use openhcl_attestation_protocol::vmgs::GSP_BUFFER_SIZE;
1122    use openhcl_attestation_protocol::vmgs::NUMBER_KP;
1123
1124    const_assert_eq!(guest_emulation_transport::api::NUMBER_GSP, NUMBER_KP as u32);
1125    const_assert_eq!(
1126        guest_emulation_transport::api::GSP_CIPHERTEXT_MAX,
1127        GSP_BUFFER_SIZE as u32
1128    );
1129
1130    let mut encrypted_gsp =
1131        [guest_emulation_transport::api::GspCiphertextContent::new_zeroed(); NUMBER_KP];
1132
1133    for (i, gsp) in encrypted_gsp.iter_mut().enumerate().take(NUMBER_KP) {
1134        if key_protector.gsp[i].gsp_length == 0 {
1135            continue;
1136        }
1137
1138        gsp.buffer[..key_protector.gsp[i].gsp_length as usize].copy_from_slice(
1139            &key_protector.gsp[i].gsp_buffer[..key_protector.gsp[i].gsp_length as usize],
1140        );
1141
1142        gsp.length = key_protector.gsp[i].gsp_length;
1143    }
1144
1145    get.guest_state_protection_data(encrypted_gsp, GspExtendedStatusFlags::new())
1146        .await
1147}
1148
1149/// Update Key Protector to remove 2nd protector, and write to VMGS
1150async fn persist_all_key_protectors(
1151    vmgs: &mut Vmgs,
1152    key_protector: &mut KeyProtector,
1153    key_protector_by_id: &mut KeyProtectorById,
1154    bios_guid: Guid,
1155    key_protector_settings: KeyProtectorSettings,
1156) -> Result<(), PersistAllKeyProtectorsError> {
1157    use openhcl_attestation_protocol::vmgs::NUMBER_KP;
1158
1159    if key_protector_settings.use_gsp_by_id && !key_protector_settings.should_write_kp {
1160        vmgs::write_key_protector_by_id(&mut key_protector_by_id.inner, vmgs, false, bios_guid)
1161            .await
1162            .map_err(PersistAllKeyProtectorsError::WriteKeyProtectorById)?;
1163    } else {
1164        // If HW Key unlocked VMGS, do not alter KP
1165        if !key_protector_settings.use_hardware_unlock {
1166            // Remove ingress KP & DEK, no longer applies to data store
1167            key_protector.dek[key_protector.active_kp as usize % NUMBER_KP]
1168                .dek_buffer
1169                .fill(0);
1170            key_protector.gsp[key_protector.active_kp as usize % NUMBER_KP].gsp_length = 0;
1171            key_protector.active_kp += 1;
1172
1173            vmgs::write_key_protector(key_protector, vmgs)
1174                .await
1175                .map_err(PersistAllKeyProtectorsError::WriteKeyProtector)?;
1176        }
1177
1178        // Update Id data to indicate this scheme is no longer in use
1179        if !key_protector_settings.use_gsp_by_id
1180            && key_protector_by_id.found_id
1181            && key_protector_by_id.inner.ported == 0
1182        {
1183            key_protector_by_id.inner.ported = 1;
1184            vmgs::write_key_protector_by_id(&mut key_protector_by_id.inner, vmgs, true, bios_guid)
1185                .await
1186                .map_err(PersistAllKeyProtectorsError::WriteKeyProtectorById)?;
1187        }
1188    }
1189
1190    Ok(())
1191}
1192
1193#[cfg(test)]
1194mod tests {
1195    use super::*;
1196    use disk_backend::Disk;
1197    use disklayer_ram::ram_disk;
1198    use get_protocol::GSP_CLEARTEXT_MAX;
1199    use get_protocol::GspExtendedStatusFlags;
1200    use key_protector::AES_WRAPPED_AES_KEY_LENGTH;
1201    use openhcl_attestation_protocol::vmgs::DEK_BUFFER_SIZE;
1202    use openhcl_attestation_protocol::vmgs::DekKp;
1203    use openhcl_attestation_protocol::vmgs::GSP_BUFFER_SIZE;
1204    use openhcl_attestation_protocol::vmgs::GspKp;
1205    use openhcl_attestation_protocol::vmgs::NUMBER_KP;
1206    use pal_async::async_test;
1207    use vmgs_format::EncryptionAlgorithm;
1208    use vmgs_format::FileId;
1209
1210    const ONE_MEGA_BYTE: u64 = 1024 * 1024;
1211
1212    fn new_test_file() -> Disk {
1213        ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
1214    }
1215
1216    async fn new_formatted_vmgs() -> Vmgs {
1217        let disk = new_test_file();
1218
1219        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
1220
1221        assert!(
1222            key_protector_is_empty(&mut vmgs).await,
1223            "Newly formatted VMGS should have an empty key protector"
1224        );
1225        assert!(
1226            key_protector_by_id_is_empty(&mut vmgs).await,
1227            "Newly formatted VMGS should have an empty key protector by id"
1228        );
1229
1230        vmgs
1231    }
1232
1233    async fn key_protector_is_empty(vmgs: &mut Vmgs) -> bool {
1234        let key_protector = vmgs::read_key_protector(vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1235            .await
1236            .unwrap();
1237
1238        key_protector.as_bytes().iter().all(|&b| b == 0)
1239    }
1240
1241    async fn key_protector_by_id_is_empty(vmgs: &mut Vmgs) -> bool {
1242        vmgs::read_key_protector_by_id(vmgs)
1243            .await
1244            .is_err_and(|err| {
1245                matches!(
1246                    err,
1247                    vmgs::ReadFromVmgsError::EntryNotFound(FileId::VM_UNIQUE_ID)
1248                )
1249            })
1250    }
1251
1252    fn new_key_protector() -> KeyProtector {
1253        // Ingress and egress KPs are assumed to be the only two KPs, therefore `NUMBER_KP` should be 2
1254        assert_eq!(NUMBER_KP, 2);
1255
1256        let ingress_dek = DekKp {
1257            dek_buffer: [1; DEK_BUFFER_SIZE],
1258        };
1259        let egress_dek = DekKp {
1260            dek_buffer: [2; DEK_BUFFER_SIZE],
1261        };
1262        let ingress_gsp = GspKp {
1263            gsp_length: GSP_BUFFER_SIZE as u32,
1264            gsp_buffer: [3; GSP_BUFFER_SIZE],
1265        };
1266        let egress_gsp = GspKp {
1267            gsp_length: GSP_BUFFER_SIZE as u32,
1268            gsp_buffer: [4; GSP_BUFFER_SIZE],
1269        };
1270        KeyProtector {
1271            dek: [ingress_dek, egress_dek],
1272            gsp: [ingress_gsp, egress_gsp],
1273            active_kp: 0,
1274        }
1275    }
1276
1277    fn new_key_protector_by_id(
1278        id_guid: Option<Guid>,
1279        ported: Option<u8>,
1280        found_id: bool,
1281    ) -> KeyProtectorById {
1282        let key_protector_by_id = openhcl_attestation_protocol::vmgs::KeyProtectorById {
1283            id_guid: id_guid.unwrap_or_else(Guid::new_random),
1284            ported: ported.unwrap_or(0),
1285            pad: [0; 3],
1286        };
1287
1288        KeyProtectorById {
1289            inner: key_protector_by_id,
1290            found_id,
1291        }
1292    }
1293
1294    #[async_test]
1295    async fn do_nothing_without_derived_keys() {
1296        let mut vmgs = new_formatted_vmgs().await;
1297
1298        let mut key_protector = new_key_protector();
1299        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1300
1301        let key_protector_settings = KeyProtectorSettings {
1302            should_write_kp: false,
1303            use_gsp_by_id: false,
1304            use_hardware_unlock: false,
1305        };
1306
1307        let bios_guid = Guid::new_random();
1308
1309        unlock_vmgs_data_store(
1310            &mut vmgs,
1311            false,
1312            &mut key_protector,
1313            &mut key_protector_by_id,
1314            None,
1315            key_protector_settings,
1316            bios_guid,
1317        )
1318        .await
1319        .unwrap();
1320
1321        assert!(key_protector_is_empty(&mut vmgs).await);
1322        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
1323
1324        // Create another instance as the previous `unlock_vmgs_data_store` took ownership of the last one
1325        let key_protector_settings = KeyProtectorSettings {
1326            should_write_kp: false,
1327            use_gsp_by_id: false,
1328            use_hardware_unlock: false,
1329        };
1330
1331        // Even if the VMGS is encrypted, if no derived keys are provided, nothing should happen
1332        unlock_vmgs_data_store(
1333            &mut vmgs,
1334            true,
1335            &mut key_protector,
1336            &mut key_protector_by_id,
1337            None,
1338            key_protector_settings,
1339            bios_guid,
1340        )
1341        .await
1342        .unwrap();
1343
1344        assert!(key_protector_is_empty(&mut vmgs).await);
1345        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
1346    }
1347
1348    #[async_test]
1349    async fn provision_vmgs_and_rotate_keys() {
1350        let mut vmgs = new_formatted_vmgs().await;
1351
1352        let mut key_protector = new_key_protector();
1353        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1354
1355        let ingress = [1; AES_GCM_KEY_LENGTH];
1356        let egress = [2; AES_GCM_KEY_LENGTH];
1357        let derived_keys = Keys {
1358            ingress,
1359            decrypt_egress: None,
1360            encrypt_egress: egress,
1361        };
1362
1363        let key_protector_settings = KeyProtectorSettings {
1364            should_write_kp: true,
1365            use_gsp_by_id: true,
1366            use_hardware_unlock: false,
1367        };
1368
1369        let bios_guid = Guid::new_random();
1370
1371        // Without encryption implies the provision path
1372        // The VMGS will be locked using the egress key
1373        unlock_vmgs_data_store(
1374            &mut vmgs,
1375            false,
1376            &mut key_protector,
1377            &mut key_protector_by_id,
1378            Some(derived_keys),
1379            key_protector_settings,
1380            bios_guid,
1381        )
1382        .await
1383        .unwrap();
1384
1385        // The ingress key is essentially ignored since the VMGS wasn't previously encrypted
1386        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1387
1388        // The egress key was used to lock the VMGS after provisioning
1389        let egress_index = vmgs.unlock_with_encryption_key(&egress).await.unwrap();
1390        // Since this is a new VMGS, the egress key is the first and only key
1391        assert_eq!(egress_index, 0);
1392
1393        // Since both `should_write_kp` and `use_gsp_by_id` are true, both key protectors should be updated
1394        assert!(!key_protector_is_empty(&mut vmgs).await);
1395        assert!(!key_protector_by_id_is_empty(&mut vmgs).await);
1396
1397        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1398            .await
1399            .unwrap();
1400        assert_eq!(found_key_protector.as_bytes(), key_protector.as_bytes());
1401
1402        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1403        assert_eq!(
1404            found_key_protector_by_id.as_bytes(),
1405            key_protector_by_id.inner.as_bytes()
1406        );
1407
1408        // Now that the VMGS has been provisioned, simulate the rotation of keys
1409        let new_egress = [3; AES_GCM_KEY_LENGTH];
1410
1411        let mut new_key_protector = new_key_protector();
1412        let mut new_key_protector_by_id = new_key_protector_by_id(None, None, false);
1413
1414        let key_protector_settings = KeyProtectorSettings {
1415            should_write_kp: true,
1416            use_gsp_by_id: true,
1417            use_hardware_unlock: false,
1418        };
1419
1420        // Ingress is now the old egress, and we provide a new new egress key
1421        let derived_keys = Keys {
1422            ingress: egress,
1423            decrypt_egress: None,
1424            encrypt_egress: new_egress,
1425        };
1426
1427        unlock_vmgs_data_store(
1428            &mut vmgs,
1429            true,
1430            &mut new_key_protector,
1431            &mut new_key_protector_by_id,
1432            Some(derived_keys),
1433            key_protector_settings,
1434            bios_guid,
1435        )
1436        .await
1437        .unwrap();
1438
1439        // We should still fail to unlock the VMGS with the original ingress key
1440        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1441        // The old egress key should no longer be able to unlock the VMGS
1442        vmgs.unlock_with_encryption_key(&egress).await.unwrap_err();
1443
1444        // The new egress key should be able to unlock the VMGS
1445        let new_egress_index = vmgs.unlock_with_encryption_key(&new_egress).await.unwrap();
1446        // The old egress key was removed, but not before the new egress key was added in the 1th slot
1447        assert_eq!(new_egress_index, 1);
1448
1449        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1450            .await
1451            .unwrap();
1452        assert_eq!(found_key_protector.as_bytes(), new_key_protector.as_bytes());
1453
1454        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1455        assert_eq!(
1456            found_key_protector_by_id.as_bytes(),
1457            new_key_protector_by_id.inner.as_bytes()
1458        );
1459    }
1460
1461    #[async_test]
1462    async fn unlock_previously_encrypted_vmgs_with_ingress_key() {
1463        let mut vmgs = new_formatted_vmgs().await;
1464
1465        let mut key_protector = new_key_protector();
1466        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1467
1468        let ingress = [1; AES_GCM_KEY_LENGTH];
1469        let egress = [2; AES_GCM_KEY_LENGTH];
1470
1471        let derived_keys = Keys {
1472            ingress,
1473            decrypt_egress: None,
1474            encrypt_egress: egress,
1475        };
1476
1477        vmgs.add_new_encryption_key(&ingress, EncryptionAlgorithm::AES_GCM)
1478            .await
1479            .unwrap();
1480
1481        // Initially, the VMGS can be unlocked using the ingress key
1482        let ingress_index = vmgs.unlock_with_encryption_key(&ingress).await.unwrap();
1483        assert_eq!(ingress_index, 0);
1484
1485        let key_protector_settings = KeyProtectorSettings {
1486            should_write_kp: true,
1487            use_gsp_by_id: true,
1488            use_hardware_unlock: false,
1489        };
1490
1491        let bios_guid = Guid::new_random();
1492
1493        unlock_vmgs_data_store(
1494            &mut vmgs,
1495            true,
1496            &mut key_protector,
1497            &mut key_protector_by_id,
1498            Some(derived_keys),
1499            key_protector_settings,
1500            bios_guid,
1501        )
1502        .await
1503        .unwrap();
1504
1505        // After the VMGS has been unlocked, the VMGS encryption key should be rotated from ingress to egress
1506        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1507        let egress_index = vmgs.unlock_with_encryption_key(&egress).await.unwrap();
1508        // The ingress key was removed, but not before the egress key was added in the 0th slot
1509        assert_eq!(egress_index, 1);
1510
1511        // Since both `should_write_kp` and `use_gsp_by_id` are true, both key protectors should be updated
1512        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1513            .await
1514            .unwrap();
1515        assert_eq!(found_key_protector.as_bytes(), key_protector.as_bytes());
1516
1517        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1518        assert_eq!(
1519            found_key_protector_by_id.as_bytes(),
1520            key_protector_by_id.inner.as_bytes()
1521        );
1522    }
1523
1524    #[async_test]
1525    async fn failed_to_persist_ingress_key_so_use_egress_key_to_unlock_vmgs() {
1526        let mut vmgs = new_formatted_vmgs().await;
1527
1528        let mut key_protector = new_key_protector();
1529        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1530
1531        let ingress = [1; AES_GCM_KEY_LENGTH];
1532        let decrypt_egress = [2; AES_GCM_KEY_LENGTH];
1533        let encrypt_egress = [3; AES_GCM_KEY_LENGTH];
1534
1535        let derived_keys = Keys {
1536            ingress,
1537            decrypt_egress: Some(decrypt_egress),
1538            encrypt_egress,
1539        };
1540
1541        // Add only the egress key to the VMGS to simulate a failure to persist the ingress key
1542        let egress_key_index = vmgs
1543            .add_new_encryption_key(&decrypt_egress, EncryptionAlgorithm::AES_GCM)
1544            .await
1545            .unwrap();
1546        assert_eq!(egress_key_index, 0);
1547
1548        let found_egress_key_index = vmgs
1549            .unlock_with_encryption_key(&decrypt_egress)
1550            .await
1551            .unwrap();
1552        assert_eq!(found_egress_key_index, egress_key_index);
1553
1554        // Confirm that the ingress key cannot be used to unlock the VMGS
1555        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1556
1557        let key_protector_settings = KeyProtectorSettings {
1558            should_write_kp: true,
1559            use_gsp_by_id: true,
1560            use_hardware_unlock: false,
1561        };
1562
1563        let bios_guid = Guid::new_random();
1564
1565        unlock_vmgs_data_store(
1566            &mut vmgs,
1567            true,
1568            &mut key_protector,
1569            &mut key_protector_by_id,
1570            Some(derived_keys),
1571            key_protector_settings,
1572            bios_guid,
1573        )
1574        .await
1575        .unwrap();
1576
1577        // Confirm that the ingress key was not added
1578        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1579
1580        // Confirm that the decrypt egress key no longer works
1581        vmgs.unlock_with_encryption_key(&decrypt_egress)
1582            .await
1583            .unwrap_err();
1584
1585        // The encrypt_egress key can unlock the VMGS and was added as a new key
1586        let found_egress_key_index = vmgs
1587            .unlock_with_encryption_key(&encrypt_egress)
1588            .await
1589            .unwrap();
1590        assert_eq!(found_egress_key_index, 1);
1591
1592        // Since both `should_write_kp` and `use_gsp_by_id` are true, both key protectors should be updated
1593        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1594            .await
1595            .unwrap();
1596        assert_eq!(found_key_protector.as_bytes(), key_protector.as_bytes());
1597
1598        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1599        assert_eq!(
1600            found_key_protector_by_id.as_bytes(),
1601            key_protector_by_id.inner.as_bytes()
1602        );
1603    }
1604
1605    #[async_test]
1606    async fn fail_to_unlock_vmgs_with_existing_ingress_key() {
1607        let mut vmgs = new_formatted_vmgs().await;
1608
1609        let mut key_protector = new_key_protector();
1610        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1611
1612        let ingress = [1; AES_GCM_KEY_LENGTH];
1613
1614        // Ingress and egress keys are the same
1615        let derived_keys = Keys {
1616            ingress,
1617            decrypt_egress: None,
1618            encrypt_egress: ingress,
1619        };
1620
1621        // Add two random keys to the VMGS to simulate unlock failure when ingress and egress keys are the same
1622        let additional_key = [2; AES_GCM_KEY_LENGTH];
1623        let yet_another_key = [3; AES_GCM_KEY_LENGTH];
1624
1625        let additional_key_index = vmgs
1626            .add_new_encryption_key(&additional_key, EncryptionAlgorithm::AES_GCM)
1627            .await
1628            .unwrap();
1629        assert_eq!(additional_key_index, 0);
1630
1631        let yet_another_key_index = vmgs
1632            .add_new_encryption_key(&yet_another_key, EncryptionAlgorithm::AES_GCM)
1633            .await
1634            .unwrap();
1635        assert_eq!(yet_another_key_index, 1);
1636
1637        let key_protector_settings = KeyProtectorSettings {
1638            should_write_kp: true,
1639            use_gsp_by_id: true,
1640            use_hardware_unlock: false,
1641        };
1642
1643        let bios_guid = Guid::new_random();
1644
1645        let unlock_result = unlock_vmgs_data_store(
1646            &mut vmgs,
1647            true,
1648            &mut key_protector,
1649            &mut key_protector_by_id,
1650            Some(derived_keys),
1651            key_protector_settings,
1652            bios_guid,
1653        )
1654        .await;
1655        assert!(unlock_result.is_err());
1656        assert_eq!(
1657            unlock_result.unwrap_err().to_string(),
1658            "failed to unlock vmgs with the existing ingress key".to_string()
1659        );
1660    }
1661
1662    #[async_test]
1663    async fn fail_to_unlock_vmgs_with_new_ingress_key() {
1664        let mut vmgs = new_formatted_vmgs().await;
1665
1666        let mut key_protector = new_key_protector();
1667        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1668
1669        let derived_keys = Keys {
1670            ingress: [1; AES_GCM_KEY_LENGTH],
1671            decrypt_egress: None,
1672            encrypt_egress: [2; AES_GCM_KEY_LENGTH],
1673        };
1674
1675        // Add two random keys to the VMGS to simulate unlock failure when ingress and egress keys are *not* the same
1676        let additional_key = [3; AES_GCM_KEY_LENGTH];
1677        let yet_another_key = [4; AES_GCM_KEY_LENGTH];
1678
1679        let additional_key_index = vmgs
1680            .add_new_encryption_key(&additional_key, EncryptionAlgorithm::AES_GCM)
1681            .await
1682            .unwrap();
1683        assert_eq!(additional_key_index, 0);
1684
1685        let yet_another_key_index = vmgs
1686            .add_new_encryption_key(&yet_another_key, EncryptionAlgorithm::AES_GCM)
1687            .await
1688            .unwrap();
1689        assert_eq!(yet_another_key_index, 1);
1690
1691        let key_protector_settings = KeyProtectorSettings {
1692            should_write_kp: true,
1693            use_gsp_by_id: true,
1694            use_hardware_unlock: false,
1695        };
1696
1697        let bios_guid = Guid::new_random();
1698
1699        let unlock_result = unlock_vmgs_data_store(
1700            &mut vmgs,
1701            true,
1702            &mut key_protector,
1703            &mut key_protector_by_id,
1704            Some(derived_keys),
1705            key_protector_settings,
1706            bios_guid,
1707        )
1708        .await;
1709        assert!(unlock_result.is_err());
1710        assert_eq!(
1711            unlock_result.unwrap_err().to_string(),
1712            "failed to unlock vmgs with the existing ingress key".to_string()
1713        );
1714    }
1715
1716    #[async_test]
1717    async fn get_derived_keys_using_id() {
1718        let bios_guid = Guid::new_random();
1719
1720        let gsp_response_by_id = GuestStateProtectionById {
1721            seed: guest_emulation_transport::api::GspCleartextContent {
1722                length: GSP_CLEARTEXT_MAX,
1723                buffer: [1; GSP_CLEARTEXT_MAX as usize * 2],
1724            },
1725            extended_status_flags: GspExtendedStatusFlags::from_bits(0),
1726        };
1727
1728        // When the key protector by id inner `id_guid` is all zeroes, the derived ingress and egress keys
1729        // should be identical.
1730        let mut key_protector_by_id =
1731            new_key_protector_by_id(Some(Guid::new_zeroed()), None, false);
1732        let derived_keys =
1733            get_derived_keys_by_id(&mut key_protector_by_id, bios_guid, gsp_response_by_id)
1734                .unwrap();
1735
1736        assert_eq!(derived_keys.ingress, derived_keys.encrypt_egress);
1737
1738        // When the key protector by id inner `id_guid` is not all zeroes, the derived ingress and egress keys
1739        // should be different.
1740        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1741        let derived_keys =
1742            get_derived_keys_by_id(&mut key_protector_by_id, bios_guid, gsp_response_by_id)
1743                .unwrap();
1744
1745        assert_ne!(derived_keys.ingress, derived_keys.encrypt_egress);
1746
1747        // When the `gsp_response_by_id` seed length is 0, deriving a key will fail.
1748        let gsp_response_by_id_with_0_length_seed = GuestStateProtectionById {
1749            seed: guest_emulation_transport::api::GspCleartextContent {
1750                length: 0,
1751                buffer: [1; GSP_CLEARTEXT_MAX as usize * 2],
1752            },
1753            extended_status_flags: GspExtendedStatusFlags::from_bits(0),
1754        };
1755
1756        let derived_keys_response = get_derived_keys_by_id(
1757            &mut key_protector_by_id,
1758            bios_guid,
1759            gsp_response_by_id_with_0_length_seed,
1760        );
1761        assert!(derived_keys_response.is_err());
1762        assert_eq!(
1763            derived_keys_response.unwrap_err().to_string(),
1764            "failed to derive an egress key based on current vm bios guid".to_string()
1765        );
1766    }
1767
1768    #[async_test]
1769    async fn pass_through_persist_all_key_protectors() {
1770        let mut vmgs = new_formatted_vmgs().await;
1771        let mut key_protector = new_key_protector();
1772        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1773        let bios_guid = Guid::new_random();
1774
1775        // Copied/cloned bits used for comparison later
1776        let kp_copy = key_protector.as_bytes().to_vec();
1777        let active_kp_copy = key_protector.active_kp;
1778
1779        // When all key protector settings are true, no actions will be taken on the key protectors or VMGS
1780        let key_protector_settings = KeyProtectorSettings {
1781            should_write_kp: true,
1782            use_gsp_by_id: true,
1783            use_hardware_unlock: true,
1784        };
1785        persist_all_key_protectors(
1786            &mut vmgs,
1787            &mut key_protector,
1788            &mut key_protector_by_id,
1789            bios_guid,
1790            key_protector_settings,
1791        )
1792        .await
1793        .unwrap();
1794
1795        assert!(key_protector_is_empty(&mut vmgs).await);
1796        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
1797
1798        // The key protector should remain unchanged
1799        assert_eq!(active_kp_copy, key_protector.active_kp);
1800        assert_eq!(kp_copy.as_slice(), key_protector.as_bytes());
1801    }
1802
1803    #[async_test]
1804    async fn persist_all_key_protectors_write_key_protector_by_id() {
1805        let mut vmgs = new_formatted_vmgs().await;
1806        let mut key_protector = new_key_protector();
1807        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1808        let bios_guid = Guid::new_random();
1809
1810        // Copied/cloned bits used for comparison later
1811        let kp_copy = key_protector.as_bytes().to_vec();
1812        let active_kp_copy = key_protector.active_kp;
1813
1814        // When `use_gsp_by_id` is true and `should_write_kp` is false, the key protector by id should be written to the VMGS
1815        let key_protector_settings = KeyProtectorSettings {
1816            should_write_kp: false,
1817            use_gsp_by_id: true,
1818            use_hardware_unlock: false,
1819        };
1820        persist_all_key_protectors(
1821            &mut vmgs,
1822            &mut key_protector,
1823            &mut key_protector_by_id,
1824            bios_guid,
1825            key_protector_settings,
1826        )
1827        .await
1828        .unwrap();
1829
1830        // The previously empty VMGS now holds the key protector by id but not the key protector
1831        assert!(key_protector_is_empty(&mut vmgs).await);
1832        assert!(!key_protector_by_id_is_empty(&mut vmgs).await);
1833
1834        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1835        assert_eq!(
1836            found_key_protector_by_id.as_bytes(),
1837            key_protector_by_id.inner.as_bytes()
1838        );
1839
1840        // The key protector should remain unchanged
1841        assert_eq!(kp_copy.as_slice(), key_protector.as_bytes());
1842        assert_eq!(active_kp_copy, key_protector.active_kp);
1843    }
1844
1845    #[async_test]
1846    async fn persist_all_key_protectors_remove_ingress_kp() {
1847        let mut vmgs = new_formatted_vmgs().await;
1848        let mut key_protector = new_key_protector();
1849        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1850        let bios_guid = Guid::new_random();
1851
1852        // Copied active KP for later use
1853        let active_kp_copy = key_protector.active_kp;
1854
1855        // When `use_gsp_by_id` is false, `should_write_kp` is true, and `use_hardware_unlock` is false, the active key protector's
1856        // active kp's dek should be zeroed, the active kp's gsp length should be set to 0, and the active kp should be incremented
1857        let key_protector_settings = KeyProtectorSettings {
1858            should_write_kp: true,
1859            use_gsp_by_id: false,
1860            use_hardware_unlock: false,
1861        };
1862        persist_all_key_protectors(
1863            &mut vmgs,
1864            &mut key_protector,
1865            &mut key_protector_by_id,
1866            bios_guid,
1867            key_protector_settings,
1868        )
1869        .await
1870        .unwrap();
1871
1872        assert!(!key_protector_is_empty(&mut vmgs).await);
1873        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
1874
1875        // The previously empty VMGS's key protector should now be overwritten
1876        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1877            .await
1878            .unwrap();
1879
1880        assert!(
1881            found_key_protector.dek[active_kp_copy as usize]
1882                .dek_buffer
1883                .iter()
1884                .all(|&b| b == 0),
1885        );
1886        assert_eq!(
1887            found_key_protector.gsp[active_kp_copy as usize].gsp_length,
1888            0
1889        );
1890        assert_eq!(found_key_protector.active_kp, active_kp_copy + 1);
1891    }
1892
1893    #[async_test]
1894    async fn persist_all_key_protectors_mark_key_protector_by_id_as_not_in_use() {
1895        let mut vmgs = new_formatted_vmgs().await;
1896        let mut key_protector = new_key_protector();
1897        let mut key_protector_by_id = new_key_protector_by_id(None, None, true);
1898        let bios_guid = Guid::new_random();
1899
1900        // When `use_gsp_by_id` is false, `should_write_kp` is true, `use_hardware_unlock` is true, and
1901        // the key protector by id is found and not ported, the key protector by id should be marked as ported
1902        let key_protector_settings = KeyProtectorSettings {
1903            should_write_kp: true,
1904            use_gsp_by_id: false,
1905            use_hardware_unlock: true,
1906        };
1907
1908        persist_all_key_protectors(
1909            &mut vmgs,
1910            &mut key_protector,
1911            &mut key_protector_by_id,
1912            bios_guid,
1913            key_protector_settings,
1914        )
1915        .await
1916        .unwrap();
1917
1918        assert!(key_protector_is_empty(&mut vmgs).await);
1919        assert!(!key_protector_by_id_is_empty(&mut vmgs).await);
1920
1921        // The previously empty VMGS's key protector by id should now be overwritten
1922        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1923        assert_eq!(found_key_protector_by_id.ported, 1);
1924        assert_eq!(
1925            found_key_protector_by_id.id_guid,
1926            key_protector_by_id.inner.id_guid
1927        );
1928    }
1929}