Skip to content
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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

IT302
Copy link
Collaborator

@IT302 IT302 commented Nov 22, 2024

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

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"] }

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?

Copy link
Collaborator Author

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

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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.

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

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?

Comment on lines 118 to 156
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))
}

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?

Copy link
Collaborator Author

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.

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 ?

Copy link
Collaborator Author

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.

kernel/src/protocols/attestation.rs Outdated Show resolved Hide resolved
Comment on lines +166 to +173
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
}

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.

kernel/src/protocols/attestation.rs Outdated Show resolved Hide resolved
Comment on lines +191 to +201
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);

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

Copy link
Collaborator Author

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.

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)

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()?

Copy link
Collaborator Author

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().

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?

Copy link
Collaborator Author

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.

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks that worked

kernel/src/vtpm/mod.rs Outdated Show resolved Hide resolved
kernel/src/vtpm/tcgtpm/mod.rs Outdated Show resolved Hide resolved
Geoffrey Ndu added 9 commits November 27, 2024 15:48
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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants