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