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