-
Notifications
You must be signed in to change notification settings - Fork 0
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
svsm: add SVSM VTPM Service Attestation #1
base: main
Are you sure you want to change the base?
Conversation
vTPM service attestation is described in section 8.3 of "Secure VM Service Module for SEV-SNP Guests, Publication #58019 Revision: 1.00 Issue Date: July 2023". It certifies the Endorsement Key (EK) of the vTPM by providing the TPMT_PUBLIC structure of the EK. This is crucial for downstream projects like Keylime, as the SVSM vTPM lacks an EK certificate found in physical TPMs to anchor trust. The attestation is part of the SVSM Attestation Protocol and uses the SVSM_ATTEST_SINGLE_SERVICE call (see section 7 of the specifications). It is triggered by making an SVSM_ATTEST_SINGLE_SERVICE call with the GUID set to c476f1eb-0123-45a5-9641-b4e7dde5bfe3. The attestation code returns the VMPL0 attestation report and the vTPM Service Manifest Data Structure (TPMT_PUBLIC structure of the EK). The REPORT_DATA in the SNP attestation request is the SHA-512 digest of the input nonce and the vTPM Service Manifest Data Structure. The vTPM initialization function was modified to generate an RSA 2048-bit EK from the TPM's Endorsement Primary Seed (EPS) and cache the public key as a TPMT_PUBLIC structure. This cached EK public key can be retrieved later for vTPM service attestation. The EK is created with the TCG default EK template (see Table 4 of the "TCG EK Credential Profile For TPM Family 2.0; Level 0 Version 2.5 Revision 2.0"). Since the EK is derived from the EPS, it can be recreated upstream at any time. For example, the same EK can be recreated in an OS using the TSS2 command "tpm2_createek -c ek.ctx -G rsa -u ek.pub" and compared against the one returned by vTPM service attestation. vTPM service attestation as specified can only return one type of EK, so the implementation supports RSA 2048-bit EK as defined in Table 4 of the "TCG EK Credential Profile For TPM Family 2.0; Level 0 Version 2.5 Revision 2.0," which is the most common Trusted Computing Group(TCG) EK type. Resolves coconut-svsm#437, resolves coconut-svsm#361 Signed-off-by: Geoffrey Ndu <[email protected]>
@@ -35,6 +35,7 @@ log = { workspace = true, features = ["max_level_info", "release_max_level_info" | |||
packit.workspace = true | |||
libtcgtpm = { workspace = true, optional = true } | |||
zerocopy.workspace = true | |||
sha2 = { workspace = true, features = ["force-soft"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just for curiosity, why we need to enable feature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is related to RustCrypto/hashes#446 that affects custom targets. svsm is x86_64-unknown-none
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about adding a comment there, because I'm not sure if force-soft
affects something else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found this as I'm running into the same problem.
kernel/src/protocols/attestation.rs
Outdated
let vaddr = guard.virt_addr() + offset; | ||
|
||
// Check that the nonce length is not greater than 4k. | ||
// If it is, return an error as something is wrong with the request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we put a link or reference to the specification that defines that nonce must be max 4KB?
kernel/src/protocols/attestation.rs
Outdated
pub fn get_nonce_gpa_and_size(&self) -> Result<(PhysAddr, usize), SvsmReqError> { | ||
let gpa = PhysAddr::from(self.nonce_gpa); | ||
if !gpa.is_aligned(8) || !valid_phys_address(gpa) || gpa.crosses_page(8) { | ||
return Err(SvsmReqError::invalid_parameter()); | ||
} | ||
|
||
//cast won't panic on amd64 as usize > u32 always | ||
let size = self.nonce_size as usize; | ||
|
||
Ok((gpa, size)) | ||
} | ||
|
||
/// Returns the manifest GPA and size | ||
/// Checks if gpa is page aligned, valid and does not cross page boundary. | ||
pub fn get_manifest_gpa_and_size(&self) -> Result<(PhysAddr, usize), SvsmReqError> { | ||
let gpa = PhysAddr::from(self.manifest_gpa); | ||
if !gpa.is_aligned(8) || !valid_phys_address(gpa) || gpa.crosses_page(8) { | ||
return Err(SvsmReqError::invalid_parameter()); | ||
} | ||
|
||
// Won't panic on amd64 as usize > u32 always | ||
let size = self.manifest_size as usize; | ||
|
||
Ok((gpa, size)) | ||
} | ||
|
||
/// Returns the guid | ||
/// Checks if gpa is page aligned, valid and does not cross page boundary | ||
pub fn get_report_gpa_and_size(&self) -> Result<(PhysAddr, usize), SvsmReqError> { | ||
let gpa = PhysAddr::from(self.report_gpa); | ||
if !gpa.is_aligned(8) || !valid_phys_address(gpa) || gpa.crosses_page(8) { | ||
return Err(SvsmReqError::invalid_parameter()); | ||
} | ||
|
||
// Won't panic on amd64 as usize > u32 always | ||
let size = self.report_size as usize; | ||
|
||
Ok((gpa, size)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should they be pub
or are they only used internally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It meant to be for internal use only. get_nonce() returns the nonce itself and there should be no need to use get_nonce_gpa_and_size() externally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay, so can we remove pub
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought, someone might want to get the gPA
and size of the nonce buffer, so leaving it as pub
. AttestSingleServiceOp
is used in other types of SVSM service attestation.
pub fn get_manifest_version(&self) -> u32 { | ||
self.manifest_ver | ||
} | ||
|
||
fn is_manifest_version_valid(&self) -> bool { | ||
//Currently only manifest version 0 is supported | ||
self.manifest_ver == 0 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe just a single function that return the version if it's valid or an error.
buf.extend_from_slice(nonce); | ||
report_req.extend_from_slice(&buf[..nonce.len()]); | ||
|
||
// Set request VMPL to 0 | ||
report_req.extend_from_slice(&0_u32.to_le_bytes()); | ||
|
||
// Set reserved bytes to zeros | ||
report_req.extend_from_slice(&[0; 28]); | ||
|
||
// Make sure buffer is big enough to hold the report | ||
report_req.resize(size_of::<SnpReportResponse>(), 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should implement try_from_as_mut_ref
for SnpReportResponse
to avoid this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that should solve it. That said, I am trying to keep the changes in this PR to a minimum. Perhaps, a separate PR to implement try_from_as_mut_ref for SnpReportResponse once this one is merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd do for sure in a separate commit, but this PR looks the right place since it's the user of that API. BTW not a strong opinion, if you prefer another PR is fine by me.
} | ||
report_req.drain(0..0x20); | ||
|
||
Ok(report_req) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also call SnpReportResponse:: validate()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
validate() is called inside get_report() which is called by get_regular_report().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, so should we move that check (if response_size < 0x20
) there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the check it as validate() does the same check. However, their is the risk of someone removing the validate() call inside get_report(). I have updated the documentation to say
that the code relies on validate() inside get_report(). I wanted to ref link the to the function in the comment but can't run "make doc" any more. Something added recently broke it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to ref link the to the function in the comment but can't run "make doc" any more. Something added recently broke it.
I just tried on the main
branch and it works, so it may be related to the changes we did to our submodule. See coconut-svsm#521
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks that worked
Closes comment Signed-off-by: Geoffrey Ndu <[email protected]>
Closes PR comment Signed-off-by: Geoffrey Ndu <[email protected]>
Blah blah Signed-off-by: Geoffrey Ndu <[email protected]>
Code harcodes request nonce to all zeros! Need to revert. This reverts commit 625c6c86be3f22edede9c6c55e319ca8a669d8aa. Signed-off-by: Geoffrey Ndu <[email protected]>
Fixes PR comment Signed-off-by: Geoffrey Ndu <[email protected]>
Fixes PR comment Signed-off-by: Geoffrey Ndu <[email protected]>
Fixes PR comments Signed-off-by: Geoffrey Ndu <[email protected]>
Fold startup and selftest into functions Signed-off-by: Geoffrey Ndu <[email protected]>
Fixes comment
vTPM service attestation is described in section 8.3 of "Secure VM Service Module for SEV-SNP Guests, Publication #58019 Revision: 1.00 Issue Date: July 2023". It certifies the Endorsement Key (EK) of the vTPM by providing the TPMT_PUBLIC structure of the EK. This is crucial for downstream projects like Keylime, as the SVSM vTPM lacks an EK certificate found in physical TPMs to anchor trust.
The attestation is part of the SVSM Attestation Protocol and uses the SVSM_ATTEST_SINGLE_SERVICE call (see section 7 of the specifications). It is triggered by making an SVSM_ATTEST_SINGLE_SERVICE call with the GUID set to c476f1eb-0123-45a5-9641-b4e7dde5bfe3. The attestation code returns the VMPL0 attestation report and the vTPM Service Manifest Data Structure (TPMT_PUBLIC structure of the EK). The REPORT_DATA in the SNP attestation request is the SHA-512 digest of the input nonce and the vTPM Service Manifest Data Structure.
The vTPM initialization function was modified to generate an RSA 2048-bit EK from the TPM's Endorsement Primary Seed (EPS) and cache the public key as a TPMT_PUBLIC structure. This cached EK public key can be retrieved later for vTPM service attestation. The EK is created with the TCG default EK template (see Table 4 of the "TCG EK Credential Profile For TPM Family 2.0; Level 0 Version 2.5 Revision 2.0"). Since the EK is derived from the EPS, it can be recreated upstream at any time. For example, the same EK can be recreated in an OS using the TSS2 command "tpm2_createek -c ek.ctx -G rsa -u ek.pub" and compared against the one returned by vTPM service attestation.
vTPM service attestation as specified can only return one type of EK, so the implementation supports RSA 2048-bit EK as defined in Table 4 of the "TCG EK Credential Profile For TPM Family 2.0; Level 0 Version 2.5 Revision 2.0," which is the most common Trusted Computing Group(TCG) EK type.
Resolves coconut-svsm#437, resolves coconut-svsm#361