underhill_attestation/
lib.rs

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