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