Skip to main content

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