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::GspType;
25use ::vmgs::Vmgs;
26use cvm_tracing::CVM_ALLOWED;
27use get_protocol::dps_json::GuestStateEncryptionPolicy;
28use guest_emulation_transport::GuestEmulationTransportClient;
29use guest_emulation_transport::api::GspExtendedStatusFlags;
30use guest_emulation_transport::api::GuestStateProtection;
31use guest_emulation_transport::api::GuestStateProtectionById;
32use guid::Guid;
33use hardware_key_sealing::HardwareDerivedKeys;
34use hardware_key_sealing::HardwareKeyProtectorExt as _;
35use key_protector::GetKeysFromKeyProtectorError;
36use key_protector::KeyProtectorExt as _;
37use mesh::MeshPayload;
38use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationVmConfig;
39use openhcl_attestation_protocol::vmgs::AES_GCM_KEY_LENGTH;
40use openhcl_attestation_protocol::vmgs::HardwareKeyProtector;
41use openhcl_attestation_protocol::vmgs::KeyProtector;
42use openhcl_attestation_protocol::vmgs::SecurityProfile;
43use openssl::pkey::Private;
44use openssl::rsa::Rsa;
45use pal_async::local::LocalDriver;
46use secure_key_release::VmgsEncryptionKeys;
47use static_assertions::const_assert_eq;
48use std::fmt::Debug;
49use tee_call::TeeCall;
50use thiserror::Error;
51use zerocopy::FromZeros;
52use zerocopy::IntoBytes;
53
54/// An attestation error.
55#[derive(Debug, Error)]
56#[error(transparent)]
57pub struct Error(AttestationErrorInner);
58
59impl<T: Into<AttestationErrorInner>> From<T> for Error {
60    fn from(value: T) -> Self {
61        Self(value.into())
62    }
63}
64
65#[derive(Debug, Error)]
66enum AttestationErrorInner {
67    #[error("read security profile from vmgs")]
68    ReadSecurityProfile(#[source] vmgs::ReadFromVmgsError),
69    #[error("failed to get derived keys")]
70    GetDerivedKeys(#[source] GetDerivedKeysError),
71    #[error("failed to read key protector from vmgs")]
72    ReadKeyProtector(#[source] vmgs::ReadFromVmgsError),
73    #[error("failed to read key protector by id from vmgs")]
74    ReadKeyProtectorById(#[source] vmgs::ReadFromVmgsError),
75    #[error("failed to unlock vmgs data store")]
76    UnlockVmgsDataStore(#[source] UnlockVmgsDataStoreError),
77    #[error("failed to read guest secret key from vmgs")]
78    ReadGuestSecretKey(#[source] vmgs::ReadFromVmgsError),
79}
80
81#[derive(Debug, Error)]
82enum GetDerivedKeysError {
83    #[error("failed to get ingress/egress keys from the the key protector")]
84    GetKeysFromKeyProtector(#[source] GetKeysFromKeyProtectorError),
85    #[error("failed to fetch GSP")]
86    FetchGuestStateProtectionById(
87        #[source] guest_emulation_transport::error::GuestStateProtectionByIdError,
88    ),
89    #[error("GSP By Id required, but no GSP By Id found")]
90    GspByIdRequiredButNotFound,
91    #[error("failed to unseal the ingress key using hardware derived keys")]
92    UnsealIngressKeyUsingHardwareDerivedKeys(
93        #[source] hardware_key_sealing::HardwareKeySealingError,
94    ),
95    #[error("failed to get an ingress key from key protector")]
96    GetIngressKeyFromKpFailed,
97    #[error("failed to get an ingress key from guest state protection")]
98    GetIngressKeyFromKGspFailed,
99    #[error("failed to get an ingress key from guest state protection by id")]
100    GetIngressKeyFromKGspByIdFailed,
101    #[error("Encryption cannot be disabled if VMGS was previously encrypted")]
102    DisableVmgsEncryptionFailed,
103    #[error("VMGS encryption is required, but no encryption sources were found")]
104    EncryptionRequiredButNotFound,
105    #[error("failed to seal the egress key using hardware derived keys")]
106    SealEgressKeyUsingHardwareDerivedKeys(#[source] hardware_key_sealing::HardwareKeySealingError),
107    #[error("failed to write to `FileId::HW_KEY_PROTECTOR` in vmgs")]
108    VmgsWriteHardwareKeyProtector(#[source] vmgs::WriteToVmgsError),
109    #[error("failed to get derived key by id")]
110    GetDerivedKeyById(#[source] GetDerivedKeysByIdError),
111    #[error("failed to derive an ingress key")]
112    DeriveIngressKey(#[source] crypto::KbkdfError),
113    #[error("failed to derive an egress key")]
114    DeriveEgressKey(#[source] crypto::KbkdfError),
115}
116
117#[derive(Debug, Error)]
118enum GetDerivedKeysByIdError {
119    #[error("failed to derive an egress key based on current vm bios guid")]
120    DeriveEgressKeyUsingCurrentVmId(#[source] crypto::KbkdfError),
121    #[error("invalid derived egress key size {key_size}, expected {expected_size}")]
122    InvalidDerivedEgressKeySize {
123        key_size: usize,
124        expected_size: usize,
125    },
126    #[error("failed to derive an ingress key based on key protector Id from vmgs")]
127    DeriveIngressKeyUsingKeyProtectorId(#[source] crypto::KbkdfError),
128    #[error("invalid derived egress key size {key_size}, expected {expected_size}")]
129    InvalidDerivedIngressKeySize {
130        key_size: usize,
131        expected_size: usize,
132    },
133}
134
135#[derive(Debug, Error)]
136enum UnlockVmgsDataStoreError {
137    #[error("failed to unlock vmgs with the existing egress key")]
138    VmgsUnlockUsingExistingEgressKey(#[source] ::vmgs::Error),
139    #[error("failed to unlock vmgs with the existing ingress key")]
140    VmgsUnlockUsingExistingIngressKey(#[source] ::vmgs::Error),
141    #[error("failed to write key protector to vmgs")]
142    WriteKeyProtector(#[source] vmgs::WriteToVmgsError),
143    #[error("failed to read key protector by id to vmgs")]
144    WriteKeyProtectorById(#[source] vmgs::WriteToVmgsError),
145    #[error("failed to update the vmgs encryption key")]
146    UpdateVmgsEncryptionKey(#[source] ::vmgs::Error),
147    #[error("failed to persist all key protectors")]
148    PersistAllKeyProtectors(#[source] PersistAllKeyProtectorsError),
149}
150
151#[derive(Debug, Error)]
152enum PersistAllKeyProtectorsError {
153    #[error("failed to write key protector to vmgs")]
154    WriteKeyProtector(#[source] vmgs::WriteToVmgsError),
155    #[error("failed to read key protector by id to vmgs")]
156    WriteKeyProtectorById(#[source] vmgs::WriteToVmgsError),
157}
158
159// Operation types for provisioning telemetry.
160#[derive(Debug)]
161enum LogOpType {
162    BeginDecryptVmgs,
163    DecryptVmgs,
164    ConvertEncryptionType,
165}
166
167/// Label used by `derive_key`
168const VMGS_KEY_DERIVE_LABEL: &[u8; 7] = b"VMGSKEY";
169
170#[derive(Debug)]
171struct Keys {
172    ingress: [u8; AES_GCM_KEY_LENGTH],
173    decrypt_egress: Option<[u8; AES_GCM_KEY_LENGTH]>,
174    encrypt_egress: [u8; AES_GCM_KEY_LENGTH],
175}
176
177/// Key protector settings
178#[derive(Clone, Copy)]
179struct KeyProtectorSettings {
180    /// Whether to update key protector
181    should_write_kp: bool,
182    /// Whether GSP by id is used
183    use_gsp_by_id: bool,
184    /// Whether hardware key sealing is used
185    use_hardware_unlock: bool,
186    /// GSP type used for decryption (for logging)
187    decrypt_gsp_type: GspType,
188    /// GSP type used for encryption (for logging)
189    encrypt_gsp_type: GspType,
190}
191
192/// Helper struct for [`protocol::vmgs::KeyProtectorById`]
193struct KeyProtectorById {
194    /// The instance of [`protocol::vmgs::KeyProtectorById`].
195    pub inner: openhcl_attestation_protocol::vmgs::KeyProtectorById,
196    /// Indicate if the instance is read from the VMGS file.
197    pub found_id: bool,
198}
199
200/// Host attestation settings obtained via the GET GSP call-out.
201pub struct HostAttestationSettings {
202    /// Whether refreshing tpm seeds is needed.
203    pub refresh_tpm_seeds: bool,
204}
205
206/// The return values of [`get_derived_keys`].
207struct DerivedKeyResult {
208    /// Optional derived keys.
209    derived_keys: Option<Keys>,
210    /// The instance of [`KeyProtectorSettings`].
211    key_protector_settings: KeyProtectorSettings,
212    /// The instance of [`GspExtendedStatusFlags`] returned by GSP.
213    gsp_extended_status_flags: GspExtendedStatusFlags,
214}
215
216/// The return values of [`initialize_platform_security`].
217pub struct PlatformAttestationData {
218    /// The instance of [`HostAttestationSettings`].
219    pub host_attestation_settings: HostAttestationSettings,
220    /// The agent data used by an attestation request.
221    pub agent_data: Option<Vec<u8>>,
222    /// The guest secret key.
223    pub guest_secret_key: Option<Vec<u8>>,
224}
225
226/// The attestation type to use.
227// TODO: Support VBS
228#[derive(Debug, MeshPayload, Copy, Clone, PartialEq, Eq)]
229pub enum AttestationType {
230    /// Use the SEV-SNP TEE for attestation.
231    Snp,
232    /// Use the TDX TEE for attestation.
233    Tdx,
234    /// Use the VBS TEE for attestation.
235    Vbs,
236    /// Use trusted host-based attestation.
237    Host,
238}
239
240/// If required, attest platform. Gets VMGS datastore key.
241///
242/// Returns `refresh_tpm_seeds` (the host side GSP service indicating
243/// whether certain state needs to be updated), along with the fully
244/// initialized VMGS client.
245pub async fn initialize_platform_security(
246    get: &GuestEmulationTransportClient,
247    bios_guid: Guid,
248    attestation_vm_config: &AttestationVmConfig,
249    vmgs: &mut Vmgs,
250    tee_call: Option<&dyn TeeCall>,
251    suppress_attestation: bool,
252    driver: LocalDriver,
253    guest_state_encryption_policy: GuestStateEncryptionPolicy,
254    strict_encryption_policy: bool,
255) -> Result<PlatformAttestationData, Error> {
256    tracing::info!(CVM_ALLOWED,
257        tee_type=?tee_call.map(|tee| tee.tee_type()),
258        secure_boot=attestation_vm_config.secure_boot,
259        tpm_enabled=attestation_vm_config.tpm_enabled,
260        tpm_persisted=attestation_vm_config.tpm_persisted,
261        "Reading security profile");
262
263    // Read Security Profile from VMGS
264    // Currently this only includes "Key Reference" data, which is not attested data, is opaque to the
265    // OpenHCL, and is passed to the IGVMm agent outside of the report contents.
266    let SecurityProfile { mut agent_data } = vmgs::read_security_profile(vmgs)
267        .await
268        .map_err(AttestationErrorInner::ReadSecurityProfile)?;
269
270    // If attestation is suppressed, return the `agent_data` that is required by
271    // TPM AK cert request.
272    if suppress_attestation {
273        tracing::info!(CVM_ALLOWED, "Suppressing attestation");
274
275        return Ok(PlatformAttestationData {
276            host_attestation_settings: HostAttestationSettings {
277                refresh_tpm_seeds: false,
278            },
279            agent_data: Some(agent_data.to_vec()),
280            guest_secret_key: None,
281        });
282    }
283
284    let VmgsEncryptionKeys {
285        ingress_rsa_kek,
286        wrapped_des_key,
287        tcb_version,
288    } = if let Some(tee_call) = tee_call {
289        tracing::info!(CVM_ALLOWED, "Retrieving key-encryption key");
290
291        // Retrieve the tenant key via attestation
292        match secure_key_release::request_vmgs_encryption_keys(
293            get,
294            tee_call,
295            vmgs,
296            attestation_vm_config,
297            &mut agent_data,
298            driver,
299        )
300        .await
301        {
302            Ok(VmgsEncryptionKeys {
303                ingress_rsa_kek,
304                wrapped_des_key,
305                tcb_version,
306            }) => {
307                tracing::info!(CVM_ALLOWED, "Successfully retrieved key-encryption key");
308
309                VmgsEncryptionKeys {
310                    ingress_rsa_kek,
311                    wrapped_des_key,
312                    tcb_version,
313                }
314            }
315            Err(e) => {
316                // Non-fatal, allowing for hardware-based recovery
317                tracing::error!(
318                    CVM_ALLOWED,
319                    error = &e as &dyn std::error::Error,
320                    "Failed to retrieve key-encryption key"
321                );
322
323                VmgsEncryptionKeys::default()
324            }
325        }
326    } else {
327        tracing::info!(CVM_ALLOWED, "Key-encryption key retrieval not required");
328
329        // Attestation is unavailable, assume no tenant key
330        VmgsEncryptionKeys::default()
331    };
332
333    // Determine the minimal size of a DEK entry based on whether `wrapped_des_key` presents
334    let dek_minimal_size = if wrapped_des_key.is_some() {
335        key_protector::AES_WRAPPED_AES_KEY_LENGTH
336    } else {
337        key_protector::RSA_WRAPPED_AES_KEY_LENGTH
338    };
339
340    // Read Key Protector blob from VMGS
341    tracing::info!(
342        CVM_ALLOWED,
343        dek_minimal_size = dek_minimal_size,
344        "Reading key protector from VMGS"
345    );
346    let mut key_protector = vmgs::read_key_protector(vmgs, dek_minimal_size)
347        .await
348        .map_err(AttestationErrorInner::ReadKeyProtector)?;
349
350    // Read VM id from VMGS
351    tracing::info!(CVM_ALLOWED, "Reading VM ID from VMGS");
352    let mut key_protector_by_id = match vmgs::read_key_protector_by_id(vmgs).await {
353        Ok(key_protector_by_id) => KeyProtectorById {
354            inner: key_protector_by_id,
355            found_id: true,
356        },
357        Err(vmgs::ReadFromVmgsError::EntryNotFound(_)) => KeyProtectorById {
358            inner: openhcl_attestation_protocol::vmgs::KeyProtectorById::new_zeroed(),
359            found_id: false,
360        },
361        Err(e) => { Err(AttestationErrorInner::ReadKeyProtectorById(e)) }?,
362    };
363
364    // Check if the VM id has been changed since last boot with KP write
365    let vm_id_changed = if key_protector_by_id.found_id {
366        let changed = key_protector_by_id.inner.id_guid != bios_guid;
367        if changed {
368            tracing::info!("VM Id has changed since last boot");
369        };
370        changed
371    } else {
372        // Previous id in KP not found means this is the first boot or the GspById
373        // is not provisioned, treat id as unchanged for this case.
374        false
375    };
376
377    let vmgs_encrypted: bool = vmgs.is_encrypted();
378
379    let start_time = std::time::SystemTime::now();
380    tracing::info!(
381        ?tcb_version,
382        vmgs_encrypted,
383        op_type = ?LogOpType::BeginDecryptVmgs,
384        "Deriving keys"
385    );
386
387    let derived_keys_result = get_derived_keys(
388        get,
389        tee_call,
390        vmgs,
391        &mut key_protector,
392        &mut key_protector_by_id,
393        bios_guid,
394        attestation_vm_config,
395        vmgs_encrypted,
396        ingress_rsa_kek.as_ref(),
397        wrapped_des_key.as_deref(),
398        tcb_version,
399        guest_state_encryption_policy,
400        strict_encryption_policy,
401    )
402    .await
403    .map_err(|e| {
404        tracing::error!(
405            CVM_ALLOWED,
406            op_type = ?LogOpType::DecryptVmgs,
407            success = false,
408            err = &e as &dyn std::error::Error,
409            latency = std::time::SystemTime::now()
410                .duration_since(start_time)
411                .map_or(0, |d| d.as_millis()),
412            "Failed to derive keys"
413        );
414        AttestationErrorInner::GetDerivedKeys(e)
415    })?;
416
417    // All Underhill VMs use VMGS encryption
418    tracing::info!("Unlocking VMGS");
419    if let Err(e) = unlock_vmgs_data_store(
420        vmgs,
421        vmgs_encrypted,
422        &mut key_protector,
423        &mut key_protector_by_id,
424        derived_keys_result.derived_keys,
425        derived_keys_result.key_protector_settings,
426        bios_guid,
427    )
428    .await
429    {
430        tracing::error!(
431            CVM_ALLOWED,
432            op_type = ?LogOpType::DecryptVmgs,
433            success = false,
434            err = &e as &dyn std::error::Error,
435            latency = std::time::SystemTime::now()
436                .duration_since(start_time)
437                .map_or(0, |d| d.as_millis()),
438            "Failed to unlock datastore"
439        );
440        get.event_log_fatal(guest_emulation_transport::api::EventLogId::ATTESTATION_FAILED)
441            .await;
442
443        Err(AttestationErrorInner::UnlockVmgsDataStore(e))?
444    }
445
446    tracing::info!(
447        CVM_ALLOWED,
448        op_type = ?LogOpType::DecryptVmgs,
449        success = true,
450        decrypt_gsp_type = ?derived_keys_result
451            .key_protector_settings
452            .decrypt_gsp_type,
453        encrypt_gsp_type = ?derived_keys_result
454            .key_protector_settings
455            .encrypt_gsp_type,
456        latency = std::time::SystemTime::now().duration_since(start_time).map_or(0, |d| d.as_millis()),
457        "Unlocked datastore"
458    );
459
460    let state_refresh_request_from_gsp = derived_keys_result
461        .gsp_extended_status_flags
462        .state_refresh_request();
463
464    let host_attestation_settings = HostAttestationSettings {
465        refresh_tpm_seeds: { state_refresh_request_from_gsp | vm_id_changed },
466    };
467
468    tracing::info!(
469        CVM_ALLOWED,
470        state_refresh_request_from_gsp = state_refresh_request_from_gsp,
471        vm_id_changed = vm_id_changed,
472        "determine if refreshing tpm seeds is needed"
473    );
474
475    // Read guest secret key from unlocked VMGS
476    let guest_secret_key = match vmgs::read_guest_secret_key(vmgs).await {
477        Ok(data) => Some(data.guest_secret_key.to_vec()),
478        Err(vmgs::ReadFromVmgsError::EntryNotFound(_)) => None,
479        Err(e) => return Err(AttestationErrorInner::ReadGuestSecretKey(e).into()),
480    };
481
482    Ok(PlatformAttestationData {
483        host_attestation_settings,
484        agent_data: Some(agent_data.to_vec()),
485        guest_secret_key,
486    })
487}
488
489/// Get ingress and egress keys for the VMGS, unlock VMGS,
490/// remove old key if necessary, and update KP.
491/// If key rolling did not complete successfully last time, there may be an
492/// old egress key in the VMGS, whose contents can be controlled by the host.
493/// This key can be used to attempt decryption but must not be used to
494/// re-encrypt the VMGS.
495async fn unlock_vmgs_data_store(
496    vmgs: &mut Vmgs,
497    vmgs_encrypted: bool,
498    key_protector: &mut KeyProtector,
499    key_protector_by_id: &mut KeyProtectorById,
500    derived_keys: Option<Keys>,
501    key_protector_settings: KeyProtectorSettings,
502    bios_guid: Guid,
503) -> Result<(), UnlockVmgsDataStoreError> {
504    let mut new_key = false; // Indicate if we need to add a new key after unlock
505
506    let Some(Keys {
507        ingress: new_ingress_key,
508        decrypt_egress: old_egress_key,
509        encrypt_egress: new_egress_key,
510    }) = derived_keys
511    else {
512        tracing::info!(
513            CVM_ALLOWED,
514            "Encryption disabled, skipping unlock vmgs data store"
515        );
516        return Ok(());
517    };
518
519    if !openssl::memcmp::eq(&new_ingress_key, &new_egress_key) {
520        tracing::trace!(CVM_ALLOWED, "EgressKey is different than IngressKey");
521        new_key = true;
522    }
523
524    // Call unlock_with_encryption_key using ingress_key if datastore is encrypted
525    let mut provision = false;
526    if vmgs_encrypted {
527        tracing::info!(CVM_ALLOWED, "Decrypting vmgs file...");
528        if let Err(e) = vmgs.unlock_with_encryption_key(&new_ingress_key).await {
529            if let Some(key) = old_egress_key {
530                // Key rolling did not complete successfully last time and there's an old
531                // egress key in the VMGS. It may be needed for decryption.
532                tracing::info!(CVM_ALLOWED, "Old EgressKey found");
533                vmgs.unlock_with_encryption_key(&key)
534                    .await
535                    .map_err(UnlockVmgsDataStoreError::VmgsUnlockUsingExistingEgressKey)?;
536            } else {
537                Err(UnlockVmgsDataStoreError::VmgsUnlockUsingExistingIngressKey(
538                    e,
539                ))?
540            }
541        }
542    } else {
543        // The datastore is not encrypted which means it's during provision.
544        tracing::info!(
545            CVM_ALLOWED,
546            "vmgs data store is not encrypted, provisioning."
547        );
548        provision = true;
549    }
550
551    tracing::info!(
552        CVM_ALLOWED,
553        should_write_kp = key_protector_settings.should_write_kp,
554        use_gsp_by_id = key_protector_settings.use_gsp_by_id,
555        use_hardware_unlock = key_protector_settings.use_hardware_unlock,
556        "key protector settings"
557    );
558
559    if key_protector_settings.should_write_kp {
560        // Update on disk KP with all seeds used, to allow for disaster recovery
561        vmgs::write_key_protector(key_protector, vmgs)
562            .await
563            .map_err(UnlockVmgsDataStoreError::WriteKeyProtector)?;
564
565        if key_protector_settings.use_gsp_by_id {
566            vmgs::write_key_protector_by_id(&mut key_protector_by_id.inner, vmgs, false, bios_guid)
567                .await
568                .map_err(UnlockVmgsDataStoreError::WriteKeyProtectorById)?;
569        }
570    }
571
572    if provision || new_key {
573        // Add the new egress key. If we are not provisioning, then this will
574        // also remove the old key. This will also remove the inactive key if
575        // last time we failed to remove it.
576        vmgs.update_encryption_key(&new_egress_key, EncryptionAlgorithm::AES_GCM)
577            .await
578            .map_err(UnlockVmgsDataStoreError::UpdateVmgsEncryptionKey)?;
579    }
580
581    // Persist KP to VMGS
582    persist_all_key_protectors(
583        vmgs,
584        key_protector,
585        key_protector_by_id,
586        bios_guid,
587        key_protector_settings,
588    )
589    .await
590    .map_err(UnlockVmgsDataStoreError::PersistAllKeyProtectors)
591}
592
593/// Update data store keys with key protectors.
594///         VMGS encryption can come from combinations of three sources,
595///         a Tenant Key (KEK), GSP, and GSP By Id.
596///         There is an Ingress Key (previously used to lock the VMGS),
597///         and an Egress Key (new key for locking the VMGS), and these
598///         keys can be derived differently, where KEK is
599///         always used if available, and GSP is preferred to GSP By Id.
600///         Ingress                     Possible Egress in order of preference [Ingress]
601///         - No Encryption             - All
602///         - GSP By Id                 - KEK + GSP, KEK + GSP By Id, GSP, [GSP By Id]
603///         - GSP (v10 VM and later)    - KEK + GSP, [GSP]
604///         - KEK (IVM only)            - KEK + GSP, KEK + GSP By Id, [KEK]
605///         - KEK + GSP By Id           - KEK + GSP, [KEK + GSP By Id]
606///         - KEK + GSP                 - [KEK + GSP]
607///
608/// NOTE: for TVM parity, only None, Gsp By Id v9.1, and Gsp By Id / Gsp v10.0 are used.
609async fn get_derived_keys(
610    get: &GuestEmulationTransportClient,
611    tee_call: Option<&dyn TeeCall>,
612    vmgs: &mut Vmgs,
613    key_protector: &mut KeyProtector,
614    key_protector_by_id: &mut KeyProtectorById,
615    bios_guid: Guid,
616    attestation_vm_config: &AttestationVmConfig,
617    is_encrypted: bool,
618    ingress_rsa_kek: Option<&Rsa<Private>>,
619    wrapped_des_key: Option<&[u8]>,
620    tcb_version: Option<u64>,
621    guest_state_encryption_policy: GuestStateEncryptionPolicy,
622    strict_encryption_policy: bool,
623) -> Result<DerivedKeyResult, GetDerivedKeysError> {
624    tracing::info!(
625        CVM_ALLOWED,
626        ?guest_state_encryption_policy,
627        strict_encryption_policy,
628        "encryption policy"
629    );
630
631    // TODO: implement hardware sealing only
632    if matches!(
633        guest_state_encryption_policy,
634        GuestStateEncryptionPolicy::HardwareSealing
635    ) {
636        todo!("hardware sealing")
637    }
638
639    let mut key_protector_settings = KeyProtectorSettings {
640        should_write_kp: true,
641        use_gsp_by_id: false,
642        use_hardware_unlock: false,
643        decrypt_gsp_type: GspType::None,
644        encrypt_gsp_type: GspType::None,
645    };
646
647    let mut derived_keys = Keys {
648        ingress: [0u8; AES_GCM_KEY_LENGTH],
649        decrypt_egress: None,
650        encrypt_egress: [0u8; AES_GCM_KEY_LENGTH],
651    };
652
653    // Ingress / Egress seed values depend on what happened previously to the datastore
654    let ingress_idx = (key_protector.active_kp % 2) as usize;
655    let egress_idx = if ingress_idx == 0 { 1 } else { 0 } as usize;
656
657    let found_dek = !key_protector.dek[ingress_idx]
658        .dek_buffer
659        .iter()
660        .all(|&x| x == 0);
661
662    // Handle key released via attestation process (tenant key) to get keys from KeyProtector
663    let (ingress_key, mut decrypt_egress_key, encrypt_egress_key, no_kek) =
664        if let Some(ingress_kek) = ingress_rsa_kek {
665            let keys = match key_protector.unwrap_and_rotate_keys(
666                ingress_kek,
667                wrapped_des_key,
668                ingress_idx,
669                egress_idx,
670            ) {
671                Ok(keys) => keys,
672                Err(e)
673                    if matches!(
674                        e,
675                        GetKeysFromKeyProtectorError::DesKeyRsaUnwrap(_)
676                            | GetKeysFromKeyProtectorError::IngressDekRsaUnwrap(_)
677                    ) =>
678                {
679                    get.event_log_fatal(
680                        guest_emulation_transport::api::EventLogId::DEK_DECRYPTION_FAILED,
681                    )
682                    .await;
683
684                    return Err(GetDerivedKeysError::GetKeysFromKeyProtector(e));
685                }
686                Err(e) => return Err(GetDerivedKeysError::GetKeysFromKeyProtector(e)),
687            };
688            (
689                keys.ingress,
690                keys.decrypt_egress,
691                keys.encrypt_egress,
692                false,
693            )
694        } else {
695            (
696                [0u8; AES_GCM_KEY_LENGTH],
697                None,
698                [0u8; AES_GCM_KEY_LENGTH],
699                true,
700            )
701        };
702
703    // Handle various sources of Guest State Protection
704    let existing_unencrypted = !vmgs.is_encrypted() && !vmgs.was_provisioned_this_boot();
705    let is_gsp_by_id = key_protector_by_id.found_id && key_protector_by_id.inner.ported != 1;
706    let is_gsp = key_protector.gsp[ingress_idx].gsp_length != 0;
707    tracing::info!(
708        CVM_ALLOWED,
709        is_encrypted,
710        is_gsp_by_id,
711        is_gsp,
712        found_dek,
713        "initial vmgs encryption state"
714    );
715    let mut requires_gsp_by_id = is_gsp_by_id;
716
717    // Attempt GSP
718    let (gsp_response, gsp_available, no_gsp, requires_gsp) = {
719        tracing::info!(CVM_ALLOWED, "attempting GSP");
720
721        let response = get_gsp_data(get, key_protector).await;
722
723        tracing::info!(
724            CVM_ALLOWED,
725            request_data_length_in_vmgs = key_protector.gsp[ingress_idx].gsp_length,
726            no_rpc_server = response.extended_status_flags.no_rpc_server(),
727            requires_rpc_server = response.extended_status_flags.requires_rpc_server(),
728            encrypted_gsp_length = response.encrypted_gsp.length,
729            "GSP response"
730        );
731
732        let no_gsp_available =
733            response.extended_status_flags.no_rpc_server() || response.encrypted_gsp.length == 0;
734
735        let no_gsp = no_gsp_available
736            // disable if auto and pre-existing guest state is not encrypted or
737            // encrypted using GspById to prevent encryption changes without
738            // explicit intent
739            || (matches!(
740                guest_state_encryption_policy,
741                GuestStateEncryptionPolicy::Auto
742            ) && (is_gsp_by_id || existing_unencrypted))
743            // disable per encryption policy (first boot only, unless strict)
744            || (matches!(
745                guest_state_encryption_policy,
746                GuestStateEncryptionPolicy::GspById | GuestStateEncryptionPolicy::None
747            ) && (!is_gsp || strict_encryption_policy));
748
749        let requires_gsp = is_gsp
750            || response.extended_status_flags.requires_rpc_server()
751            || (matches!(
752                guest_state_encryption_policy,
753                GuestStateEncryptionPolicy::GspKey
754            ) && strict_encryption_policy);
755
756        // If the VMGS is encrypted, but no key protection data is found,
757        // assume GspById encryption is enabled, but no ID file was written.
758        if is_encrypted && !requires_gsp_by_id && !requires_gsp && !found_dek {
759            requires_gsp_by_id = true;
760        }
761
762        (response, !no_gsp_available, no_gsp, requires_gsp)
763    };
764
765    // Attempt GSP By Id protection if GSP is not available, when changing
766    // schemes, or as requested
767    let (gsp_response_by_id, gsp_by_id_available, no_gsp_by_id) = if no_gsp || requires_gsp_by_id {
768        tracing::info!(CVM_ALLOWED, "attempting GSP By Id");
769
770        let gsp_response_by_id = get
771            .guest_state_protection_data_by_id()
772            .await
773            .map_err(GetDerivedKeysError::FetchGuestStateProtectionById)?;
774
775        let no_gsp_by_id_available = gsp_response_by_id.extended_status_flags.no_registry_file();
776
777        let no_gsp_by_id = no_gsp_by_id_available
778            // disable if auto and pre-existing guest state is unencrypted
779            // to prevent encryption changes without explicit intent
780            || (matches!(
781                guest_state_encryption_policy,
782                GuestStateEncryptionPolicy::Auto
783            ) && existing_unencrypted)
784            // disable per encryption policy (first boot only, unless strict)
785            || (matches!(
786                guest_state_encryption_policy,
787                GuestStateEncryptionPolicy::None
788            ) && (!requires_gsp_by_id || strict_encryption_policy));
789
790        if no_gsp_by_id && requires_gsp_by_id {
791            Err(GetDerivedKeysError::GspByIdRequiredButNotFound)?
792        }
793
794        (
795            gsp_response_by_id,
796            Some(!no_gsp_by_id_available),
797            no_gsp_by_id,
798        )
799    } else {
800        (GuestStateProtectionById::new_zeroed(), None, true)
801    };
802
803    // If sources of encryption used last are missing, attempt to unseal VMGS key with hardware key
804    if (no_kek && found_dek) || (no_gsp && requires_gsp) || (no_gsp_by_id && requires_gsp_by_id) {
805        // If possible, get ingressKey from hardware sealed data
806        let (hardware_key_protector, hardware_derived_keys) = if let Some(tee_call) = tee_call {
807            let hardware_key_protector = match vmgs::read_hardware_key_protector(vmgs).await {
808                Ok(hardware_key_protector) => Some(hardware_key_protector),
809                Err(e) => {
810                    // non-fatal
811                    tracing::warn!(
812                        CVM_ALLOWED,
813                        error = &e as &dyn std::error::Error,
814                        "failed to read HW_KEY_PROTECTOR from Vmgs"
815                    );
816                    None
817                }
818            };
819
820            let hardware_derived_keys = tee_call.supports_get_derived_key().and_then(|tee_call| {
821                if let Some(hardware_key_protector) = &hardware_key_protector {
822                    match HardwareDerivedKeys::derive_key(
823                        tee_call,
824                        attestation_vm_config,
825                        hardware_key_protector.header.tcb_version,
826                    ) {
827                        Ok(hardware_derived_key) => Some(hardware_derived_key),
828                        Err(e) => {
829                            // non-fatal
830                            tracing::warn!(
831                                CVM_ALLOWED,
832                                error = &e as &dyn std::error::Error,
833                                "failed to derive hardware keys using HW_KEY_PROTECTOR",
834                            );
835                            None
836                        }
837                    }
838                } else {
839                    None
840                }
841            });
842
843            (hardware_key_protector, hardware_derived_keys)
844        } else {
845            (None, None)
846        };
847
848        if let (Some(hardware_key_protector), Some(hardware_derived_keys)) =
849            (hardware_key_protector, hardware_derived_keys)
850        {
851            derived_keys.ingress = hardware_key_protector
852                .unseal_key(&hardware_derived_keys)
853                .map_err(GetDerivedKeysError::UnsealIngressKeyUsingHardwareDerivedKeys)?;
854            derived_keys.decrypt_egress = None;
855            derived_keys.encrypt_egress = derived_keys.ingress;
856
857            key_protector_settings.should_write_kp = false;
858            key_protector_settings.use_hardware_unlock = true;
859
860            tracing::warn!(
861                CVM_ALLOWED,
862                "Using hardware-derived key to recover VMGS DEK"
863            );
864
865            return Ok(DerivedKeyResult {
866                derived_keys: Some(derived_keys),
867                key_protector_settings,
868                gsp_extended_status_flags: gsp_response.extended_status_flags,
869            });
870        } else {
871            if no_kek && found_dek {
872                Err(GetDerivedKeysError::GetIngressKeyFromKpFailed)?
873            } else if no_gsp && requires_gsp {
874                Err(GetDerivedKeysError::GetIngressKeyFromKGspFailed)?
875            } else {
876                // no_gsp_by_id && requires_gsp_by_id
877                Err(GetDerivedKeysError::GetIngressKeyFromKGspByIdFailed)?
878            }
879        }
880    }
881
882    tracing::info!(
883        CVM_ALLOWED,
884        kek = !no_kek,
885        gsp_available,
886        gsp = !no_gsp,
887        gsp_by_id_available = ?gsp_by_id_available,
888        gsp_by_id = !no_gsp_by_id,
889        "Encryption sources"
890    );
891
892    // Check if sources of encryption are available
893    if no_kek && no_gsp && no_gsp_by_id {
894        if is_encrypted {
895            Err(GetDerivedKeysError::DisableVmgsEncryptionFailed)?
896        }
897        match guest_state_encryption_policy {
898            // fail if some minimum level of encryption was required
899            GuestStateEncryptionPolicy::GspById
900            | GuestStateEncryptionPolicy::GspKey
901            | GuestStateEncryptionPolicy::HardwareSealing => {
902                Err(GetDerivedKeysError::EncryptionRequiredButNotFound)?
903            }
904            GuestStateEncryptionPolicy::Auto | GuestStateEncryptionPolicy::None => {
905                tracing::info!(CVM_ALLOWED, "No VMGS encryption used.");
906
907                return Ok(DerivedKeyResult {
908                    derived_keys: None,
909                    key_protector_settings,
910                    gsp_extended_status_flags: gsp_response.extended_status_flags,
911                });
912            }
913        }
914    }
915
916    // Attempt to get hardware derived keys
917    let hardware_derived_keys = tee_call
918        .and_then(|tee_call| tee_call.supports_get_derived_key())
919        .and_then(|tee_call| {
920            if let Some(tcb_version) = tcb_version {
921                match HardwareDerivedKeys::derive_key(tee_call, attestation_vm_config, tcb_version)
922                {
923                    Ok(keys) => Some(keys),
924                    Err(e) => {
925                        // non-fatal
926                        tracing::warn!(
927                            CVM_ALLOWED,
928                            error = &e as &dyn std::error::Error,
929                            "failed to derive hardware keys"
930                        );
931                        None
932                    }
933                }
934            } else {
935                None
936            }
937        });
938
939    // Use tenant key (KEK only)
940    if no_gsp && no_gsp_by_id {
941        tracing::info!(CVM_ALLOWED, "No GSP used with SKR");
942
943        derived_keys.ingress = ingress_key;
944        derived_keys.decrypt_egress = decrypt_egress_key;
945        derived_keys.encrypt_egress = encrypt_egress_key;
946
947        if let Some(hardware_derived_keys) = hardware_derived_keys {
948            let hardware_key_protector = HardwareKeyProtector::seal_key(
949                &hardware_derived_keys,
950                &derived_keys.encrypt_egress,
951            )
952            .map_err(GetDerivedKeysError::SealEgressKeyUsingHardwareDerivedKeys)?;
953            vmgs::write_hardware_key_protector(&hardware_key_protector, vmgs)
954                .await
955                .map_err(GetDerivedKeysError::VmgsWriteHardwareKeyProtector)?;
956
957            tracing::info!(CVM_ALLOWED, "hardware key protector updated (no GSP used)");
958        }
959
960        return Ok(DerivedKeyResult {
961            derived_keys: Some(derived_keys),
962            key_protector_settings,
963            gsp_extended_status_flags: gsp_response.extended_status_flags,
964        });
965    }
966
967    // GSP By Id derives keys differently,
968    // because key is shared across VMs different context must be used (Id GUID)
969    if (no_kek && no_gsp) || requires_gsp_by_id {
970        let derived_keys_by_id =
971            get_derived_keys_by_id(key_protector_by_id, bios_guid, gsp_response_by_id)
972                .map_err(GetDerivedKeysError::GetDerivedKeyById)?;
973
974        if no_kek && no_gsp {
975            if matches!(
976                guest_state_encryption_policy,
977                GuestStateEncryptionPolicy::GspById | GuestStateEncryptionPolicy::Auto
978            ) {
979                tracing::info!(CVM_ALLOWED, "Using GspById");
980            } else {
981                // Log a warning here to indicate that the VMGS state is out of
982                // sync with the VM's configuration.
983                //
984                // This should only happen if strict encryption policy is
985                // disabled and one of the following is true:
986                // - The VM is configured to have no encryption, but it already
987                //   has GspById encryption.
988                // - The VM is configured to use GspKey, but GspKey is not
989                //   available and GspById is.
990                tracing::warn!(CVM_ALLOWED, "Allowing GspById");
991            };
992
993            // Not required for Id protection
994            key_protector_settings.should_write_kp = false;
995            key_protector_settings.use_gsp_by_id = true;
996            key_protector_settings.decrypt_gsp_type = GspType::GspById;
997            key_protector_settings.encrypt_gsp_type = GspType::GspById;
998
999            return Ok(DerivedKeyResult {
1000                derived_keys: Some(derived_keys_by_id),
1001                key_protector_settings,
1002                gsp_extended_status_flags: gsp_response.extended_status_flags,
1003            });
1004        }
1005
1006        derived_keys.ingress = derived_keys_by_id.ingress;
1007
1008        tracing::info!(
1009            CVM_ALLOWED,
1010            op_type = ?LogOpType::ConvertEncryptionType,
1011            "Converting GSP method."
1012        );
1013    }
1014
1015    let egress_seed;
1016    let mut ingress_seed = None;
1017
1018    // To get to this point, either KEK or GSP must be available
1019    // Mix tenant key with GSP key to create data store encryption keys
1020    // Covers possible egress combinations:
1021    // GSP, GSP + KEK, GSP By Id + KEK
1022
1023    if requires_gsp_by_id || no_gsp {
1024        // If DEK exists, ingress is either KEK or KEK + GSP By Id
1025        // If no DEK, then ingress was Gsp By Id (derived above)
1026        if found_dek {
1027            if requires_gsp_by_id {
1028                ingress_seed = Some(
1029                    gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize]
1030                        .to_vec(),
1031                );
1032                key_protector_settings.decrypt_gsp_type = GspType::GspById;
1033            } else {
1034                derived_keys.ingress = ingress_key;
1035            }
1036        } else {
1037            key_protector_settings.decrypt_gsp_type = GspType::GspById;
1038        }
1039
1040        // Choose best available egress seed
1041        if no_gsp {
1042            egress_seed =
1043                gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize].to_vec();
1044            key_protector_settings.use_gsp_by_id = true;
1045            key_protector_settings.encrypt_gsp_type = GspType::GspById;
1046        } else {
1047            egress_seed =
1048                gsp_response.new_gsp.buffer[..gsp_response.new_gsp.length as usize].to_vec();
1049            key_protector_settings.encrypt_gsp_type = GspType::GspKey;
1050        }
1051    } else {
1052        // `no_gsp` is false, using `gsp_response`
1053
1054        if gsp_response.decrypted_gsp[ingress_idx].length == 0
1055            && gsp_response.decrypted_gsp[egress_idx].length == 0
1056        {
1057            tracing::info!(CVM_ALLOWED, "Applying GSP.");
1058
1059            // VMGS has never had any GSP applied.
1060            // Leave ingress key untouched, derive egress key with new seed.
1061            egress_seed =
1062                gsp_response.new_gsp.buffer[..gsp_response.new_gsp.length as usize].to_vec();
1063
1064            // Ingress key is either zero or tenant only.
1065            // Only copy in the case where a tenant key was released.
1066            if !no_kek {
1067                derived_keys.ingress = ingress_key;
1068            }
1069
1070            key_protector_settings.encrypt_gsp_type = GspType::GspKey;
1071        } else {
1072            tracing::info!(CVM_ALLOWED, "Using existing GSP.");
1073
1074            ingress_seed = Some(
1075                gsp_response.decrypted_gsp[ingress_idx].buffer
1076                    [..gsp_response.decrypted_gsp[ingress_idx].length as usize]
1077                    .to_vec(),
1078            );
1079
1080            if gsp_response.decrypted_gsp[egress_idx].length == 0 {
1081                // Derive ingress with saved seed, derive egress with new seed.
1082                egress_seed =
1083                    gsp_response.new_gsp.buffer[..gsp_response.new_gsp.length as usize].to_vec();
1084            } else {
1085                // System failed during data store unlock, and is in indeterminate state.
1086                // The egress key might have been applied, or the ingress key might be valid.
1087                // Use saved KP, derive ingress/egress keys to attempt recovery.
1088                // Do not update the saved KP with new seed value.
1089                egress_seed = gsp_response.decrypted_gsp[egress_idx].buffer
1090                    [..gsp_response.decrypted_gsp[egress_idx].length as usize]
1091                    .to_vec();
1092                key_protector_settings.should_write_kp = false;
1093                decrypt_egress_key = Some(encrypt_egress_key);
1094            }
1095
1096            key_protector_settings.decrypt_gsp_type = GspType::GspKey;
1097            key_protector_settings.encrypt_gsp_type = GspType::GspKey;
1098        }
1099    }
1100
1101    // Derive key used to lock data store previously
1102    if let Some(seed) = ingress_seed {
1103        derived_keys.ingress = crypto::derive_key(&ingress_key, &seed, VMGS_KEY_DERIVE_LABEL)
1104            .map_err(GetDerivedKeysError::DeriveIngressKey)?;
1105    }
1106
1107    // Always derive a new egress key using best available seed
1108    derived_keys.decrypt_egress = decrypt_egress_key
1109        .map(|key| crypto::derive_key(&key, &egress_seed, VMGS_KEY_DERIVE_LABEL))
1110        .transpose()
1111        .map_err(GetDerivedKeysError::DeriveEgressKey)?;
1112
1113    derived_keys.encrypt_egress =
1114        crypto::derive_key(&encrypt_egress_key, &egress_seed, VMGS_KEY_DERIVE_LABEL)
1115            .map_err(GetDerivedKeysError::DeriveEgressKey)?;
1116
1117    if key_protector_settings.should_write_kp {
1118        // Update with all seeds used, but do not write until data store is unlocked
1119        key_protector.gsp[egress_idx]
1120            .gsp_buffer
1121            .copy_from_slice(&gsp_response.encrypted_gsp.buffer);
1122        key_protector.gsp[egress_idx].gsp_length = gsp_response.encrypted_gsp.length;
1123
1124        if let Some(hardware_derived_keys) = hardware_derived_keys {
1125            let hardware_key_protector = HardwareKeyProtector::seal_key(
1126                &hardware_derived_keys,
1127                &derived_keys.encrypt_egress,
1128            )
1129            .map_err(GetDerivedKeysError::SealEgressKeyUsingHardwareDerivedKeys)?;
1130
1131            vmgs::write_hardware_key_protector(&hardware_key_protector, vmgs)
1132                .await
1133                .map_err(GetDerivedKeysError::VmgsWriteHardwareKeyProtector)?;
1134
1135            tracing::info!(CVM_ALLOWED, "hardware key protector updated");
1136        }
1137    }
1138
1139    if matches!(
1140        guest_state_encryption_policy,
1141        GuestStateEncryptionPolicy::GspKey | GuestStateEncryptionPolicy::Auto
1142    ) {
1143        tracing::info!(CVM_ALLOWED, "Using Gsp");
1144    } else {
1145        // Log a warning here to indicate that the VMGS state is out of
1146        // sync with the VM's configuration.
1147        //
1148        // This should only happen if the VM is configured to have no
1149        // encryption or GspById encryption, but it already has GspKey
1150        // encryption and strict encryption policy is disabled.
1151        tracing::warn!(CVM_ALLOWED, "Allowing Gsp");
1152    }
1153
1154    Ok(DerivedKeyResult {
1155        derived_keys: Some(derived_keys),
1156        key_protector_settings,
1157        gsp_extended_status_flags: gsp_response.extended_status_flags,
1158    })
1159}
1160
1161/// Update data store keys with key protectors based on VmUniqueId & host seed.
1162fn get_derived_keys_by_id(
1163    key_protector_by_id: &mut KeyProtectorById,
1164    bios_guid: Guid,
1165    gsp_response_by_id: GuestStateProtectionById,
1166) -> Result<Keys, GetDerivedKeysByIdError> {
1167    // This does not handle tenant encrypted VMGS files or Isolated VM,
1168    // or the case where an unlock/relock fails and a snapshot is
1169    // made from that file (the Id cannot change in that failure path).
1170    // When converted to a later scheme, Egress Key will be overwritten.
1171
1172    // Always derive a new egress key from current VmUniqueId
1173    let new_egress_key = crypto::derive_key(
1174        &gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize],
1175        bios_guid.as_bytes(),
1176        VMGS_KEY_DERIVE_LABEL,
1177    )
1178    .map_err(GetDerivedKeysByIdError::DeriveEgressKeyUsingCurrentVmId)?;
1179
1180    if new_egress_key.len() != AES_GCM_KEY_LENGTH {
1181        Err(GetDerivedKeysByIdError::InvalidDerivedEgressKeySize {
1182            key_size: new_egress_key.len(),
1183            expected_size: AES_GCM_KEY_LENGTH,
1184        })?
1185    }
1186
1187    // Ingress values depend on what happened previously to the datastore.
1188    // If not previously encrypted (no saved Id), then Ingress Key not required.
1189    let new_ingress_key = if key_protector_by_id.inner.id_guid != Guid::default() {
1190        // Derive key used to lock data store previously
1191        crypto::derive_key(
1192            &gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize],
1193            key_protector_by_id.inner.id_guid.as_bytes(),
1194            VMGS_KEY_DERIVE_LABEL,
1195        )
1196        .map_err(GetDerivedKeysByIdError::DeriveIngressKeyUsingKeyProtectorId)?
1197    } else {
1198        // If data store is not encrypted, Ingress should equal Egress
1199        new_egress_key
1200    };
1201
1202    if new_ingress_key.len() != AES_GCM_KEY_LENGTH {
1203        Err(GetDerivedKeysByIdError::InvalidDerivedIngressKeySize {
1204            key_size: new_ingress_key.len(),
1205            expected_size: AES_GCM_KEY_LENGTH,
1206        })?
1207    }
1208
1209    Ok(Keys {
1210        ingress: new_ingress_key,
1211        decrypt_egress: None,
1212        encrypt_egress: new_egress_key,
1213    })
1214}
1215
1216/// Prepare the request payload and request GSP from the host via GET.
1217async fn get_gsp_data(
1218    get: &GuestEmulationTransportClient,
1219    key_protector: &mut KeyProtector,
1220) -> GuestStateProtection {
1221    use openhcl_attestation_protocol::vmgs::GSP_BUFFER_SIZE;
1222    use openhcl_attestation_protocol::vmgs::NUMBER_KP;
1223
1224    const_assert_eq!(guest_emulation_transport::api::NUMBER_GSP, NUMBER_KP as u32);
1225    const_assert_eq!(
1226        guest_emulation_transport::api::GSP_CIPHERTEXT_MAX,
1227        GSP_BUFFER_SIZE as u32
1228    );
1229
1230    let mut encrypted_gsp =
1231        [guest_emulation_transport::api::GspCiphertextContent::new_zeroed(); NUMBER_KP];
1232
1233    for (i, gsp) in encrypted_gsp.iter_mut().enumerate().take(NUMBER_KP) {
1234        if key_protector.gsp[i].gsp_length == 0 {
1235            continue;
1236        }
1237
1238        gsp.buffer[..key_protector.gsp[i].gsp_length as usize].copy_from_slice(
1239            &key_protector.gsp[i].gsp_buffer[..key_protector.gsp[i].gsp_length as usize],
1240        );
1241
1242        gsp.length = key_protector.gsp[i].gsp_length;
1243    }
1244
1245    get.guest_state_protection_data(encrypted_gsp, GspExtendedStatusFlags::new())
1246        .await
1247}
1248
1249/// Update Key Protector to remove 2nd protector, and write to VMGS
1250async fn persist_all_key_protectors(
1251    vmgs: &mut Vmgs,
1252    key_protector: &mut KeyProtector,
1253    key_protector_by_id: &mut KeyProtectorById,
1254    bios_guid: Guid,
1255    key_protector_settings: KeyProtectorSettings,
1256) -> Result<(), PersistAllKeyProtectorsError> {
1257    use openhcl_attestation_protocol::vmgs::NUMBER_KP;
1258
1259    if key_protector_settings.use_gsp_by_id && !key_protector_settings.should_write_kp {
1260        vmgs::write_key_protector_by_id(&mut key_protector_by_id.inner, vmgs, false, bios_guid)
1261            .await
1262            .map_err(PersistAllKeyProtectorsError::WriteKeyProtectorById)?;
1263    } else {
1264        // If HW Key unlocked VMGS, do not alter KP
1265        if !key_protector_settings.use_hardware_unlock {
1266            // Remove ingress KP & DEK, no longer applies to data store
1267            key_protector.dek[key_protector.active_kp as usize % NUMBER_KP]
1268                .dek_buffer
1269                .fill(0);
1270            key_protector.gsp[key_protector.active_kp as usize % NUMBER_KP].gsp_length = 0;
1271            key_protector.active_kp += 1;
1272
1273            vmgs::write_key_protector(key_protector, vmgs)
1274                .await
1275                .map_err(PersistAllKeyProtectorsError::WriteKeyProtector)?;
1276        }
1277
1278        // Update Id data to indicate this scheme is no longer in use
1279        if !key_protector_settings.use_gsp_by_id
1280            && key_protector_by_id.found_id
1281            && key_protector_by_id.inner.ported == 0
1282        {
1283            key_protector_by_id.inner.ported = 1;
1284            vmgs::write_key_protector_by_id(&mut key_protector_by_id.inner, vmgs, true, bios_guid)
1285                .await
1286                .map_err(PersistAllKeyProtectorsError::WriteKeyProtectorById)?;
1287        }
1288    }
1289
1290    Ok(())
1291}
1292
1293/// Module that implements the mock [`TeeCall`] for testing purposes
1294#[cfg(test)]
1295pub mod test_utils {
1296    use tee_call::GetAttestationReportResult;
1297    use tee_call::HW_DERIVED_KEY_LENGTH;
1298    use tee_call::REPORT_DATA_SIZE;
1299    use tee_call::TeeCall;
1300    use tee_call::TeeCallGetDerivedKey;
1301    use tee_call::TeeType;
1302
1303    /// Mock implementation of [`TeeCall`] with get derived key support for testing purposes
1304    pub struct MockTeeCall {
1305        /// Mock TCB version to return from get_attestation_report
1306        pub tcb_version: u64,
1307    }
1308
1309    impl MockTeeCall {
1310        /// Create a new instance of [`MockTeeCall`].
1311        pub fn new(tcb_version: u64) -> Self {
1312            Self { tcb_version }
1313        }
1314    }
1315
1316    impl TeeCall for MockTeeCall {
1317        fn get_attestation_report(
1318            &self,
1319            report_data: &[u8; REPORT_DATA_SIZE],
1320        ) -> Result<GetAttestationReportResult, tee_call::Error> {
1321            let mut report =
1322                [0x6c; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
1323            report[..REPORT_DATA_SIZE].copy_from_slice(report_data);
1324
1325            Ok(GetAttestationReportResult {
1326                report: report.to_vec(),
1327                tcb_version: Some(self.tcb_version),
1328            })
1329        }
1330
1331        fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
1332            Some(self)
1333        }
1334
1335        fn tee_type(&self) -> TeeType {
1336            // Use Snp for testing
1337            TeeType::Snp
1338        }
1339    }
1340
1341    impl TeeCallGetDerivedKey for MockTeeCall {
1342        fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; 32], tee_call::Error> {
1343            // Base test key; mix in policy so different policies yield different derived secrets
1344            let mut key: [u8; HW_DERIVED_KEY_LENGTH] = [0xab; HW_DERIVED_KEY_LENGTH];
1345
1346            // Use mutation to simulate the policy
1347            let tcb = tcb_version.to_le_bytes();
1348            for (i, b) in key.iter_mut().enumerate() {
1349                *b ^= tcb[i % tcb.len()];
1350            }
1351
1352            Ok(key)
1353        }
1354    }
1355
1356    /// Mock implementation of [`TeeCall`] without get derived key support for testing purposes
1357    pub struct MockTeeCallNoGetDerivedKey;
1358
1359    impl TeeCall for MockTeeCallNoGetDerivedKey {
1360        fn get_attestation_report(
1361            &self,
1362            report_data: &[u8; REPORT_DATA_SIZE],
1363        ) -> Result<GetAttestationReportResult, tee_call::Error> {
1364            let mut report =
1365                [0x6c; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
1366            report[..REPORT_DATA_SIZE].copy_from_slice(report_data);
1367
1368            Ok(GetAttestationReportResult {
1369                report: report.to_vec(),
1370                tcb_version: None,
1371            })
1372        }
1373
1374        fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
1375            None
1376        }
1377
1378        fn tee_type(&self) -> TeeType {
1379            // Use Snp for testing
1380            TeeType::Snp
1381        }
1382    }
1383}
1384
1385#[cfg(test)]
1386mod tests {
1387    use super::*;
1388    use crate::test_utils::MockTeeCallNoGetDerivedKey;
1389    use disk_backend::Disk;
1390    use disklayer_ram::ram_disk;
1391    use get_protocol::GSP_CLEARTEXT_MAX;
1392    use get_protocol::GspExtendedStatusFlags;
1393    use guest_emulation_device::IgvmAgentAction;
1394    use guest_emulation_device::IgvmAgentTestPlan;
1395    use guest_emulation_transport::test_utilities::TestGet;
1396    use key_protector::AES_WRAPPED_AES_KEY_LENGTH;
1397    use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestType;
1398    use openhcl_attestation_protocol::vmgs::DEK_BUFFER_SIZE;
1399    use openhcl_attestation_protocol::vmgs::DekKp;
1400    use openhcl_attestation_protocol::vmgs::GSP_BUFFER_SIZE;
1401    use openhcl_attestation_protocol::vmgs::GspKp;
1402    use openhcl_attestation_protocol::vmgs::NUMBER_KP;
1403    use pal_async::DefaultDriver;
1404    use pal_async::async_test;
1405    use pal_async::task::Spawn;
1406    use std::collections::VecDeque;
1407    use test_utils::MockTeeCall;
1408    use test_with_tracing::test;
1409    use vmgs_format::EncryptionAlgorithm;
1410    use vmgs_format::FileId;
1411
1412    const ONE_MEGA_BYTE: u64 = 1024 * 1024;
1413
1414    fn new_test_file() -> Disk {
1415        ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
1416    }
1417
1418    async fn new_formatted_vmgs() -> Vmgs {
1419        let disk = new_test_file();
1420
1421        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
1422
1423        assert!(
1424            key_protector_is_empty(&mut vmgs).await,
1425            "Newly formatted VMGS should have an empty key protector"
1426        );
1427        assert!(
1428            key_protector_by_id_is_empty(&mut vmgs).await,
1429            "Newly formatted VMGS should have an empty key protector by id"
1430        );
1431
1432        vmgs
1433    }
1434
1435    async fn key_protector_is_empty(vmgs: &mut Vmgs) -> bool {
1436        let key_protector = vmgs::read_key_protector(vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1437            .await
1438            .unwrap();
1439
1440        key_protector.as_bytes().iter().all(|&b| b == 0)
1441    }
1442
1443    async fn key_protector_by_id_is_empty(vmgs: &mut Vmgs) -> bool {
1444        vmgs::read_key_protector_by_id(vmgs)
1445            .await
1446            .is_err_and(|err| {
1447                matches!(
1448                    err,
1449                    vmgs::ReadFromVmgsError::EntryNotFound(FileId::VM_UNIQUE_ID)
1450                )
1451            })
1452    }
1453
1454    async fn hardware_key_protector_is_empty(vmgs: &mut Vmgs) -> bool {
1455        vmgs::read_hardware_key_protector(vmgs)
1456            .await
1457            .is_err_and(|err| {
1458                matches!(
1459                    err,
1460                    vmgs::ReadFromVmgsError::EntryNotFound(FileId::HW_KEY_PROTECTOR)
1461                )
1462            })
1463    }
1464
1465    fn new_key_protector() -> KeyProtector {
1466        // Ingress and egress KPs are assumed to be the only two KPs, therefore `NUMBER_KP` should be 2
1467        assert_eq!(NUMBER_KP, 2);
1468
1469        let ingress_dek = DekKp {
1470            dek_buffer: [1; DEK_BUFFER_SIZE],
1471        };
1472        let egress_dek = DekKp {
1473            dek_buffer: [2; DEK_BUFFER_SIZE],
1474        };
1475        let ingress_gsp = GspKp {
1476            gsp_length: GSP_BUFFER_SIZE as u32,
1477            gsp_buffer: [3; GSP_BUFFER_SIZE],
1478        };
1479        let egress_gsp = GspKp {
1480            gsp_length: GSP_BUFFER_SIZE as u32,
1481            gsp_buffer: [4; GSP_BUFFER_SIZE],
1482        };
1483        KeyProtector {
1484            dek: [ingress_dek, egress_dek],
1485            gsp: [ingress_gsp, egress_gsp],
1486            active_kp: 0,
1487        }
1488    }
1489
1490    fn new_key_protector_by_id(
1491        id_guid: Option<Guid>,
1492        ported: Option<u8>,
1493        found_id: bool,
1494    ) -> KeyProtectorById {
1495        let key_protector_by_id = openhcl_attestation_protocol::vmgs::KeyProtectorById {
1496            id_guid: id_guid.unwrap_or_else(Guid::new_random),
1497            ported: ported.unwrap_or(0),
1498            pad: [0; 3],
1499        };
1500
1501        KeyProtectorById {
1502            inner: key_protector_by_id,
1503            found_id,
1504        }
1505    }
1506
1507    async fn new_test_get(
1508        spawn: impl Spawn,
1509        enable_igvm_attest: bool,
1510        plan: Option<IgvmAgentTestPlan>,
1511    ) -> TestGet {
1512        if enable_igvm_attest {
1513            const TEST_DEVICE_MEMORY_SIZE: u64 = 64;
1514            // Use `DeviceTestMemory` to set up shared memory required by the IGVM_ATTEST GET calls.
1515            let dev_test_mem = user_driver_emulated_mock::DeviceTestMemory::new(
1516                TEST_DEVICE_MEMORY_SIZE,
1517                true,
1518                "test-attest",
1519            );
1520
1521            let mut test_get = guest_emulation_transport::test_utilities::new_transport_pair(
1522                spawn,
1523                None,
1524                get_protocol::ProtocolVersion::NICKEL_REV2,
1525                Some(dev_test_mem.guest_memory()),
1526                plan,
1527            )
1528            .await;
1529
1530            test_get.client.set_gpa_allocator(dev_test_mem.dma_client());
1531
1532            test_get
1533        } else {
1534            guest_emulation_transport::test_utilities::new_transport_pair(
1535                spawn,
1536                None,
1537                get_protocol::ProtocolVersion::NICKEL_REV2,
1538                None,
1539                None,
1540            )
1541            .await
1542        }
1543    }
1544
1545    fn new_attestation_vm_config() -> AttestationVmConfig {
1546        AttestationVmConfig {
1547            current_time: None,
1548            root_cert_thumbprint: String::new(),
1549            console_enabled: false,
1550            secure_boot: false,
1551            tpm_enabled: true,
1552            tpm_persisted: true,
1553            filtered_vpci_devices_allowed: false,
1554            vm_unique_id: String::new(),
1555        }
1556    }
1557
1558    #[async_test]
1559    async fn do_nothing_without_derived_keys() {
1560        let mut vmgs = new_formatted_vmgs().await;
1561
1562        let mut key_protector = new_key_protector();
1563        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1564
1565        let key_protector_settings = KeyProtectorSettings {
1566            should_write_kp: false,
1567            use_gsp_by_id: false,
1568            use_hardware_unlock: false,
1569            decrypt_gsp_type: GspType::None,
1570            encrypt_gsp_type: GspType::None,
1571        };
1572
1573        let bios_guid = Guid::new_random();
1574
1575        unlock_vmgs_data_store(
1576            &mut vmgs,
1577            false,
1578            &mut key_protector,
1579            &mut key_protector_by_id,
1580            None,
1581            key_protector_settings,
1582            bios_guid,
1583        )
1584        .await
1585        .unwrap();
1586
1587        assert!(key_protector_is_empty(&mut vmgs).await);
1588        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
1589
1590        // Create another instance as the previous `unlock_vmgs_data_store` took ownership of the last one
1591        let key_protector_settings = KeyProtectorSettings {
1592            should_write_kp: false,
1593            use_gsp_by_id: false,
1594            use_hardware_unlock: false,
1595            decrypt_gsp_type: GspType::None,
1596            encrypt_gsp_type: GspType::None,
1597        };
1598
1599        // Even if the VMGS is encrypted, if no derived keys are provided, nothing should happen
1600        unlock_vmgs_data_store(
1601            &mut vmgs,
1602            true,
1603            &mut key_protector,
1604            &mut key_protector_by_id,
1605            None,
1606            key_protector_settings,
1607            bios_guid,
1608        )
1609        .await
1610        .unwrap();
1611
1612        assert!(key_protector_is_empty(&mut vmgs).await);
1613        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
1614    }
1615
1616    #[async_test]
1617    async fn provision_vmgs_and_rotate_keys() {
1618        let mut vmgs = new_formatted_vmgs().await;
1619
1620        let mut key_protector = new_key_protector();
1621        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1622
1623        let ingress = [1; AES_GCM_KEY_LENGTH];
1624        let egress = [2; AES_GCM_KEY_LENGTH];
1625        let derived_keys = Keys {
1626            ingress,
1627            decrypt_egress: None,
1628            encrypt_egress: egress,
1629        };
1630
1631        let key_protector_settings = KeyProtectorSettings {
1632            should_write_kp: true,
1633            use_gsp_by_id: true,
1634            use_hardware_unlock: false,
1635            decrypt_gsp_type: GspType::GspById,
1636            encrypt_gsp_type: GspType::GspById,
1637        };
1638
1639        let bios_guid = Guid::new_random();
1640
1641        // Without encryption implies the provision path
1642        // The VMGS will be locked using the egress key
1643        unlock_vmgs_data_store(
1644            &mut vmgs,
1645            false,
1646            &mut key_protector,
1647            &mut key_protector_by_id,
1648            Some(derived_keys),
1649            key_protector_settings,
1650            bios_guid,
1651        )
1652        .await
1653        .unwrap();
1654
1655        // The ingress key is essentially ignored since the VMGS wasn't previously encrypted
1656        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1657
1658        // The egress key was used to lock the VMGS after provisioning
1659        vmgs.unlock_with_encryption_key(&egress).await.unwrap();
1660        // Since this is a new VMGS, the egress key is the first and only key
1661        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(0));
1662
1663        // Since both `should_write_kp` and `use_gsp_by_id` are true, both key protectors should be updated
1664        assert!(!key_protector_is_empty(&mut vmgs).await);
1665        assert!(!key_protector_by_id_is_empty(&mut vmgs).await);
1666
1667        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1668            .await
1669            .unwrap();
1670        assert_eq!(found_key_protector.as_bytes(), key_protector.as_bytes());
1671
1672        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1673        assert_eq!(
1674            found_key_protector_by_id.as_bytes(),
1675            key_protector_by_id.inner.as_bytes()
1676        );
1677
1678        // Now that the VMGS has been provisioned, simulate the rotation of keys
1679        let new_egress = [3; AES_GCM_KEY_LENGTH];
1680
1681        let mut new_key_protector = new_key_protector();
1682        let mut new_key_protector_by_id = new_key_protector_by_id(None, None, false);
1683
1684        let key_protector_settings = KeyProtectorSettings {
1685            should_write_kp: true,
1686            use_gsp_by_id: true,
1687            use_hardware_unlock: false,
1688            decrypt_gsp_type: GspType::GspById,
1689            encrypt_gsp_type: GspType::GspById,
1690        };
1691
1692        // Ingress is now the old egress, and we provide a new new egress key
1693        let derived_keys = Keys {
1694            ingress: egress,
1695            decrypt_egress: None,
1696            encrypt_egress: new_egress,
1697        };
1698
1699        unlock_vmgs_data_store(
1700            &mut vmgs,
1701            true,
1702            &mut new_key_protector,
1703            &mut new_key_protector_by_id,
1704            Some(derived_keys),
1705            key_protector_settings,
1706            bios_guid,
1707        )
1708        .await
1709        .unwrap();
1710
1711        // We should still fail to unlock the VMGS with the original ingress key
1712        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1713        // The old egress key should no longer be able to unlock the VMGS
1714        vmgs.unlock_with_encryption_key(&egress).await.unwrap_err();
1715
1716        // The new egress key should be able to unlock the VMGS
1717        vmgs.unlock_with_encryption_key(&new_egress).await.unwrap();
1718        // The old egress key was removed, but not before the new egress key was added in the 1th slot
1719        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
1720
1721        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1722            .await
1723            .unwrap();
1724        assert_eq!(found_key_protector.as_bytes(), new_key_protector.as_bytes());
1725
1726        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1727        assert_eq!(
1728            found_key_protector_by_id.as_bytes(),
1729            new_key_protector_by_id.inner.as_bytes()
1730        );
1731    }
1732
1733    #[async_test]
1734    async fn unlock_previously_encrypted_vmgs_with_ingress_key() {
1735        let mut vmgs = new_formatted_vmgs().await;
1736
1737        let mut key_protector = new_key_protector();
1738        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1739
1740        let ingress = [1; AES_GCM_KEY_LENGTH];
1741        let egress = [2; AES_GCM_KEY_LENGTH];
1742
1743        let derived_keys = Keys {
1744            ingress,
1745            decrypt_egress: None,
1746            encrypt_egress: egress,
1747        };
1748
1749        vmgs.update_encryption_key(&ingress, EncryptionAlgorithm::AES_GCM)
1750            .await
1751            .unwrap();
1752
1753        // Initially, the VMGS can be unlocked using the ingress key
1754        vmgs.unlock_with_encryption_key(&ingress).await.unwrap();
1755        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(0));
1756
1757        let key_protector_settings = KeyProtectorSettings {
1758            should_write_kp: true,
1759            use_gsp_by_id: true,
1760            use_hardware_unlock: false,
1761            decrypt_gsp_type: GspType::GspById,
1762            encrypt_gsp_type: GspType::GspById,
1763        };
1764
1765        let bios_guid = Guid::new_random();
1766
1767        unlock_vmgs_data_store(
1768            &mut vmgs,
1769            true,
1770            &mut key_protector,
1771            &mut key_protector_by_id,
1772            Some(derived_keys),
1773            key_protector_settings,
1774            bios_guid,
1775        )
1776        .await
1777        .unwrap();
1778
1779        // After the VMGS has been unlocked, the VMGS encryption key should be rotated from ingress to egress
1780        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1781        vmgs.unlock_with_encryption_key(&egress).await.unwrap();
1782        // The ingress key was removed, but not before the egress key was added in the 0th slot
1783        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
1784
1785        // Since both `should_write_kp` and `use_gsp_by_id` are true, both key protectors should be updated
1786        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1787            .await
1788            .unwrap();
1789        assert_eq!(found_key_protector.as_bytes(), key_protector.as_bytes());
1790
1791        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1792        assert_eq!(
1793            found_key_protector_by_id.as_bytes(),
1794            key_protector_by_id.inner.as_bytes()
1795        );
1796    }
1797
1798    #[async_test]
1799    async fn failed_to_persist_ingress_key_so_use_egress_key_to_unlock_vmgs() {
1800        let mut vmgs = new_formatted_vmgs().await;
1801
1802        let mut key_protector = new_key_protector();
1803        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1804
1805        let ingress = [1; AES_GCM_KEY_LENGTH];
1806        let decrypt_egress = [2; AES_GCM_KEY_LENGTH];
1807        let encrypt_egress = [3; AES_GCM_KEY_LENGTH];
1808
1809        let derived_keys = Keys {
1810            ingress,
1811            decrypt_egress: Some(decrypt_egress),
1812            encrypt_egress,
1813        };
1814
1815        // Add only the egress key to the VMGS to simulate a failure to persist the ingress key
1816        vmgs.test_add_new_encryption_key(&decrypt_egress, EncryptionAlgorithm::AES_GCM)
1817            .await
1818            .unwrap();
1819        let egress_key_index = vmgs.test_get_active_datastore_key_index().unwrap();
1820        assert_eq!(egress_key_index, 0);
1821
1822        vmgs.unlock_with_encryption_key(&decrypt_egress)
1823            .await
1824            .unwrap();
1825        let found_egress_key_index = vmgs.test_get_active_datastore_key_index().unwrap();
1826        assert_eq!(found_egress_key_index, egress_key_index);
1827
1828        // Confirm that the ingress key cannot be used to unlock the VMGS
1829        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1830
1831        let key_protector_settings = KeyProtectorSettings {
1832            should_write_kp: true,
1833            use_gsp_by_id: true,
1834            use_hardware_unlock: false,
1835            decrypt_gsp_type: GspType::GspById,
1836            encrypt_gsp_type: GspType::GspById,
1837        };
1838
1839        let bios_guid = Guid::new_random();
1840
1841        unlock_vmgs_data_store(
1842            &mut vmgs,
1843            true,
1844            &mut key_protector,
1845            &mut key_protector_by_id,
1846            Some(derived_keys),
1847            key_protector_settings,
1848            bios_guid,
1849        )
1850        .await
1851        .unwrap();
1852
1853        // Confirm that the ingress key was not added
1854        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
1855
1856        // Confirm that the decrypt egress key no longer works
1857        vmgs.unlock_with_encryption_key(&decrypt_egress)
1858            .await
1859            .unwrap_err();
1860
1861        // The encrypt_egress key can unlock the VMGS and was added as a new key
1862        vmgs.unlock_with_encryption_key(&encrypt_egress)
1863            .await
1864            .unwrap();
1865        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
1866
1867        // Since both `should_write_kp` and `use_gsp_by_id` are true, both key protectors should be updated
1868        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
1869            .await
1870            .unwrap();
1871        assert_eq!(found_key_protector.as_bytes(), key_protector.as_bytes());
1872
1873        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
1874        assert_eq!(
1875            found_key_protector_by_id.as_bytes(),
1876            key_protector_by_id.inner.as_bytes()
1877        );
1878    }
1879
1880    #[async_test]
1881    async fn fail_to_unlock_vmgs_with_existing_ingress_key() {
1882        let mut vmgs = new_formatted_vmgs().await;
1883
1884        let mut key_protector = new_key_protector();
1885        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1886
1887        let ingress = [1; AES_GCM_KEY_LENGTH];
1888
1889        // Ingress and egress keys are the same
1890        let derived_keys = Keys {
1891            ingress,
1892            decrypt_egress: None,
1893            encrypt_egress: ingress,
1894        };
1895
1896        // Add two random keys to the VMGS to simulate unlock failure when ingress and egress keys are the same
1897        let additional_key = [2; AES_GCM_KEY_LENGTH];
1898        let yet_another_key = [3; AES_GCM_KEY_LENGTH];
1899
1900        vmgs.test_add_new_encryption_key(&additional_key, EncryptionAlgorithm::AES_GCM)
1901            .await
1902            .unwrap();
1903        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(0));
1904
1905        vmgs.test_add_new_encryption_key(&yet_another_key, EncryptionAlgorithm::AES_GCM)
1906            .await
1907            .unwrap();
1908        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
1909
1910        let key_protector_settings = KeyProtectorSettings {
1911            should_write_kp: true,
1912            use_gsp_by_id: true,
1913            use_hardware_unlock: false,
1914            decrypt_gsp_type: GspType::GspById,
1915            encrypt_gsp_type: GspType::GspById,
1916        };
1917
1918        let bios_guid = Guid::new_random();
1919
1920        let unlock_result = unlock_vmgs_data_store(
1921            &mut vmgs,
1922            true,
1923            &mut key_protector,
1924            &mut key_protector_by_id,
1925            Some(derived_keys),
1926            key_protector_settings,
1927            bios_guid,
1928        )
1929        .await;
1930        assert!(unlock_result.is_err());
1931        assert_eq!(
1932            unlock_result.unwrap_err().to_string(),
1933            "failed to unlock vmgs with the existing ingress key".to_string()
1934        );
1935    }
1936
1937    #[async_test]
1938    async fn fail_to_unlock_vmgs_with_new_ingress_key() {
1939        let mut vmgs = new_formatted_vmgs().await;
1940
1941        let mut key_protector = new_key_protector();
1942        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
1943
1944        let derived_keys = Keys {
1945            ingress: [1; AES_GCM_KEY_LENGTH],
1946            decrypt_egress: None,
1947            encrypt_egress: [2; AES_GCM_KEY_LENGTH],
1948        };
1949
1950        // Add two random keys to the VMGS to simulate unlock failure when ingress and egress keys are *not* the same
1951        let additional_key = [3; AES_GCM_KEY_LENGTH];
1952        let yet_another_key = [4; AES_GCM_KEY_LENGTH];
1953
1954        vmgs.test_add_new_encryption_key(&additional_key, EncryptionAlgorithm::AES_GCM)
1955            .await
1956            .unwrap();
1957        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(0));
1958
1959        vmgs.test_add_new_encryption_key(&yet_another_key, EncryptionAlgorithm::AES_GCM)
1960            .await
1961            .unwrap();
1962        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
1963
1964        let key_protector_settings = KeyProtectorSettings {
1965            should_write_kp: true,
1966            use_gsp_by_id: true,
1967            use_hardware_unlock: false,
1968            decrypt_gsp_type: GspType::GspById,
1969            encrypt_gsp_type: GspType::GspById,
1970        };
1971
1972        let bios_guid = Guid::new_random();
1973
1974        let unlock_result = unlock_vmgs_data_store(
1975            &mut vmgs,
1976            true,
1977            &mut key_protector,
1978            &mut key_protector_by_id,
1979            Some(derived_keys),
1980            key_protector_settings,
1981            bios_guid,
1982        )
1983        .await;
1984        assert!(unlock_result.is_err());
1985        assert_eq!(
1986            unlock_result.unwrap_err().to_string(),
1987            "failed to unlock vmgs with the existing ingress key".to_string()
1988        );
1989    }
1990
1991    #[async_test]
1992    async fn get_derived_keys_using_id() {
1993        let bios_guid = Guid::new_random();
1994
1995        let gsp_response_by_id = GuestStateProtectionById {
1996            seed: guest_emulation_transport::api::GspCleartextContent {
1997                length: GSP_CLEARTEXT_MAX,
1998                buffer: [1; GSP_CLEARTEXT_MAX as usize * 2],
1999            },
2000            extended_status_flags: GspExtendedStatusFlags::from_bits(0),
2001        };
2002
2003        // When the key protector by id inner `id_guid` is all zeroes, the derived ingress and egress keys
2004        // should be identical.
2005        let mut key_protector_by_id =
2006            new_key_protector_by_id(Some(Guid::new_zeroed()), None, false);
2007        let derived_keys =
2008            get_derived_keys_by_id(&mut key_protector_by_id, bios_guid, gsp_response_by_id)
2009                .unwrap();
2010
2011        assert_eq!(derived_keys.ingress, derived_keys.encrypt_egress);
2012
2013        // When the key protector by id inner `id_guid` is not all zeroes, the derived ingress and egress keys
2014        // should be different.
2015        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2016        let derived_keys =
2017            get_derived_keys_by_id(&mut key_protector_by_id, bios_guid, gsp_response_by_id)
2018                .unwrap();
2019
2020        assert_ne!(derived_keys.ingress, derived_keys.encrypt_egress);
2021
2022        // When the `gsp_response_by_id` seed length is 0, deriving a key will fail.
2023        let gsp_response_by_id_with_0_length_seed = GuestStateProtectionById {
2024            seed: guest_emulation_transport::api::GspCleartextContent {
2025                length: 0,
2026                buffer: [1; GSP_CLEARTEXT_MAX as usize * 2],
2027            },
2028            extended_status_flags: GspExtendedStatusFlags::from_bits(0),
2029        };
2030
2031        let derived_keys_response = get_derived_keys_by_id(
2032            &mut key_protector_by_id,
2033            bios_guid,
2034            gsp_response_by_id_with_0_length_seed,
2035        );
2036        assert!(derived_keys_response.is_err());
2037        assert_eq!(
2038            derived_keys_response.unwrap_err().to_string(),
2039            "failed to derive an egress key based on current vm bios guid".to_string()
2040        );
2041    }
2042
2043    #[async_test]
2044    async fn pass_through_persist_all_key_protectors() {
2045        let mut vmgs = new_formatted_vmgs().await;
2046        let mut key_protector = new_key_protector();
2047        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2048        let bios_guid = Guid::new_random();
2049
2050        // Copied/cloned bits used for comparison later
2051        let kp_copy = key_protector.as_bytes().to_vec();
2052        let active_kp_copy = key_protector.active_kp;
2053
2054        // When all key protector settings are true, no actions will be taken on the key protectors or VMGS
2055        let key_protector_settings = KeyProtectorSettings {
2056            should_write_kp: true,
2057            use_gsp_by_id: true,
2058            use_hardware_unlock: true,
2059            decrypt_gsp_type: GspType::GspById,
2060            encrypt_gsp_type: GspType::GspById,
2061        };
2062        persist_all_key_protectors(
2063            &mut vmgs,
2064            &mut key_protector,
2065            &mut key_protector_by_id,
2066            bios_guid,
2067            key_protector_settings,
2068        )
2069        .await
2070        .unwrap();
2071
2072        assert!(key_protector_is_empty(&mut vmgs).await);
2073        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
2074
2075        // The key protector should remain unchanged
2076        assert_eq!(active_kp_copy, key_protector.active_kp);
2077        assert_eq!(kp_copy.as_slice(), key_protector.as_bytes());
2078    }
2079
2080    #[async_test]
2081    async fn persist_all_key_protectors_write_key_protector_by_id() {
2082        let mut vmgs = new_formatted_vmgs().await;
2083        let mut key_protector = new_key_protector();
2084        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2085        let bios_guid = Guid::new_random();
2086
2087        // Copied/cloned bits used for comparison later
2088        let kp_copy = key_protector.as_bytes().to_vec();
2089        let active_kp_copy = key_protector.active_kp;
2090
2091        // When `use_gsp_by_id` is true and `should_write_kp` is false, the key protector by id should be written to the VMGS
2092        let key_protector_settings = KeyProtectorSettings {
2093            should_write_kp: false,
2094            use_gsp_by_id: true,
2095            use_hardware_unlock: false,
2096            decrypt_gsp_type: GspType::GspById,
2097            encrypt_gsp_type: GspType::GspById,
2098        };
2099        persist_all_key_protectors(
2100            &mut vmgs,
2101            &mut key_protector,
2102            &mut key_protector_by_id,
2103            bios_guid,
2104            key_protector_settings,
2105        )
2106        .await
2107        .unwrap();
2108
2109        // The previously empty VMGS now holds the key protector by id but not the key protector
2110        assert!(key_protector_is_empty(&mut vmgs).await);
2111        assert!(!key_protector_by_id_is_empty(&mut vmgs).await);
2112
2113        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
2114        assert_eq!(
2115            found_key_protector_by_id.as_bytes(),
2116            key_protector_by_id.inner.as_bytes()
2117        );
2118
2119        // The key protector should remain unchanged
2120        assert_eq!(kp_copy.as_slice(), key_protector.as_bytes());
2121        assert_eq!(active_kp_copy, key_protector.active_kp);
2122    }
2123
2124    #[async_test]
2125    async fn persist_all_key_protectors_remove_ingress_kp() {
2126        let mut vmgs = new_formatted_vmgs().await;
2127        let mut key_protector = new_key_protector();
2128        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2129        let bios_guid = Guid::new_random();
2130
2131        // Copied active KP for later use
2132        let active_kp_copy = key_protector.active_kp;
2133
2134        // When `use_gsp_by_id` is false, `should_write_kp` is true, and `use_hardware_unlock` is false, the active key protector's
2135        // 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
2136        let key_protector_settings = KeyProtectorSettings {
2137            should_write_kp: true,
2138            use_gsp_by_id: false,
2139            use_hardware_unlock: false,
2140            decrypt_gsp_type: GspType::None,
2141            encrypt_gsp_type: GspType::None,
2142        };
2143        persist_all_key_protectors(
2144            &mut vmgs,
2145            &mut key_protector,
2146            &mut key_protector_by_id,
2147            bios_guid,
2148            key_protector_settings,
2149        )
2150        .await
2151        .unwrap();
2152
2153        assert!(!key_protector_is_empty(&mut vmgs).await);
2154        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
2155
2156        // The previously empty VMGS's key protector should now be overwritten
2157        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
2158            .await
2159            .unwrap();
2160
2161        assert!(
2162            found_key_protector.dek[active_kp_copy as usize]
2163                .dek_buffer
2164                .iter()
2165                .all(|&b| b == 0),
2166        );
2167        assert_eq!(
2168            found_key_protector.gsp[active_kp_copy as usize].gsp_length,
2169            0
2170        );
2171        assert_eq!(found_key_protector.active_kp, active_kp_copy + 1);
2172    }
2173
2174    #[async_test]
2175    async fn persist_all_key_protectors_mark_key_protector_by_id_as_not_in_use() {
2176        let mut vmgs = new_formatted_vmgs().await;
2177        let mut key_protector = new_key_protector();
2178        let mut key_protector_by_id = new_key_protector_by_id(None, None, true);
2179        let bios_guid = Guid::new_random();
2180
2181        // When `use_gsp_by_id` is false, `should_write_kp` is true, `use_hardware_unlock` is true, and
2182        // the key protector by id is found and not ported, the key protector by id should be marked as ported
2183        let key_protector_settings = KeyProtectorSettings {
2184            should_write_kp: true,
2185            use_gsp_by_id: false,
2186            use_hardware_unlock: true,
2187            decrypt_gsp_type: GspType::None,
2188            encrypt_gsp_type: GspType::None,
2189        };
2190
2191        persist_all_key_protectors(
2192            &mut vmgs,
2193            &mut key_protector,
2194            &mut key_protector_by_id,
2195            bios_guid,
2196            key_protector_settings,
2197        )
2198        .await
2199        .unwrap();
2200
2201        assert!(key_protector_is_empty(&mut vmgs).await);
2202        assert!(!key_protector_by_id_is_empty(&mut vmgs).await);
2203
2204        // The previously empty VMGS's key protector by id should now be overwritten
2205        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
2206        assert_eq!(found_key_protector_by_id.ported, 1);
2207        assert_eq!(
2208            found_key_protector_by_id.id_guid,
2209            key_protector_by_id.inner.id_guid
2210        );
2211    }
2212
2213    // --- initialize_platform_security tests ---
2214
2215    #[async_test]
2216    async fn init_sec_suppress_attestation(driver: DefaultDriver) {
2217        let mut vmgs = new_formatted_vmgs().await;
2218
2219        // Write non-zero agent data to VMGS so we can verify it is returned.
2220        let agent = SecurityProfile {
2221            agent_data: [0xAA; openhcl_attestation_protocol::vmgs::AGENT_DATA_MAX_SIZE],
2222        };
2223        vmgs.write_file(FileId::ATTEST, agent.as_bytes())
2224            .await
2225            .unwrap();
2226
2227        // Ensure no IGVM attest call out
2228        let get_pair = new_test_get(driver, false, None).await;
2229
2230        let bios_guid = Guid::new_random();
2231        let att_cfg = new_attestation_vm_config();
2232
2233        // Ensure VMGS is not encrypted and agent data is empty before the call
2234        assert!(!vmgs.is_encrypted());
2235
2236        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
2237        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
2238        let res = initialize_platform_security(
2239            &get_pair.client,
2240            bios_guid,
2241            &att_cfg,
2242            &mut vmgs,
2243            None, // no TEE when suppressed
2244            true, // suppress_attestation
2245            ldriver,
2246            GuestStateEncryptionPolicy::None,
2247            true,
2248        )
2249        .await
2250        .unwrap();
2251
2252        // VMGS remains unencrypted and KP/HWKP not written.
2253        assert!(!vmgs.is_encrypted());
2254        assert!(key_protector_is_empty(&mut vmgs).await);
2255        assert!(hardware_key_protector_is_empty(&mut vmgs).await);
2256        // Agent data passed through
2257        assert_eq!(res.agent_data.unwrap(), agent.agent_data.to_vec());
2258        // Secure key should be None without pre-provisioning
2259        assert!(res.guest_secret_key.is_none());
2260    }
2261
2262    #[async_test]
2263    async fn init_sec_secure_key_release_with_wrapped_key_request(driver: DefaultDriver) {
2264        let mut vmgs = new_formatted_vmgs().await;
2265
2266        // IGVM attest is required
2267        let get_pair = new_test_get(driver, true, None).await;
2268
2269        let bios_guid = Guid::new_random();
2270        let att_cfg = new_attestation_vm_config();
2271        let tee = MockTeeCall::new(0x1234);
2272
2273        // Ensure VMGS is not encrypted and agent data is empty before the call
2274        assert!(!vmgs.is_encrypted());
2275
2276        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
2277        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
2278        let res = initialize_platform_security(
2279            &get_pair.client,
2280            bios_guid,
2281            &att_cfg,
2282            &mut vmgs,
2283            Some(&tee),
2284            false,
2285            ldriver.clone(),
2286            GuestStateEncryptionPolicy::Auto,
2287            true,
2288        )
2289        .await
2290        .unwrap();
2291
2292        // VMGS is now encrypted and HWKP is updated.
2293        assert!(vmgs.is_encrypted());
2294        assert!(!hardware_key_protector_is_empty(&mut vmgs).await);
2295
2296        // Agent data should be the same as `key_reference` in the WRAPPED_KEY response.
2297        // See vm/devices/get/guest_emulation_device/src/test_igvm_agent.rs for the expected response.
2298        let key_reference = serde_json::json!({
2299            "key_info": {
2300                "host": "name"
2301            },
2302            "attestation_info": {
2303                "host": "attestation_name"
2304            }
2305        });
2306        let key_reference = serde_json::to_string(&key_reference).unwrap();
2307        let key_reference = key_reference.as_bytes();
2308        let mut expected_agent_data =
2309            [0u8; openhcl_attestation_protocol::vmgs::AGENT_DATA_MAX_SIZE];
2310        expected_agent_data[..key_reference.len()].copy_from_slice(key_reference);
2311        assert_eq!(res.agent_data.unwrap(), expected_agent_data.to_vec());
2312        // Secure key should be None without pre-provisioning
2313        assert!(res.guest_secret_key.is_none());
2314
2315        // Second call: VMGS unlock via SKR should succeed
2316        initialize_platform_security(
2317            &get_pair.client,
2318            bios_guid,
2319            &att_cfg,
2320            &mut vmgs,
2321            Some(&tee),
2322            false,
2323            ldriver,
2324            GuestStateEncryptionPolicy::Auto,
2325            true,
2326        )
2327        .await
2328        .unwrap();
2329
2330        // VMGS should remain encrypted
2331        assert!(vmgs.is_encrypted());
2332    }
2333
2334    #[async_test]
2335    async fn init_sec_secure_key_release_without_wrapped_key_request(driver: DefaultDriver) {
2336        let mut vmgs = new_formatted_vmgs().await;
2337
2338        // Write non-zero agent data to workaround the WRAPPED_KEY_REQUEST requirement.
2339        let agent = SecurityProfile {
2340            agent_data: [0xAA; openhcl_attestation_protocol::vmgs::AGENT_DATA_MAX_SIZE],
2341        };
2342        vmgs.write_file(FileId::ATTEST, agent.as_bytes())
2343            .await
2344            .unwrap();
2345
2346        // Skip WRAPPED_KEY_REQUEST for both boots
2347        let mut plan = IgvmAgentTestPlan::default();
2348        plan.insert(
2349            IgvmAttestRequestType::WRAPPED_KEY_REQUEST,
2350            VecDeque::from([IgvmAgentAction::NoResponse, IgvmAgentAction::NoResponse]),
2351        );
2352
2353        // IGVM attest is required
2354        let get_pair = new_test_get(driver, true, Some(plan)).await;
2355
2356        let bios_guid = Guid::new_random();
2357        let att_cfg = new_attestation_vm_config();
2358        let tee = MockTeeCall::new(0x1234);
2359
2360        // Ensure VMGS is not encrypted and agent data is empty before the call
2361        assert!(!vmgs.is_encrypted());
2362
2363        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
2364        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
2365        let res = initialize_platform_security(
2366            &get_pair.client,
2367            bios_guid,
2368            &att_cfg,
2369            &mut vmgs,
2370            Some(&tee),
2371            false,
2372            ldriver.clone(),
2373            GuestStateEncryptionPolicy::Auto,
2374            true,
2375        )
2376        .await
2377        .unwrap();
2378
2379        // VMGS is now encrypted and HWKP is updated.
2380        assert!(vmgs.is_encrypted());
2381        assert!(!hardware_key_protector_is_empty(&mut vmgs).await);
2382        // Agent data passed through
2383        assert_eq!(res.agent_data.clone().unwrap(), agent.agent_data.to_vec());
2384        // Secure key should be None without pre-provisioning
2385        assert!(res.guest_secret_key.is_none());
2386
2387        // Second call: VMGS unlock via SKR should succeed
2388        let res = initialize_platform_security(
2389            &get_pair.client,
2390            bios_guid,
2391            &att_cfg,
2392            &mut vmgs,
2393            Some(&tee),
2394            false,
2395            ldriver,
2396            GuestStateEncryptionPolicy::Auto,
2397            true,
2398        )
2399        .await
2400        .unwrap();
2401
2402        // VMGS should remain encrypted
2403        assert!(vmgs.is_encrypted());
2404        // Agent data passed through
2405        assert_eq!(res.agent_data.clone().unwrap(), agent.agent_data.to_vec());
2406        // Secure key should be None without pre-provisioning
2407        assert!(res.guest_secret_key.is_none());
2408    }
2409
2410    #[async_test]
2411    async fn init_sec_secure_key_release_hw_sealing_backup(driver: DefaultDriver) {
2412        let mut vmgs = new_formatted_vmgs().await;
2413
2414        // IGVM attest is required
2415        let mut plan = IgvmAgentTestPlan::default();
2416        plan.insert(
2417            IgvmAttestRequestType::WRAPPED_KEY_REQUEST,
2418            VecDeque::from([
2419                IgvmAgentAction::RespondSuccess,
2420                IgvmAgentAction::RespondFailure,
2421            ]),
2422        );
2423
2424        let get_pair = new_test_get(driver, true, Some(plan)).await;
2425
2426        let bios_guid = Guid::new_random();
2427        let att_cfg = new_attestation_vm_config();
2428
2429        // Ensure VMGS is not encrypted and agent data is empty before the call
2430        assert!(!vmgs.is_encrypted());
2431
2432        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
2433        let tee = MockTeeCall::new(0x1234);
2434        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
2435        let res = initialize_platform_security(
2436            &get_pair.client,
2437            bios_guid,
2438            &att_cfg,
2439            &mut vmgs,
2440            Some(&tee),
2441            false,
2442            ldriver.clone(),
2443            GuestStateEncryptionPolicy::Auto,
2444            true,
2445        )
2446        .await
2447        .unwrap();
2448
2449        // VMGS is now encrypted and HWKP is updated.
2450        assert!(vmgs.is_encrypted());
2451        assert!(!hardware_key_protector_is_empty(&mut vmgs).await);
2452        // Agent data should be the same as `key_reference` in the WRAPPED_KEY response.
2453        // See vm/devices/get/guest_emulation_device/src/test_igvm_agent.rs for the expected response.
2454        let key_reference = serde_json::json!({
2455            "key_info": {
2456                "host": "name"
2457            },
2458            "attestation_info": {
2459                "host": "attestation_name"
2460            }
2461        });
2462        let key_reference = serde_json::to_string(&key_reference).unwrap();
2463        let key_reference = key_reference.as_bytes();
2464        let mut expected_agent_data =
2465            [0u8; openhcl_attestation_protocol::vmgs::AGENT_DATA_MAX_SIZE];
2466        expected_agent_data[..key_reference.len()].copy_from_slice(key_reference);
2467        assert_eq!(res.agent_data.unwrap(), expected_agent_data.to_vec());
2468        // Secure key should be None without pre-provisioning
2469        assert!(res.guest_secret_key.is_none());
2470
2471        // Second call: VMGS unlock via key recovered with hardware sealing
2472        // NOTE: The test relies on the test GED to return failing WRAPPED_KEY response
2473        // with retry recommendation as false to skip the retry loop in
2474        // secure_key_release::request_vmgs_encryption_keys. Otherwise, the test will stuck
2475        // on the timer.sleep() as the the driver is not progressed.
2476        initialize_platform_security(
2477            &get_pair.client,
2478            bios_guid,
2479            &att_cfg,
2480            &mut vmgs,
2481            Some(&tee),
2482            false,
2483            ldriver,
2484            GuestStateEncryptionPolicy::Auto,
2485            true,
2486        )
2487        .await
2488        .unwrap();
2489
2490        // VMGS should remain encrypted
2491        assert!(vmgs.is_encrypted());
2492    }
2493
2494    #[async_test]
2495    async fn init_sec_secure_key_release_no_hw_sealing_backup(driver: DefaultDriver) {
2496        let mut vmgs = new_formatted_vmgs().await;
2497
2498        // IGVM attest is required
2499        let mut plan = IgvmAgentTestPlan::default();
2500        plan.insert(
2501            IgvmAttestRequestType::WRAPPED_KEY_REQUEST,
2502            VecDeque::from([
2503                IgvmAgentAction::RespondSuccess,
2504                IgvmAgentAction::RespondFailure,
2505            ]),
2506        );
2507
2508        let get_pair = new_test_get(driver, true, Some(plan)).await;
2509
2510        let bios_guid = Guid::new_random();
2511        let att_cfg = new_attestation_vm_config();
2512        // Without hardware sealing support
2513        let tee = MockTeeCallNoGetDerivedKey {};
2514
2515        // Ensure VMGS is not encrypted and agent data is empty before the call
2516        assert!(!vmgs.is_encrypted());
2517
2518        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
2519        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
2520        let res = initialize_platform_security(
2521            &get_pair.client,
2522            bios_guid,
2523            &att_cfg,
2524            &mut vmgs,
2525            Some(&tee),
2526            false,
2527            ldriver.clone(),
2528            GuestStateEncryptionPolicy::Auto,
2529            true,
2530        )
2531        .await
2532        .unwrap();
2533
2534        // VMGS is now encrypted but HWKP remains empty.
2535        assert!(vmgs.is_encrypted());
2536        assert!(hardware_key_protector_is_empty(&mut vmgs).await);
2537        // Agent data should be the same as `key_reference` in the WRAPPED_KEY response.
2538        // See vm/devices/get/guest_emulation_device/src/test_igvm_agent.rs for the expected response.
2539        let key_reference = serde_json::json!({
2540            "key_info": {
2541                "host": "name"
2542            },
2543            "attestation_info": {
2544                "host": "attestation_name"
2545            }
2546        });
2547        let key_reference = serde_json::to_string(&key_reference).unwrap();
2548        let key_reference = key_reference.as_bytes();
2549        let mut expected_agent_data =
2550            [0u8; openhcl_attestation_protocol::vmgs::AGENT_DATA_MAX_SIZE];
2551        expected_agent_data[..key_reference.len()].copy_from_slice(key_reference);
2552        assert_eq!(res.agent_data.unwrap(), expected_agent_data.to_vec());
2553        // Secure key should be None without pre-provisioning
2554        assert!(res.guest_secret_key.is_none());
2555
2556        // Second call: VMGS unlock should fail without hardware sealing support
2557        let result = initialize_platform_security(
2558            &get_pair.client,
2559            bios_guid,
2560            &att_cfg,
2561            &mut vmgs,
2562            Some(&tee),
2563            false,
2564            ldriver,
2565            GuestStateEncryptionPolicy::Auto,
2566            true,
2567        )
2568        .await;
2569
2570        assert!(result.is_err());
2571    }
2572}