Skip to content

Commit 806b6cc

Browse files
authored
openhcl: pass encryption policy and hcl feature flags (#1844)
Allow the host to specify OpenHCL features and encryption policy for the VMGS.
1 parent a2f1ada commit 806b6cc

File tree

11 files changed

+251
-25
lines changed

11 files changed

+251
-25
lines changed

openhcl/underhill_attestation/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ openssl-vendored = ["openssl/vendored"]
1515
guest_emulation_transport.workspace = true
1616
openhcl_attestation_protocol.workspace = true
1717
vmgs = { workspace = true, features = ["encryption_ossl"] }
18+
get_protocol.workspace = true
1819
guid.workspace = true
1920
mesh.workspace = true
2021
openssl_kdf.workspace = true
@@ -37,7 +38,6 @@ zerocopy.workspace = true
3738
[target.'cfg(target_os = "linux")'.dev-dependencies]
3839
disklayer_ram.workspace = true
3940
disk_backend.workspace = true
40-
get_protocol.workspace = true
4141
vmgs_format.workspace = true
4242

4343
[lints]

openhcl/underhill_attestation/src/lib.rs

Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub use igvm_attest::ak_cert::parse_response as parse_ak_cert_response;
2323
use ::vmgs::EncryptionAlgorithm;
2424
use ::vmgs::Vmgs;
2525
use cvm_tracing::CVM_ALLOWED;
26+
use get_protocol::dps_json::GuestStateEncryptionPolicy;
2627
use guest_emulation_transport::GuestEmulationTransportClient;
2728
use guest_emulation_transport::api::GspExtendedStatusFlags;
2829
use guest_emulation_transport::api::GuestStateProtection;
@@ -100,6 +101,8 @@ enum GetDerivedKeysError {
100101
GetIngressKeyFromKGspByIdFailed,
101102
#[error("Encryption cannot be disabled if VMGS was previously encrypted")]
102103
DisableVmgsEncryptionFailed,
104+
#[error("VMGS encryption is required, but no encryption sources were found")]
105+
EncryptionRequiredButNotFound,
103106
#[error("failed to seal the egress key using hardware derived keys")]
104107
SealEgressKeyUsingHardwareDerivedKeys(#[source] hardware_key_sealing::HardwareKeySealingError),
105108
#[error("failed to write to `FileId::HW_KEY_PROTECTOR` in vmgs")]
@@ -237,6 +240,8 @@ pub async fn initialize_platform_security(
237240
attestation_type: AttestationType,
238241
suppress_attestation: bool,
239242
driver: LocalDriver,
243+
guest_state_encryption_policy: GuestStateEncryptionPolicy,
244+
strict_encryption_policy: bool,
240245
) -> Result<PlatformAttestationData, Error> {
241246
tracing::info!(CVM_ALLOWED,
242247
attestation_type=?attestation_type,
@@ -357,6 +362,8 @@ pub async fn initialize_platform_security(
357362
ingress_rsa_kek.as_ref(),
358363
wrapped_des_key.as_deref(),
359364
tcb_version,
365+
guest_state_encryption_policy,
366+
strict_encryption_policy,
360367
)
361368
.await
362369
.map_err(AttestationErrorInner::GetDerivedKeys)?;
@@ -562,7 +569,24 @@ async fn get_derived_keys(
562569
ingress_rsa_kek: Option<&Rsa<Private>>,
563570
wrapped_des_key: Option<&[u8]>,
564571
tcb_version: Option<u64>,
572+
guest_state_encryption_policy: GuestStateEncryptionPolicy,
573+
strict_encryption_policy: bool,
565574
) -> Result<DerivedKeyResult, GetDerivedKeysError> {
575+
tracing::info!(
576+
CVM_ALLOWED,
577+
?guest_state_encryption_policy,
578+
strict_encryption_policy,
579+
"encryption policy"
580+
);
581+
582+
// TODO: implement hardware sealing only
583+
if matches!(
584+
guest_state_encryption_policy,
585+
GuestStateEncryptionPolicy::HardwareSealing
586+
) {
587+
todo!("hardware sealing")
588+
}
589+
566590
let mut key_protector_settings = KeyProtectorSettings {
567591
should_write_kp: true,
568592
use_gsp_by_id: false,
@@ -626,13 +650,20 @@ async fn get_derived_keys(
626650
};
627651

628652
// Handle various sources of Guest State Protection
629-
let mut requires_gsp_by_id =
630-
key_protector_by_id.found_id && key_protector_by_id.inner.ported != 1;
653+
let is_gsp_by_id = key_protector_by_id.found_id && key_protector_by_id.inner.ported != 1;
654+
let is_gsp = key_protector.gsp[ingress_idx].gsp_length != 0;
655+
tracing::info!(
656+
CVM_ALLOWED,
657+
is_encrypted,
658+
is_gsp_by_id,
659+
is_gsp,
660+
found_dek,
661+
"initial vmgs encryption state"
662+
);
663+
let mut requires_gsp_by_id = is_gsp_by_id;
631664

632665
// Attempt GSP
633666
let (gsp_response, no_gsp, requires_gsp) = {
634-
let found_kp = key_protector.gsp[ingress_idx].gsp_length != 0;
635-
636667
let response = get_gsp_data(get, key_protector).await;
637668

638669
tracing::info!(
@@ -644,10 +675,19 @@ async fn get_derived_keys(
644675
"GSP response"
645676
);
646677

647-
let no_gsp =
648-
response.extended_status_flags.no_rpc_server() || response.encrypted_gsp.length == 0;
649-
650-
let requires_gsp = found_kp || response.extended_status_flags.requires_rpc_server();
678+
let no_gsp = response.extended_status_flags.no_rpc_server()
679+
|| response.encrypted_gsp.length == 0
680+
|| (matches!(
681+
guest_state_encryption_policy,
682+
GuestStateEncryptionPolicy::GspById | GuestStateEncryptionPolicy::None
683+
) && (!is_gsp || strict_encryption_policy));
684+
685+
let requires_gsp = is_gsp
686+
|| response.extended_status_flags.requires_rpc_server()
687+
|| matches!(
688+
guest_state_encryption_policy,
689+
GuestStateEncryptionPolicy::GspKey
690+
);
651691

652692
// If the VMGS is encrypted, but no key protection data is found,
653693
// assume GspById encryption is enabled, but no ID file was written.
@@ -658,14 +698,19 @@ async fn get_derived_keys(
658698
(response, no_gsp, requires_gsp)
659699
};
660700

661-
// Attempt GSP By Id protection if GSP is not available, or when changing schemes.
701+
// Attempt GSP By Id protection if GSP is not available, when changing
702+
// schemes, or as requested
662703
let (gsp_response_by_id, no_gsp_by_id) = if no_gsp || requires_gsp_by_id {
663704
let gsp_response_by_id = get
664705
.guest_state_protection_data_by_id()
665706
.await
666707
.map_err(GetDerivedKeysError::FetchGuestStateProtectionById)?;
667708

668-
let no_gsp_by_id = gsp_response_by_id.extended_status_flags.no_registry_file();
709+
let no_gsp_by_id = gsp_response_by_id.extended_status_flags.no_registry_file()
710+
|| (matches!(
711+
guest_state_encryption_policy,
712+
GuestStateEncryptionPolicy::None
713+
) && (!requires_gsp_by_id || strict_encryption_policy));
669714

670715
if no_gsp_by_id && requires_gsp_by_id {
671716
Err(GetDerivedKeysError::GspByIdRequiredButNotFound)?
@@ -768,14 +813,23 @@ async fn get_derived_keys(
768813
if is_encrypted {
769814
Err(GetDerivedKeysError::DisableVmgsEncryptionFailed)?
770815
}
771-
772-
tracing::trace!(CVM_ALLOWED, "No VMGS encryption used.");
773-
774-
return Ok(DerivedKeyResult {
775-
derived_keys: None,
776-
key_protector_settings,
777-
gsp_extended_status_flags: gsp_response.extended_status_flags,
778-
});
816+
match guest_state_encryption_policy {
817+
// fail if some minimum level of encryption was required
818+
GuestStateEncryptionPolicy::GspById
819+
| GuestStateEncryptionPolicy::GspKey
820+
| GuestStateEncryptionPolicy::HardwareSealing => {
821+
Err(GetDerivedKeysError::EncryptionRequiredButNotFound)?
822+
}
823+
GuestStateEncryptionPolicy::Auto | GuestStateEncryptionPolicy::None => {
824+
tracing::info!(CVM_ALLOWED, "No VMGS encryption used.");
825+
826+
return Ok(DerivedKeyResult {
827+
derived_keys: None,
828+
key_protector_settings,
829+
gsp_extended_status_flags: gsp_response.extended_status_flags,
830+
});
831+
}
832+
}
779833
}
780834

781835
// Attempt to get hardware derived keys
@@ -803,7 +857,7 @@ async fn get_derived_keys(
803857

804858
// Use tenant key (KEK only)
805859
if no_gsp && no_gsp_by_id {
806-
tracing::trace!(CVM_ALLOWED, "No GSP used with SKR");
860+
tracing::info!(CVM_ALLOWED, "No GSP used with SKR");
807861

808862
derived_keys.ingress = ingress_key;
809863
derived_keys.decrypt_egress = decrypt_egress_key;
@@ -837,7 +891,20 @@ async fn get_derived_keys(
837891
.map_err(GetDerivedKeysError::GetDerivedKeyById)?;
838892

839893
if no_kek && no_gsp {
840-
tracing::trace!(CVM_ALLOWED, "Using GSP with ID.");
894+
if matches!(
895+
guest_state_encryption_policy,
896+
GuestStateEncryptionPolicy::None
897+
) {
898+
// Log a warning here to indicate that the VMGS state is out of
899+
// sync with the VM's configuration.
900+
//
901+
// This should only happen if the VM is configured to
902+
// have no encryption, but it already has GspById encryption
903+
// and strict encryption policy is disabled.
904+
tracing::warn!(CVM_ALLOWED, "Allowing GspById");
905+
} else {
906+
tracing::info!(CVM_ALLOWED, "Using GspById");
907+
}
841908

842909
// Not required for Id protection
843910
key_protector_settings.should_write_kp = false;
@@ -852,7 +919,7 @@ async fn get_derived_keys(
852919

853920
derived_keys.ingress = derived_keys_by_id.ingress;
854921

855-
tracing::trace!(CVM_ALLOWED, "Converting GSP method.");
922+
tracing::info!(CVM_ALLOWED, "Converting GSP method.");
856923
}
857924

858925
let egress_seed;
@@ -969,6 +1036,21 @@ async fn get_derived_keys(
9691036
}
9701037
}
9711038

1039+
if matches!(
1040+
guest_state_encryption_policy,
1041+
GuestStateEncryptionPolicy::None | GuestStateEncryptionPolicy::GspById
1042+
) {
1043+
// Log a warning here to indicate that the VMGS state is out of
1044+
// sync with the VM's configuration.
1045+
//
1046+
// This should only happen if the VM is configured to have no
1047+
// encryption or GspById encryption, but it already has GspKey
1048+
// encryption and strict encryption policy is disabled.
1049+
tracing::warn!(CVM_ALLOWED, "Allowing Gsp");
1050+
} else {
1051+
tracing::info!(CVM_ALLOWED, "Using Gsp");
1052+
}
1053+
9721054
Ok(DerivedKeyResult {
9731055
derived_keys: Some(derived_keys),
9741056
key_protector_settings,

openhcl/underhill_core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ async fn launch_workers(
328328
nvme_always_flr: opt.nvme_always_flr,
329329
test_configuration: opt.test_configuration,
330330
disable_uefi_frontpage: opt.disable_uefi_frontpage,
331+
guest_state_encryption_policy: opt.guest_state_encryption_policy,
331332
};
332333

333334
let (mut remote_console_cfg, framebuffer_access) =

openhcl/underhill_core/src/options.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,28 @@ impl std::str::FromStr for TestScenarioConfig {
3333
}
3434
}
3535

36+
#[derive(Clone, Debug, MeshPayload)]
37+
pub enum GuestStateEncryptionPolicyCli {
38+
Auto,
39+
None,
40+
GspById,
41+
GspKey,
42+
}
43+
44+
impl std::str::FromStr for GuestStateEncryptionPolicyCli {
45+
type Err = anyhow::Error;
46+
47+
fn from_str(s: &str) -> Result<GuestStateEncryptionPolicyCli, anyhow::Error> {
48+
match s {
49+
"AUTO" | "0" => Ok(GuestStateEncryptionPolicyCli::Auto),
50+
"NONE" | "1" => Ok(GuestStateEncryptionPolicyCli::None),
51+
"GSP_BY_ID" | "2" => Ok(GuestStateEncryptionPolicyCli::GspById),
52+
"GSP_KEY" | "3" => Ok(GuestStateEncryptionPolicyCli::GspKey),
53+
_ => Err(anyhow::anyhow!("Invalid encryption policy: {}", s)),
54+
}
55+
}
56+
}
57+
3658
// We've made our own parser here instead of using something like clap in order
3759
// to save on compiled file size. We don't need all the features a crate can provide.
3860
/// underhill core command-line and environment variable options.
@@ -157,6 +179,10 @@ pub struct Options {
157179
/// will result in UEFI terminating, shutting down the guest instead of
158180
/// showing the frontpage.
159181
pub disable_uefi_frontpage: bool,
182+
183+
/// (HCL_GUEST_STATE_ENCRYPTION_POLICY=\<GuestStateEncryptionPolicyCli\>)
184+
/// Specify which guest state encryption policy to use.
185+
pub guest_state_encryption_policy: Option<GuestStateEncryptionPolicyCli>,
160186
}
161187

162188
impl Options {
@@ -259,6 +285,15 @@ impl Options {
259285
});
260286
let disable_uefi_frontpage = parse_env_bool("OPENHCL_DISABLE_UEFI_FRONTPAGE");
261287
let signal_vtl0_started = parse_env_bool("OPENHCL_SIGNAL_VTL0_STARTED");
288+
let guest_state_encryption_policy = parse_env_string("HCL_GUEST_STATE_ENCRYPTION_POLICY")
289+
.and_then(|x| {
290+
x.to_string_lossy()
291+
.parse::<GuestStateEncryptionPolicyCli>()
292+
.map_err(|e| {
293+
tracing::warn!("failed to parse HCL_GUEST_STATE_ENCRYPTION_POLICY: {}", e)
294+
})
295+
.ok()
296+
});
262297

263298
let mut args = std::env::args().chain(extra_args);
264299
// Skip our own filename.
@@ -316,6 +351,7 @@ impl Options {
316351
nvme_always_flr,
317352
test_configuration,
318353
disable_uefi_frontpage,
354+
guest_state_encryption_policy,
319355
})
320356
}
321357

0 commit comments

Comments
 (0)