Skip to main content

underhill_attestation/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! This modules implements attestation protocols for Underhill to support TVM
5//! and CVM, including getting a tenant key via secure key release (SKR) for
6//! unlocking VMGS and requesting an attestation key (AK) certificate for TPM.
7//! The module also implements the VMGS unlocking process based on SKR.
8
9#![cfg(target_os = "linux")]
10#![forbid(unsafe_code)]
11
12mod hardware_key_sealing;
13mod igvm_attest;
14mod jwt;
15mod key_protector;
16pub mod runtime_sealing;
17mod secure_key_release;
18mod vmgs;
19
20#[cfg(test)]
21mod test_helpers;
22
23pub use igvm_attest::Error as IgvmAttestError;
24pub use igvm_attest::IgvmAttestRequestHelper;
25pub use igvm_attest::ak_cert::AkCertResponse;
26pub use igvm_attest::ak_cert::parse_metadata as parse_ak_cert_metadata;
27pub use igvm_attest::ak_cert::parse_response as parse_ak_cert_response;
28
29use crate::hardware_key_sealing::HardwareKeySealingError;
30use crate::jwt::JwtError;
31use crate::jwt::JwtHelper;
32use ::vmgs::EncryptionAlgorithm;
33use ::vmgs::GspType;
34use ::vmgs::Vmgs;
35use crypto::rsa::RsaKeyPair;
36use crypto::sha_256::sha_256;
37use cvm_tracing::CVM_ALLOWED;
38use get_protocol::dps_json::GuestStateEncryptionPolicy;
39use guest_emulation_transport::GuestEmulationTransportClient;
40use guest_emulation_transport::api::GspExtendedStatusFlags;
41use guest_emulation_transport::api::GuestStateProtection;
42use guest_emulation_transport::api::GuestStateProtectionById;
43use guid::Guid;
44use hardware_key_sealing::HardwareDerivedKeys;
45use key_protector::GetKeysFromKeyProtectorError;
46use key_protector::KeyProtectorExt as _;
47use mesh::MeshPayload;
48use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationVmConfig;
49use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::HardwareSealingPolicy;
50use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::VmgsProvisioner;
51use openhcl_attestation_protocol::vmgs::AES_GCM_KEY_LENGTH;
52use openhcl_attestation_protocol::vmgs::AGENT_DATA_MAX_SIZE;
53use openhcl_attestation_protocol::vmgs::HW_KEY_PROTECTOR_CURRENT_VERSION;
54use openhcl_attestation_protocol::vmgs::HardwareKeyProtectorV3;
55use openhcl_attestation_protocol::vmgs::KeyProtector;
56use openhcl_attestation_protocol::vmgs::SecurityProfile;
57use pal_async::local::LocalDriver;
58use secure_key_release::VmgsEncryptionKeys;
59use serde::Deserialize;
60use serde::Serialize;
61use static_assertions::const_assert_eq;
62use std::fmt::Debug;
63use tee_call::KeyDerivationPolicy;
64use tee_call::REPORT_DATA_SIZE;
65use tee_call::TeeCall;
66use tee_call::TeeType;
67use thiserror::Error;
68use zerocopy::FromZeros;
69use zerocopy::IntoBytes;
70
71/// An attestation error.
72#[derive(Debug, Error)]
73#[error(transparent)]
74pub struct Error(AttestationErrorInner);
75
76impl<T: Into<AttestationErrorInner>> From<T> for Error {
77    fn from(value: T) -> Self {
78        Self(value.into())
79    }
80}
81
82#[derive(Debug, Error)]
83enum AttestationErrorInner {
84    #[error("read security profile from vmgs")]
85    ReadSecurityProfile(#[source] vmgs::ReadFromVmgsError),
86    #[error("failed to get derived keys")]
87    GetDerivedKeys(#[source] GetDerivedKeysError),
88    #[error("failed to read key protector from vmgs")]
89    ReadKeyProtector(#[source] vmgs::ReadFromVmgsError),
90    #[error("failed to read key protector by id from vmgs")]
91    ReadKeyProtectorById(#[source] vmgs::ReadFromVmgsError),
92    #[error("failed to unlock vmgs data store")]
93    UnlockVmgsDataStore(#[source] UnlockVmgsDataStoreError),
94    #[error("failed to finalize required hardware sealing")]
95    FinalizeHardwareSealing(#[source] FinalizeHardwareSealingError),
96    #[error("failed to read guest secret key from vmgs")]
97    ReadGuestSecretKey(#[source] vmgs::ReadFromVmgsError),
98    #[error("failed to verify VMGS provenance")]
99    Provenance(#[source] ProvenanceError),
100    #[error("failed to get an attestation report")]
101    GetAttestationReport(#[source] tee_call::Error),
102    #[error(
103        "host requested HardwareSealing GSP, but hardware sealing is not available \
104         (tee_available={tee_available}, hardware_sealing_policy={hardware_sealing_policy:?})"
105    )]
106    HardwareSealingRequestedButNotAvailable {
107        tee_available: bool,
108        hardware_sealing_policy: HardwareSealingPolicy,
109    },
110}
111
112#[derive(Debug, Error)]
113enum GetDerivedKeysError {
114    #[error("failed to get ingress/egress keys from the key protector")]
115    GetKeysFromKeyProtector(#[source] GetKeysFromKeyProtectorError),
116    #[error("failed to fetch GSP")]
117    FetchGuestStateProtectionById(
118        #[source] guest_emulation_transport::error::GuestStateProtectionByIdError,
119    ),
120    #[error("GSP By Id required, but no GSP By Id found")]
121    GspByIdRequiredButNotFound,
122    #[error("failed to unseal the ingress key using hardware derived keys")]
123    UnsealIngressKeyUsingHardwareDerivedKeys(#[source] HardwareKeySealingError),
124    #[error("failed to get an ingress key from hardware key protector")]
125    GetIngressKeyFromHardwareKeyProtectorFailed,
126    #[error("failed to get an ingress key from key protector")]
127    GetIngressKeyFromKpFailed,
128    #[error("failed to get an ingress key from guest state protection")]
129    GetIngressKeyFromKGspFailed,
130    #[error("failed to get an ingress key from guest state protection by id")]
131    GetIngressKeyFromKGspByIdFailed,
132    #[error("Encryption cannot be disabled if VMGS was previously encrypted")]
133    DisableVmgsEncryptionFailed,
134    #[error("VMGS encryption is required, but no encryption sources were found")]
135    EncryptionRequiredButNotFound,
136    #[error("failed to seal the egress key using hardware derived keys")]
137    SealEgressKeyUsingHardwareDerivedKeys(#[source] HardwareKeySealingError),
138    #[error("failed to write to `FileId::HW_KEY_PROTECTOR` in vmgs")]
139    VmgsWriteHardwareKeyProtector(#[source] vmgs::WriteToVmgsError),
140    #[error("failed to get derived key by id")]
141    GetDerivedKeyById(#[source] GetDerivedKeysByIdError),
142    #[error("failed to derive an ingress key")]
143    DeriveIngressKey(#[source] crypto::kbkdf::KbkdfError),
144    #[error("failed to derive an egress key")]
145    DeriveEgressKey(#[source] crypto::kbkdf::KbkdfError),
146    #[error("Hardware sealing is required, but not supported")]
147    HardwareSealingRequiredButNotSupported,
148}
149
150#[derive(Debug, Error)]
151enum GetDerivedKeysByIdError {
152    #[error("failed to derive an egress key based on current vm bios guid")]
153    DeriveEgressKeyUsingCurrentVmId(#[source] crypto::kbkdf::KbkdfError),
154    #[error("invalid derived egress key size {key_size}, expected {expected_size}")]
155    InvalidDerivedEgressKeySize {
156        key_size: usize,
157        expected_size: usize,
158    },
159    #[error("failed to derive an ingress key based on key protector Id from vmgs")]
160    DeriveIngressKeyUsingKeyProtectorId(#[source] crypto::kbkdf::KbkdfError),
161    #[error("invalid derived egress key size {key_size}, expected {expected_size}")]
162    InvalidDerivedIngressKeySize {
163        key_size: usize,
164        expected_size: usize,
165    },
166}
167
168#[derive(Debug, Error)]
169enum UnlockVmgsDataStoreError {
170    #[error("failed to unlock vmgs with the existing egress key")]
171    VmgsUnlockUsingExistingEgressKey(#[source] ::vmgs::Error),
172    #[error("failed to unlock vmgs with the existing ingress key")]
173    VmgsUnlockUsingExistingIngressKey(#[source] ::vmgs::Error),
174    #[error("failed to write key protector to vmgs")]
175    WriteKeyProtector(#[source] vmgs::WriteToVmgsError),
176    #[error("failed to write key protector by id to vmgs")]
177    WriteKeyProtectorById(#[source] vmgs::WriteToVmgsError),
178    #[error("failed to update the vmgs encryption key")]
179    UpdateVmgsEncryptionKey(#[source] ::vmgs::Error),
180    #[error("failed to persist all key protectors")]
181    PersistAllKeyProtectors(#[source] PersistAllKeyProtectorsError),
182}
183
184#[derive(Debug, Error)]
185enum PersistAllKeyProtectorsError {
186    #[error("failed to write key protector to vmgs")]
187    KeyProtector(#[source] vmgs::WriteToVmgsError),
188    #[error("failed to write key protector by id to vmgs")]
189    KeyProtectorById(#[source] vmgs::WriteToVmgsError),
190    #[error("failed to write hardware key protector to vmgs")]
191    HardwareKeyProtector(#[source] vmgs::WriteToVmgsError),
192}
193
194#[derive(Debug, Error)]
195enum ProvenanceError {
196    #[error("failed to decode provenance doc")]
197    DecodeProvenanceDoc(#[source] JwtError),
198    #[error("failed to verify JWT signature")]
199    VerifySignature(#[source] JwtError),
200    #[error("invalid signature")]
201    InvalidSignature,
202    #[error("missing leaf certificate subject common name")]
203    MissingLeafCertSubjectName,
204    #[error("invalid root certificate")]
205    InvalidRootCert,
206    #[error("failed to convert VMGSID data")]
207    InvalidVmgsidData(#[source] std::str::Utf8Error),
208    #[error("failed to parse VMGSID seed data")]
209    ParseVmgsidSeedData,
210    #[error("failed to decode VMGSID seed data")]
211    DecodeVmgsidData(#[source] hex::FromHexError),
212    #[error("X509 certificate error")]
213    X509Error(#[source] crypto::x509::X509Error),
214    #[error("SP800-108 KDF error")]
215    KdfError(#[source] crypto::kbkdf::KbkdfError),
216    #[error("failed to parse VMGSID")]
217    ParseVmgsid(#[source] guid::ParseError),
218}
219
220/// Failure to confirm boot-time hardware protector identity or durability.
221#[derive(Debug, Error)]
222enum FinalizeHardwareSealingError {
223    #[error("no hardware protector was sealed for this unlock attempt")]
224    MissingSealedKey,
225    #[error("active VMGS encryption key is unavailable")]
226    ActiveKey(#[source] ::vmgs::Error),
227    #[error("sealed DEK does not match the active VMGS encryption key")]
228    ActiveKeyMismatch,
229    #[error("failed to flush the hardware protector and VMGS metadata")]
230    Flush(#[source] ::vmgs::Error),
231}
232
233// Operation types for provisioning telemetry.
234#[derive(Debug)]
235enum LogOpType {
236    BeginDecryptVmgs,
237    DecryptVmgs,
238    ConvertEncryptionType,
239}
240
241/// Label used by `derive_key`
242const VMGS_KEY_DERIVE_LABEL: &[u8; 7] = b"VMGSKEY";
243
244/// KBKDF from SP800-108, using HMAC-SHA-256.
245fn derive_key(
246    key: &[u8],
247    context: &[u8],
248    label: &[u8],
249) -> Result<[u8; AES_GCM_KEY_LENGTH], crypto::kbkdf::KbkdfError> {
250    let output = crypto::kbkdf::kbkdf_hmac(
251        key,
252        context,
253        label,
254        AES_GCM_KEY_LENGTH,
255        crypto::HashAlgorithm::Sha256,
256    )?;
257    Ok(output.try_into().unwrap())
258}
259
260#[derive(Debug)]
261struct Keys {
262    ingress: [u8; AES_GCM_KEY_LENGTH],
263    decrypt_egress: Option<[u8; AES_GCM_KEY_LENGTH]>,
264    encrypt_egress: [u8; AES_GCM_KEY_LENGTH],
265}
266
267/// Key protector settings
268#[derive(Clone, Copy)]
269struct KeyProtectorSettings {
270    /// Whether to update key protector
271    should_write_kp: bool,
272    /// Whether GSP by id is used
273    use_gsp_by_id: bool,
274    /// Whether hardware key sealing is used
275    use_hardware_unlock: bool,
276    /// GSP type used for decryption (for logging)
277    decrypt_gsp_type: GspType,
278    /// GSP type used for encryption (for logging)
279    encrypt_gsp_type: GspType,
280}
281
282/// Helper struct for [`protocol::vmgs::KeyProtectorById`]
283struct KeyProtectorById {
284    /// The instance of [`protocol::vmgs::KeyProtectorById`].
285    pub inner: openhcl_attestation_protocol::vmgs::KeyProtectorById,
286    /// Indicate if the instance is read from the VMGS file.
287    pub found_id: bool,
288}
289
290/// Host attestation settings obtained via the GET GSP call-out.
291pub struct HostAttestationSettings {
292    /// Whether refreshing tpm seeds is needed.
293    pub refresh_tpm_seeds: bool,
294}
295
296/// The return values of [`get_derived_keys`].
297struct DerivedKeyResult {
298    /// Optional derived keys.
299    derived_keys: Option<Keys>,
300    /// The instance of [`KeyProtectorSettings`].
301    key_protector_settings: KeyProtectorSettings,
302    /// The instance of [`GspExtendedStatusFlags`] returned by GSP.
303    gsp_extended_status_flags: GspExtendedStatusFlags,
304    /// Optional hardware key protector.
305    hardware_key_protector: Option<HardwareKeyProtectorV3>,
306    /// This attempt sealed and successfully wrote its egress DEK's protector
307    /// before unlock. Never inferred from an existing VMGS entry.
308    hardware_key_protector_written: bool,
309}
310
311/// Only returned after this attempt successfully unlocks and persists VMGS.
312struct UnlockResult {
313    state_refresh_request: bool,
314    /// A freshly sealed protector for the active DEK was written and flushed.
315    hardware_sealed: bool,
316}
317
318/// The return values of [`initialize_platform_security`].
319pub struct PlatformAttestationData {
320    /// The instance of [`HostAttestationSettings`].
321    pub host_attestation_settings: HostAttestationSettings,
322    /// The agent data used by an attestation request.
323    pub agent_data: Option<Vec<u8>>,
324    /// The guest secret key.
325    pub guest_secret_key: Option<Vec<u8>>,
326    /// Runtime-only floor collected from trusted local boot reports, without
327    /// extra hardware calls. Reports are retained across SKR errors and unlock
328    /// retries, but export requires successful hardware sealing and persistence
329    /// for the active DEK in the successful unlock attempt, including a final
330    /// flush. `None` also means unsupported/disabled sealing, no report, or a
331    /// malformed, incompatible, or lowered observation. In that case runtime
332    /// hardware resealing must stay disabled; do not lazily initialize after an event.
333    pub runtime_tcb_floor: Option<runtime_sealing::RuntimeTcbFloor>,
334}
335
336/// The attestation type to use.
337#[derive(Debug, MeshPayload, Copy, Clone, PartialEq, Eq)]
338pub enum AttestationType {
339    /// Use the SEV-SNP TEE for attestation.
340    Snp,
341    /// Use the TDX TEE for attestation.
342    Tdx,
343    /// Use the VBS TEE for attestation.
344    Vbs,
345    /// Use the CCA TEE for attestation,
346    Cca,
347    /// Use trusted host-based attestation.
348    Host,
349}
350
351/// Request VMGS encryption keys and unlock the VMGS.
352/// If successful, return the state refresh and hardware sealing outcomes for
353/// this attempt. If unsuccessful, return an error and a bool indicating
354/// whether to retry.
355async fn try_unlock_vmgs(
356    get: &GuestEmulationTransportClient,
357    bios_guid: Guid,
358    attestation_vm_config: &AttestationVmConfig,
359    vmgs: &mut Vmgs,
360    tee_call: Option<&dyn TeeCall>,
361    guest_state_encryption_policy: GuestStateEncryptionPolicy,
362    strict_encryption_policy: bool,
363    require_hardware_sealing: bool,
364    agent_data: &mut [u8; AGENT_DATA_MAX_SIZE],
365    key_protector_by_id: &mut KeyProtectorById,
366    boot_tcb_floor: &mut runtime_sealing::BootTcbFloor,
367) -> Result<UnlockResult, (AttestationErrorInner, bool)> {
368    let skr_response = if let Some(tee_call) = tee_call {
369        if !require_hardware_sealing {
370            tracing::info!(CVM_ALLOWED, "Retrieving key-encryption key");
371            // Retrieve the tenant key via attestation
372            secure_key_release::request_vmgs_encryption_keys(
373                get,
374                tee_call,
375                vmgs,
376                attestation_vm_config,
377                agent_data,
378                boot_tcb_floor,
379            )
380            .await
381        } else {
382            tracing::info!(
383                CVM_ALLOWED,
384                "Getting attestation report only for hardware sealing"
385            );
386
387            let report = tee_call
388                .get_attestation_report(&[0; REPORT_DATA_SIZE])
389                .map_err(|e| (AttestationErrorInner::GetAttestationReport(e), false))?;
390            boot_tcb_floor.observe(tee_call, &report);
391
392            Ok(VmgsEncryptionKeys {
393                ingress_rsa_kek: None,
394                wrapped_des_key: None,
395                key_derivation_svn: report.key_derivation_svn,
396            })
397        }
398    } else {
399        tracing::info!(CVM_ALLOWED, "Key-encryption key retrieval not required");
400
401        // Attestation is unavailable, assume no tenant key
402        Ok(VmgsEncryptionKeys::default())
403    };
404
405    let retry = match &skr_response {
406        Ok(_) => false,
407        Err((_, r)) => *r,
408    };
409
410    let skip_hw_unsealing = matches!(
411        &skr_response,
412        Err((
413            secure_key_release::RequestVmgsEncryptionKeysError::ParseIgvmAttestKeyReleaseResponse(
414                igvm_attest::key_release::KeyReleaseError::ParseHeader(
415                    igvm_attest::Error::Attestation {
416                        skip_hw_unsealing_signal: true,
417                        ..
418                    },
419                ),
420            ),
421            _,
422        ))
423    );
424
425    let VmgsEncryptionKeys {
426        ingress_rsa_kek,
427        wrapped_des_key,
428        key_derivation_svn,
429    } = match skr_response {
430        Ok(k) => {
431            tracing::info!(CVM_ALLOWED, "Successfully retrieved key-encryption key");
432            k
433        }
434        Err((e, _)) => {
435            // Non-fatal, allowing for hardware-based recovery
436            tracing::error!(
437                CVM_ALLOWED,
438                error = &e as &dyn std::error::Error,
439                "Failed to retrieve key-encryption key"
440            );
441
442            VmgsEncryptionKeys::default()
443        }
444    };
445
446    let mut key_protector = if !require_hardware_sealing {
447        // Determine the minimal size of a DEK entry based on whether `wrapped_des_key` is present
448        let dek_minimal_size = if wrapped_des_key.is_some() {
449            key_protector::AES_WRAPPED_AES_KEY_LENGTH
450        } else {
451            key_protector::RSA_WRAPPED_AES_KEY_LENGTH
452        };
453
454        // Read Key Protector blob from VMGS
455        tracing::info!(
456            CVM_ALLOWED,
457            dek_minimal_size = dek_minimal_size,
458            "Reading key protector from VMGS"
459        );
460
461        vmgs::read_key_protector(vmgs, dek_minimal_size)
462            .await
463            .map_err(|e| (AttestationErrorInner::ReadKeyProtector(e), false))?
464    } else {
465        tracing::info!(
466            CVM_ALLOWED,
467            "Hardware sealing is required, skip reading key protector from VMGS"
468        );
469        KeyProtector::new_zeroed()
470    };
471
472    let start_time = std::time::SystemTime::now();
473    let vmgs_encrypted = vmgs.encrypted();
474
475    // Only the `Hash` policy mixes the OpenHCL measurement into the hardware key
476    // derivation; the other policies do not, allowing the sealed key to survive
477    // OpenHCL measurement changes (e.g. servicing).
478    let mix_measurement = matches!(
479        attestation_vm_config.hardware_sealing_policy,
480        HardwareSealingPolicy::Hash
481    );
482
483    let key_derivation_policy = key_derivation_svn.map(|svn| KeyDerivationPolicy {
484        svn,
485        mix_measurement,
486    });
487
488    tracing::info!(
489        CVM_ALLOWED,
490        key_derivation_policy=?key_derivation_policy,
491        vmgs_encrypted,
492        op_type = ?LogOpType::BeginDecryptVmgs,
493        "Deriving keys"
494    );
495
496    let derived_keys_result = get_derived_keys(
497        get,
498        tee_call,
499        vmgs,
500        &mut key_protector,
501        key_protector_by_id,
502        bios_guid,
503        attestation_vm_config,
504        vmgs_encrypted,
505        ingress_rsa_kek.as_ref(),
506        wrapped_des_key.as_deref(),
507        key_derivation_policy,
508        guest_state_encryption_policy,
509        strict_encryption_policy,
510        require_hardware_sealing,
511        skip_hw_unsealing,
512    )
513    .await
514    .map_err(|e| {
515        tracing::error!(
516            CVM_ALLOWED,
517            op_type = ?LogOpType::DecryptVmgs,
518            success = false,
519            err = &e as &dyn std::error::Error,
520            latency = std::time::SystemTime::now()
521                .duration_since(start_time)
522                .map_or(0, |d| d.as_millis()),
523            "Failed to derive keys"
524        );
525        (AttestationErrorInner::GetDerivedKeys(e), retry)
526    })?;
527
528    tracing::info!("Unlocking VMGS");
529
530    // Capture key identity before consuming Keys. A deferred protector is
531    // persisted by unlock_vmgs_data_store; early writes are tracked explicitly.
532    // Neither a preexisting entry nor success in a failed attempt qualifies.
533    let sealed_egress_key = derived_keys_result
534        .derived_keys
535        .as_ref()
536        .filter(|_| {
537            derived_keys_result.hardware_key_protector.is_some()
538                || derived_keys_result.hardware_key_protector_written
539        })
540        .map(|keys| keys.encrypt_egress);
541
542    if let Err(e) = unlock_vmgs_data_store(
543        vmgs,
544        vmgs_encrypted,
545        &mut key_protector,
546        key_protector_by_id,
547        derived_keys_result.hardware_key_protector,
548        derived_keys_result.derived_keys,
549        derived_keys_result.key_protector_settings,
550        bios_guid,
551    )
552    .await
553    {
554        tracing::error!(
555            CVM_ALLOWED,
556            op_type = ?LogOpType::DecryptVmgs,
557            success = false,
558            err = &e as &dyn std::error::Error,
559            latency = std::time::SystemTime::now()
560                .duration_since(start_time)
561                .map_or(0, |d| d.as_millis()),
562            "Failed to unlock datastore"
563        );
564        get.event_log_fatal(guest_emulation_transport::api::EventLogId::ATTESTATION_FAILED)
565            .await;
566
567        Err((AttestationErrorInner::UnlockVmgsDataStore(e), retry))?;
568    }
569
570    // Hardware recovery is essential even in stateful mode if SKR/GSP did
571    // not supply the ingress DEK. Do not treat that path as an optional backup.
572    let sealing_required = require_hardware_sealing
573        || derived_keys_result
574            .key_protector_settings
575            .use_hardware_unlock;
576    let hardware_sealed =
577        match finalize_hardware_sealing(vmgs, sealed_egress_key, sealing_required).await {
578            Ok(sealed) => sealed,
579            Err(error) => {
580                tracing::error!(
581                    CVM_ALLOWED,
582                    op_type = ?LogOpType::DecryptVmgs,
583                    success = false,
584                    error = &error as &dyn std::error::Error,
585                    "Failed to finalize required hardware sealing"
586                );
587                get.event_log_fatal(guest_emulation_transport::api::EventLogId::ATTESTATION_FAILED)
588                    .await;
589                // Unlock may already have rotated the DEK and written metadata.
590                // Do not replay that flow using the SKR retry flag after a flush
591                // failure. Abort boot rather than report unconfirmed durability.
592                return Err((AttestationErrorInner::FinalizeHardwareSealing(error), false));
593            }
594        };
595
596    tracing::info!(
597        CVM_ALLOWED,
598        op_type = ?LogOpType::DecryptVmgs,
599        success = true,
600        decrypt_gsp_type = ?derived_keys_result
601            .key_protector_settings
602            .decrypt_gsp_type,
603        encrypt_gsp_type = ?derived_keys_result
604            .key_protector_settings
605            .encrypt_gsp_type,
606        latency = std::time::SystemTime::now().duration_since(start_time).map_or(0, |d| d.as_millis()),
607        "Unlocked datastore"
608    );
609
610    Ok(UnlockResult {
611        state_refresh_request: derived_keys_result
612            .gsp_extended_status_flags
613            .state_refresh_request(),
614        hardware_sealed,
615    })
616}
617
618/// Check that the sealed DEK is active, then flush completed boot writes.
619/// Required sealing (including hardware-unseal recovery) must fail boot if
620/// identity or durability cannot be confirmed. Optional backup failures only
621/// disable runtime enrollment. No keys are regenerated or writes replayed here.
622async fn finalize_hardware_sealing(
623    vmgs: &mut Vmgs,
624    sealed_egress_key: Option<[u8; AES_GCM_KEY_LENGTH]>,
625    required: bool,
626) -> Result<bool, FinalizeHardwareSealingError> {
627    if sealed_egress_key.is_none() && !required {
628        return Ok(false);
629    }
630    let result = async {
631        let sealed_egress_key =
632            sealed_egress_key.ok_or(FinalizeHardwareSealingError::MissingSealedKey)?;
633        // Old-egress recovery can leave another key active. Compare trusted
634        // in-memory key state, never an untrusted protector header.
635        let active = vmgs
636            .active_encryption_key()
637            .map_err(FinalizeHardwareSealingError::ActiveKey)?;
638        if !constant_time_eq::constant_time_eq_32(active, &sealed_egress_key) {
639            return Err(FinalizeHardwareSealingError::ActiveKeyMismatch);
640        }
641        vmgs.flush()
642            .await
643            .map_err(FinalizeHardwareSealingError::Flush)
644    }
645    .await;
646    match result {
647        Ok(()) => Ok(true),
648        Err(error) if required => Err(error),
649        Err(error) => {
650            tracelimit::warn_ratelimited!(
651                CVM_ALLOWED,
652                error = &error as &dyn std::error::Error,
653                "Failed to finalize optional hardware sealing; runtime hardware resealing disabled"
654            );
655            Ok(false)
656        }
657    }
658}
659
660/// If required, attest platform. Gets VMGS datastore key.
661///
662/// Returns `refresh_tpm_seeds` (the host side GSP service indicating
663/// whether certain state needs to be updated), along with the fully
664/// initialized VMGS client.
665pub async fn initialize_platform_security(
666    get: &GuestEmulationTransportClient,
667    bios_guid: Guid,
668    attestation_vm_config: &AttestationVmConfig,
669    vmgs: &mut Vmgs,
670    tee_call: Option<&dyn TeeCall>,
671    suppress_attestation: bool,
672    driver: LocalDriver,
673    guest_state_encryption_policy: GuestStateEncryptionPolicy,
674    strict_encryption_policy: bool,
675) -> Result<PlatformAttestationData, Error> {
676    const MAXIMUM_RETRY_COUNT: usize = 10;
677    const NO_RETRY_COUNT: usize = 1;
678
679    tracing::info!(CVM_ALLOWED,
680        tee_type=?tee_call.map(|tee| tee.tee_type()),
681        secure_boot=attestation_vm_config.secure_boot,
682        tpm_enabled=attestation_vm_config.tpm_enabled,
683        tpm_persisted=attestation_vm_config.tpm_persisted,
684        // Hardware sealing requires a TEE that can derive keys, so support is
685        // gated on both the TEE being present and exposing `get_derived_key`.
686        hardware_sealing_supported=tee_call.and_then(|tee| tee.supports_get_derived_key()).is_some(),
687        hardware_sealing_policy=?attestation_vm_config.hardware_sealing_policy,
688        "Reading security profile");
689
690    // Read Security Profile from VMGS
691    // Currently this only includes "Key Reference" data, which is not attested data, is opaque to the
692    // OpenHCL, and is passed to the IGVMm agent outside of the report contents.
693    let SecurityProfile { mut agent_data } = vmgs::read_security_profile(vmgs)
694        .await
695        .map_err(AttestationErrorInner::ReadSecurityProfile)?;
696
697    // Hardware sealing is *required* (the only source of the VMGS DEK) when all
698    // hold: the VM is a CVM (tee_call available), it is stateless
699    // (suppress_attestation = true, so SKR is bypassed), the host requests
700    // `HardwareSealing`, and the attested config permits it
701    // (hardware_sealing_policy != None). Host invariant: the host selects
702    // exclusive hardware sealing only in stateless mode paired with
703    // `HardwareSealingPolicy::{Hash, Signer}`. In stateful mode it is at most a
704    // backup recovery path, so this flag stays false.
705    let require_hardware_sealing = tee_call.is_some()
706        && suppress_attestation
707        && matches!(
708            guest_state_encryption_policy,
709            GuestStateEncryptionPolicy::HardwareSealing
710        )
711        && !matches!(
712            attestation_vm_config.hardware_sealing_policy,
713            HardwareSealingPolicy::None
714        );
715
716    // TDX has no signer/identity key-policy register to substitute for `MRTD`
717    // when deriving a measurement-independent key, so signer-based hardware
718    // sealing can never succeed on TDX. Reject the unsupported combination here,
719    // where both the sealing policy and the TEE type are known, so we can fail
720    // closed with a precise diagnostic instead of only tripping the low-level
721    // guard deep in `TdxCall::get_derived_key` (kept as defense in depth).
722    if matches!(tee_call.map(|tee| tee.tee_type()), Some(TeeType::Tdx))
723        && matches!(
724            attestation_vm_config.hardware_sealing_policy,
725            HardwareSealingPolicy::Signer
726        )
727    {
728        tracing::error!(
729            CVM_ALLOWED,
730            "signer-based hardware sealing is not supported on TDX; TDX has no \
731             identity key-policy register to bind a measurement-independent key to"
732        );
733        // Exclusive (stateless) hardware sealing is the only encryption source,
734        // so fail closed. In the stateful (backup) case the error log above
735        // suffices; the low-level `tee_call` guard skips the backup seal.
736        if require_hardware_sealing {
737            return Err(
738                AttestationErrorInner::HardwareSealingRequestedButNotAvailable {
739                    tee_available: tee_call.is_some(),
740                    hardware_sealing_policy: attestation_vm_config.hardware_sealing_policy,
741                }
742                .into(),
743            );
744        }
745    }
746
747    // A stateful (`!suppress_attestation`) request for `HardwareSealing` breaks
748    // the invariant above. We don't fail closed because the normal attestation
749    // flow still encrypts the VMGS (the directive is just downgraded to a
750    // backup); warn so the host bug is observable.
751    if !suppress_attestation
752        && matches!(
753            guest_state_encryption_policy,
754            GuestStateEncryptionPolicy::HardwareSealing
755        )
756    {
757        tracing::warn!(
758            CVM_ALLOWED,
759            ?guest_state_encryption_policy,
760            hardware_sealing_policy = ?attestation_vm_config.hardware_sealing_policy,
761            "host requested exclusive hardware sealing in stateful mode; \
762             ignoring and proceeding with the normal attestation flow"
763        );
764    }
765
766    // Attestation is suppressed and hardware sealing is not required, indicating that VMGS encryption is bypassed.
767    // Skip the attestation flow and return the `agent_data` that is required by TPM AK cert request.
768    if suppress_attestation && !require_hardware_sealing {
769        // This branch is the normal stateless (attestation-suppressed) path when
770        // hardware sealing is not requested. The one exception is when the host
771        // requested `GuestStateEncryptionPolicy::HardwareSealing` but failed to
772        // provide a usable hardware sealing policy on a CVM (the host invariant
773        // above), in which case we fail closed rather than silently downgrading
774        // to no encryption.
775        if matches!(
776            guest_state_encryption_policy,
777            GuestStateEncryptionPolicy::HardwareSealing
778        ) {
779            return Err(
780                AttestationErrorInner::HardwareSealingRequestedButNotAvailable {
781                    tee_available: tee_call.is_some(),
782                    hardware_sealing_policy: attestation_vm_config.hardware_sealing_policy,
783                }
784                .into(),
785            );
786        }
787
788        tracing::info!(
789            CVM_ALLOWED,
790            ?guest_state_encryption_policy,
791            hardware_sealing_policy = ?attestation_vm_config.hardware_sealing_policy,
792            tee_available = tee_call.is_some(),
793            "Suppressing attestation; VMGS encryption is bypassed"
794        );
795
796        return Ok(PlatformAttestationData {
797            host_attestation_settings: HostAttestationSettings {
798                refresh_tpm_seeds: false,
799            },
800            agent_data: Some(agent_data.to_vec()),
801            guest_secret_key: None,
802            runtime_tcb_floor: None,
803        });
804    }
805
806    let (mut key_protector_by_id, vm_id_changed) = if !require_hardware_sealing {
807        // Read VM id from VMGS
808        tracing::info!(CVM_ALLOWED, "Reading VM ID from VMGS");
809        let key_protector_by_id = match vmgs::read_key_protector_by_id(vmgs).await {
810            Ok(key_protector_by_id) => KeyProtectorById {
811                inner: key_protector_by_id,
812                found_id: true,
813            },
814            Err(vmgs::ReadFromVmgsError::EntryNotFound(_)) => KeyProtectorById {
815                inner: openhcl_attestation_protocol::vmgs::KeyProtectorById::new_zeroed(),
816                found_id: false,
817            },
818            Err(e) => { Err(AttestationErrorInner::ReadKeyProtectorById(e)) }?,
819        };
820
821        // Check if the VM id has been changed since last boot with KP write
822        let vm_id_changed = if key_protector_by_id.found_id {
823            let changed = key_protector_by_id.inner.id_guid != bios_guid;
824            if changed {
825                tracing::info!("VM Id has changed since last boot");
826            };
827            changed
828        } else {
829            // Previous id in KP not found means this is the first boot or the GspById
830            // is not provisioned, treat id as unchanged for this case.
831            false
832        };
833
834        (key_protector_by_id, vm_id_changed)
835    } else {
836        // When hardware sealing is required, the key protector by id is not used, and VM id change does not trigger state refresh.
837        (
838            KeyProtectorById {
839                inner: openhcl_attestation_protocol::vmgs::KeyProtectorById::new_zeroed(),
840                found_id: false,
841            },
842            false,
843        )
844    };
845
846    // Retry attestation call-out if necessary (if VMGS encrypted).
847    // The IGVm Agent could be down for servicing, or the TDX service VM might not be ready, or a dynamic firmware
848    // update could mean that the report was not verifiable.
849    let vmgs_encrypted: bool = vmgs.encrypted();
850    let max_retry = if vmgs_encrypted {
851        MAXIMUM_RETRY_COUNT
852    } else {
853        NO_RETRY_COUNT
854    };
855
856    let mut timer = pal_async::timer::PolledTimer::new(&driver);
857    let mut i = 0;
858    // Observe existing reports at their source, independently of SKR success.
859    // Never recreate this collector on retry or request a fallback report.
860    let mut boot_tcb_floor = runtime_sealing::BootTcbFloor::new(tee_call, attestation_vm_config);
861
862    let UnlockResult {
863        state_refresh_request: state_refresh_request_from_gsp,
864        hardware_sealed,
865    } = loop {
866        tracing::info!(CVM_ALLOWED, attempt = i, "attempt to unlock VMGS file");
867
868        let response = try_unlock_vmgs(
869            get,
870            bios_guid,
871            attestation_vm_config,
872            vmgs,
873            tee_call,
874            guest_state_encryption_policy,
875            strict_encryption_policy,
876            require_hardware_sealing,
877            &mut agent_data,
878            &mut key_protector_by_id,
879            &mut boot_tcb_floor,
880        )
881        .await;
882
883        match response {
884            Ok(result) => break result,
885            Err((e, false)) => Err(e)?,
886            Err((e, true)) => {
887                if i >= max_retry - 1 {
888                    Err(e)?
889                }
890            }
891        }
892
893        // Stall on retries
894        timer.sleep(std::time::Duration::new(1, 0)).await;
895        i += 1;
896    };
897
898    let host_attestation_settings = HostAttestationSettings {
899        refresh_tpm_seeds: { state_refresh_request_from_gsp | vm_id_changed },
900    };
901
902    tracing::info!(
903        CVM_ALLOWED,
904        state_refresh_request_from_gsp = state_refresh_request_from_gsp,
905        vm_id_changed = vm_id_changed,
906        "determine if refreshing tpm seeds is needed"
907    );
908
909    // Read guest secret key from unlocked VMGS
910    let guest_secret_key = match vmgs::read_guest_secret_key(vmgs).await {
911        Ok(data) => Some(data.guest_secret_key.to_vec()),
912        Err(vmgs::ReadFromVmgsError::EntryNotFound(_)) => None,
913        Err(e) => return Err(AttestationErrorInner::ReadGuestSecretKey(e).into()),
914    };
915
916    Ok(PlatformAttestationData {
917        host_attestation_settings,
918        agent_data: Some(agent_data.to_vec()),
919        guest_secret_key,
920        runtime_tcb_floor: boot_tcb_floor.finish().filter(|_| hardware_sealed),
921    })
922}
923
924/// Get ingress and egress keys for the VMGS, unlock VMGS,
925/// remove old key if necessary, and update KP.
926/// If key rolling did not complete successfully last time, there may be an
927/// old egress key in the VMGS, whose contents can be controlled by the host.
928/// This key can be used to attempt decryption but must not be used to
929/// re-encrypt the VMGS.
930async fn unlock_vmgs_data_store(
931    vmgs: &mut Vmgs,
932    vmgs_encrypted: bool,
933    key_protector: &mut KeyProtector,
934    key_protector_by_id: &mut KeyProtectorById,
935    hardware_key_protector: Option<HardwareKeyProtectorV3>,
936    derived_keys: Option<Keys>,
937    key_protector_settings: KeyProtectorSettings,
938    bios_guid: Guid,
939) -> Result<(), UnlockVmgsDataStoreError> {
940    let mut new_key = false; // Indicate if we need to add a new key after unlock
941
942    let Some(Keys {
943        ingress: new_ingress_key,
944        decrypt_egress: old_egress_key,
945        encrypt_egress: new_egress_key,
946    }) = derived_keys
947    else {
948        tracing::info!(
949            CVM_ALLOWED,
950            "Encryption disabled, skipping unlock vmgs data store"
951        );
952        return Ok(());
953    };
954
955    if !constant_time_eq::constant_time_eq_32(&new_ingress_key, &new_egress_key) {
956        tracing::trace!(CVM_ALLOWED, "EgressKey is different than IngressKey");
957        new_key = true;
958    }
959
960    // Call unlock_with_encryption_key using ingress_key if datastore is encrypted
961    let mut provision = false;
962    if vmgs_encrypted {
963        tracing::info!(CVM_ALLOWED, "Decrypting vmgs file...");
964        if let Err(e) = vmgs.unlock_with_encryption_key(&new_ingress_key).await {
965            if let Some(key) = old_egress_key {
966                // Key rolling did not complete successfully last time and there's an old
967                // egress key in the VMGS. It may be needed for decryption.
968                tracing::info!(CVM_ALLOWED, "Old EgressKey found");
969                vmgs.unlock_with_encryption_key(&key)
970                    .await
971                    .map_err(UnlockVmgsDataStoreError::VmgsUnlockUsingExistingEgressKey)?;
972            } else {
973                Err(UnlockVmgsDataStoreError::VmgsUnlockUsingExistingIngressKey(
974                    e,
975                ))?
976            }
977        }
978    } else {
979        // The datastore is not encrypted which means it's during provision.
980        tracing::info!(
981            CVM_ALLOWED,
982            "vmgs data store is not encrypted, provisioning."
983        );
984        provision = true;
985    }
986
987    tracing::info!(
988        CVM_ALLOWED,
989        should_write_kp = key_protector_settings.should_write_kp,
990        use_gsp_by_id = key_protector_settings.use_gsp_by_id,
991        use_hardware_unlock = key_protector_settings.use_hardware_unlock,
992        "key protector settings"
993    );
994
995    if key_protector_settings.should_write_kp {
996        // Update on disk KP with all seeds used, to allow for disaster recovery
997        vmgs::write_key_protector(key_protector, vmgs)
998            .await
999            .map_err(UnlockVmgsDataStoreError::WriteKeyProtector)?;
1000
1001        if key_protector_settings.use_gsp_by_id {
1002            vmgs::write_key_protector_by_id(&mut key_protector_by_id.inner, vmgs, false, bios_guid)
1003                .await
1004                .map_err(UnlockVmgsDataStoreError::WriteKeyProtectorById)?;
1005        }
1006    }
1007
1008    if provision || new_key {
1009        // Add the new egress key. If we are not provisioning, then this will
1010        // also remove the old key. This will also remove the inactive key if
1011        // last time we failed to remove it.
1012        vmgs.update_encryption_key(&new_egress_key, EncryptionAlgorithm::AES_GCM)
1013            .await
1014            .map_err(UnlockVmgsDataStoreError::UpdateVmgsEncryptionKey)?;
1015    }
1016
1017    // Persist KP to VMGS
1018    persist_all_key_protectors(
1019        vmgs,
1020        key_protector,
1021        key_protector_by_id,
1022        hardware_key_protector.as_ref(),
1023        bios_guid,
1024        key_protector_settings,
1025    )
1026    .await
1027    .map_err(UnlockVmgsDataStoreError::PersistAllKeyProtectors)
1028}
1029
1030/// Update data store keys with key protectors.
1031///         VMGS encryption can come from combinations of three sources,
1032///         a Tenant Key (KEK), GSP, and GSP By Id.
1033///         There is an Ingress Key (previously used to lock the VMGS),
1034///         and an Egress Key (new key for locking the VMGS), and these
1035///         keys can be derived differently, where KEK is
1036///         always used if available, and GSP is preferred to GSP By Id.
1037///         Ingress                     Possible Egress in order of preference [Ingress]
1038///         - No Encryption             - All
1039///         - GSP By Id                 - KEK + GSP, KEK + GSP By Id, GSP, [GSP By Id]
1040///         - GSP (v10 VM and later)    - KEK + GSP, [GSP]
1041///         - KEK (IVM only)            - KEK + GSP, KEK + GSP By Id, [KEK]
1042///         - KEK + GSP By Id           - KEK + GSP, [KEK + GSP By Id]
1043///         - KEK + GSP                 - [KEK + GSP]
1044///
1045/// NOTE: for TVM parity, only None, Gsp By Id v9.1, and Gsp By Id / Gsp v10.0 are used.
1046#[expect(clippy::fn_params_excessive_bools)]
1047async fn get_derived_keys(
1048    get: &GuestEmulationTransportClient,
1049    tee_call: Option<&dyn TeeCall>,
1050    vmgs: &mut Vmgs,
1051    key_protector: &mut KeyProtector,
1052    key_protector_by_id: &mut KeyProtectorById,
1053    bios_guid: Guid,
1054    attestation_vm_config: &AttestationVmConfig,
1055    is_encrypted: bool,
1056    ingress_rsa_kek: Option<&RsaKeyPair>,
1057    wrapped_des_key: Option<&[u8]>,
1058    key_derivation_policy: Option<KeyDerivationPolicy>,
1059    guest_state_encryption_policy: GuestStateEncryptionPolicy,
1060    strict_encryption_policy: bool,
1061    require_hardware_sealing: bool,
1062    skip_hw_unsealing: bool,
1063) -> Result<DerivedKeyResult, GetDerivedKeysError> {
1064    tracing::info!(
1065        CVM_ALLOWED,
1066        ?guest_state_encryption_policy,
1067        strict_encryption_policy,
1068        "encryption policy"
1069    );
1070
1071    let mut key_protector_settings = KeyProtectorSettings {
1072        should_write_kp: true,
1073        use_gsp_by_id: false,
1074        use_hardware_unlock: false,
1075        decrypt_gsp_type: GspType::None,
1076        encrypt_gsp_type: GspType::None,
1077    };
1078
1079    let mut derived_keys = Keys {
1080        ingress: [0u8; AES_GCM_KEY_LENGTH],
1081        decrypt_egress: None,
1082        encrypt_egress: [0u8; AES_GCM_KEY_LENGTH],
1083    };
1084
1085    // Ingress / Egress seed values depend on what happened previously to the datastore
1086    let ingress_idx = (key_protector.active_kp % 2) as usize;
1087    let egress_idx = if ingress_idx == 0 { 1 } else { 0 } as usize;
1088
1089    let found_dek = !key_protector.dek[ingress_idx]
1090        .dek_buffer
1091        .iter()
1092        .all(|&x| x == 0);
1093
1094    // Handle key released via attestation process (tenant key) to get keys from KeyProtector
1095    let (ingress_key, mut decrypt_egress_key, encrypt_egress_key, no_kek) =
1096        if let Some(ingress_kek) = ingress_rsa_kek {
1097            let keys = match key_protector.unwrap_and_rotate_keys(
1098                ingress_kek,
1099                wrapped_des_key,
1100                ingress_idx,
1101                egress_idx,
1102            ) {
1103                Ok(keys) => keys,
1104                Err(e)
1105                    if matches!(
1106                        e,
1107                        GetKeysFromKeyProtectorError::DesKeyRsaUnwrap(_)
1108                            | GetKeysFromKeyProtectorError::IngressDekRsaUnwrap(_)
1109                    ) =>
1110                {
1111                    get.event_log_fatal(
1112                        guest_emulation_transport::api::EventLogId::DEK_DECRYPTION_FAILED,
1113                    )
1114                    .await;
1115
1116                    return Err(GetDerivedKeysError::GetKeysFromKeyProtector(e));
1117                }
1118                Err(e) => return Err(GetDerivedKeysError::GetKeysFromKeyProtector(e)),
1119            };
1120            (
1121                keys.ingress,
1122                keys.decrypt_egress,
1123                keys.encrypt_egress,
1124                false,
1125            )
1126        } else {
1127            (
1128                [0u8; AES_GCM_KEY_LENGTH],
1129                None,
1130                [0u8; AES_GCM_KEY_LENGTH],
1131                true,
1132            )
1133        };
1134
1135    // Handle various sources of Guest State Protection
1136    let existing_unencrypted = !vmgs.encrypted() && !vmgs.was_provisioned_this_boot();
1137    let is_gsp_by_id = key_protector_by_id.found_id && key_protector_by_id.inner.ported != 1;
1138    let is_gsp = key_protector.gsp[ingress_idx].gsp_length != 0;
1139    tracing::info!(
1140        CVM_ALLOWED,
1141        is_encrypted,
1142        is_gsp_by_id,
1143        is_gsp,
1144        found_dek,
1145        "initial vmgs encryption state"
1146    );
1147    let mut requires_gsp_by_id = is_gsp_by_id;
1148
1149    // Attempt GSP
1150    let (gsp_response, gsp_available, no_gsp, requires_gsp) = if require_hardware_sealing {
1151        // In stateless + hardware sealing mode, the VMGS DEK comes exclusively
1152        // from hardware sealing, and the TPM seeds must remain stable across
1153        // boots. Skip the GSP host callout entirely so that a host-provided
1154        // `state_refresh_request` can never trigger a TPM seed refresh.
1155        tracing::info!(
1156            CVM_ALLOWED,
1157            "Hardware sealing is required, skip GSP callout"
1158        );
1159        (GuestStateProtection::new_zeroed(), false, true, false)
1160    } else {
1161        tracing::info!(CVM_ALLOWED, "attempting GSP");
1162
1163        let response = get_gsp_data(get, key_protector).await;
1164
1165        tracing::info!(
1166            CVM_ALLOWED,
1167            request_data_length_in_vmgs = key_protector.gsp[ingress_idx].gsp_length,
1168            no_rpc_server = response.extended_status_flags.no_rpc_server(),
1169            requires_rpc_server = response.extended_status_flags.requires_rpc_server(),
1170            encrypted_gsp_length = response.encrypted_gsp.length,
1171            "GSP response"
1172        );
1173
1174        let no_gsp_available =
1175            response.extended_status_flags.no_rpc_server() || response.encrypted_gsp.length == 0;
1176
1177        let no_gsp = no_gsp_available
1178            // disable if auto and pre-existing guest state is not encrypted or
1179            // encrypted using GspById to prevent encryption changes without
1180            // explicit intent
1181            || (matches!(
1182                guest_state_encryption_policy,
1183                GuestStateEncryptionPolicy::Auto
1184            ) && (is_gsp_by_id || existing_unencrypted))
1185            // disable per encryption policy (first boot only, unless strict)
1186            || (matches!(
1187                guest_state_encryption_policy,
1188                GuestStateEncryptionPolicy::GspById | GuestStateEncryptionPolicy::None
1189            ) && (!is_gsp || strict_encryption_policy));
1190
1191        let requires_gsp = is_gsp
1192            || response.extended_status_flags.requires_rpc_server()
1193            || (matches!(
1194                guest_state_encryption_policy,
1195                GuestStateEncryptionPolicy::GspKey
1196            ) && strict_encryption_policy);
1197
1198        // If the VMGS is encrypted, but no key protection data is found,
1199        // assume GspById encryption is enabled, but no ID file was written.
1200        if is_encrypted && !requires_gsp_by_id && !requires_gsp && !found_dek {
1201            requires_gsp_by_id = true;
1202        }
1203
1204        (response, !no_gsp_available, no_gsp, requires_gsp)
1205    };
1206
1207    // Attempt GSP By Id protection if GSP is not available, when changing
1208    // schemes, or as requested. Skipped entirely in hardware sealing mode
1209    // for the same reason the GSP callout is skipped above.
1210    let (gsp_response_by_id, gsp_by_id_available, no_gsp_by_id) = if !require_hardware_sealing
1211        && (no_gsp || requires_gsp_by_id)
1212    {
1213        tracing::info!(CVM_ALLOWED, "attempting GSP By Id");
1214
1215        let gsp_response_by_id = get
1216            .guest_state_protection_data_by_id()
1217            .await
1218            .map_err(GetDerivedKeysError::FetchGuestStateProtectionById)?;
1219
1220        let no_gsp_by_id_available = gsp_response_by_id.extended_status_flags.no_registry_file();
1221
1222        let no_gsp_by_id = no_gsp_by_id_available
1223                // disable if auto and pre-existing guest state is unencrypted
1224                // to prevent encryption changes without explicit intent
1225                || (matches!(
1226                    guest_state_encryption_policy,
1227                    GuestStateEncryptionPolicy::Auto
1228                ) && existing_unencrypted)
1229                // disable per encryption policy (first boot only, unless strict)
1230                || (matches!(
1231                    guest_state_encryption_policy,
1232                    GuestStateEncryptionPolicy::None
1233                ) && (!requires_gsp_by_id || strict_encryption_policy));
1234
1235        if no_gsp_by_id && requires_gsp_by_id {
1236            Err(GetDerivedKeysError::GspByIdRequiredButNotFound)?
1237        }
1238
1239        (
1240            gsp_response_by_id,
1241            Some(!no_gsp_by_id_available),
1242            no_gsp_by_id,
1243        )
1244    } else {
1245        (GuestStateProtectionById::new_zeroed(), None, true)
1246    };
1247
1248    // If sources of encryption used last are missing, attempt to unseal VMGS key with hardware key
1249    if (no_kek && found_dek)
1250        || (no_gsp && requires_gsp)
1251        || (no_gsp_by_id && requires_gsp_by_id)
1252        || (require_hardware_sealing && is_encrypted)
1253    {
1254        // If possible, get ingressKey from hardware sealed data
1255        let (hardware_key_protector, hardware_derived_keys) = if let Some(tee_call) = tee_call {
1256            let hardware_key_protector = match vmgs::read_hardware_key_protector(vmgs).await {
1257                Ok(hardware_key_protector) => Some(hardware_key_protector),
1258                Err(e) => {
1259                    // non-fatal
1260                    tracing::warn!(
1261                        CVM_ALLOWED,
1262                        error = &e as &dyn std::error::Error,
1263                        "failed to read HW_KEY_PROTECTOR from Vmgs"
1264                    );
1265                    None
1266                }
1267            };
1268
1269            let hardware_derived_keys = tee_call.supports_get_derived_key().and_then(|tee_call| {
1270                if let Some(hardware_key_protector) = &hardware_key_protector {
1271                    // `key_derivation_policy` returns `None` for formats that
1272                    // cannot be re-derived by this OpenHCL (v1, which always
1273                    // mixes the measurement, or an unknown version/tee-type).
1274                    let Some(policy) = hardware_key_protector.key_derivation_policy() else {
1275                        tracing::error!(
1276                            CVM_ALLOWED,
1277                            version = hardware_key_protector.version(),
1278                            current_version = HW_KEY_PROTECTOR_CURRENT_VERSION,
1279                            "incompatible HW_KEY_PROTECTOR; skip VMGS DEK unsealing with hardware key protector."
1280                        );
1281                        return None;
1282                    };
1283
1284                    match HardwareDerivedKeys::derive_key(tee_call, attestation_vm_config, policy) {
1285                        Ok(hardware_derived_key) => Some(hardware_derived_key),
1286                        Err(e) => {
1287                            // non-fatal
1288                            tracing::warn!(
1289                                CVM_ALLOWED,
1290                                error = &e as &dyn std::error::Error,
1291                                version = hardware_key_protector.version(),
1292                                "failed to derive hardware keys using HW_KEY_PROTECTOR",
1293                            );
1294                            None
1295                        }
1296                    }
1297                } else {
1298                    None
1299                }
1300            });
1301
1302            // When the IGVM agent signals skip_hw_unsealing, set both
1303            // hardware_key_protector and hardware_derived_keys to None
1304            // so the code falls through to the scheme-specific error below.
1305            // When hardware sealing keys were actually available, additionally
1306            // emit a warning and a host event that make the skip visible.
1307            if skip_hw_unsealing {
1308                if hardware_key_protector.is_some() && hardware_derived_keys.is_some() {
1309                    tracing::warn!(
1310                        CVM_ALLOWED,
1311                        "Skipping hardware unsealing of VMGS DEK as signaled by IGVM agent"
1312                    );
1313                    get.event_log_fatal(
1314                        guest_emulation_transport::api::EventLogId::DEK_HARDWARE_UNSEALING_SKIPPED,
1315                    )
1316                    .await;
1317
1318                    (None, None)
1319                } else {
1320                    tracing::info!(
1321                        CVM_ALLOWED,
1322                        hardware_key_protector = hardware_key_protector.is_some(),
1323                        hardware_derived_keys = hardware_derived_keys.is_some(),
1324                        "skip_hw_unsealing signaled but hardware key data not available, \
1325                         falling through to scheme-specific error"
1326                    );
1327                    (None, None)
1328                }
1329            } else {
1330                (hardware_key_protector, hardware_derived_keys)
1331            }
1332        } else {
1333            (None, None)
1334        };
1335
1336        if let (Some(hardware_key_protector), Some(hardware_derived_keys)) =
1337            (hardware_key_protector, hardware_derived_keys)
1338        {
1339            let dek = match hardware_key_protector.unseal_key(&hardware_derived_keys) {
1340                Ok(dek) => dek,
1341                Err(e @ HardwareKeySealingError::HardwareKeyProtectorHmacVerificationFailed)
1342                    if require_hardware_sealing =>
1343                {
1344                    tracing::error!(
1345                        CVM_ALLOWED,
1346                        "hardware unsealing failed due to inconsistent hardware-derived keys"
1347                    );
1348
1349                    get.event_log_fatal(
1350                        guest_emulation_transport::api::EventLogId::DEK_HARDWARE_SEALING_INVALID_KEY,
1351                    )
1352                    .await;
1353
1354                    return Err(GetDerivedKeysError::UnsealIngressKeyUsingHardwareDerivedKeys(e));
1355                }
1356                Err(e) => {
1357                    return Err(GetDerivedKeysError::UnsealIngressKeyUsingHardwareDerivedKeys(e));
1358                }
1359            };
1360
1361            derived_keys.ingress = dek;
1362            derived_keys.decrypt_egress = None;
1363
1364            let hardware_key_protector = if require_hardware_sealing && is_encrypted {
1365                // Generate a new key on every boot for key rotation
1366                let mut new_dek = [0u8; AES_GCM_KEY_LENGTH];
1367                getrandom::fill(&mut new_dek).expect("rng failure");
1368
1369                let updated_hardware_key_protector =
1370                    hardware_key_sealing::seal_key(&hardware_derived_keys, &new_dek)
1371                        .map_err(GetDerivedKeysError::SealEgressKeyUsingHardwareDerivedKeys)?;
1372
1373                derived_keys.encrypt_egress = new_dek;
1374
1375                tracing::info!(
1376                    CVM_ALLOWED,
1377                    "Non-first boot with VMGS hardware sealing mode. Generate a new random key for VMGS DEK rotation."
1378                );
1379
1380                // Use the updated key protector in the exclusive hardware sealing scenario
1381                // to support per-boot key rotation
1382                updated_hardware_key_protector
1383            } else {
1384                derived_keys.encrypt_egress = derived_keys.ingress;
1385
1386                tracing::warn!(
1387                    CVM_ALLOWED,
1388                    "Using hardware-derived key to recover VMGS DEK"
1389                );
1390
1391                // Re-seal the recovered DEK as a v3 protector (also migrates a
1392                // legacy v2 protector to the current format).
1393                hardware_key_sealing::seal_key(&hardware_derived_keys, &derived_keys.ingress)
1394                    .map_err(GetDerivedKeysError::SealEgressKeyUsingHardwareDerivedKeys)?
1395            };
1396
1397            key_protector_settings.should_write_kp = false;
1398            key_protector_settings.use_hardware_unlock = true;
1399
1400            return Ok(DerivedKeyResult {
1401                derived_keys: Some(derived_keys),
1402                key_protector_settings,
1403                gsp_extended_status_flags: gsp_response.extended_status_flags,
1404                hardware_key_protector: Some(hardware_key_protector),
1405                hardware_key_protector_written: false,
1406            });
1407        } else {
1408            if require_hardware_sealing && is_encrypted {
1409                get.event_log_fatal(
1410                    guest_emulation_transport::api::EventLogId::DEK_HARDWARE_SEALING_FAILED,
1411                )
1412                .await;
1413                return Err(GetDerivedKeysError::GetIngressKeyFromHardwareKeyProtectorFailed);
1414            } else if no_kek && found_dek {
1415                return Err(GetDerivedKeysError::GetIngressKeyFromKpFailed);
1416            } else if no_gsp && requires_gsp {
1417                return Err(GetDerivedKeysError::GetIngressKeyFromKGspFailed);
1418            } else {
1419                // no_gsp_by_id && requires_gsp_by_id
1420                return Err(GetDerivedKeysError::GetIngressKeyFromKGspByIdFailed);
1421            }
1422        }
1423    }
1424
1425    tracing::info!(
1426        CVM_ALLOWED,
1427        kek = !no_kek,
1428        gsp_available,
1429        gsp = !no_gsp,
1430        gsp_by_id_available = ?gsp_by_id_available,
1431        gsp_by_id = !no_gsp_by_id,
1432        hw_sealing = require_hardware_sealing,
1433        "Encryption sources"
1434    );
1435
1436    // Attempt to get hardware derived keys
1437    let hardware_derived_keys = tee_call
1438        .and_then(|tee_call| tee_call.supports_get_derived_key())
1439        .and_then(|tee_call| {
1440            if let Some(policy) = key_derivation_policy {
1441                match HardwareDerivedKeys::derive_key(tee_call, attestation_vm_config, policy) {
1442                    Ok(keys) => Some(keys),
1443                    Err(e) => {
1444                        // non-fatal
1445                        tracing::warn!(
1446                            CVM_ALLOWED,
1447                            error = &e as &dyn std::error::Error,
1448                            "failed to derive hardware keys"
1449                        );
1450                        None
1451                    }
1452                }
1453            } else {
1454                None
1455            }
1456        });
1457
1458    // Let hardware sealing take precedence over other sources if it's required
1459    if require_hardware_sealing && !is_encrypted {
1460        let Some(hardware_derived_keys) = hardware_derived_keys else {
1461            get.event_log_fatal(
1462                guest_emulation_transport::api::EventLogId::DEK_HARDWARE_SEALING_FAILED,
1463            )
1464            .await;
1465            return Err(GetDerivedKeysError::HardwareSealingRequiredButNotSupported);
1466        };
1467
1468        let mut new_dek = [0u8; AES_GCM_KEY_LENGTH];
1469        getrandom::fill(&mut new_dek).expect("rng failure");
1470
1471        let hardware_key_protector =
1472            match hardware_key_sealing::seal_key(&hardware_derived_keys, &new_dek) {
1473                Ok(hardware_key_protector) => hardware_key_protector,
1474                Err(e) => {
1475                    get.event_log_fatal(
1476                        guest_emulation_transport::api::EventLogId::DEK_HARDWARE_SEALING_FAILED,
1477                    )
1478                    .await;
1479                    return Err(GetDerivedKeysError::SealEgressKeyUsingHardwareDerivedKeys(
1480                        e,
1481                    ));
1482                }
1483            };
1484
1485        derived_keys.ingress = [0u8; AES_GCM_KEY_LENGTH];
1486        derived_keys.decrypt_egress = None;
1487        derived_keys.encrypt_egress = new_dek;
1488
1489        tracing::info!(
1490            CVM_ALLOWED,
1491            "First boot with VMGS hardware sealing mode. Generate a new random key for VMGS encryption."
1492        );
1493
1494        return Ok(DerivedKeyResult {
1495            derived_keys: Some(derived_keys),
1496            key_protector_settings,
1497            gsp_extended_status_flags: gsp_response.extended_status_flags,
1498            hardware_key_protector: Some(hardware_key_protector),
1499            hardware_key_protector_written: false,
1500        });
1501    }
1502
1503    // Check if sources of encryption are available
1504    if no_kek && no_gsp && no_gsp_by_id {
1505        if is_encrypted {
1506            Err(GetDerivedKeysError::DisableVmgsEncryptionFailed)?
1507        }
1508        match guest_state_encryption_policy {
1509            // fail if some minimum level of encryption was required
1510            GuestStateEncryptionPolicy::GspById
1511            | GuestStateEncryptionPolicy::GspKey
1512            | GuestStateEncryptionPolicy::HardwareSealing => {
1513                Err(GetDerivedKeysError::EncryptionRequiredButNotFound)?
1514            }
1515            GuestStateEncryptionPolicy::Auto | GuestStateEncryptionPolicy::None => {
1516                tracing::info!(CVM_ALLOWED, "No VMGS encryption used.");
1517
1518                return Ok(DerivedKeyResult {
1519                    derived_keys: None,
1520                    key_protector_settings,
1521                    gsp_extended_status_flags: gsp_response.extended_status_flags,
1522                    hardware_key_protector: None,
1523                    hardware_key_protector_written: false,
1524                });
1525            }
1526        }
1527    }
1528
1529    let mut hardware_key_protector_written = false;
1530
1531    // Use tenant key (KEK only)
1532    if no_gsp && no_gsp_by_id {
1533        tracing::info!(CVM_ALLOWED, "No GSP used with SKR");
1534
1535        derived_keys.ingress = ingress_key;
1536        derived_keys.decrypt_egress = decrypt_egress_key;
1537        derived_keys.encrypt_egress = encrypt_egress_key;
1538
1539        if let Some(hardware_derived_keys) = hardware_derived_keys {
1540            let hardware_key_protector = hardware_key_sealing::seal_key(
1541                &hardware_derived_keys,
1542                &derived_keys.encrypt_egress,
1543            )
1544            .map_err(GetDerivedKeysError::SealEgressKeyUsingHardwareDerivedKeys)?;
1545            vmgs::write_hardware_key_protector(&hardware_key_protector, vmgs)
1546                .await
1547                .map_err(GetDerivedKeysError::VmgsWriteHardwareKeyProtector)?;
1548            hardware_key_protector_written = true;
1549
1550            tracing::info!(CVM_ALLOWED, "hardware key protector updated (no GSP used)");
1551        }
1552
1553        return Ok(DerivedKeyResult {
1554            derived_keys: Some(derived_keys),
1555            key_protector_settings,
1556            gsp_extended_status_flags: gsp_response.extended_status_flags,
1557            hardware_key_protector: None,
1558            hardware_key_protector_written,
1559        });
1560    }
1561
1562    // GSP By Id derives
1563    // because key is shared across VMs different context must be used (Id GUID)
1564    if (no_kek && no_gsp) || requires_gsp_by_id {
1565        let derived_keys_by_id =
1566            get_derived_keys_by_id(key_protector_by_id, bios_guid, gsp_response_by_id)
1567                .map_err(GetDerivedKeysError::GetDerivedKeyById)?;
1568
1569        if no_kek && no_gsp {
1570            if matches!(
1571                guest_state_encryption_policy,
1572                GuestStateEncryptionPolicy::GspById | GuestStateEncryptionPolicy::Auto
1573            ) {
1574                tracing::info!(CVM_ALLOWED, "Using GspById");
1575            } else {
1576                // Log a warning here to indicate that the VMGS state is out of
1577                // sync with the VM's configuration.
1578                //
1579                // This should only happen if strict encryption policy is
1580                // disabled and one of the following is true:
1581                // - The VM is configured to have no encryption, but it already
1582                //   has GspById encryption.
1583                // - The VM is configured to use GspKey, but GspKey is not
1584                //   available and GspById is.
1585                tracing::warn!(CVM_ALLOWED, "Allowing GspById");
1586            };
1587
1588            // Not required for Id protection
1589            key_protector_settings.should_write_kp = false;
1590            key_protector_settings.use_gsp_by_id = true;
1591            key_protector_settings.decrypt_gsp_type = GspType::GspById;
1592            key_protector_settings.encrypt_gsp_type = GspType::GspById;
1593
1594            return Ok(DerivedKeyResult {
1595                derived_keys: Some(derived_keys_by_id),
1596                key_protector_settings,
1597                gsp_extended_status_flags: gsp_response.extended_status_flags,
1598                hardware_key_protector: None,
1599                hardware_key_protector_written: false,
1600            });
1601        }
1602
1603        derived_keys.ingress = derived_keys_by_id.ingress;
1604
1605        tracing::info!(
1606            CVM_ALLOWED,
1607            op_type = ?LogOpType::ConvertEncryptionType,
1608            "Converting GSP method."
1609        );
1610    }
1611
1612    let egress_seed;
1613    let mut ingress_seed = None;
1614
1615    // To get to this point, either KEK or GSP must be available
1616    // Mix tenant key with GSP key to create data store encryption keys
1617    // Covers possible egress combinations:
1618    // GSP, GSP + KEK, GSP By Id + KEK
1619
1620    if requires_gsp_by_id || no_gsp {
1621        // If DEK exists, ingress is either KEK or KEK + GSP By Id
1622        // If no DEK, then ingress was Gsp By Id (derived above)
1623        if found_dek {
1624            if requires_gsp_by_id {
1625                ingress_seed = Some(
1626                    gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize]
1627                        .to_vec(),
1628                );
1629                key_protector_settings.decrypt_gsp_type = GspType::GspById;
1630            } else {
1631                derived_keys.ingress = ingress_key;
1632            }
1633        } else {
1634            key_protector_settings.decrypt_gsp_type = GspType::GspById;
1635        }
1636
1637        // Choose best available egress seed
1638        if no_gsp {
1639            egress_seed =
1640                gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize].to_vec();
1641            key_protector_settings.use_gsp_by_id = true;
1642            key_protector_settings.encrypt_gsp_type = GspType::GspById;
1643        } else {
1644            egress_seed =
1645                gsp_response.new_gsp.buffer[..gsp_response.new_gsp.length as usize].to_vec();
1646            key_protector_settings.encrypt_gsp_type = GspType::GspKey;
1647        }
1648    } else {
1649        // `no_gsp` is false, using `gsp_response`
1650
1651        if gsp_response.decrypted_gsp[ingress_idx].length == 0
1652            && gsp_response.decrypted_gsp[egress_idx].length == 0
1653        {
1654            tracing::info!(CVM_ALLOWED, "Applying GSP.");
1655
1656            // VMGS has never had any GSP applied.
1657            // Leave ingress key untouched, derive egress key with new seed.
1658            egress_seed =
1659                gsp_response.new_gsp.buffer[..gsp_response.new_gsp.length as usize].to_vec();
1660
1661            // Ingress key is either zero or tenant only.
1662            // Only copy in the case where a tenant key was released.
1663            if !no_kek {
1664                derived_keys.ingress = ingress_key;
1665            }
1666
1667            key_protector_settings.encrypt_gsp_type = GspType::GspKey;
1668        } else {
1669            tracing::info!(CVM_ALLOWED, "Using existing GSP.");
1670
1671            ingress_seed = Some(
1672                gsp_response.decrypted_gsp[ingress_idx].buffer
1673                    [..gsp_response.decrypted_gsp[ingress_idx].length as usize]
1674                    .to_vec(),
1675            );
1676
1677            if gsp_response.decrypted_gsp[egress_idx].length == 0 {
1678                // Derive ingress with saved seed, derive egress with new seed.
1679                egress_seed =
1680                    gsp_response.new_gsp.buffer[..gsp_response.new_gsp.length as usize].to_vec();
1681            } else {
1682                // System failed during data store unlock, and is in indeterminate state.
1683                // The egress key might have been applied, or the ingress key might be valid.
1684                // Use saved KP, derive ingress/egress keys to attempt recovery.
1685                // Do not update the saved KP with new seed value.
1686                egress_seed = gsp_response.decrypted_gsp[egress_idx].buffer
1687                    [..gsp_response.decrypted_gsp[egress_idx].length as usize]
1688                    .to_vec();
1689                key_protector_settings.should_write_kp = false;
1690                decrypt_egress_key = Some(encrypt_egress_key);
1691            }
1692
1693            key_protector_settings.decrypt_gsp_type = GspType::GspKey;
1694            key_protector_settings.encrypt_gsp_type = GspType::GspKey;
1695        }
1696    }
1697
1698    // Derive key used to lock data store previously
1699    if let Some(seed) = ingress_seed {
1700        derived_keys.ingress = derive_key(&ingress_key, &seed, VMGS_KEY_DERIVE_LABEL)
1701            .map_err(GetDerivedKeysError::DeriveIngressKey)?;
1702    }
1703
1704    // Always derive a new egress key using best available seed
1705    derived_keys.decrypt_egress = decrypt_egress_key
1706        .map(|key| derive_key(&key, &egress_seed, VMGS_KEY_DERIVE_LABEL))
1707        .transpose()
1708        .map_err(GetDerivedKeysError::DeriveEgressKey)?;
1709
1710    derived_keys.encrypt_egress =
1711        derive_key(&encrypt_egress_key, &egress_seed, VMGS_KEY_DERIVE_LABEL)
1712            .map_err(GetDerivedKeysError::DeriveEgressKey)?;
1713
1714    if key_protector_settings.should_write_kp {
1715        // Update with all seeds used, but do not write until data store is unlocked
1716        key_protector.gsp[egress_idx]
1717            .gsp_buffer
1718            .copy_from_slice(&gsp_response.encrypted_gsp.buffer);
1719        key_protector.gsp[egress_idx].gsp_length = gsp_response.encrypted_gsp.length;
1720
1721        if let Some(hardware_derived_keys) = hardware_derived_keys {
1722            let hardware_key_protector = hardware_key_sealing::seal_key(
1723                &hardware_derived_keys,
1724                &derived_keys.encrypt_egress,
1725            )
1726            .map_err(GetDerivedKeysError::SealEgressKeyUsingHardwareDerivedKeys)?;
1727
1728            vmgs::write_hardware_key_protector(&hardware_key_protector, vmgs)
1729                .await
1730                .map_err(GetDerivedKeysError::VmgsWriteHardwareKeyProtector)?;
1731            hardware_key_protector_written = true;
1732
1733            tracing::info!(CVM_ALLOWED, "hardware key protector updated");
1734        }
1735    }
1736
1737    if matches!(
1738        guest_state_encryption_policy,
1739        GuestStateEncryptionPolicy::GspKey | GuestStateEncryptionPolicy::Auto
1740    ) {
1741        tracing::info!(CVM_ALLOWED, "Using Gsp");
1742    } else {
1743        // Log a warning here to indicate that the VMGS state is out of
1744        // sync with the VM's configuration.
1745        //
1746        // This should only happen if the VM is configured to have no
1747        // encryption or GspById encryption, but it already has GspKey
1748        // encryption and strict encryption policy is disabled.
1749        tracing::warn!(CVM_ALLOWED, "Allowing Gsp");
1750    }
1751
1752    Ok(DerivedKeyResult {
1753        derived_keys: Some(derived_keys),
1754        key_protector_settings,
1755        gsp_extended_status_flags: gsp_response.extended_status_flags,
1756        hardware_key_protector: None,
1757        hardware_key_protector_written,
1758    })
1759}
1760
1761/// Update data store keys with key protectors based on VmUniqueId & host seed.
1762fn get_derived_keys_by_id(
1763    key_protector_by_id: &mut KeyProtectorById,
1764    bios_guid: Guid,
1765    gsp_response_by_id: GuestStateProtectionById,
1766) -> Result<Keys, GetDerivedKeysByIdError> {
1767    // This does not handle tenant encrypted VMGS files or Isolated VM,
1768    // or the case where an unlock/relock fails and a snapshot is
1769    // made from that file (the Id cannot change in that failure path).
1770    // When converted to a later scheme, Egress Key will be overwritten.
1771
1772    // Always derive a new egress key from current VmUniqueId
1773    let new_egress_key = derive_key(
1774        &gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize],
1775        bios_guid.as_bytes(),
1776        VMGS_KEY_DERIVE_LABEL,
1777    )
1778    .map_err(GetDerivedKeysByIdError::DeriveEgressKeyUsingCurrentVmId)?;
1779
1780    if new_egress_key.len() != AES_GCM_KEY_LENGTH {
1781        Err(GetDerivedKeysByIdError::InvalidDerivedEgressKeySize {
1782            key_size: new_egress_key.len(),
1783            expected_size: AES_GCM_KEY_LENGTH,
1784        })?
1785    }
1786
1787    // Ingress values depend on what happened previously to the datastore.
1788    // If not previously encrypted (no saved Id), then Ingress Key not required.
1789    let new_ingress_key = if key_protector_by_id.inner.id_guid != Guid::default() {
1790        // Derive key used to lock data store previously
1791        derive_key(
1792            &gsp_response_by_id.seed.buffer[..gsp_response_by_id.seed.length as usize],
1793            key_protector_by_id.inner.id_guid.as_bytes(),
1794            VMGS_KEY_DERIVE_LABEL,
1795        )
1796        .map_err(GetDerivedKeysByIdError::DeriveIngressKeyUsingKeyProtectorId)?
1797    } else {
1798        // If data store is not encrypted, Ingress should equal Egress
1799        new_egress_key
1800    };
1801
1802    if new_ingress_key.len() != AES_GCM_KEY_LENGTH {
1803        Err(GetDerivedKeysByIdError::InvalidDerivedIngressKeySize {
1804            key_size: new_ingress_key.len(),
1805            expected_size: AES_GCM_KEY_LENGTH,
1806        })?
1807    }
1808
1809    Ok(Keys {
1810        ingress: new_ingress_key,
1811        decrypt_egress: None,
1812        encrypt_egress: new_egress_key,
1813    })
1814}
1815
1816/// Prepare the request payload and request GSP from the host via GET.
1817async fn get_gsp_data(
1818    get: &GuestEmulationTransportClient,
1819    key_protector: &mut KeyProtector,
1820) -> GuestStateProtection {
1821    use openhcl_attestation_protocol::vmgs::GSP_BUFFER_SIZE;
1822    use openhcl_attestation_protocol::vmgs::NUMBER_KP;
1823
1824    const_assert_eq!(guest_emulation_transport::api::NUMBER_GSP, NUMBER_KP as u32);
1825    const_assert_eq!(
1826        guest_emulation_transport::api::GSP_CIPHERTEXT_MAX,
1827        GSP_BUFFER_SIZE as u32
1828    );
1829
1830    let mut encrypted_gsp =
1831        [guest_emulation_transport::api::GspCiphertextContent::new_zeroed(); NUMBER_KP];
1832
1833    for (i, gsp) in encrypted_gsp.iter_mut().enumerate().take(NUMBER_KP) {
1834        if key_protector.gsp[i].gsp_length == 0 {
1835            continue;
1836        }
1837
1838        gsp.buffer[..key_protector.gsp[i].gsp_length as usize].copy_from_slice(
1839            &key_protector.gsp[i].gsp_buffer[..key_protector.gsp[i].gsp_length as usize],
1840        );
1841
1842        gsp.length = key_protector.gsp[i].gsp_length;
1843    }
1844
1845    get.guest_state_protection_data(encrypted_gsp, GspExtendedStatusFlags::new())
1846        .await
1847}
1848
1849/// Update Key Protector to remove 2nd protector, and write to VMGS
1850async fn persist_all_key_protectors(
1851    vmgs: &mut Vmgs,
1852    key_protector: &mut KeyProtector,
1853    key_protector_by_id: &mut KeyProtectorById,
1854    hardware_key_protector: Option<&HardwareKeyProtectorV3>,
1855    bios_guid: Guid,
1856    key_protector_settings: KeyProtectorSettings,
1857) -> Result<(), PersistAllKeyProtectorsError> {
1858    use openhcl_attestation_protocol::vmgs::NUMBER_KP;
1859
1860    if key_protector_settings.use_gsp_by_id && !key_protector_settings.should_write_kp {
1861        vmgs::write_key_protector_by_id(&mut key_protector_by_id.inner, vmgs, false, bios_guid)
1862            .await
1863            .map_err(PersistAllKeyProtectorsError::KeyProtectorById)?;
1864    } else {
1865        // When a hardware key protector is present, the VMGS DEK is sealed by the
1866        // hardware-derived key (either recovered via hardware unsealing, or
1867        // freshly generated/rotated in exclusive hardware sealing mode). In that
1868        // case persist the hardware key protector instead of altering the regular
1869        // key protector.
1870        if let Some(hardware_key_protector) = hardware_key_protector {
1871            vmgs::write_hardware_key_protector(hardware_key_protector, vmgs)
1872                .await
1873                .map_err(PersistAllKeyProtectorsError::HardwareKeyProtector)?;
1874        } else {
1875            // Remove ingress KP & DEK, no longer applies to data store
1876            key_protector.dek[key_protector.active_kp as usize % NUMBER_KP]
1877                .dek_buffer
1878                .fill(0);
1879            key_protector.gsp[key_protector.active_kp as usize % NUMBER_KP].gsp_length = 0;
1880            key_protector.active_kp += 1;
1881
1882            vmgs::write_key_protector(key_protector, vmgs)
1883                .await
1884                .map_err(PersistAllKeyProtectorsError::KeyProtector)?;
1885        }
1886
1887        // Update Id data to indicate this scheme is no longer in use
1888        if !key_protector_settings.use_gsp_by_id
1889            && key_protector_by_id.found_id
1890            && key_protector_by_id.inner.ported == 0
1891        {
1892            key_protector_by_id.inner.ported = 1;
1893            vmgs::write_key_protector_by_id(&mut key_protector_by_id.inner, vmgs, true, bios_guid)
1894                .await
1895                .map_err(PersistAllKeyProtectorsError::KeyProtectorById)?;
1896        }
1897    }
1898
1899    Ok(())
1900}
1901
1902#[derive(Debug, Serialize, Deserialize)]
1903struct ProvenanceJwtBody {
1904    #[serde(rename = "VMGSID")]
1905    pub vmgsid: String,
1906}
1907
1908/// Read the VMGS provenance doc and produce runtime claims
1909pub fn get_provenance_claims(prov_file: &[u8]) -> Result<VmgsProvisioner, Error> {
1910    let jwt = JwtHelper::<ProvenanceJwtBody>::from(prov_file)
1911        .map_err(ProvenanceError::DecodeProvenanceDoc)
1912        .map_err(AttestationErrorInner::Provenance)?;
1913    let valid = jwt
1914        .verify_signature()
1915        .map_err(ProvenanceError::VerifySignature)
1916        .map_err(AttestationErrorInner::Provenance)?;
1917
1918    if !valid {
1919        return Err(Error(AttestationErrorInner::Provenance(
1920            ProvenanceError::InvalidSignature,
1921        )));
1922    }
1923
1924    let cert_chain = jwt
1925        .cert_chain()
1926        .map_err(ProvenanceError::DecodeProvenanceDoc)
1927        .map_err(AttestationErrorInner::Provenance)?;
1928    let leaf = &cert_chain[0];
1929
1930    let sn = leaf
1931        .subject_common_name()
1932        .map_err(ProvenanceError::X509Error)
1933        .map_err(AttestationErrorInner::Provenance)?
1934        .ok_or(AttestationErrorInner::Provenance(
1935            ProvenanceError::MissingLeafCertSubjectName,
1936        ))?;
1937
1938    let root = cert_chain.last().ok_or(AttestationErrorInner::Provenance(
1939        ProvenanceError::InvalidRootCert,
1940    ))?;
1941    let digest = sha_256(
1942        &(root
1943            .to_der()
1944            .map_err(ProvenanceError::X509Error)
1945            .map_err(AttestationErrorInner::Provenance)?),
1946    );
1947    let signer = format!(
1948        "did:x509:0:sha256:{}:subject:{}",
1949        hex::encode_upper(digest),
1950        sn
1951    );
1952    let vmgsid = jwt.jwt.body.vmgsid;
1953
1954    Ok(VmgsProvisioner {
1955        id: Guid::parse(vmgsid.as_bytes())
1956            .map_err(ProvenanceError::ParseVmgsid)
1957            .map_err(AttestationErrorInner::Provenance)?,
1958        signer,
1959    })
1960}
1961
1962/// Derive the expected VMGSID from the encrypted seed data.
1963pub fn derive_vmgsid(seed_file: &[u8]) -> Result<Guid, Error> {
1964    let seed_file_str = str::from_utf8(seed_file)
1965        .map_err(ProvenanceError::InvalidVmgsidData)
1966        .map_err(AttestationErrorInner::Provenance)?;
1967
1968    // The seed file has four fields separated by commas, but the fourth field
1969    // is just the length of the first field. Ignore any fields beyond the first
1970    // three (so the provisioning service can change the format later without
1971    // breaking anything).
1972    let parts = seed_file_str
1973        .split(',')
1974        .map(|s| s.trim())
1975        .collect::<Vec<&str>>();
1976    if parts.len() < 3 {
1977        Err(AttestationErrorInner::Provenance(
1978            ProvenanceError::ParseVmgsidSeedData,
1979        ))?;
1980    }
1981
1982    let seed = hex::decode(parts[0])
1983        .map_err(ProvenanceError::DecodeVmgsidData)
1984        .map_err(AttestationErrorInner::Provenance)?;
1985    let label = hex::decode(parts[1])
1986        .map_err(ProvenanceError::DecodeVmgsidData)
1987        .map_err(AttestationErrorInner::Provenance)?;
1988    let context = hex::decode(parts[2])
1989        .map_err(ProvenanceError::DecodeVmgsidData)
1990        .map_err(AttestationErrorInner::Provenance)?;
1991
1992    let key = crypto::kbkdf::kbkdf_hmac(&seed, &context, &label, 32, crypto::HashAlgorithm::Sha256)
1993        .map_err(ProvenanceError::KdfError)
1994        .map_err(AttestationErrorInner::Provenance)?;
1995
1996    Ok(Guid::from_slice(&key[0..16].try_into().unwrap()))
1997}
1998
1999/// Module that implements the mock [`TeeCall`] for testing purposes
2000#[cfg(test)]
2001pub mod test_utils {
2002    use tee_call::GetAttestationReportResult;
2003    use tee_call::HW_DERIVED_KEY_LENGTH;
2004    use tee_call::KeyDerivationPolicy;
2005    use tee_call::REPORT_DATA_SIZE;
2006    use tee_call::TeeCall;
2007    use tee_call::TeeCallGetDerivedKey;
2008    use tee_call::TeeType;
2009
2010    /// Mock implementation of [`TeeCall`] with get derived key support for testing purposes
2011    pub struct MockTeeCall {
2012        /// Mock measurement data
2013        pub measurement: [u8; 32],
2014        /// Mock TCB version returned in attestation reports
2015        pub tcb_version: u64,
2016    }
2017
2018    impl MockTeeCall {
2019        /// Create a new instance of [`MockTeeCall`].
2020        pub fn new(measurement: [u8; 32]) -> Self {
2021            Self {
2022                measurement,
2023                tcb_version: 0x1234,
2024            }
2025        }
2026
2027        /// Update the mock measurement data.
2028        pub fn update_measurement(&mut self, measurement: [u8; 32]) {
2029            self.measurement = measurement;
2030        }
2031    }
2032
2033    impl TeeCall for MockTeeCall {
2034        fn get_attestation_report(
2035            &self,
2036            report_data: &[u8; REPORT_DATA_SIZE],
2037        ) -> Result<GetAttestationReportResult, tee_call::Error> {
2038            let mut report =
2039                [0x6c; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
2040            report[..REPORT_DATA_SIZE].copy_from_slice(report_data);
2041
2042            Ok(GetAttestationReportResult {
2043                report: report.to_vec(),
2044                key_derivation_svn: Some(tee_call::KeyDerivationSvn::Snp {
2045                    tcb_version: self.tcb_version,
2046                }),
2047            })
2048        }
2049
2050        fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
2051            Some(self)
2052        }
2053
2054        fn tee_type(&self) -> TeeType {
2055            // Use Snp for testing
2056            TeeType::Snp
2057        }
2058    }
2059
2060    impl TeeCallGetDerivedKey for MockTeeCall {
2061        fn get_derived_key(
2062            &self,
2063            policy: KeyDerivationPolicy,
2064        ) -> Result<[u8; 32], tee_call::Error> {
2065            // Base test key; mix in policy so different policies yield different derived secrets
2066            let mut key: [u8; HW_DERIVED_KEY_LENGTH] = [0xab; HW_DERIVED_KEY_LENGTH];
2067
2068            // Mock is SNP; mix in the recorded TCB version.
2069            let tcb_version = match policy.svn {
2070                tee_call::KeyDerivationSvn::Snp { tcb_version } => tcb_version,
2071                tee_call::KeyDerivationSvn::Tdx { .. } => 0,
2072            };
2073            let tcb = tcb_version.to_le_bytes();
2074            for (i, b) in key.iter_mut().enumerate() {
2075                if policy.mix_measurement {
2076                    *b ^= self.measurement[i] ^ tcb[i % tcb.len()];
2077                } else {
2078                    *b ^= tcb[i % tcb.len()];
2079                }
2080            }
2081
2082            Ok(key)
2083        }
2084    }
2085
2086    /// Mock implementation of [`TeeCall`] without get derived key support for testing purposes
2087    pub struct MockTeeCallNoGetDerivedKey;
2088
2089    impl TeeCall for MockTeeCallNoGetDerivedKey {
2090        fn get_attestation_report(
2091            &self,
2092            report_data: &[u8; REPORT_DATA_SIZE],
2093        ) -> Result<GetAttestationReportResult, tee_call::Error> {
2094            let mut report =
2095                [0x6c; openhcl_attestation_protocol::igvm_attest::get::SNP_VM_REPORT_SIZE];
2096            report[..REPORT_DATA_SIZE].copy_from_slice(report_data);
2097
2098            Ok(GetAttestationReportResult {
2099                report: report.to_vec(),
2100                key_derivation_svn: None,
2101            })
2102        }
2103
2104        fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
2105            None
2106        }
2107
2108        fn tee_type(&self) -> TeeType {
2109            // Use Snp for testing
2110            TeeType::Snp
2111        }
2112    }
2113}
2114
2115#[cfg(test)]
2116mod tests {
2117    mod flush_fault;
2118
2119    use super::*;
2120    use crate::test_utils::MockTeeCallNoGetDerivedKey;
2121    use disk_backend::Disk;
2122    use disklayer_ram::ram_disk;
2123    use get_protocol::GSP_CLEARTEXT_MAX;
2124    use get_protocol::GspExtendedStatusFlags;
2125    use guest_emulation_device::IgvmAgentAction;
2126    use guest_emulation_device::IgvmAgentTestPlan;
2127    use guest_emulation_device::test_utilities::Event;
2128    use guest_emulation_device::test_utilities::TestGetResponses;
2129    use guest_emulation_transport::test_utilities::TestGet;
2130    use key_protector::AES_WRAPPED_AES_KEY_LENGTH;
2131    use openhcl_attestation_protocol::igvm_attest::get::IgvmAttestRequestType;
2132    use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationTpmVersion;
2133    use openhcl_attestation_protocol::vmgs::DEK_BUFFER_SIZE;
2134    use openhcl_attestation_protocol::vmgs::DekKp;
2135    use openhcl_attestation_protocol::vmgs::GSP_BUFFER_SIZE;
2136    use openhcl_attestation_protocol::vmgs::GspKp;
2137    use openhcl_attestation_protocol::vmgs::NUMBER_KP;
2138    use pal_async::DefaultDriver;
2139    use pal_async::async_test;
2140    use pal_async::task::Spawn;
2141    use std::collections::VecDeque;
2142    use test_utils::MockTeeCall;
2143    use test_with_tracing::test;
2144    use vmgs_format::EncryptionAlgorithm;
2145    use vmgs_format::FileId;
2146
2147    const ONE_MEGA_BYTE: u64 = 1024 * 1024;
2148
2149    fn test_attestation_config() -> AttestationVmConfig {
2150        AttestationVmConfig {
2151            current_time: None,
2152            root_cert_thumbprint: String::new(),
2153            console_enabled: false,
2154            interactive_console_enabled: false,
2155            ipmi_enabled: false,
2156            secure_boot: false,
2157            tpm_enabled: true,
2158            tpm_version: AttestationTpmVersion::V138,
2159            tpm_persisted: true,
2160            filtered_vpci_devices_allowed: false,
2161            vm_unique_id: String::new(),
2162            vmgs_provisioner: None,
2163            hardware_sealing_policy: HardwareSealingPolicy::None,
2164        }
2165    }
2166
2167    /// Models a trusted local SNP report with real ABI offsets, independently
2168    /// of the deliberately opaque reports used by the older SKR fixtures.
2169    struct BootReportTee {
2170        inner: MockTeeCall,
2171        report_data: parking_lot::Mutex<Vec<[u8; REPORT_DATA_SIZE]>>,
2172        malformed: bool,
2173        fail_derivation: bool,
2174        derivation_calls: parking_lot::Mutex<usize>,
2175    }
2176
2177    impl BootReportTee {
2178        fn new() -> Self {
2179            Self {
2180                inner: MockTeeCall::new([0x12; 32]),
2181                report_data: parking_lot::Mutex::new(Vec::new()),
2182                malformed: false,
2183                fail_derivation: false,
2184                derivation_calls: parking_lot::Mutex::new(0),
2185            }
2186        }
2187    }
2188
2189    impl TeeCall for BootReportTee {
2190        fn get_attestation_report(
2191            &self,
2192            report_data: &[u8; REPORT_DATA_SIZE],
2193        ) -> Result<tee_call::GetAttestationReportResult, tee_call::Error> {
2194            self.report_data.lock().push(*report_data);
2195            let mut report = vec![0; 1184];
2196            report[..4].copy_from_slice(&3u32.to_le_bytes());
2197            report[0x50..0x50 + REPORT_DATA_SIZE].copy_from_slice(report_data);
2198            report[0x180..0x188].copy_from_slice(&self.inner.tcb_version.to_le_bytes());
2199            report[0x188] = 0x19;
2200            if self.malformed {
2201                report.truncate(1183);
2202            }
2203            Ok(tee_call::GetAttestationReportResult {
2204                report,
2205                key_derivation_svn: Some(tee_call::KeyDerivationSvn::Snp {
2206                    tcb_version: self.inner.tcb_version,
2207                }),
2208            })
2209        }
2210
2211        fn supports_get_derived_key(&self) -> Option<&dyn tee_call::TeeCallGetDerivedKey> {
2212            Some(self)
2213        }
2214
2215        fn tee_type(&self) -> TeeType {
2216            TeeType::Snp
2217        }
2218    }
2219
2220    impl tee_call::TeeCallGetDerivedKey for BootReportTee {
2221        fn get_derived_key(
2222            &self,
2223            policy: KeyDerivationPolicy,
2224        ) -> Result<[u8; 32], tee_call::Error> {
2225            *self.derivation_calls.lock() += 1;
2226            if self.fail_derivation {
2227                return Err(tee_call::Error::AllZeroKey);
2228            }
2229            self.inner
2230                .supports_get_derived_key()
2231                .unwrap()
2232                .get_derived_key(policy)
2233        }
2234    }
2235
2236    /// Models LM at report return, before the first hardware derivation. The
2237    /// source supplies the trusted report; all subsequent derivations run on
2238    /// the destination. This is an ordering model, not a real migration.
2239    struct FirstDerivationMigrationTee {
2240        source: BootReportTee,
2241        destination: BootReportTee,
2242        derivation_policies: parking_lot::Mutex<Vec<KeyDerivationPolicy>>,
2243    }
2244
2245    impl FirstDerivationMigrationTee {
2246        fn new() -> Self {
2247            let mut destination = BootReportTee::new();
2248            // MockTeeCall mixes this context into the actual derived bytes.
2249            // Use it to model a different hardware secret, not a change to the
2250            // guest image or attested VM configuration during LM.
2251            destination.inner.update_measurement([0x34; 32]);
2252            Self {
2253                source: BootReportTee::new(),
2254                destination,
2255                derivation_policies: parking_lot::Mutex::new(Vec::new()),
2256            }
2257        }
2258    }
2259
2260    impl TeeCall for FirstDerivationMigrationTee {
2261        fn get_attestation_report(
2262            &self,
2263            report_data: &[u8; REPORT_DATA_SIZE],
2264        ) -> Result<tee_call::GetAttestationReportResult, tee_call::Error> {
2265            assert!(
2266                self.source.report_data.lock().is_empty(),
2267                "extra boot report"
2268            );
2269            assert!(self.derivation_policies.lock().is_empty());
2270            assert_eq!(*self.source.derivation_calls.lock(), 0);
2271            assert_eq!(*self.destination.derivation_calls.lock(), 0);
2272            self.source.get_attestation_report(report_data)
2273        }
2274
2275        fn supports_get_derived_key(&self) -> Option<&dyn tee_call::TeeCallGetDerivedKey> {
2276            Some(self)
2277        }
2278
2279        fn tee_type(&self) -> TeeType {
2280            TeeType::Snp
2281        }
2282    }
2283
2284    impl tee_call::TeeCallGetDerivedKey for FirstDerivationMigrationTee {
2285        fn get_derived_key(
2286            &self,
2287            policy: KeyDerivationPolicy,
2288        ) -> Result<[u8; 32], tee_call::Error> {
2289            assert_eq!(&*self.source.report_data.lock(), &[[0; REPORT_DATA_SIZE]]);
2290            assert!(self.destination.report_data.lock().is_empty());
2291            self.derivation_policies.lock().push(policy);
2292            // This destination fixture supports only its own SVN. Inject a
2293            // TEE error for the unsupported source SVN; MockTeeCall otherwise
2294            // accepts arbitrary requested SVNs without checking its local TCB.
2295            if !matches!(policy.svn, tee_call::KeyDerivationSvn::Snp { tcb_version }
2296                if tcb_version == self.destination.inner.tcb_version)
2297            {
2298                return Err(tee_call::Error::AllZeroKey);
2299            }
2300            self.destination
2301                .supports_get_derived_key()
2302                .unwrap()
2303                .get_derived_key(policy)
2304        }
2305    }
2306
2307    fn new_test_file() -> Disk {
2308        ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
2309    }
2310
2311    /// Supplies exactly one trusted report per unlock attempt, while retaining
2312    /// the existing mock's hardware-key derivation and claims-hash recording.
2313    struct SequencedBootReportTee {
2314        inner: BootReportTee,
2315        report_svns: parking_lot::Mutex<VecDeque<u64>>,
2316    }
2317
2318    impl TeeCall for SequencedBootReportTee {
2319        fn get_attestation_report(
2320            &self,
2321            report_data: &[u8; REPORT_DATA_SIZE],
2322        ) -> Result<tee_call::GetAttestationReportResult, tee_call::Error> {
2323            let tcb_version = self
2324                .report_svns
2325                .lock()
2326                .pop_front()
2327                .expect("unexpected extra boot report acquisition");
2328            let mut result = self.inner.get_attestation_report(report_data)?;
2329            result.report[0x180..0x188].copy_from_slice(&tcb_version.to_le_bytes());
2330            result.key_derivation_svn = Some(tee_call::KeyDerivationSvn::Snp { tcb_version });
2331            Ok(result)
2332        }
2333
2334        fn supports_get_derived_key(&self) -> Option<&dyn tee_call::TeeCallGetDerivedKey> {
2335            self.inner.supports_get_derived_key()
2336        }
2337
2338        fn tee_type(&self) -> TeeType {
2339            self.inner.tee_type()
2340        }
2341    }
2342
2343    async fn new_formatted_vmgs() -> Vmgs {
2344        let disk = new_test_file();
2345
2346        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2347
2348        assert!(
2349            key_protector_is_empty(&mut vmgs).await,
2350            "Newly formatted VMGS should have an empty key protector"
2351        );
2352        assert!(
2353            key_protector_by_id_is_empty(&mut vmgs).await,
2354            "Newly formatted VMGS should have an empty key protector by id"
2355        );
2356
2357        vmgs
2358    }
2359
2360    async fn key_protector_is_empty(vmgs: &mut Vmgs) -> bool {
2361        let key_protector = vmgs::read_key_protector(vmgs, AES_WRAPPED_AES_KEY_LENGTH)
2362            .await
2363            .unwrap();
2364
2365        key_protector.as_bytes().iter().all(|&b| b == 0)
2366    }
2367
2368    async fn key_protector_by_id_is_empty(vmgs: &mut Vmgs) -> bool {
2369        vmgs::read_key_protector_by_id(vmgs)
2370            .await
2371            .is_err_and(|err| {
2372                matches!(
2373                    err,
2374                    vmgs::ReadFromVmgsError::EntryNotFound(FileId::VM_UNIQUE_ID)
2375                )
2376            })
2377    }
2378
2379    async fn hardware_key_protector_is_empty(vmgs: &mut Vmgs) -> bool {
2380        vmgs::read_hardware_key_protector(vmgs)
2381            .await
2382            .is_err_and(|err| {
2383                matches!(
2384                    err,
2385                    vmgs::ReadFromVmgsError::EntryNotFound(FileId::HW_KEY_PROTECTOR)
2386                )
2387            })
2388    }
2389
2390    fn new_key_protector() -> KeyProtector {
2391        // Ingress and egress KPs are assumed to be the only two KPs, therefore `NUMBER_KP` should be 2
2392        assert_eq!(NUMBER_KP, 2);
2393
2394        let ingress_dek = DekKp {
2395            dek_buffer: [1; DEK_BUFFER_SIZE],
2396        };
2397        let egress_dek = DekKp {
2398            dek_buffer: [2; DEK_BUFFER_SIZE],
2399        };
2400        let ingress_gsp = GspKp {
2401            gsp_length: GSP_BUFFER_SIZE as u32,
2402            gsp_buffer: [3; GSP_BUFFER_SIZE],
2403        };
2404        let egress_gsp = GspKp {
2405            gsp_length: GSP_BUFFER_SIZE as u32,
2406            gsp_buffer: [4; GSP_BUFFER_SIZE],
2407        };
2408        KeyProtector {
2409            dek: [ingress_dek, egress_dek],
2410            gsp: [ingress_gsp, egress_gsp],
2411            active_kp: 0,
2412        }
2413    }
2414
2415    fn new_key_protector_by_id(
2416        id_guid: Option<Guid>,
2417        ported: Option<u8>,
2418        found_id: bool,
2419    ) -> KeyProtectorById {
2420        let key_protector_by_id = openhcl_attestation_protocol::vmgs::KeyProtectorById {
2421            id_guid: id_guid.unwrap_or_else(Guid::new_random),
2422            ported: ported.unwrap_or(0),
2423            pad: [0; 3],
2424        };
2425
2426        KeyProtectorById {
2427            inner: key_protector_by_id,
2428            found_id,
2429        }
2430    }
2431
2432    async fn new_test_get(
2433        spawn: impl Spawn,
2434        enable_igvm_attest: bool,
2435        plan: Option<IgvmAgentTestPlan>,
2436    ) -> TestGet {
2437        if enable_igvm_attest {
2438            const TEST_DEVICE_MEMORY_SIZE: u64 = 64;
2439            // Use `DeviceTestMemory` to set up shared memory required by the IGVM_ATTEST GET calls.
2440            let dev_test_mem = user_driver_emulated_mock::DeviceTestMemory::new(
2441                TEST_DEVICE_MEMORY_SIZE,
2442                true,
2443                "test-attest",
2444            );
2445
2446            let mut test_get = guest_emulation_transport::test_utilities::new_transport_pair(
2447                spawn,
2448                None,
2449                get_protocol::ProtocolVersion::NICKEL_REV2,
2450                Some(dev_test_mem.guest_memory()),
2451                plan,
2452            )
2453            .await;
2454
2455            test_get.client.set_gpa_allocator(dev_test_mem.dma_client());
2456
2457            test_get
2458        } else {
2459            guest_emulation_transport::test_utilities::new_transport_pair(
2460                spawn,
2461                None,
2462                get_protocol::ProtocolVersion::NICKEL_REV2,
2463                None,
2464                None,
2465            )
2466            .await
2467        }
2468    }
2469
2470    fn new_attestation_vm_config() -> AttestationVmConfig {
2471        AttestationVmConfig {
2472            current_time: None,
2473            root_cert_thumbprint: String::new(),
2474            console_enabled: false,
2475            interactive_console_enabled: false,
2476            ipmi_enabled: false,
2477            secure_boot: false,
2478            tpm_enabled: true,
2479            tpm_version: AttestationTpmVersion::V138,
2480            tpm_persisted: true,
2481            hardware_sealing_policy: HardwareSealingPolicy::None,
2482            filtered_vpci_devices_allowed: false,
2483            vm_unique_id: String::new(),
2484            vmgs_provisioner: None,
2485        }
2486    }
2487
2488    #[async_test]
2489    async fn do_nothing_without_derived_keys() {
2490        let mut vmgs = new_formatted_vmgs().await;
2491
2492        let mut key_protector = new_key_protector();
2493        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2494
2495        let key_protector_settings = KeyProtectorSettings {
2496            should_write_kp: false,
2497            use_gsp_by_id: false,
2498            use_hardware_unlock: false,
2499            decrypt_gsp_type: GspType::None,
2500            encrypt_gsp_type: GspType::None,
2501        };
2502
2503        let bios_guid = Guid::new_random();
2504
2505        unlock_vmgs_data_store(
2506            &mut vmgs,
2507            false,
2508            &mut key_protector,
2509            &mut key_protector_by_id,
2510            None,
2511            None,
2512            key_protector_settings,
2513            bios_guid,
2514        )
2515        .await
2516        .unwrap();
2517
2518        assert!(key_protector_is_empty(&mut vmgs).await);
2519        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
2520
2521        // Create another instance as the previous `unlock_vmgs_data_store` took ownership of the last one
2522        let key_protector_settings = KeyProtectorSettings {
2523            should_write_kp: false,
2524            use_gsp_by_id: false,
2525            use_hardware_unlock: false,
2526            decrypt_gsp_type: GspType::None,
2527            encrypt_gsp_type: GspType::None,
2528        };
2529
2530        // Even if the VMGS is encrypted, if no derived keys are provided, nothing should happen
2531        unlock_vmgs_data_store(
2532            &mut vmgs,
2533            true,
2534            &mut key_protector,
2535            &mut key_protector_by_id,
2536            None,
2537            None,
2538            key_protector_settings,
2539            bios_guid,
2540        )
2541        .await
2542        .unwrap();
2543
2544        assert!(key_protector_is_empty(&mut vmgs).await);
2545        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
2546    }
2547
2548    #[async_test]
2549    async fn provision_vmgs_and_rotate_keys() {
2550        let mut vmgs = new_formatted_vmgs().await;
2551
2552        let mut key_protector = new_key_protector();
2553        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2554
2555        let ingress = [1; AES_GCM_KEY_LENGTH];
2556        let egress = [2; AES_GCM_KEY_LENGTH];
2557        let derived_keys = Keys {
2558            ingress,
2559            decrypt_egress: None,
2560            encrypt_egress: egress,
2561        };
2562
2563        let key_protector_settings = KeyProtectorSettings {
2564            should_write_kp: true,
2565            use_gsp_by_id: true,
2566            use_hardware_unlock: false,
2567            decrypt_gsp_type: GspType::GspById,
2568            encrypt_gsp_type: GspType::GspById,
2569        };
2570
2571        let bios_guid = Guid::new_random();
2572
2573        // Without encryption implies the provision path
2574        // The VMGS will be locked using the egress key
2575        unlock_vmgs_data_store(
2576            &mut vmgs,
2577            false,
2578            &mut key_protector,
2579            &mut key_protector_by_id,
2580            None,
2581            Some(derived_keys),
2582            key_protector_settings,
2583            bios_guid,
2584        )
2585        .await
2586        .unwrap();
2587
2588        // The ingress key is essentially ignored since the VMGS wasn't previously encrypted
2589        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
2590
2591        // The egress key was used to lock the VMGS after provisioning
2592        vmgs.unlock_with_encryption_key(&egress).await.unwrap();
2593        // Since this is a new VMGS, the egress key is the first and only key
2594        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(0));
2595
2596        // Since both `should_write_kp` and `use_gsp_by_id` are true, both key protectors should be updated
2597        assert!(!key_protector_is_empty(&mut vmgs).await);
2598        assert!(!key_protector_by_id_is_empty(&mut vmgs).await);
2599
2600        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
2601            .await
2602            .unwrap();
2603        assert_eq!(found_key_protector.as_bytes(), key_protector.as_bytes());
2604
2605        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
2606        assert_eq!(
2607            found_key_protector_by_id.as_bytes(),
2608            key_protector_by_id.inner.as_bytes()
2609        );
2610
2611        // Now that the VMGS has been provisioned, simulate the rotation of keys
2612        let new_egress = [3; AES_GCM_KEY_LENGTH];
2613
2614        let mut new_key_protector = new_key_protector();
2615        let mut new_key_protector_by_id = new_key_protector_by_id(None, None, false);
2616
2617        let key_protector_settings = KeyProtectorSettings {
2618            should_write_kp: true,
2619            use_gsp_by_id: true,
2620            use_hardware_unlock: false,
2621            decrypt_gsp_type: GspType::GspById,
2622            encrypt_gsp_type: GspType::GspById,
2623        };
2624
2625        // Ingress is now the old egress, and we provide a new new egress key
2626        let derived_keys = Keys {
2627            ingress: egress,
2628            decrypt_egress: None,
2629            encrypt_egress: new_egress,
2630        };
2631
2632        unlock_vmgs_data_store(
2633            &mut vmgs,
2634            true,
2635            &mut new_key_protector,
2636            &mut new_key_protector_by_id,
2637            None,
2638            Some(derived_keys),
2639            key_protector_settings,
2640            bios_guid,
2641        )
2642        .await
2643        .unwrap();
2644
2645        // We should still fail to unlock the VMGS with the original ingress key
2646        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
2647        // The old egress key should no longer be able to unlock the VMGS
2648        vmgs.unlock_with_encryption_key(&egress).await.unwrap_err();
2649
2650        // The new egress key should be able to unlock the VMGS
2651        vmgs.unlock_with_encryption_key(&new_egress).await.unwrap();
2652        // The old egress key was removed, but not before the new egress key was added in the 1th slot
2653        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
2654
2655        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
2656            .await
2657            .unwrap();
2658        assert_eq!(found_key_protector.as_bytes(), new_key_protector.as_bytes());
2659
2660        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
2661        assert_eq!(
2662            found_key_protector_by_id.as_bytes(),
2663            new_key_protector_by_id.inner.as_bytes()
2664        );
2665    }
2666
2667    #[async_test]
2668    async fn unlock_previously_encrypted_vmgs_with_ingress_key() {
2669        let mut vmgs = new_formatted_vmgs().await;
2670
2671        let mut key_protector = new_key_protector();
2672        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2673
2674        let ingress = [1; AES_GCM_KEY_LENGTH];
2675        let egress = [2; AES_GCM_KEY_LENGTH];
2676
2677        let derived_keys = Keys {
2678            ingress,
2679            decrypt_egress: None,
2680            encrypt_egress: egress,
2681        };
2682
2683        vmgs.update_encryption_key(&ingress, EncryptionAlgorithm::AES_GCM)
2684            .await
2685            .unwrap();
2686
2687        // Initially, the VMGS can be unlocked using the ingress key
2688        vmgs.unlock_with_encryption_key(&ingress).await.unwrap();
2689        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(0));
2690
2691        let key_protector_settings = KeyProtectorSettings {
2692            should_write_kp: true,
2693            use_gsp_by_id: true,
2694            use_hardware_unlock: false,
2695            decrypt_gsp_type: GspType::GspById,
2696            encrypt_gsp_type: GspType::GspById,
2697        };
2698
2699        let bios_guid = Guid::new_random();
2700
2701        unlock_vmgs_data_store(
2702            &mut vmgs,
2703            true,
2704            &mut key_protector,
2705            &mut key_protector_by_id,
2706            None,
2707            Some(derived_keys),
2708            key_protector_settings,
2709            bios_guid,
2710        )
2711        .await
2712        .unwrap();
2713
2714        // After the VMGS has been unlocked, the VMGS encryption key should be rotated from ingress to egress
2715        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
2716        vmgs.unlock_with_encryption_key(&egress).await.unwrap();
2717        // The ingress key was removed, but not before the egress key was added in the 0th slot
2718        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
2719
2720        // Since both `should_write_kp` and `use_gsp_by_id` are true, both key protectors should be updated
2721        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
2722            .await
2723            .unwrap();
2724        assert_eq!(found_key_protector.as_bytes(), key_protector.as_bytes());
2725
2726        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
2727        assert_eq!(
2728            found_key_protector_by_id.as_bytes(),
2729            key_protector_by_id.inner.as_bytes()
2730        );
2731    }
2732
2733    #[async_test]
2734    async fn failed_to_persist_ingress_key_so_use_egress_key_to_unlock_vmgs() {
2735        let mut vmgs = new_formatted_vmgs().await;
2736
2737        let mut key_protector = new_key_protector();
2738        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2739
2740        let ingress = [1; AES_GCM_KEY_LENGTH];
2741        let decrypt_egress = [2; AES_GCM_KEY_LENGTH];
2742        let encrypt_egress = [3; AES_GCM_KEY_LENGTH];
2743
2744        let derived_keys = Keys {
2745            ingress,
2746            decrypt_egress: Some(decrypt_egress),
2747            encrypt_egress,
2748        };
2749
2750        // Add only the egress key to the VMGS to simulate a failure to persist the ingress key
2751        vmgs.test_add_new_encryption_key(&decrypt_egress, EncryptionAlgorithm::AES_GCM)
2752            .await
2753            .unwrap();
2754        let egress_key_index = vmgs.test_get_active_datastore_key_index().unwrap();
2755        assert_eq!(egress_key_index, 0);
2756
2757        vmgs.unlock_with_encryption_key(&decrypt_egress)
2758            .await
2759            .unwrap();
2760        let found_egress_key_index = vmgs.test_get_active_datastore_key_index().unwrap();
2761        assert_eq!(found_egress_key_index, egress_key_index);
2762
2763        // Confirm that the ingress key cannot be used to unlock the VMGS
2764        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
2765
2766        let key_protector_settings = KeyProtectorSettings {
2767            should_write_kp: true,
2768            use_gsp_by_id: true,
2769            use_hardware_unlock: false,
2770            decrypt_gsp_type: GspType::GspById,
2771            encrypt_gsp_type: GspType::GspById,
2772        };
2773
2774        let bios_guid = Guid::new_random();
2775
2776        unlock_vmgs_data_store(
2777            &mut vmgs,
2778            true,
2779            &mut key_protector,
2780            &mut key_protector_by_id,
2781            None,
2782            Some(derived_keys),
2783            key_protector_settings,
2784            bios_guid,
2785        )
2786        .await
2787        .unwrap();
2788
2789        // Confirm that the ingress key was not added
2790        vmgs.unlock_with_encryption_key(&ingress).await.unwrap_err();
2791
2792        // Confirm that the decrypt egress key no longer works
2793        vmgs.unlock_with_encryption_key(&decrypt_egress)
2794            .await
2795            .unwrap_err();
2796
2797        // The encrypt_egress key can unlock the VMGS and was added as a new key
2798        vmgs.unlock_with_encryption_key(&encrypt_egress)
2799            .await
2800            .unwrap();
2801        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
2802
2803        // Since both `should_write_kp` and `use_gsp_by_id` are true, both key protectors should be updated
2804        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
2805            .await
2806            .unwrap();
2807        assert_eq!(found_key_protector.as_bytes(), key_protector.as_bytes());
2808
2809        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
2810        assert_eq!(
2811            found_key_protector_by_id.as_bytes(),
2812            key_protector_by_id.inner.as_bytes()
2813        );
2814    }
2815
2816    #[async_test]
2817    async fn fail_to_unlock_vmgs_with_existing_ingress_key() {
2818        let mut vmgs = new_formatted_vmgs().await;
2819
2820        let mut key_protector = new_key_protector();
2821        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2822
2823        let ingress = [1; AES_GCM_KEY_LENGTH];
2824
2825        // Ingress and egress keys are the same
2826        let derived_keys = Keys {
2827            ingress,
2828            decrypt_egress: None,
2829            encrypt_egress: ingress,
2830        };
2831
2832        // Add two random keys to the VMGS to simulate unlock failure when ingress and egress keys are the same
2833        let additional_key = [2; AES_GCM_KEY_LENGTH];
2834        let yet_another_key = [3; AES_GCM_KEY_LENGTH];
2835
2836        vmgs.test_add_new_encryption_key(&additional_key, EncryptionAlgorithm::AES_GCM)
2837            .await
2838            .unwrap();
2839        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(0));
2840
2841        vmgs.test_add_new_encryption_key(&yet_another_key, EncryptionAlgorithm::AES_GCM)
2842            .await
2843            .unwrap();
2844        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
2845
2846        let key_protector_settings = KeyProtectorSettings {
2847            should_write_kp: true,
2848            use_gsp_by_id: true,
2849            use_hardware_unlock: false,
2850            decrypt_gsp_type: GspType::GspById,
2851            encrypt_gsp_type: GspType::GspById,
2852        };
2853
2854        let bios_guid = Guid::new_random();
2855
2856        let unlock_result = unlock_vmgs_data_store(
2857            &mut vmgs,
2858            true,
2859            &mut key_protector,
2860            &mut key_protector_by_id,
2861            None,
2862            Some(derived_keys),
2863            key_protector_settings,
2864            bios_guid,
2865        )
2866        .await;
2867        assert!(unlock_result.is_err());
2868        assert_eq!(
2869            unlock_result.unwrap_err().to_string(),
2870            "failed to unlock vmgs with the existing ingress key".to_string()
2871        );
2872    }
2873
2874    #[async_test]
2875    async fn fail_to_unlock_vmgs_with_new_ingress_key() {
2876        let mut vmgs = new_formatted_vmgs().await;
2877
2878        let mut key_protector = new_key_protector();
2879        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2880
2881        let derived_keys = Keys {
2882            ingress: [1; AES_GCM_KEY_LENGTH],
2883            decrypt_egress: None,
2884            encrypt_egress: [2; AES_GCM_KEY_LENGTH],
2885        };
2886
2887        // Add two random keys to the VMGS to simulate unlock failure when ingress and egress keys are *not* the same
2888        let additional_key = [3; AES_GCM_KEY_LENGTH];
2889        let yet_another_key = [4; AES_GCM_KEY_LENGTH];
2890
2891        vmgs.test_add_new_encryption_key(&additional_key, EncryptionAlgorithm::AES_GCM)
2892            .await
2893            .unwrap();
2894        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(0));
2895
2896        vmgs.test_add_new_encryption_key(&yet_another_key, EncryptionAlgorithm::AES_GCM)
2897            .await
2898            .unwrap();
2899        assert_eq!(vmgs.test_get_active_datastore_key_index(), Some(1));
2900
2901        let key_protector_settings = KeyProtectorSettings {
2902            should_write_kp: true,
2903            use_gsp_by_id: true,
2904            use_hardware_unlock: false,
2905            decrypt_gsp_type: GspType::GspById,
2906            encrypt_gsp_type: GspType::GspById,
2907        };
2908
2909        let bios_guid = Guid::new_random();
2910
2911        let unlock_result = unlock_vmgs_data_store(
2912            &mut vmgs,
2913            true,
2914            &mut key_protector,
2915            &mut key_protector_by_id,
2916            None,
2917            Some(derived_keys),
2918            key_protector_settings,
2919            bios_guid,
2920        )
2921        .await;
2922        assert!(unlock_result.is_err());
2923        assert_eq!(
2924            unlock_result.unwrap_err().to_string(),
2925            "failed to unlock vmgs with the existing ingress key".to_string()
2926        );
2927    }
2928
2929    #[async_test]
2930    async fn get_derived_keys_using_id() {
2931        let bios_guid = Guid::new_random();
2932
2933        let gsp_response_by_id = GuestStateProtectionById {
2934            seed: guest_emulation_transport::api::GspCleartextContent {
2935                length: GSP_CLEARTEXT_MAX,
2936                buffer: [1; GSP_CLEARTEXT_MAX as usize * 2],
2937            },
2938            extended_status_flags: GspExtendedStatusFlags::from_bits(0),
2939        };
2940
2941        // When the key protector by id inner `id_guid` is all zeroes, the derived ingress and egress keys
2942        // should be identical.
2943        let mut key_protector_by_id =
2944            new_key_protector_by_id(Some(Guid::new_zeroed()), None, false);
2945        let derived_keys =
2946            get_derived_keys_by_id(&mut key_protector_by_id, bios_guid, gsp_response_by_id)
2947                .unwrap();
2948
2949        assert_eq!(derived_keys.ingress, derived_keys.encrypt_egress);
2950
2951        // When the key protector by id inner `id_guid` is not all zeroes, the derived ingress and egress keys
2952        // should be different.
2953        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2954        let derived_keys =
2955            get_derived_keys_by_id(&mut key_protector_by_id, bios_guid, gsp_response_by_id)
2956                .unwrap();
2957
2958        assert_ne!(derived_keys.ingress, derived_keys.encrypt_egress);
2959
2960        // When the `gsp_response_by_id` seed length is 0, deriving a key will fail.
2961        let gsp_response_by_id_with_0_length_seed = GuestStateProtectionById {
2962            seed: guest_emulation_transport::api::GspCleartextContent {
2963                length: 0,
2964                buffer: [1; GSP_CLEARTEXT_MAX as usize * 2],
2965            },
2966            extended_status_flags: GspExtendedStatusFlags::from_bits(0),
2967        };
2968
2969        let derived_keys_response = get_derived_keys_by_id(
2970            &mut key_protector_by_id,
2971            bios_guid,
2972            gsp_response_by_id_with_0_length_seed,
2973        );
2974        assert!(derived_keys_response.is_err());
2975        assert_eq!(
2976            derived_keys_response.unwrap_err().to_string(),
2977            "failed to derive an egress key based on current vm bios guid".to_string()
2978        );
2979    }
2980
2981    #[async_test]
2982    async fn pass_through_persist_all_key_protectors() {
2983        let mut vmgs = new_formatted_vmgs().await;
2984        let mut key_protector = new_key_protector();
2985        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
2986        let bios_guid = Guid::new_random();
2987
2988        // Copied/cloned bits used for comparison later
2989        let kp_copy = key_protector.as_bytes().to_vec();
2990        let active_kp_copy = key_protector.active_kp;
2991
2992        // When all key protector settings are true, no actions will be taken on the key protectors or VMGS
2993        let key_protector_settings = KeyProtectorSettings {
2994            should_write_kp: true,
2995            use_gsp_by_id: true,
2996            use_hardware_unlock: true,
2997            decrypt_gsp_type: GspType::GspById,
2998            encrypt_gsp_type: GspType::GspById,
2999        };
3000        persist_all_key_protectors(
3001            &mut vmgs,
3002            &mut key_protector,
3003            &mut key_protector_by_id,
3004            Some(&HardwareKeyProtectorV3::new_zeroed()),
3005            bios_guid,
3006            key_protector_settings,
3007        )
3008        .await
3009        .unwrap();
3010
3011        assert!(key_protector_is_empty(&mut vmgs).await);
3012        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
3013
3014        // The key protector should remain unchanged
3015        assert_eq!(active_kp_copy, key_protector.active_kp);
3016        assert_eq!(kp_copy.as_slice(), key_protector.as_bytes());
3017    }
3018
3019    #[async_test]
3020    async fn hardware_sealing_first_boot_creates_hwkp_and_encrypts_vmgs(driver: DefaultDriver) {
3021        let mut vmgs = new_formatted_vmgs().await;
3022        // Start with an empty KP to simulate brand-new VMGS with no DEK/GSP present
3023        let mut key_protector = KeyProtector::new_zeroed();
3024        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
3025        let bios_guid = Guid::new_random();
3026
3027        // Create a GET client backed by the test host
3028        let get_pair = guest_emulation_transport::test_utilities::new_transport_pair(
3029            driver,
3030            None,
3031            get_protocol::ProtocolVersion::NICKEL_REV2,
3032            None,
3033            None,
3034        )
3035        .await;
3036
3037        let mock_tee_call = MockTeeCall::new([0x8a; 32]);
3038
3039        // No KEK, no GSP. Require HardwareSealing and VMGS is not encrypted.
3040        let derived = get_derived_keys(
3041            &get_pair.client,
3042            Some(&mock_tee_call),
3043            &mut vmgs,
3044            &mut key_protector,
3045            &mut key_protector_by_id,
3046            bios_guid,
3047            &AttestationVmConfig {
3048                current_time: None,
3049                root_cert_thumbprint: String::new(),
3050                console_enabled: false,
3051                interactive_console_enabled: false,
3052                ipmi_enabled: false,
3053                secure_boot: false,
3054                tpm_enabled: false,
3055                tpm_version: AttestationTpmVersion::V138,
3056                tpm_persisted: false,
3057                hardware_sealing_policy: HardwareSealingPolicy::Hash,
3058                filtered_vpci_devices_allowed: true,
3059                vm_unique_id: String::new(),
3060                vmgs_provisioner: None,
3061            },
3062            false,
3063            None,
3064            None,
3065            Some(KeyDerivationPolicy {
3066                svn: tee_call::KeyDerivationSvn::Snp {
3067                    tcb_version: 0x1234,
3068                },
3069                mix_measurement: true,
3070            }),
3071            GuestStateEncryptionPolicy::HardwareSealing,
3072            true,
3073            true,
3074            false,
3075        )
3076        .await
3077        .unwrap();
3078
3079        // It must produce an egress key and HWKP
3080        assert!(derived.derived_keys.is_some());
3081        assert!(derived.hardware_key_protector.is_some());
3082
3083        // Capture the egress key before `derived.derived_keys` is consumed below
3084        let egress_key = derived.derived_keys.as_ref().unwrap().encrypt_egress;
3085
3086        // Apply to VMGS and verify encryption using egress key
3087        unlock_vmgs_data_store(
3088            &mut vmgs,
3089            false,
3090            &mut key_protector,
3091            &mut key_protector_by_id,
3092            derived.hardware_key_protector,
3093            derived.derived_keys,
3094            derived.key_protector_settings,
3095            bios_guid,
3096        )
3097        .await
3098        .unwrap();
3099
3100        // VMGS should be unlockable with the egress key
3101        vmgs.unlock_with_encryption_key(&egress_key).await.unwrap();
3102
3103        // VMGS should not be unlockable with an all-zero key (ingress was zeroed)
3104        vmgs.unlock_with_encryption_key(&[0; AES_GCM_KEY_LENGTH])
3105            .await
3106            .unwrap_err();
3107    }
3108
3109    #[async_test]
3110    async fn hardware_sealing_recovery_uses_hwkp_v2_when_encrypted(driver: DefaultDriver) {
3111        let mut vmgs = new_formatted_vmgs().await;
3112
3113        // Pre-encrypt VMGS to simulate previous boot
3114        let bootstrap = [0x33; AES_GCM_KEY_LENGTH];
3115        vmgs.test_add_new_encryption_key(&bootstrap, EncryptionAlgorithm::AES_GCM)
3116            .await
3117            .unwrap();
3118
3119        let mut key_protector = new_key_protector();
3120        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
3121        let bios_guid = Guid::new_random();
3122
3123        // Create a HWKP V2 by sealing current key and writing to VMGS
3124        let mock_tee_call = MockTeeCall::new([0x8a; 32]);
3125
3126        let hdk = HardwareDerivedKeys::derive_key(
3127            mock_tee_call.supports_get_derived_key().unwrap(),
3128            &AttestationVmConfig {
3129                current_time: None,
3130                root_cert_thumbprint: String::new(),
3131                console_enabled: false,
3132                interactive_console_enabled: false,
3133                ipmi_enabled: false,
3134                secure_boot: false,
3135                tpm_enabled: false,
3136                tpm_version: AttestationTpmVersion::V138,
3137                tpm_persisted: false,
3138                hardware_sealing_policy: HardwareSealingPolicy::Hash,
3139                filtered_vpci_devices_allowed: true,
3140                vm_unique_id: String::new(),
3141                vmgs_provisioner: None,
3142            },
3143            KeyDerivationPolicy {
3144                svn: tee_call::KeyDerivationSvn::Snp {
3145                    tcb_version: 0x1234,
3146                },
3147                mix_measurement: true,
3148            },
3149        )
3150        .unwrap();
3151        let hwkp = hardware_key_sealing::seal_key(&hdk, &bootstrap).unwrap();
3152        vmgs::write_hardware_key_protector(&hwkp, &mut vmgs)
3153            .await
3154            .unwrap();
3155
3156        // Now call get_derived_keys with HardwareSealing required and VMGS encrypted
3157        // Create a GET client backed by the test host
3158        let get_pair = guest_emulation_transport::test_utilities::new_transport_pair(
3159            driver,
3160            None,
3161            get_protocol::ProtocolVersion::NICKEL_REV2,
3162            None,
3163            None,
3164        )
3165        .await;
3166
3167        let derived = get_derived_keys(
3168            &get_pair.client,
3169            Some(&mock_tee_call),
3170            &mut vmgs,
3171            &mut key_protector,
3172            &mut key_protector_by_id,
3173            bios_guid,
3174            &AttestationVmConfig {
3175                current_time: None,
3176                root_cert_thumbprint: String::new(),
3177                console_enabled: false,
3178                interactive_console_enabled: false,
3179                ipmi_enabled: false,
3180                secure_boot: false,
3181                tpm_enabled: false,
3182                tpm_version: AttestationTpmVersion::V138,
3183                tpm_persisted: false,
3184                hardware_sealing_policy: HardwareSealingPolicy::Hash,
3185                filtered_vpci_devices_allowed: true,
3186                vm_unique_id: String::new(),
3187                vmgs_provisioner: None,
3188            },
3189            true,
3190            None,
3191            None,
3192            Some(KeyDerivationPolicy {
3193                svn: tee_call::KeyDerivationSvn::Snp {
3194                    tcb_version: 0x1234,
3195                },
3196                mix_measurement: true,
3197            }),
3198            GuestStateEncryptionPolicy::HardwareSealing,
3199            true,
3200            true,
3201            false,
3202        )
3203        .await
3204        .unwrap();
3205
3206        // Should have recovered ingress from HWKP and rotated egress
3207        let keys = derived.derived_keys.unwrap();
3208        assert_eq!(keys.ingress, bootstrap);
3209        assert_ne!(keys.encrypt_egress, keys.ingress);
3210    }
3211
3212    /// In stateless + hardware sealing mode, the GSP / GSP-by-id host callout
3213    /// must be skipped so that a host-requested `state_refresh` can never
3214    /// trigger a TPM seed refresh. Here the GSP callout is skipped entirely, so
3215    /// `get_derived_keys` must report no state refresh.
3216    ///
3217    /// See `non_sealing_propagates_gsp_state_refresh` for the contrasting case
3218    /// that proves a host-provided `state_refresh` is otherwise honored.
3219    #[async_test]
3220    async fn hardware_sealing_skips_gsp_state_refresh(driver: DefaultDriver) {
3221        let mut vmgs = new_formatted_vmgs().await;
3222        let mut key_protector = KeyProtector::new_zeroed();
3223        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
3224        let bios_guid = Guid::new_random();
3225
3226        // No scripted GSP responses are needed: hardware sealing mode skips the
3227        // GSP callout entirely.
3228        let get_pair = guest_emulation_transport::test_utilities::new_transport_pair(
3229            driver,
3230            None,
3231            get_protocol::ProtocolVersion::NICKEL_REV2,
3232            None,
3233            None,
3234        )
3235        .await;
3236
3237        let mock_tee_call = MockTeeCall::new([0x8a; 32]);
3238
3239        let derived = get_derived_keys(
3240            &get_pair.client,
3241            Some(&mock_tee_call),
3242            &mut vmgs,
3243            &mut key_protector,
3244            &mut key_protector_by_id,
3245            bios_guid,
3246            &AttestationVmConfig {
3247                current_time: None,
3248                root_cert_thumbprint: String::new(),
3249                console_enabled: false,
3250                interactive_console_enabled: false,
3251                ipmi_enabled: false,
3252                secure_boot: false,
3253                tpm_enabled: false,
3254                tpm_version: AttestationTpmVersion::V138,
3255                tpm_persisted: false,
3256                hardware_sealing_policy: HardwareSealingPolicy::Hash,
3257                filtered_vpci_devices_allowed: true,
3258                vm_unique_id: String::new(),
3259                vmgs_provisioner: None,
3260            },
3261            false,
3262            None,
3263            None,
3264            Some(KeyDerivationPolicy {
3265                svn: tee_call::KeyDerivationSvn::Snp {
3266                    tcb_version: 0x1234,
3267                },
3268                mix_measurement: true,
3269            }),
3270            GuestStateEncryptionPolicy::HardwareSealing,
3271            true,
3272            true,
3273            false,
3274        )
3275        .await
3276        .unwrap();
3277
3278        // The GSP callout is skipped in sealing mode, so the host-requested
3279        // state refresh must not be reported.
3280        assert!(!derived.gsp_extended_status_flags.state_refresh_request());
3281    }
3282
3283    /// Companion to `hardware_sealing_skips_gsp_state_refresh`: in the normal
3284    /// (non-sealing) flow the GSP callout IS made, so a host-provided
3285    /// `state_refresh` is propagated. This proves the host actually injects the
3286    /// flag, making the sealing-mode assertion meaningful.
3287    #[async_test]
3288    async fn non_sealing_propagates_gsp_state_refresh(driver: DefaultDriver) {
3289        let mut vmgs = new_formatted_vmgs().await;
3290        let mut key_protector = KeyProtector::new_zeroed();
3291        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
3292        let bios_guid = Guid::new_random();
3293
3294        // Script the host GSP response to report `state_refresh_request`, and a
3295        // GSP-by-id response with no registry file (no encryption source). The
3296        // non-sealing flow makes both callouts and captures the GSP flags.
3297        let gsp_response = TestGetResponses::new(Event::Response(
3298            get_protocol::GuestStateProtectionResponse {
3299                message_header: get_protocol::HeaderGeneric::new(
3300                    get_protocol::HostRequests::GUEST_STATE_PROTECTION,
3301                ),
3302                encrypted_gsp: get_protocol::GspCiphertextContent::new_zeroed(),
3303                decrypted_gsp: [get_protocol::GspCleartextContent::new_zeroed();
3304                    get_protocol::NUMBER_GSP as usize],
3305                extended_status_flags: GspExtendedStatusFlags::new()
3306                    .with_state_refresh_request(true),
3307            }
3308            .as_bytes()
3309            .to_vec(),
3310        ));
3311        let gsp_by_id_response = TestGetResponses::new(Event::Response(
3312            get_protocol::GuestStateProtectionByIdResponse {
3313                message_header: get_protocol::HeaderGeneric::new(
3314                    get_protocol::HostRequests::GUEST_STATE_PROTECTION_BY_ID,
3315                ),
3316                seed: get_protocol::GspCleartextContent::new_zeroed(),
3317                extended_status_flags: GspExtendedStatusFlags::new().with_no_registry_file(true),
3318            }
3319            .as_bytes()
3320            .to_vec(),
3321        ));
3322
3323        let get_pair = guest_emulation_transport::test_utilities::new_transport_pair(
3324            driver,
3325            Some(vec![gsp_response, gsp_by_id_response]),
3326            get_protocol::ProtocolVersion::NICKEL_REV2,
3327            None,
3328            None,
3329        )
3330        .await;
3331
3332        let derived = get_derived_keys(
3333            &get_pair.client,
3334            None,
3335            &mut vmgs,
3336            &mut key_protector,
3337            &mut key_protector_by_id,
3338            bios_guid,
3339            &new_attestation_vm_config(),
3340            false,
3341            None,
3342            None,
3343            None,
3344            GuestStateEncryptionPolicy::Auto,
3345            false,
3346            false,
3347            false,
3348        )
3349        .await
3350        .unwrap();
3351
3352        // The GSP callout is made in the normal flow, so the host-requested
3353        // state refresh is reported.
3354        assert!(derived.gsp_extended_status_flags.state_refresh_request());
3355    }
3356
3357    #[async_test]
3358    async fn persist_all_key_protectors_write_key_protector_by_id() {
3359        let mut vmgs = new_formatted_vmgs().await;
3360        let mut key_protector = new_key_protector();
3361        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
3362        let bios_guid = Guid::new_random();
3363
3364        // Copied/cloned bits used for comparison later
3365        let kp_copy = key_protector.as_bytes().to_vec();
3366        let active_kp_copy = key_protector.active_kp;
3367
3368        // When `use_gsp_by_id` is true and `should_write_kp` is false, the key protector by id should be written to the VMGS
3369        let key_protector_settings = KeyProtectorSettings {
3370            should_write_kp: false,
3371            use_gsp_by_id: true,
3372            use_hardware_unlock: false,
3373            decrypt_gsp_type: GspType::GspById,
3374            encrypt_gsp_type: GspType::GspById,
3375        };
3376        persist_all_key_protectors(
3377            &mut vmgs,
3378            &mut key_protector,
3379            &mut key_protector_by_id,
3380            None,
3381            bios_guid,
3382            key_protector_settings,
3383        )
3384        .await
3385        .unwrap();
3386
3387        // The previously empty VMGS now holds the key protector by id but not the key protector
3388        assert!(key_protector_is_empty(&mut vmgs).await);
3389        assert!(!key_protector_by_id_is_empty(&mut vmgs).await);
3390
3391        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
3392        assert_eq!(
3393            found_key_protector_by_id.as_bytes(),
3394            key_protector_by_id.inner.as_bytes()
3395        );
3396
3397        // The key protector should remain unchanged
3398        assert_eq!(kp_copy.as_slice(), key_protector.as_bytes());
3399        assert_eq!(active_kp_copy, key_protector.active_kp);
3400    }
3401
3402    #[async_test]
3403    async fn persist_all_key_protectors_remove_ingress_kp() {
3404        let mut vmgs = new_formatted_vmgs().await;
3405        let mut key_protector = new_key_protector();
3406        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
3407        let bios_guid = Guid::new_random();
3408
3409        // Copied active KP for later use
3410        let active_kp_copy = key_protector.active_kp;
3411
3412        // When `use_gsp_by_id` is false, `should_write_kp` is true, and `use_hardware_unlock` is false, the active key protector's
3413        // 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
3414        let key_protector_settings = KeyProtectorSettings {
3415            should_write_kp: true,
3416            use_gsp_by_id: false,
3417            use_hardware_unlock: false,
3418            decrypt_gsp_type: GspType::None,
3419            encrypt_gsp_type: GspType::None,
3420        };
3421        persist_all_key_protectors(
3422            &mut vmgs,
3423            &mut key_protector,
3424            &mut key_protector_by_id,
3425            None,
3426            bios_guid,
3427            key_protector_settings,
3428        )
3429        .await
3430        .unwrap();
3431
3432        assert!(!key_protector_is_empty(&mut vmgs).await);
3433        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
3434
3435        // The previously empty VMGS's key protector should now be overwritten
3436        let found_key_protector = vmgs::read_key_protector(&mut vmgs, AES_WRAPPED_AES_KEY_LENGTH)
3437            .await
3438            .unwrap();
3439
3440        assert!(
3441            found_key_protector.dek[active_kp_copy as usize]
3442                .dek_buffer
3443                .iter()
3444                .all(|&b| b == 0),
3445        );
3446        assert_eq!(
3447            found_key_protector.gsp[active_kp_copy as usize].gsp_length,
3448            0
3449        );
3450        assert_eq!(found_key_protector.active_kp, active_kp_copy + 1);
3451    }
3452
3453    #[async_test]
3454    async fn persist_all_key_protectors_mark_key_protector_by_id_as_not_in_use() {
3455        let mut vmgs = new_formatted_vmgs().await;
3456        let mut key_protector = new_key_protector();
3457        let mut key_protector_by_id = new_key_protector_by_id(None, None, true);
3458        let bios_guid = Guid::new_random();
3459
3460        // When `use_gsp_by_id` is false, `should_write_kp` is true, `use_hardware_unlock` is true, and
3461        // the key protector by id is found and not ported, the key protector by id should be marked as ported
3462        let key_protector_settings = KeyProtectorSettings {
3463            should_write_kp: true,
3464            use_gsp_by_id: false,
3465            use_hardware_unlock: true,
3466            decrypt_gsp_type: GspType::None,
3467            encrypt_gsp_type: GspType::None,
3468        };
3469
3470        persist_all_key_protectors(
3471            &mut vmgs,
3472            &mut key_protector,
3473            &mut key_protector_by_id,
3474            Some(&HardwareKeyProtectorV3::new_zeroed()),
3475            bios_guid,
3476            key_protector_settings,
3477        )
3478        .await
3479        .unwrap();
3480
3481        assert!(key_protector_is_empty(&mut vmgs).await);
3482        assert!(!key_protector_by_id_is_empty(&mut vmgs).await);
3483
3484        // The previously empty VMGS's key protector by id should now be overwritten
3485        let found_key_protector_by_id = vmgs::read_key_protector_by_id(&mut vmgs).await.unwrap();
3486        assert_eq!(found_key_protector_by_id.ported, 1);
3487        assert_eq!(
3488            found_key_protector_by_id.id_guid,
3489            key_protector_by_id.inner.id_guid
3490        );
3491    }
3492
3493    // --- initialize_platform_security tests ---
3494
3495    #[async_test]
3496    async fn init_sec_required_stateless_sealing_enrolls_and_rotates(driver: DefaultDriver) {
3497        let get_pair = new_test_get(driver, false, None).await;
3498        let disk = new_test_file();
3499        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
3500        let mut config = new_attestation_vm_config();
3501        config.tpm_persisted = false;
3502        config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
3503        let tee = BootReportTee::new();
3504        let bios_guid = Guid::new_random();
3505        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
3506        let mut previous_dek = None;
3507
3508        for boot in 1..=2 {
3509            let result = initialize_platform_security(
3510                &get_pair.client,
3511                bios_guid,
3512                &config,
3513                &mut vmgs,
3514                Some(&tee),
3515                true,
3516                ldriver.clone(),
3517                GuestStateEncryptionPolicy::HardwareSealing,
3518                true,
3519            )
3520            .await
3521            .unwrap();
3522            assert!(result.runtime_tcb_floor.is_some());
3523            assert!(!result.host_attestation_settings.refresh_tpm_seeds);
3524            assert!(vmgs.encrypted());
3525            let active_dek = *vmgs.active_encryption_key().unwrap();
3526            assert_ne!(previous_dek, Some(active_dek));
3527            assert_eq!(&*tee.report_data.lock(), &vec![[0; REPORT_DATA_SIZE]; boot]);
3528
3529            // Reopen without an extra test-side flush: enrollment finalized
3530            // persistence for this active DEK, not just a deferred protector.
3531            drop(vmgs);
3532            vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
3533            assert!(matches!(
3534                vmgs.active_encryption_key(),
3535                Err(::vmgs::Error::NeedsUnlock)
3536            ));
3537            let protector = vmgs::read_hardware_key_protector(&mut vmgs).await.unwrap();
3538            let keys = HardwareDerivedKeys::derive_key(
3539                tee.supports_get_derived_key().unwrap(),
3540                &config,
3541                protector.key_derivation_policy().unwrap(),
3542            )
3543            .unwrap();
3544            assert_eq!(protector.unseal_key(&keys).unwrap(), active_dek);
3545            previous_dek = Some(active_dek);
3546        }
3547    }
3548
3549    #[async_test]
3550    async fn init_sec_required_stateless_lm_before_first_derivation_seals_on_destination(
3551        driver: DefaultDriver,
3552    ) {
3553        let get_pair = new_test_get(driver, false, None).await;
3554        let mut config = new_attestation_vm_config();
3555        config.tpm_persisted = false;
3556        config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
3557        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
3558
3559        for migrate_after_report in [false, true] {
3560            let disk = new_test_file();
3561            let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
3562            assert!(!vmgs.encrypted());
3563            assert!(hardware_key_protector_is_empty(&mut vmgs).await);
3564            let tee = FirstDerivationMigrationTee::new();
3565            assert_eq!(
3566                tee.source.inner.tcb_version,
3567                tee.destination.inner.tcb_version
3568            );
3569            let boot_tee: &dyn TeeCall = if migrate_after_report {
3570                &tee
3571            } else {
3572                // LM before report acquisition: boot starts on the destination.
3573                &tee.destination
3574            };
3575            let result = initialize_platform_security(
3576                &get_pair.client,
3577                Guid::new_random(),
3578                &config,
3579                &mut vmgs,
3580                Some(boot_tee),
3581                true,
3582                ldriver.clone(),
3583                GuestStateEncryptionPolicy::HardwareSealing,
3584                true,
3585            )
3586            .await
3587            .unwrap();
3588            assert!(result.runtime_tcb_floor.is_some());
3589            assert!(!result.host_attestation_settings.refresh_tpm_seeds);
3590            assert!(vmgs.encrypted());
3591            let active_dek = *vmgs.active_encryption_key().unwrap();
3592            assert_eq!(*tee.source.derivation_calls.lock(), 0);
3593            assert_eq!(*tee.destination.derivation_calls.lock(), 1);
3594
3595            // No test-side flush: boot must persist the destination protector
3596            // and encrypt VMGS with the very DEK that protector contains.
3597            drop(vmgs);
3598            let mut reopened = Vmgs::open(disk, None).await.unwrap();
3599            assert!(reopened.encrypted());
3600            assert!(matches!(
3601                reopened.active_encryption_key(),
3602                Err(::vmgs::Error::NeedsUnlock)
3603            ));
3604            let protector = vmgs::read_hardware_key_protector(&mut reopened)
3605                .await
3606                .unwrap();
3607            let policy = KeyDerivationPolicy {
3608                svn: tee_call::KeyDerivationSvn::Snp {
3609                    tcb_version: tee.source.inner.tcb_version,
3610                },
3611                mix_measurement: true,
3612            };
3613            let stored_policy = protector.key_derivation_policy().unwrap();
3614            assert!(stored_policy.mix_measurement);
3615            assert!(
3616                matches!(stored_policy.svn, tee_call::KeyDerivationSvn::Snp { tcb_version }
3617                if tcb_version == tee.source.inner.tcb_version)
3618            );
3619            if migrate_after_report {
3620                let policies = tee.derivation_policies.lock();
3621                assert_eq!(policies.len(), 1);
3622                assert!(policies[0].mix_measurement);
3623                assert!(
3624                    matches!(policies[0].svn, tee_call::KeyDerivationSvn::Snp { tcb_version }
3625                    if tcb_version == tee.source.inner.tcb_version)
3626                );
3627            } else {
3628                assert!(tee.derivation_policies.lock().is_empty());
3629            }
3630
3631            // Derive independently from both contexts using identical SVN,
3632            // policy and configuration, not merely different mock identities.
3633            let source = tee.source.inner.supports_get_derived_key().unwrap();
3634            let destination = tee.destination.inner.supports_get_derived_key().unwrap();
3635            assert_ne!(
3636                source.get_derived_key(policy).unwrap(),
3637                destination.get_derived_key(policy).unwrap()
3638            );
3639            let source_keys = HardwareDerivedKeys::derive_key(source, &config, policy).unwrap();
3640            assert!(matches!(
3641                protector.unseal_key(&source_keys),
3642                Err(HardwareKeySealingError::HardwareKeyProtectorHmacVerificationFailed)
3643            ));
3644            let destination_keys =
3645                HardwareDerivedKeys::derive_key(destination, &config, policy).unwrap();
3646            let unsealed_dek = protector.unseal_key(&destination_keys).unwrap();
3647            assert_eq!(unsealed_dek, active_dek);
3648            reopened
3649                .unlock_with_encryption_key(&unsealed_dek)
3650                .await
3651                .unwrap();
3652            assert_eq!(reopened.active_encryption_key().unwrap(), &active_dek);
3653
3654            // Neither enrollment nor test-side verification may fetch another
3655            // report. The only report belongs to the chosen side of LM.
3656            if migrate_after_report {
3657                assert_eq!(&*tee.source.report_data.lock(), &[[0; REPORT_DATA_SIZE]]);
3658                assert!(tee.destination.report_data.lock().is_empty());
3659            } else {
3660                assert!(tee.source.report_data.lock().is_empty());
3661                assert_eq!(
3662                    &*tee.destination.report_data.lock(),
3663                    &[[0; REPORT_DATA_SIZE]]
3664                );
3665            }
3666        }
3667    }
3668
3669    #[async_test]
3670    async fn init_sec_required_stateless_lm_rejects_source_svn_without_enrollment(
3671        driver: DefaultDriver,
3672    ) {
3673        let get_pair = new_test_get(driver, false, None).await;
3674        let disk = new_test_file();
3675        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
3676        assert!(!vmgs.encrypted());
3677        assert!(hardware_key_protector_is_empty(&mut vmgs).await);
3678        let mut config = new_attestation_vm_config();
3679        config.tpm_persisted = false;
3680        config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
3681        let mut tee = FirstDerivationMigrationTee::new();
3682        // Raise one SNP TCB component on the source without changing the
3683        // destination, which cannot derive at the source report's higher SVN.
3684        tee.source.inner.tcb_version += 1;
3685        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
3686        let result = initialize_platform_security(
3687            &get_pair.client,
3688            Guid::new_random(),
3689            &config,
3690            &mut vmgs,
3691            Some(&tee),
3692            true,
3693            ldriver,
3694            GuestStateEncryptionPolicy::HardwareSealing,
3695            true,
3696        )
3697        .await;
3698        // Failure returns no PlatformAttestationData, hence no runtime floor.
3699        // Do not assume an automatic reboot/retry with a destination report.
3700        assert!(matches!(
3701            result,
3702            Err(Error(AttestationErrorInner::GetDerivedKeys(
3703                GetDerivedKeysError::HardwareSealingRequiredButNotSupported
3704            )))
3705        ));
3706        {
3707            let policies = tee.derivation_policies.lock();
3708            assert_eq!(policies.len(), 1);
3709            assert!(policies[0].mix_measurement);
3710            assert!(
3711                matches!(policies[0].svn, tee_call::KeyDerivationSvn::Snp { tcb_version }
3712                if tcb_version == tee.source.inner.tcb_version)
3713            );
3714        }
3715        assert_eq!(&*tee.source.report_data.lock(), &[[0; REPORT_DATA_SIZE]]);
3716        assert!(tee.destination.report_data.lock().is_empty());
3717        assert_eq!(*tee.source.derivation_calls.lock(), 0);
3718        assert_eq!(*tee.destination.derivation_calls.lock(), 0);
3719        assert!(!vmgs.encrypted());
3720        assert!(vmgs.active_encryption_key().is_err());
3721        assert!(hardware_key_protector_is_empty(&mut vmgs).await);
3722        assert!(key_protector_is_empty(&mut vmgs).await);
3723        assert!(key_protector_by_id_is_empty(&mut vmgs).await);
3724
3725        drop(vmgs);
3726        let mut reopened = Vmgs::open(disk, None).await.unwrap();
3727        assert!(!reopened.encrypted());
3728        assert!(reopened.active_encryption_key().is_err());
3729        assert!(hardware_key_protector_is_empty(&mut reopened).await);
3730        assert!(key_protector_is_empty(&mut reopened).await);
3731        assert!(key_protector_by_id_is_empty(&mut reopened).await);
3732    }
3733
3734    #[async_test]
3735    async fn init_sec_required_stateless_derivation_failure_cannot_boot(driver: DefaultDriver) {
3736        let get_pair = new_test_get(driver, false, None).await;
3737        let mut config = new_attestation_vm_config();
3738        config.tpm_persisted = false;
3739        config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
3740        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
3741
3742        for encrypted in [false, true] {
3743            let disk = new_test_file();
3744            let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
3745            let bios_guid = Guid::new_random();
3746            let mut tee = BootReportTee::new();
3747            if encrypted {
3748                let provisioned = initialize_platform_security(
3749                    &get_pair.client,
3750                    bios_guid,
3751                    &config,
3752                    &mut vmgs,
3753                    Some(&tee),
3754                    true,
3755                    ldriver.clone(),
3756                    GuestStateEncryptionPolicy::HardwareSealing,
3757                    true,
3758                )
3759                .await
3760                .unwrap();
3761                assert!(provisioned.runtime_tcb_floor.is_some());
3762                drop(vmgs);
3763                vmgs = Vmgs::open(disk, None).await.unwrap();
3764            }
3765            tee.fail_derivation = true;
3766            tee.report_data.lock().clear();
3767            *tee.derivation_calls.lock() = 0;
3768            let result = initialize_platform_security(
3769                &get_pair.client,
3770                bios_guid,
3771                &config,
3772                &mut vmgs,
3773                Some(&tee),
3774                true,
3775                ldriver.clone(),
3776                GuestStateEncryptionPolicy::HardwareSealing,
3777                true,
3778            )
3779            .await;
3780            assert!(result.is_err());
3781            assert_eq!(&*tee.report_data.lock(), &[[0; REPORT_DATA_SIZE]]);
3782            assert!(*tee.derivation_calls.lock() > 0);
3783            assert_eq!(vmgs.encrypted(), encrypted);
3784            assert!(vmgs.active_encryption_key().is_err());
3785        }
3786    }
3787
3788    #[async_test]
3789    async fn init_sec_optional_sealing_requires_this_boot_write(driver: DefaultDriver) {
3790        let get_pair = new_test_get(driver, true, None).await;
3791        let mut config = new_attestation_vm_config();
3792        config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
3793        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
3794
3795        // The default GED has no GSP. Check both automatic policy and
3796        // explicitly disabled GSP with tenant-key-only encryption.
3797        for policy in [
3798            GuestStateEncryptionPolicy::Auto,
3799            GuestStateEncryptionPolicy::None,
3800        ] {
3801            let disk = new_test_file();
3802            let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
3803            let bios_guid = Guid::new_random();
3804            let mut tee = BootReportTee::new();
3805            let provisioned = initialize_platform_security(
3806                &get_pair.client,
3807                bios_guid,
3808                &config,
3809                &mut vmgs,
3810                Some(&tee),
3811                false,
3812                ldriver.clone(),
3813                policy,
3814                true,
3815            )
3816            .await
3817            .unwrap();
3818            assert!(provisioned.runtime_tcb_floor.is_some());
3819            let old_dek = *vmgs.active_encryption_key().unwrap();
3820            let old_protector = vmgs::read_hardware_key_protector(&mut vmgs).await.unwrap();
3821            drop(vmgs);
3822            vmgs = Vmgs::open(disk, None).await.unwrap();
3823
3824            // Hardware still supports derivation and returns a valid trusted
3825            // report, but deriving the optional backup key now fails. The
3826            // preexisting valid protector must not enroll the newly active DEK.
3827            tee.fail_derivation = true;
3828            *tee.derivation_calls.lock() = 0;
3829            let result = initialize_platform_security(
3830                &get_pair.client,
3831                bios_guid,
3832                &config,
3833                &mut vmgs,
3834                Some(&tee),
3835                false,
3836                ldriver.clone(),
3837                policy,
3838                true,
3839            )
3840            .await
3841            .unwrap();
3842            assert!(result.runtime_tcb_floor.is_none());
3843            assert!(*tee.derivation_calls.lock() > 0);
3844            assert_eq!(tee.report_data.lock().len(), 2);
3845            assert!(vmgs.encrypted());
3846            assert_ne!(vmgs.active_encryption_key().unwrap(), &old_dek);
3847            let protector = vmgs::read_hardware_key_protector(&mut vmgs).await.unwrap();
3848            // Readback is test evidence only, never the enrollment decision.
3849            tee.fail_derivation = false;
3850            let keys = HardwareDerivedKeys::derive_key(
3851                tee.supports_get_derived_key().unwrap(),
3852                &config,
3853                old_protector.key_derivation_policy().unwrap(),
3854            )
3855            .unwrap();
3856            assert_eq!(protector.unseal_key(&keys).unwrap(), old_dek);
3857        }
3858    }
3859
3860    #[async_test]
3861    async fn derived_gsp_tracks_writes_but_gsp_by_id_does_not(driver: DefaultDriver) {
3862        for use_gsp_by_id in [false, true] {
3863            for fail_derivation in [false, true] {
3864                let mut vmgs = new_formatted_vmgs().await;
3865                let mut config = new_attestation_vm_config();
3866                config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
3867                let mut tee = BootReportTee::new();
3868                tee.fail_derivation = fail_derivation;
3869                let mut gsp = get_protocol::GuestStateProtectionResponse::new_zeroed();
3870                gsp.message_header = get_protocol::HeaderGeneric::new(
3871                    get_protocol::HostRequests::GUEST_STATE_PROTECTION,
3872                );
3873                if !use_gsp_by_id {
3874                    gsp.encrypted_gsp.length = 32;
3875                    gsp.encrypted_gsp.buffer[..32].fill(0x55);
3876                }
3877                let mut responses = vec![TestGetResponses::new(Event::Response(
3878                    gsp.as_bytes().to_vec(),
3879                ))];
3880                if use_gsp_by_id {
3881                    let mut by_id = get_protocol::GuestStateProtectionByIdResponse::new_zeroed();
3882                    by_id.message_header = get_protocol::HeaderGeneric::new(
3883                        get_protocol::HostRequests::GUEST_STATE_PROTECTION_BY_ID,
3884                    );
3885                    by_id.seed.length = 32;
3886                    by_id.seed.buffer[..32].fill(0x66);
3887                    responses.push(TestGetResponses::new(Event::Response(
3888                        by_id.as_bytes().to_vec(),
3889                    )));
3890                }
3891                let get_pair = guest_emulation_transport::test_utilities::new_transport_pair(
3892                    driver.clone(),
3893                    Some(responses),
3894                    get_protocol::ProtocolVersion::NICKEL_REV2,
3895                    None,
3896                    None,
3897                )
3898                .await;
3899                let mut kp = KeyProtector::new_zeroed();
3900                let mut kp_by_id = new_key_protector_by_id(Some(Guid::default()), None, false);
3901                let bios_guid = Guid::new_random();
3902                let derived = get_derived_keys(
3903                    &get_pair.client,
3904                    Some(&tee),
3905                    &mut vmgs,
3906                    &mut kp,
3907                    &mut kp_by_id,
3908                    bios_guid,
3909                    &config,
3910                    false,
3911                    None,
3912                    None,
3913                    Some(KeyDerivationPolicy {
3914                        svn: tee_call::KeyDerivationSvn::Snp {
3915                            tcb_version: tee.inner.tcb_version,
3916                        },
3917                        mix_measurement: true,
3918                    }),
3919                    GuestStateEncryptionPolicy::Auto,
3920                    true,
3921                    false,
3922                    false,
3923                )
3924                .await
3925                .unwrap();
3926                let eligible = !use_gsp_by_id && !fail_derivation;
3927                assert_eq!(derived.hardware_key_protector_written, eligible);
3928                assert!(derived.hardware_key_protector.is_none());
3929                assert_eq!(derived.key_protector_settings.use_gsp_by_id, use_gsp_by_id);
3930                let sealed_key = derived
3931                    .derived_keys
3932                    .as_ref()
3933                    .filter(|_| derived.hardware_key_protector_written)
3934                    .map(|keys| keys.encrypt_egress);
3935                unlock_vmgs_data_store(
3936                    &mut vmgs,
3937                    false,
3938                    &mut kp,
3939                    &mut kp_by_id,
3940                    derived.hardware_key_protector,
3941                    derived.derived_keys,
3942                    derived.key_protector_settings,
3943                    bios_guid,
3944                )
3945                .await
3946                .unwrap();
3947                assert!(vmgs.encrypted());
3948                assert_eq!(
3949                    finalize_hardware_sealing(&mut vmgs, sealed_key, false)
3950                        .await
3951                        .unwrap(),
3952                    eligible
3953                );
3954            }
3955        }
3956    }
3957
3958    #[async_test]
3959    async fn old_egress_unlock_does_not_enroll_different_sealed_key() {
3960        let mut vmgs = new_formatted_vmgs().await;
3961        let active_dek = [0x33; AES_GCM_KEY_LENGTH];
3962        let sealed_dek = [0x44; AES_GCM_KEY_LENGTH];
3963        vmgs.test_add_new_encryption_key(&active_dek, EncryptionAlgorithm::AES_GCM)
3964            .await
3965            .unwrap();
3966        let tee = BootReportTee::new();
3967        let mut config = new_attestation_vm_config();
3968        config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
3969        let hardware_keys = HardwareDerivedKeys::derive_key(
3970            tee.supports_get_derived_key().unwrap(),
3971            &config,
3972            KeyDerivationPolicy {
3973                svn: tee_call::KeyDerivationSvn::Snp {
3974                    tcb_version: tee.inner.tcb_version,
3975                },
3976                mix_measurement: true,
3977            },
3978        )
3979        .unwrap();
3980        let protector = hardware_key_sealing::seal_key(&hardware_keys, &sealed_dek).unwrap();
3981        let mut key_protector = KeyProtector::new_zeroed();
3982        let mut key_protector_by_id = new_key_protector_by_id(None, None, false);
3983        unlock_vmgs_data_store(
3984            &mut vmgs,
3985            true,
3986            &mut key_protector,
3987            &mut key_protector_by_id,
3988            Some(protector),
3989            Some(Keys {
3990                ingress: sealed_dek,
3991                decrypt_egress: Some(active_dek),
3992                encrypt_egress: sealed_dek,
3993            }),
3994            KeyProtectorSettings {
3995                should_write_kp: false,
3996                use_gsp_by_id: false,
3997                use_hardware_unlock: false,
3998                decrypt_gsp_type: GspType::None,
3999                encrypt_gsp_type: GspType::None,
4000            },
4001            Guid::new_random(),
4002        )
4003        .await
4004        .unwrap();
4005        assert_eq!(vmgs.active_encryption_key().unwrap(), &active_dek);
4006        assert!(
4007            !finalize_hardware_sealing(&mut vmgs, Some(sealed_dek), false)
4008                .await
4009                .unwrap()
4010        );
4011        assert!(
4012            !finalize_hardware_sealing(&mut vmgs, None, false)
4013                .await
4014                .unwrap()
4015        );
4016    }
4017
4018    fn init_sec_with_retry_reports(
4019        report_svns: [u64; 2],
4020    ) -> (
4021        Option<runtime_sealing::RuntimeTcbFloor>,
4022        [u8; AES_GCM_KEY_LENGTH],
4023    ) {
4024        // GET tasks need their own running executor. Keep the local executor
4025        // alive around initialization itself so its real one-second retry timer
4026        // progresses, rather than returning an orphaned LocalDriver.
4027        let (get_thread, driver) = pal_async::DefaultPool::spawn_on_thread("boot-report-retry-get");
4028        let result = pal_async::local::block_with_io(async |ldriver| {
4029            let mut plan = IgvmAgentTestPlan::default();
4030            plan.insert(
4031                IgvmAttestRequestType::KEY_RELEASE_REQUEST,
4032                VecDeque::from([
4033                    IgvmAgentAction::RespondSuccess,
4034                    // RespondFailure and RespondFailureSkipHwUnsealing both
4035                    // explicitly set retry=false. NoResponse completes GET
4036                    // with an empty response, producing a retryable parse error.
4037                    IgvmAgentAction::NoResponse,
4038                    IgvmAgentAction::RespondSuccess,
4039                ]),
4040            );
4041            let get_pair = new_test_get(driver, true, Some(plan)).await;
4042            let bios_guid = Guid::new_random();
4043            let mut config = new_attestation_vm_config();
4044            config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
4045            let disk = new_test_file();
4046            let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
4047            let provision_tee = BootReportTee::new();
4048
4049            // Provisioning is setup only: neither its report nor its collector
4050            // participates in the single retried initialization below.
4051            let provisioned = initialize_platform_security(
4052                &get_pair.client,
4053                bios_guid,
4054                &config,
4055                &mut vmgs,
4056                Some(&provision_tee),
4057                false,
4058                ldriver.clone(),
4059                GuestStateEncryptionPolicy::Auto,
4060                true,
4061            )
4062            .await
4063            .unwrap();
4064            assert!(provisioned.runtime_tcb_floor.is_some());
4065            assert_eq!(provision_tee.report_data.lock().len(), 1);
4066            assert!(vmgs.encrypted());
4067            assert!(!hardware_key_protector_is_empty(&mut vmgs).await);
4068
4069            // A working hardware backup would make the first failed SKR
4070            // attempt succeed without retrying. Remove only that backup and
4071            // reopen the encrypted store to require an actual SKR unlock.
4072            vmgs.delete_file(FileId::HW_KEY_PROTECTOR).await.unwrap();
4073            vmgs.flush().await.unwrap();
4074            drop(vmgs);
4075            let mut vmgs = Vmgs::open(disk, None).await.unwrap();
4076            assert!(vmgs.encrypted());
4077            assert!(matches!(
4078                vmgs.active_encryption_key(),
4079                Err(::vmgs::Error::NeedsUnlock)
4080            ));
4081            assert!(hardware_key_protector_is_empty(&mut vmgs).await);
4082
4083            let tee = SequencedBootReportTee {
4084                inner: BootReportTee::new(),
4085                report_svns: parking_lot::Mutex::new(report_svns.into()),
4086            };
4087            let recovered = initialize_platform_security(
4088                &get_pair.client,
4089                bios_guid,
4090                &config,
4091                &mut vmgs,
4092                Some(&tee),
4093                false,
4094                ldriver,
4095                GuestStateEncryptionPolicy::Auto,
4096                true,
4097            )
4098            .await
4099            .unwrap();
4100
4101            // Both attempts belong to the invocation above. No collector or
4102            // fallback report may be requested, and both keep their SKR hash.
4103            assert!(tee.report_svns.lock().is_empty());
4104            {
4105                let reports = tee.inner.report_data.lock();
4106                assert_eq!(reports.len(), 2);
4107                assert!(reports.iter().all(|data| *data != [0; REPORT_DATA_SIZE]));
4108            }
4109            assert_eq!(provision_tee.report_data.lock().len(), 1);
4110            assert!(vmgs.encrypted());
4111            let active_dek = *vmgs.active_encryption_key().unwrap();
4112
4113            // Boot sealing still uses the successful report's SVN, including
4114            // a lower SVN, rather than substituting the runtime collector's
4115            // high-water mark or making that collector's failure fatal.
4116            let protector = vmgs::read_hardware_key_protector(&mut vmgs).await.unwrap();
4117            let policy = protector.key_derivation_policy().unwrap();
4118            assert!(policy.mix_measurement);
4119            assert!(matches!(
4120                policy.svn,
4121                tee_call::KeyDerivationSvn::Snp { tcb_version } if tcb_version == report_svns[1]
4122            ));
4123            let keys = HardwareDerivedKeys::derive_key(
4124                tee.supports_get_derived_key().unwrap(),
4125                &config,
4126                policy,
4127            )
4128            .unwrap();
4129            assert_eq!(protector.unseal_key(&keys).unwrap(), active_dek);
4130            vmgs.unlock_with_encryption_key(&active_dek).await.unwrap();
4131
4132            (recovered.runtime_tcb_floor, active_dek)
4133        });
4134        get_thread.join().unwrap();
4135        result
4136    }
4137
4138    #[test]
4139    fn init_sec_retry_high_then_lower_svn_succeeds_without_runtime_floor() {
4140        let (floor, _) = init_sec_with_retry_reports([0x1235, 0x1234]);
4141        // Recreating the collector on retry, or observing only successful SKR
4142        // reports, would incorrectly export the second (lower) report here.
4143        assert!(floor.is_none());
4144    }
4145
4146    #[test]
4147    fn init_sec_retry_low_then_higher_svn_exports_highest_runtime_floor() {
4148        let (floor, active_dek) = init_sec_with_retry_reports([0x1234, 0x1235]);
4149        let mut floor = floor.unwrap();
4150        let mut config = new_attestation_vm_config();
4151        config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
4152
4153        // Use a separate runtime TEE so these intentional fresh reports cannot
4154        // hide extra boot report acquisitions in the two-attempt assertion.
4155        let mut tee = BootReportTee::new();
4156        let err = floor
4157            .create_protector(&tee, &config, &active_dek)
4158            .unwrap_err();
4159        assert_eq!(
4160            err.to_string(),
4161            "local TCB has a component below the runtime floor"
4162        );
4163        tee.inner.tcb_version = 0x1235;
4164        let protector = floor.create_protector(&tee, &config, &active_dek).unwrap();
4165        assert!(
4166            runtime_sealing::protector_matches(&tee, &config, &protector, &active_dek).unwrap()
4167        );
4168        assert_eq!(&*tee.report_data.lock(), &[[0; REPORT_DATA_SIZE]; 2]);
4169    }
4170
4171    #[async_test]
4172    async fn init_sec_hardware_cached_lower_svn_preserves_boot_policy(driver: DefaultDriver) {
4173        let get_pair = new_test_get(driver, false, None).await;
4174        for malformed in [false, true] {
4175            let mut vmgs = new_formatted_vmgs().await;
4176            let bootstrap = [0x33; AES_GCM_KEY_LENGTH];
4177            vmgs.test_add_new_encryption_key(&bootstrap, EncryptionAlgorithm::AES_GCM)
4178                .await
4179                .unwrap();
4180            let mut tee = BootReportTee::new();
4181            let mut config = new_attestation_vm_config();
4182            config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
4183            let cached_svn = tee.inner.tcb_version;
4184            let keys = HardwareDerivedKeys::derive_key(
4185                tee.supports_get_derived_key().unwrap(),
4186                &config,
4187                KeyDerivationPolicy {
4188                    svn: tee_call::KeyDerivationSvn::Snp {
4189                        tcb_version: cached_svn,
4190                    },
4191                    mix_measurement: true,
4192                },
4193            )
4194            .unwrap();
4195            let protector = hardware_key_sealing::seal_key(&keys, &bootstrap).unwrap();
4196            vmgs::write_hardware_key_protector(&protector, &mut vmgs)
4197                .await
4198                .unwrap();
4199
4200            // Current hardware has advanced, but the boot unseal/rotation path
4201            // must continue using the older cached policy, not the runtime floor.
4202            tee.inner.tcb_version += 1;
4203            tee.malformed = malformed;
4204            let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
4205            let result = initialize_platform_security(
4206                &get_pair.client,
4207                Guid::new_random(),
4208                &config,
4209                &mut vmgs,
4210                Some(&tee),
4211                true,
4212                ldriver,
4213                GuestStateEncryptionPolicy::HardwareSealing,
4214                true,
4215            )
4216            .await
4217            .unwrap();
4218            assert!(vmgs.encrypted());
4219            assert!(!result.host_attestation_settings.refresh_tpm_seeds);
4220            // Exactly the original hardware-only report; no collector report.
4221            assert_eq!(&*tee.report_data.lock(), &[[0; REPORT_DATA_SIZE]]);
4222            let rotated = vmgs::read_hardware_key_protector(&mut vmgs).await.unwrap();
4223            assert!(matches!(
4224                rotated.key_derivation_policy().unwrap().svn,
4225                tee_call::KeyDerivationSvn::Snp { tcb_version } if tcb_version == cached_svn
4226            ));
4227            let active_dek = rotated.unseal_key(&keys).unwrap();
4228            vmgs.unlock_with_encryption_key(&active_dek).await.unwrap();
4229
4230            if malformed {
4231                // Collection failure must not make a previously valid boot fail.
4232                assert!(result.runtime_tcb_floor.is_none());
4233            } else {
4234                let mut floor = result.runtime_tcb_floor.unwrap();
4235                tee.inner.tcb_version = cached_svn;
4236                let err = floor
4237                    .create_protector(&tee, &config, &active_dek)
4238                    .unwrap_err();
4239                assert_eq!(
4240                    err.to_string(),
4241                    "local TCB has a component below the runtime floor"
4242                );
4243            }
4244        }
4245    }
4246
4247    #[async_test]
4248    async fn init_sec_skr_failure_hardware_fallback_exports_existing_report(driver: DefaultDriver) {
4249        let mut vmgs = new_formatted_vmgs().await;
4250        let mut plan = IgvmAgentTestPlan::default();
4251        plan.insert(
4252            IgvmAttestRequestType::WRAPPED_KEY_REQUEST,
4253            VecDeque::from([
4254                IgvmAgentAction::RespondSuccess,
4255                IgvmAgentAction::RespondFailure,
4256            ]),
4257        );
4258        let get_pair = new_test_get(driver, true, Some(plan)).await;
4259        let bios_guid = Guid::new_random();
4260        let mut config = new_attestation_vm_config();
4261        config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
4262        let mut tee = BootReportTee::new();
4263        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
4264        let first = initialize_platform_security(
4265            &get_pair.client,
4266            bios_guid,
4267            &config,
4268            &mut vmgs,
4269            Some(&tee),
4270            false,
4271            ldriver.clone(),
4272            GuestStateEncryptionPolicy::Auto,
4273            true,
4274        )
4275        .await
4276        .unwrap();
4277        assert!(first.runtime_tcb_floor.is_some());
4278        assert_eq!(tee.report_data.lock().len(), 1);
4279        assert!(!hardware_key_protector_is_empty(&mut vmgs).await);
4280
4281        // SKR fails after report acquisition on the next boot. The fallback
4282        // unseals at the cached SVN, but must export the newer observed floor.
4283        let cached_svn = tee.inner.tcb_version;
4284        tee.inner.tcb_version += 1;
4285        let recovered = initialize_platform_security(
4286            &get_pair.client,
4287            bios_guid,
4288            &config,
4289            &mut vmgs,
4290            Some(&tee),
4291            false,
4292            ldriver,
4293            GuestStateEncryptionPolicy::Auto,
4294            true,
4295        )
4296        .await
4297        .unwrap();
4298        assert!(vmgs.encrypted());
4299        {
4300            let reports = tee.report_data.lock();
4301            assert_eq!(reports.len(), 2);
4302            // Both reports retain their SKR claims hashes, not zero report_data.
4303            assert!(reports.iter().all(|data| *data != [0; REPORT_DATA_SIZE]));
4304        }
4305        let mut floor = recovered.runtime_tcb_floor.unwrap();
4306        tee.inner.tcb_version = cached_svn;
4307        let err = floor.create_protector(&tee, &config, &[0; 32]).unwrap_err();
4308        assert_eq!(
4309            err.to_string(),
4310            "local TCB has a component below the runtime floor"
4311        );
4312    }
4313
4314    #[async_test]
4315    async fn init_sec_suppressed_eligible_tee_does_not_bootstrap_floor(driver: DefaultDriver) {
4316        let mut vmgs = new_formatted_vmgs().await;
4317        let get_pair = new_test_get(driver, false, None).await;
4318        let tee = BootReportTee::new();
4319        let mut config = new_attestation_vm_config();
4320        config.hardware_sealing_policy = HardwareSealingPolicy::Hash;
4321        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
4322        let result = initialize_platform_security(
4323            &get_pair.client,
4324            Guid::new_random(),
4325            &config,
4326            &mut vmgs,
4327            Some(&tee),
4328            true,
4329            ldriver,
4330            GuestStateEncryptionPolicy::None,
4331            true,
4332        )
4333        .await
4334        .unwrap();
4335        assert!(result.runtime_tcb_floor.is_none());
4336        assert!(tee.report_data.lock().is_empty());
4337        assert_eq!(*tee.derivation_calls.lock(), 0);
4338        assert!(!vmgs.encrypted());
4339    }
4340
4341    #[async_test]
4342    async fn init_sec_suppress_attestation(driver: DefaultDriver) {
4343        let mut vmgs = new_formatted_vmgs().await;
4344
4345        // Write non-zero agent data to VMGS so we can verify it is returned.
4346        let agent = SecurityProfile {
4347            agent_data: [0xAA; AGENT_DATA_MAX_SIZE],
4348        };
4349        vmgs.write_file(FileId::ATTEST, agent.as_bytes())
4350            .await
4351            .unwrap();
4352
4353        // Ensure no IGVM attest call out
4354        let get_pair = new_test_get(driver, false, None).await;
4355
4356        let bios_guid = Guid::new_random();
4357        let att_cfg = test_attestation_config();
4358
4359        // Ensure VMGS is not encrypted and agent data is empty before the call
4360        assert!(!vmgs.encrypted());
4361
4362        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
4363        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
4364        let res = initialize_platform_security(
4365            &get_pair.client,
4366            bios_guid,
4367            &att_cfg,
4368            &mut vmgs,
4369            None, // no TEE when suppressed
4370            true, // suppress_attestation
4371            ldriver,
4372            GuestStateEncryptionPolicy::None,
4373            true,
4374        )
4375        .await
4376        .unwrap();
4377
4378        // VMGS remains unencrypted and KP/HWKP not written.
4379        assert!(!vmgs.encrypted());
4380        assert!(key_protector_is_empty(&mut vmgs).await);
4381        assert!(hardware_key_protector_is_empty(&mut vmgs).await);
4382        // Agent data passed through
4383        assert_eq!(res.agent_data.unwrap(), agent.agent_data.to_vec());
4384        // Secure key should be None without pre-provisioning
4385        assert!(res.guest_secret_key.is_none());
4386    }
4387
4388    #[async_test]
4389    async fn init_sec_secure_key_release_with_wrapped_key_request(driver: DefaultDriver) {
4390        let mut vmgs = new_formatted_vmgs().await;
4391
4392        // IGVM attest is required
4393        let get_pair = new_test_get(driver, true, None).await;
4394
4395        let bios_guid = Guid::new_random();
4396        let att_cfg = new_attestation_vm_config();
4397        let tee = MockTeeCall::new([0x12u8; 32]);
4398
4399        // Ensure VMGS is not encrypted and agent data is empty before the call
4400        assert!(!vmgs.encrypted());
4401
4402        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
4403        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
4404        let res = initialize_platform_security(
4405            &get_pair.client,
4406            bios_guid,
4407            &att_cfg,
4408            &mut vmgs,
4409            Some(&tee),
4410            false,
4411            ldriver.clone(),
4412            GuestStateEncryptionPolicy::Auto,
4413            true,
4414        )
4415        .await
4416        .unwrap();
4417
4418        // VMGS is now encrypted and HWKP is updated.
4419        assert!(vmgs.encrypted());
4420        assert!(!hardware_key_protector_is_empty(&mut vmgs).await);
4421
4422        // Agent data should be the same as `key_reference` in the WRAPPED_KEY response.
4423        // See vm/devices/get/guest_emulation_device/src/test_igvm_agent.rs for the expected response.
4424        let key_reference = serde_json::json!({
4425            "key_info": {
4426                "host": "name"
4427            },
4428            "attestation_info": {
4429                "host": "attestation_name"
4430            }
4431        });
4432        let key_reference = serde_json::to_string(&key_reference).unwrap();
4433        let key_reference = key_reference.as_bytes();
4434        let mut expected_agent_data = [0u8; AGENT_DATA_MAX_SIZE];
4435        expected_agent_data[..key_reference.len()].copy_from_slice(key_reference);
4436        assert_eq!(res.agent_data.unwrap(), expected_agent_data.to_vec());
4437        // Secure key should be None without pre-provisioning
4438        assert!(res.guest_secret_key.is_none());
4439
4440        // Second call: VMGS unlock via SKR should succeed
4441        initialize_platform_security(
4442            &get_pair.client,
4443            bios_guid,
4444            &att_cfg,
4445            &mut vmgs,
4446            Some(&tee),
4447            false,
4448            ldriver,
4449            GuestStateEncryptionPolicy::Auto,
4450            true,
4451        )
4452        .await
4453        .unwrap();
4454
4455        // VMGS should remain encrypted
4456        assert!(vmgs.encrypted());
4457    }
4458
4459    #[async_test]
4460    async fn init_sec_secure_key_release_without_wrapped_key_request(driver: DefaultDriver) {
4461        let mut vmgs = new_formatted_vmgs().await;
4462
4463        // Write non-zero agent data to workaround the WRAPPED_KEY_REQUEST requirement.
4464        let agent = SecurityProfile {
4465            agent_data: [0xAA; AGENT_DATA_MAX_SIZE],
4466        };
4467        vmgs.write_file(FileId::ATTEST, agent.as_bytes())
4468            .await
4469            .unwrap();
4470
4471        // Skip WRAPPED_KEY_REQUEST for both boots
4472        let mut plan = IgvmAgentTestPlan::default();
4473        plan.insert(
4474            IgvmAttestRequestType::WRAPPED_KEY_REQUEST,
4475            VecDeque::from([IgvmAgentAction::NoResponse, IgvmAgentAction::NoResponse]),
4476        );
4477
4478        // IGVM attest is required
4479        let get_pair = new_test_get(driver, true, Some(plan)).await;
4480
4481        let bios_guid = Guid::new_random();
4482        let att_cfg = new_attestation_vm_config();
4483        let tee = MockTeeCall::new([0x12u8; 32]);
4484
4485        // Ensure VMGS is not encrypted and agent data is empty before the call
4486        assert!(!vmgs.encrypted());
4487
4488        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
4489        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
4490        let res = initialize_platform_security(
4491            &get_pair.client,
4492            bios_guid,
4493            &att_cfg,
4494            &mut vmgs,
4495            Some(&tee),
4496            false,
4497            ldriver.clone(),
4498            GuestStateEncryptionPolicy::Auto,
4499            true,
4500        )
4501        .await
4502        .unwrap();
4503
4504        // VMGS is now encrypted and HWKP is updated.
4505        assert!(vmgs.encrypted());
4506        assert!(!hardware_key_protector_is_empty(&mut vmgs).await);
4507        // Agent data passed through
4508        assert_eq!(res.agent_data.clone().unwrap(), agent.agent_data.to_vec());
4509        // Secure key should be None without pre-provisioning
4510        assert!(res.guest_secret_key.is_none());
4511
4512        // Second call: VMGS unlock via SKR should succeed
4513        let res = initialize_platform_security(
4514            &get_pair.client,
4515            bios_guid,
4516            &att_cfg,
4517            &mut vmgs,
4518            Some(&tee),
4519            false,
4520            ldriver,
4521            GuestStateEncryptionPolicy::Auto,
4522            true,
4523        )
4524        .await
4525        .unwrap();
4526
4527        // VMGS should remain encrypted
4528        assert!(vmgs.encrypted());
4529        // Agent data passed through
4530        assert_eq!(res.agent_data.clone().unwrap(), agent.agent_data.to_vec());
4531        // Secure key should be None without pre-provisioning
4532        assert!(res.guest_secret_key.is_none());
4533    }
4534
4535    #[async_test]
4536    async fn init_sec_secure_key_release_hw_sealing_backup(driver: DefaultDriver) {
4537        let mut vmgs = new_formatted_vmgs().await;
4538
4539        // IGVM attest is required
4540        let mut plan = IgvmAgentTestPlan::default();
4541        plan.insert(
4542            IgvmAttestRequestType::WRAPPED_KEY_REQUEST,
4543            VecDeque::from([
4544                IgvmAgentAction::RespondSuccess,
4545                // initialize_platform_security will attempt SKR/unlock 10 times
4546                IgvmAgentAction::RespondFailure,
4547                IgvmAgentAction::RespondFailure,
4548                IgvmAgentAction::RespondFailure,
4549                IgvmAgentAction::RespondFailure,
4550                IgvmAgentAction::RespondFailure,
4551                IgvmAgentAction::RespondFailure,
4552                IgvmAgentAction::RespondFailure,
4553                IgvmAgentAction::RespondFailure,
4554                IgvmAgentAction::RespondFailure,
4555                IgvmAgentAction::RespondFailure,
4556            ]),
4557        );
4558
4559        let get_pair = new_test_get(driver, true, Some(plan)).await;
4560
4561        let bios_guid = Guid::new_random();
4562        let att_cfg = test_attestation_config();
4563
4564        // Ensure VMGS is not encrypted and agent data is empty before the call
4565        assert!(!vmgs.encrypted());
4566
4567        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
4568        let tee = MockTeeCall::new([0x12u8; 32]);
4569        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
4570        let res = initialize_platform_security(
4571            &get_pair.client,
4572            bios_guid,
4573            &att_cfg,
4574            &mut vmgs,
4575            Some(&tee),
4576            false,
4577            ldriver.clone(),
4578            GuestStateEncryptionPolicy::Auto,
4579            true,
4580        )
4581        .await
4582        .unwrap();
4583
4584        // VMGS is now encrypted and HWKP is updated.
4585        assert!(vmgs.encrypted());
4586        assert!(!hardware_key_protector_is_empty(&mut vmgs).await);
4587        // Agent data should be the same as `key_reference` in the WRAPPED_KEY response.
4588        // See vm/devices/get/guest_emulation_device/src/test_igvm_agent.rs for the expected response.
4589        let key_reference = serde_json::json!({
4590            "key_info": {
4591                "host": "name"
4592            },
4593            "attestation_info": {
4594                "host": "attestation_name"
4595            }
4596        });
4597        let key_reference = serde_json::to_string(&key_reference).unwrap();
4598        let key_reference = key_reference.as_bytes();
4599        let mut expected_agent_data = [0u8; AGENT_DATA_MAX_SIZE];
4600        expected_agent_data[..key_reference.len()].copy_from_slice(key_reference);
4601        assert_eq!(res.agent_data.unwrap(), expected_agent_data.to_vec());
4602        // Secure key should be None without pre-provisioning
4603        assert!(res.guest_secret_key.is_none());
4604
4605        // Second call: VMGS unlock via key recovered with hardware sealing
4606        // NOTE: The test relies on the test GED to return failing WRAPPED_KEY response
4607        // with retry recommendation as false to skip the retry loop in
4608        // secure_key_release::request_vmgs_encryption_keys. Otherwise, the test will stuck
4609        // on the timer.sleep() as the the driver is not progressed.
4610        initialize_platform_security(
4611            &get_pair.client,
4612            bios_guid,
4613            &att_cfg,
4614            &mut vmgs,
4615            Some(&tee),
4616            false,
4617            ldriver,
4618            GuestStateEncryptionPolicy::Auto,
4619            true,
4620        )
4621        .await
4622        .unwrap();
4623
4624        // VMGS should remain encrypted
4625        assert!(vmgs.encrypted());
4626    }
4627
4628    #[async_test]
4629    async fn init_sec_secure_key_release_skip_hw_unsealing(driver: DefaultDriver) {
4630        let mut vmgs = new_formatted_vmgs().await;
4631
4632        // IGVM attest is required
4633        // KEY_RELEASE succeeds on first boot, fails with skip_hw_unsealing on second boot.
4634        // WRAPPED_KEY is not in the plan, so it falls back to default (success) every time.
4635        let mut plan = IgvmAgentTestPlan::default();
4636        plan.insert(
4637            IgvmAttestRequestType::KEY_RELEASE_REQUEST,
4638            VecDeque::from([
4639                IgvmAgentAction::RespondSuccess,
4640                IgvmAgentAction::RespondFailureSkipHwUnsealing,
4641            ]),
4642        );
4643
4644        let get_pair = new_test_get(driver, true, Some(plan)).await;
4645
4646        let bios_guid = Guid::new_random();
4647        let att_cfg = test_attestation_config();
4648
4649        // Ensure VMGS is not encrypted and agent data is empty before the call
4650        assert!(!vmgs.encrypted());
4651
4652        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
4653        let tee = MockTeeCall::new([0x12u8; 32]);
4654        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
4655        let res = initialize_platform_security(
4656            &get_pair.client,
4657            bios_guid,
4658            &att_cfg,
4659            &mut vmgs,
4660            Some(&tee),
4661            false,
4662            ldriver.clone(),
4663            GuestStateEncryptionPolicy::Auto,
4664            true,
4665        )
4666        .await
4667        .unwrap();
4668
4669        // VMGS is now encrypted and HWKP is updated.
4670        assert!(vmgs.encrypted());
4671        assert!(!hardware_key_protector_is_empty(&mut vmgs).await);
4672        // Agent data should be the same as `key_reference` in the WRAPPED_KEY response.
4673        let key_reference = serde_json::json!({
4674            "key_info": {
4675                "host": "name"
4676            },
4677            "attestation_info": {
4678                "host": "attestation_name"
4679            }
4680        });
4681        let key_reference = serde_json::to_string(&key_reference).unwrap();
4682        let key_reference = key_reference.as_bytes();
4683        let mut expected_agent_data = [0u8; AGENT_DATA_MAX_SIZE];
4684        expected_agent_data[..key_reference.len()].copy_from_slice(key_reference);
4685        assert_eq!(res.agent_data.unwrap(), expected_agent_data.to_vec());
4686        // Secure key should be None without pre-provisioning
4687        assert!(res.guest_secret_key.is_none());
4688
4689        // Second call: KEY_RELEASE fails with skip_hw_unsealing signal.
4690        // The skip_hw_unsealing signal causes the hardware unsealing fallback to be
4691        // skipped, so VMGS unlock should fail.
4692        // NOTE: The test relies on the test GED to return failing KEY_RELEASE response
4693        // with retry recommendation as false so the retry loop terminates immediately.
4694        // Otherwise, the test will get stuck on timer.sleep() as the driver is not
4695        // progressed.
4696        let result = initialize_platform_security(
4697            &get_pair.client,
4698            bios_guid,
4699            &att_cfg,
4700            &mut vmgs,
4701            Some(&tee),
4702            false,
4703            ldriver,
4704            GuestStateEncryptionPolicy::Auto,
4705            true,
4706        )
4707        .await;
4708
4709        assert!(result.is_err());
4710    }
4711
4712    #[async_test]
4713    async fn init_sec_secure_key_release_no_hw_sealing_backup(driver: DefaultDriver) {
4714        let mut vmgs = new_formatted_vmgs().await;
4715
4716        // IGVM attest is required
4717        let mut plan = IgvmAgentTestPlan::default();
4718        plan.insert(
4719            IgvmAttestRequestType::WRAPPED_KEY_REQUEST,
4720            VecDeque::from([
4721                IgvmAgentAction::RespondSuccess,
4722                // initialize_platform_security will attempt SKR/unlock 10 times
4723                IgvmAgentAction::RespondFailure,
4724                IgvmAgentAction::RespondFailure,
4725                IgvmAgentAction::RespondFailure,
4726                IgvmAgentAction::RespondFailure,
4727                IgvmAgentAction::RespondFailure,
4728                IgvmAgentAction::RespondFailure,
4729                IgvmAgentAction::RespondFailure,
4730                IgvmAgentAction::RespondFailure,
4731                IgvmAgentAction::RespondFailure,
4732                IgvmAgentAction::RespondFailure,
4733            ]),
4734        );
4735
4736        let get_pair = new_test_get(driver, true, Some(plan)).await;
4737
4738        let bios_guid = Guid::new_random();
4739        let att_cfg = test_attestation_config();
4740        // Without hardware sealing support
4741        let tee = MockTeeCallNoGetDerivedKey {};
4742
4743        // Ensure VMGS is not encrypted and agent data is empty before the call
4744        assert!(!vmgs.encrypted());
4745
4746        // Obtain a LocalDriver briefly, then run the async flow under the pool executor
4747        let ldriver = pal_async::local::block_with_io(|ld| async move { ld });
4748        let res = initialize_platform_security(
4749            &get_pair.client,
4750            bios_guid,
4751            &att_cfg,
4752            &mut vmgs,
4753            Some(&tee),
4754            false,
4755            ldriver.clone(),
4756            GuestStateEncryptionPolicy::Auto,
4757            true,
4758        )
4759        .await
4760        .unwrap();
4761
4762        // VMGS is now encrypted but HWKP remains empty.
4763        assert!(vmgs.encrypted());
4764        assert!(hardware_key_protector_is_empty(&mut vmgs).await);
4765        // Agent data should be the same as `key_reference` in the WRAPPED_KEY response.
4766        // See vm/devices/get/guest_emulation_device/src/test_igvm_agent.rs for the expected response.
4767        let key_reference = serde_json::json!({
4768            "key_info": {
4769                "host": "name"
4770            },
4771            "attestation_info": {
4772                "host": "attestation_name"
4773            }
4774        });
4775        let key_reference = serde_json::to_string(&key_reference).unwrap();
4776        let key_reference = key_reference.as_bytes();
4777        let mut expected_agent_data = [0u8; AGENT_DATA_MAX_SIZE];
4778        expected_agent_data[..key_reference.len()].copy_from_slice(key_reference);
4779        assert_eq!(res.agent_data.unwrap(), expected_agent_data.to_vec());
4780        // Secure key should be None without pre-provisioning
4781        assert!(res.guest_secret_key.is_none());
4782
4783        // Second call: VMGS unlock should fail without hardware sealing support
4784        let result = initialize_platform_security(
4785            &get_pair.client,
4786            bios_guid,
4787            &att_cfg,
4788            &mut vmgs,
4789            Some(&tee),
4790            false,
4791            ldriver,
4792            GuestStateEncryptionPolicy::Auto,
4793            true,
4794        )
4795        .await;
4796
4797        assert!(result.is_err());
4798    }
4799
4800    #[test]
4801    fn test_get_provenance_claims() {
4802        // Test JWT: not a valid credential or secret for anything.
4803        const PROVENANCE_DOC: &str = include_str!("../test_data/valid_jwt");
4804        let doc = PROVENANCE_DOC.trim().strip_prefix("placeholder_").unwrap();
4805        let claims = get_provenance_claims(doc.as_bytes()).unwrap();
4806        assert_eq!(
4807            claims.id,
4808            guid::guid!("03020100-0504-0706-0809-0a0b0c0d0e0f")
4809        );
4810        assert_eq!(
4811            claims.signer,
4812            "did:x509:0:sha256:EA76599D86897382AA519FF2BC0FA6B9C15D60DA2EBE53E72139CD317B0797ED:subject:fican.cvmprovisioningservice.core.azure-test.net"
4813        );
4814    }
4815
4816    #[test]
4817    fn test_derive_vmgsid() {
4818        const SEED_DOC_1: &str = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F,4C6162656C5F435053,436F6E746578745F564D4753,32";
4819        const SEED_DOC_2: &str = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F,4C6162656C5F435053,436F6E746578745F564D4753";
4820        const SEED_DOC_3: &str = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F,4C6162656C5F435053,436F6E746578745F564D4753,32,ABCDEF";
4821        const GUID: Guid = guid::guid!("b0587f2d-11e6-9f66-1af4-8b4a619147c8");
4822
4823        let vmgsid1 = derive_vmgsid(SEED_DOC_1.as_bytes()).unwrap();
4824        assert_eq!(vmgsid1, GUID);
4825
4826        let vmgsid2 = derive_vmgsid(SEED_DOC_2.as_bytes()).unwrap();
4827        assert_eq!(vmgsid2, GUID);
4828
4829        let vmgsid3 = derive_vmgsid(SEED_DOC_3.as_bytes()).unwrap();
4830        assert_eq!(vmgsid3, GUID);
4831    }
4832}