Skip to content

Fix up AKCert index attributes #1550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 122 additions & 12 deletions vm/devices/tpm/src/tpm_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,9 +549,9 @@ impl TpmEngineHelper {
// VM has a small-vTPM mitigation marker. Don't touch anything, but
// log whether the AK cert exists, as that previous write might have
// failed.
let mut output = [0u8; MAX_NV_INDEX_SIZE as usize];
let mut output = vec![0u8; MAX_NV_INDEX_SIZE as usize];
let r = self.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut output);
tracing::warn!("VM has 16k vTPM mitigation marker; not resizing AKCert index");
tracing::warn!("VM has 16k vTPM mitigation marker");
match r {
Err(e) => tracing::error!(
err = &e as &dyn std::error::Error,
Expand All @@ -564,7 +564,40 @@ impl TpmEngineHelper {
let nv_bits = TpmaNvBits::from(res.nv_public.nv_public.attributes.0.get());
let size = res.nv_public.nv_public.data_size.get();

tracing::info!(?nv_bits, size, "AKCert index exists")
tracing::info!(?nv_bits, size, "AKCert index exists");

if nv_bits.nv_platformcreate() {
tracing::info!("AKCert index is platform owned; restoring owner auth");
let existing_cert = self.take_existing_ak_cert()?;
if let AkCertType::PlatformOwned(cert) = existing_cert {
self.nv_define_space(
TPM20_RH_OWNER,
0,
TPM_NV_INDEX_AIK_CERT,
cert.len() as u16,
)
.map_err(|error| {
TpmHelperError::TpmCommandError {
command_debug_info: CommandDebugInfo {
command_code: CommandCodeEnum::NV_DefineSpace,
auth_handle: Some(TPM20_RH_OWNER),
nv_index: Some(TPM_NV_INDEX_AIK_CERT),
},
error,
}
})?;

self.nv_write(TPM20_RH_OWNER, None, TPM_NV_INDEX_AIK_CERT, &cert)
.map_err(|error| TpmHelperError::TpmCommandError {
command_debug_info: CommandDebugInfo {
command_code: CommandCodeEnum::NV_Write,
auth_handle: Some(TPM20_RH_OWNER),
nv_index: Some(TPM_NV_INDEX_AIK_CERT),
},
error,
})?;
}
}
}
Ok(NvIndexState::Uninitialized) => {
tracing::warn!("AKCert index uninitialized with mitigation marker")
Expand Down Expand Up @@ -635,12 +668,22 @@ impl TpmEngineHelper {
"allocate nv index for previous platform AK cert"
);

let (handle, auth, write_auth_handle) = if will_mitigate_cert {
(TPM20_RH_OWNER, None, TPM20_RH_OWNER)
} else {
(
TPM20_RH_PLATFORM,
Some(auth_value),
ReservedHandle(TPM_NV_INDEX_AIK_CERT.into()),
)
};

let result = self
.nv_define_space(TPM20_RH_PLATFORM, auth_value, TPM_NV_INDEX_AIK_CERT, size)
.nv_define_space(handle, auth.unwrap_or(0), TPM_NV_INDEX_AIK_CERT, size)
.map_err(|error| TpmHelperError::TpmCommandError {
command_debug_info: CommandDebugInfo {
command_code: CommandCodeEnum::NV_DefineSpace,
auth_handle: Some(TPM20_RH_PLATFORM),
auth_handle: Some(handle),
nv_index: Some(TPM_NV_INDEX_AIK_CERT),
},
error,
Expand Down Expand Up @@ -668,7 +711,17 @@ impl TpmEngineHelper {
// boot-time AK cert request fails.
tracing::info!("Preserve previous AK cert across boot");

self.write_to_nv_index(auth_value, TPM_NV_INDEX_AIK_CERT, &cert)?;
self.nv_write(write_auth_handle, auth, TPM_NV_INDEX_AIK_CERT, &cert)
.map_err(|error| TpmHelperError::TpmCommandError {
command_debug_info: CommandDebugInfo {
command_code: CommandCodeEnum::NV_Write,
auth_handle: Some(ReservedHandle(
TPM_NV_INDEX_AIK_CERT.into(),
)),
nv_index: Some(TPM_NV_INDEX_AIK_CERT),
},
error,
})?;
}
}
}
Expand Down Expand Up @@ -1436,12 +1489,20 @@ impl TpmEngineHelper {
let session_tag = SessionTagEnum::Sessions;

// Use password-based authorization and allow owner to read
let attributes = TpmaNvBits::new()
.with_nv_authread(true)
.with_nv_authwrite(true)
.with_nv_ownerread(true)
.with_nv_platformcreate(true)
.with_nv_no_da(true);
let attributes = if auth_handle == TPM20_RH_PLATFORM {
TpmaNvBits::new()
.with_nv_authread(true)
.with_nv_authwrite(true)
.with_nv_ownerread(true)
.with_nv_platformcreate(true)
.with_nv_no_da(true)
} else {
TpmaNvBits::new()
.with_nv_ownerread(true)
.with_nv_ownerwrite(true)
.with_nv_authread(true)
.with_nv_authwrite(true)
};

let public_info = TpmsNvPublic::new(
nv_index,
Expand Down Expand Up @@ -3552,4 +3613,53 @@ mod tests {
panic!()
}
}

#[test]
fn test_restore_owner_defined() {
const AK_CERT_INPUT_512: [u8; 512] = [7u8; 512];

let mut tpm_engine_helper = create_tpm_engine_helper();
restart_tpm_engine(&mut tpm_engine_helper, false, true);

// Test allocating a platform-defined AKCert index and mitigating it back to owner-defined.

let result =
tpm_engine_helper.allocate_guest_attestation_nv_indices(AUTH_VALUE, false, true, false);
assert!(result.is_ok());

let result = tpm_engine_helper
.find_nv_index(TPM_NV_INDEX_AIK_CERT)
.expect("find_nv_index should succeed")
.expect("AKCert NV index present");
let nv_bits = TpmaNvBits::from(result.nv_public.nv_public.attributes.0.get());
assert!(nv_bits.nv_platformcreate());

let result = tpm_engine_helper.write_to_nv_index(
AUTH_VALUE,
TPM_NV_INDEX_AIK_CERT,
&AK_CERT_INPUT_512,
);
assert!(result.is_ok());

let result = tpm_engine_helper.nv_define_space(
TPM20_RH_PLATFORM,
AUTH_VALUE,
TPM_NV_INDEX_MITIGATED,
1,
);
assert!(result.is_ok());

// TPM has a platform-defined AKCert index and a mitigation marker. This should restore
// the owner-defined AKCert index.
let result =
tpm_engine_helper.allocate_guest_attestation_nv_indices(AUTH_VALUE, false, true, true);
assert!(result.is_ok());

let result = tpm_engine_helper
.find_nv_index(TPM_NV_INDEX_AIK_CERT)
.expect("find_nv_index should succeed")
.expect("AKCert NV index present");
let nv_bits = TpmaNvBits::from(result.nv_public.nv_public.attributes.0.get());
assert!(!nv_bits.nv_platformcreate());
}
}