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

Add support for validating VLEK certificates #67

Merged
merged 1 commit into from
Sep 11, 2023
Merged

Conversation

deeglaze
Copy link
Collaborator

This changes the AuthorKeyEn report field to a better-named SignerInfo. The interpretation of this field selects which key the fake signer will use when signing reports. The VLEK certificate extensions are now checked against AMD's VLEK specification. The difference is VLEK certs are like VCEK certs, except the HWID extension is swapped with a CSP_ID extension.

Tests have been updated to reflect these changes, including new VLEK-specific test cases.

Copy link
Contributor

@katexochen katexochen left a comment

Choose a reason for hiding this comment

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

Thanks for the patch! 😸

kds/kds.go Show resolved Hide resolved
@derpsteb
Copy link
Contributor

derpsteb commented Aug 5, 2023

Hey @deeglaze ,
thanks a lot for taking the time to implement this! I tested the code by verifying the report from an EC2 instance. In the end I got it to work, but I had to botch things up a bit. It seems like there are even more assumptions in the certificate validation of go-sev-guest that are not true for VLEKs. Or some of the certificates are not issued correctly. I wouldn't know, because I don't have a specification of how a VLEK is supposed to look like ^^.

In any case, this is what I found:

  • ParseProductName: The product name of AWS' VLEK only specifies the model, not the stepping. So the model is "Milan". Not "Milan-B0". I am reading the VCEK spec Rev 0.51 from Jan 2023. Section 1.4, Table 3 lets me think that "Milan" is actually the product name and the VLEK is correct. Chapter 3, Table 8, footnote 1 specifies that productName in that specific extension (of a VCEK) refers to an extended version of the product name. Not sure how that translates to VLEKs.
  • ValidateAskX509: The CN of a VLEK is "SEV-VLEK". The CN of the ASK for that VLEK is "SEV-VLEK-Milan". This also creates an error in validateKDSCertIssuer.
  • validateCRLlink: The ASK for VLEKs get their CRL from https://kdsintf.amd.com/vlek/v1/<product>/crl. The ARK is still at https://kdsintf.amd.com/vcek/v1/<product>/crl.
  • crossCheckSevX509: The embedded ASK from ask_ark_milan.sevcert does not match the new ASK for VLEKs.

Not sure how these discrepancies should be handled. I guess we need to change go-sev-guest's assumptions?
I will let the AWS engineers working on this know about the library. Maybe they want to share a perspective.

Best,
Otto

@deeglaze
Copy link
Collaborator Author

deeglaze commented Aug 5, 2023

Thanks Otto. This helps, since I don't have a real example of a VLEK to work from. Do you have a binary that you can share that I could use for testing purposes?

@deeglaze deeglaze force-pushed the vlek branch 4 times, most recently from 3745e45 to f2ed12c Compare August 5, 2023 21:26
@deeglaze
Copy link
Collaborator Author

deeglaze commented Aug 5, 2023

I've added the VLEK bundle from the KDS and fixed up the issues you mentioned. Please give the updated patch another try.

Comment on lines 242 to 256
switch key {
case abi.VcekReportSigner:
intermediates.AddCert(r.Ask)
case abi.VlekReportSigner:
intermediates.AddCert(r.Asvk)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Move the r.Ask == nil case from the if in L236 to the switch.

Suggested change
switch key {
case abi.VcekReportSigner:
intermediates.AddCert(r.Ask)
case abi.VlekReportSigner:
intermediates.AddCert(r.Asvk)
}
switch key {
case abi.VcekReportSigner:
if r.Ask == nil {
return nil
}
intermediates.AddCert(r.Ask)
case abi.VlekReportSigner:
if r.Asvk == nil {
return nil
}
intermediates.AddCert(r.Asvk)
}

That was the only problem left during my testing 🎉

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Nice, thanks so much for your help testing. It's tough without a spec :P

verify/verify.go Outdated
if ica == nil {
return fmt.Errorf("root of trust missing intermediate certificate authority certificate for key %v", key)
}
if _, err := cert.Verify(*r.X509Options(opts.Now, key)); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if _, err := cert.Verify(*r.X509Options(opts.Now, key)); err != nil {
tmp := r.X509Options(opts.Now, key)
if tmp == nil {
return fmt.Errorf("internal error: could not get X509 options for %v", key)
}
if _, err := cert.Verify(*tmp); err != nil {

Not sure how to call that variable ^^

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah yes, uninitialized intermediate certs will lead to a nil dereference. Good catch.

@derpsteb
Copy link
Contributor

derpsteb commented Aug 7, 2023

Nicee. Works now :P. LGTM, once the two changes are included 👍

That's the raw cert table from the ExtendedReport I used for testing. Hope that is what you were looking for.

a8074bc2a25a483eaae639c045a0b8a130000000300500000000000000000000000000000000000000000000000000003082052c308202dba003020102020100304606092a864886f70d01010a3039a00f300d06096086480165030402020500a11c301a06092a864886f70d010108300d06096086480165030402020500a203020130a30302010130818031143012060355040b0c0b456e67696e656572696e67310b30090603550406130255533114301206035504070c0b53616e746120436c617261310b300906035504080c024341311f301d060355040a0c16416476616e636564204d6963726f20446576696365733117301506035504030c0e5345562d564c454b2d4d696c616e301e170d3233303731393039303031375a170d3234303731393039303031375a307a31143012060355040b0c0b456e67696e656572696e67310b30090603550406130255533114301206035504070c0b53616e746120436c617261310b300906035504080c024341311f301d060355040a0c16416476616e636564204d6963726f20446576696365733111300f06035504030c085345562d564c454b3076301006072a8648ce3d020106052b8104002203620004d92b028c99fd884b2eee8b57ad6a20f1d97c510d82903942e0481f80dbfb1f739ec22241265c2ac53c8fb22a60b35db43946808716eaa7a73b4d2e86ac98169527b17faf6fd3c18df44c08281ca16964a03f153e404e9334510f60edae1f6804a381f13081ee301006092b060104019c7801010403020100301406092b060104019c780102040716054d696c616e3011060a2b060104019c7801030104030201033011060a2b060104019c7801030204030201003011060a2b060104019c7801030404030201003011060a2b060104019c7801030504030201003011060a2b060104019c7801030604030201003011060a2b060104019c7801030704030201003011060a2b060104019c78010303040302010a3011060a2b060104019c780103080403020173302c06092b060104019c780105041f161d434e3d63632d65752d776573742d312e616d617a6f6e6177732e636f6d304606092a864886f70d01010a3039a00f300d06096086480165030402020500a11c301a06092a864886f70d010108300d06096086480165030402020500a203020130a30302010103820201001b5328d5a2ac15b456d55f5d79214095d655ddbf3c0b6313edbeeb7d9adf610b2e96ea86a99a29e30e5c85c0b554dab4bfaef8d162bd8b2cb8a9de90fc65e26c2e57e8688371853f48f68ee657e3dc5a45fad711673e894fe198b6e401ed414cab6730711203f13cdf2bcbb5fbab1bc7fa34308216198a43e0e20f4497f2419dda3a8ecdecf50719700a21146b01d440cd0e3448a8bb700e8f40b7d358d075b277d46b7fcb0d6d96dce85adf2c3f2db322e2b500a2dd41be1114d72c746113070640404e7bb7a6b3ae0b4e4d21270b176030cb9a8923a09393ffc2bc6b63c2e65f957cc13ba6b2cfc216e85798f96ac82aa5ae864dbe26bd953b2558305cac4a1e149bc562e09abc3dffe20fd2ab8d9fec0e01625707017cebb6e22a9a9d557d324a65c665320ea8881db04790ad1061aca7aef228b1a9a14116525936fa3aeb6b29aa6b00e322fcf8e3db76214db49355771025921e3cfe49b9d2d9b26519c6275a74695e06991756267cb25b2a3060e73956871d45551d7f00d749b6810a634e170907b55cc00aa2a9914c46e7ac21d4480fbb52813e11f1e2429dddcf5c5a49c1d50b14ff1a6758a25cf8fccc3dd820ae31fab8761fcde490d3d7ed3ae39ed43a5f92746e1798eb00c8f3307cd30539447e70be86bd5f03f2e25f8ed2827a90f33e13931429e72e0ef87d81d663db0d8954bab381a77b3afa1e5e8d572a8f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Copy link
Contributor

@derpsteb derpsteb left a comment

Choose a reason for hiding this comment

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

Works as expected :)

@@ -293,6 +295,13 @@ func mbz(data []uint8, lo, hi int) error {
return nil
}

func mbz64(data uint64, base string, hi, lo int) error {
if (data>>lo)&((1<<(hi-lo+1))-1) != 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add some comments here? It's not quite clear what being checked here.

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.

cert.NotAfter = b.VcekCreationTime.Add(vcekExpirationYears * 365 * 24 * time.Hour)
var hwid [64]byte
cert.ExtraExtensions = CustomVcekExtensions(kds.TCBParts{}, hwid)
func (b *AmdSignerBuilder) vekPrecert(creationTime time.Time, hwid []byte, serialNumber *big.Int, key abi.ReportSigner) *x509.Certificate {
Copy link
Collaborator

Choose a reason for hiding this comment

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

"vek" stands for "vcek/vlek"? This feels a bit confusing. Can we just call it AmdKeyPrecert or something like that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

vek is versioned endorsement key. I just expanded to endorsementKey and dropped the versioned.

b.Vlek = signed
return err
}

// CertChain creates a test-only certificate chain from the keys and configurables in b.
func (b *AmdSignerBuilder) CertChain() (*AmdSigner, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we call this TestOnlyCertChain() or so to make it clear this is test-only, since it's not in a test file?

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.

RootBundles: map[string]string{"Milan": string(milanCerts)},
Certs: &kpb.Certificates{},
RootBundles: map[string]RootBundle{"Milan": {
VcekBundle: string(testdata.MilanBytes),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we change this also to "MilanVcekBytes"?

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.

@@ -25,11 +25,16 @@ import _ "embed"
//go:embed vcek.testcer
var VcekBytes []byte

// MilanBytes is the Milan product cert_chain as issued by the AMD KDS.
// MilanBytes is the Milan product vcek cert_chain as issued by the AMD KDS.
//
//go:embed milan.testcer
var MilanBytes []byte
Copy link
Collaborator

Choose a reason for hiding this comment

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

MilanVcekBytes?

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.

@@ -165,7 +166,11 @@ func TestSnpReportSignature(t *testing.T) {
if !bytes.Equal(got, want) {
t.Errorf("%s: GetRawReport(%v) = %v, want %v", tc.Name, tc.Input, got, want)
}
if err := SnpReportSignature(raw, d.Signer.Vcek); err != nil {
key := d.Signer.Vcek
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add an enum or so here for vcek/vlek instead of a bool tc.Vlek? That may be more readable and in case more type of certs are added in the future.

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.

This changes the AuthorKeyEn report field to a better-named SignerInfo.
The interpretation of this field selects which key the fake signer will
use when signing reports. The VLEK certificate extensions are now
checked against AMD's VLEK specification. The difference is VLEK certs
are like VCEK certs, except the HWID extension is swapped with a CSP_ID
extension.

Tests have been updated to reflect these changes, including new
VLEK-specific test cases.

Signed-off-by: Dionna Glaze <dionnaglaze@google.com>
@deeglaze deeglaze merged commit ccc7134 into google:main Sep 11, 2023
8 checks passed
@deeglaze deeglaze deleted the vlek branch February 8, 2024 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants