Skip to content

Commit 52c72c1

Browse files
authored
Merge pull request #2 from deeglaze/main
Export methods for testing externally
2 parents 414ba1a + 9dee0c1 commit 52c72c1

File tree

4 files changed

+116
-63
lines changed

4 files changed

+116
-63
lines changed

README.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,34 @@ verify.SnpAttestation(myAttestation, &verify.Options{})
7373

7474
#### `Options` type
7575

76-
This type contains two fields: `CheckRevocations bool`, and `Getter
77-
HTTPSGetter`. If `CheckRevocations` is true, then `Getter` must be non-nil and
78-
implement the `HTTPSGetter` interface.
76+
This type contains three fields:
77+
78+
* `CheckRevocations bool`: if true, then `SnpAttestation` will download the
79+
certificate revocation list (CRL) and check for revocations.
80+
* `Getter HTTPSGetter`: must be non-`nil` if `CheckRevocations` is true.
81+
* `TrustedRoots map[string][]*AMDRootCerts`: if `nil`, uses the library's embedded certificates.
82+
Maps a platform name to all allowed root certifications for that platform (e.g., Milan).
7983

8084
The `HTTPSGetter` interface consists of a single method `Get(url string)
8185
([]byte, error)` that should return the body of the HTTPS response.
8286

87+
88+
#### `AMDRootCerts` type
89+
90+
This type has 6 fields, the first 3 of which are mandatory:
91+
92+
* `Platform string`: the name of the platform this bundle is for (e.g., `"Milan"`).
93+
* `AskX509 *x509.Certificate`: an X.509 representation of the AMD SEV Signer intermediate key (ASK)'s certificate.
94+
* `ArkX509 *x509.Certificate`: an X.509 representation of the AMD SEV Root key (ARK)'s certificate.
95+
* `AskSev *abi.AskCert`: if non-`nil`, will cross-check with
96+
`AskX509`. Represents the information present in the AMD SEV certificate
97+
format for the ASK.
98+
* `ArkSev *abi.AskCert`: if non-`nil`, will cross-check with
99+
`ArkX509`. Represents the information present in the AMD SEV certificate
100+
format for the ARK.
101+
* `CRL *x509.RevocationList`: the certificate revocation list signed by the ARK.
102+
Will be populated if `SnpAttestation` is called with `CheckRevocations: true`.
103+
83104
## License
84105

85106
go-sev-guest is released under the Apache 2.0 license.

testing/test_cases.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ var oneReport = `
9090
// We can't sign the report with AMD keys, and verification isn't the client's responsibility, so
9191
// we keep the signature zeros.
9292
// Similarly, we leave the randomly-generated fields zero.
93-
func testRawReport(userData [64]byte) [abi.ReportSize]byte {
93+
func TestRawReport(userData [64]byte) [abi.ReportSize]byte {
9494
var r [abi.ReportSize]byte
9595
// Set Version to 2
9696
binary.LittleEndian.PutUint32(r[0x00:0x04], 2)
@@ -127,8 +127,8 @@ type TestCase struct {
127127

128128
// TestCases returns common test cases for get_report.
129129
func TestCases() []TestCase {
130-
zeroRaw := testRawReport(userZeros)
131-
oneRaw := testRawReport(userZeros1)
130+
zeroRaw := TestRawReport(userZeros)
131+
oneRaw := TestRawReport(userZeros1)
132132
return []TestCase{
133133
{
134134
Name: "zeros",

verify/verify.go

Lines changed: 54 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,21 @@ type AMDRootCerts struct {
7373
// ArkX509 is an X.509 certificate for the AMD root key (ARK).
7474
ArkX509 *x509.Certificate
7575
// AskSev is the AMD certificate representation of the AMD signing key that certifies
76-
// versioned chip endoresement keys.
76+
// versioned chip endoresement keys. If present, the information must match AskX509.
7777
AskSev *abi.AskCert
7878
// ArkSev is the AMD certificate representation of the self-signed AMD root key that
79-
// certifies the AMD signing key.
79+
// certifies the AMD signing key. If present, the information must match ArkX509.
8080
ArkSev *abi.AskCert
8181
// Protects concurrent updates to CRL.
8282
mu sync.Mutex
8383
// CRL is the certificate revocation list for this AMD platform. Populated once, only when a
8484
// revocation is checked.
85-
CRL *x509.RevocationList
86-
noCrossCheckTestOnly bool
85+
CRL *x509.RevocationList
8786
}
8887

8988
// DefaultRootCerts holds AMD's SEV API certificate format for ASK and ARK keys as published here
9089
// https://developer.amd.com/wp-content/resources/ask_ark_milan.cert
91-
var DefaultRootCerts AMDRootCerts
90+
var DefaultRootCerts map[string]*AMDRootCerts
9291

9392
// Unmarshal populates ASK and ARK certificates from AMD SEV format certificates in data.
9493
func (r *AMDRootCerts) Unmarshal(data []byte) error {
@@ -161,7 +160,11 @@ func (r *AMDRootCerts) X509Options() *x509.VerifyOptions {
161160

162161
// Parse ASK, ARK certificates from the embedded AMD certificate file.
163162
func init() {
164-
DefaultRootCerts.Unmarshal(askArkMilanBytes)
163+
milanCerts := new(AMDRootCerts)
164+
milanCerts.Unmarshal(askArkMilanBytes)
165+
DefaultRootCerts = map[string]*AMDRootCerts{
166+
"Milan": milanCerts,
167+
}
165168
}
166169

167170
func askVerifiedBy(signee, signer *abi.AskCert, signeeName, signerName string) error {
@@ -276,7 +279,7 @@ func (r *AMDRootCerts) validateRootX509(x *x509.Certificate, version int, role,
276279
// cryptographic signatures.
277280
func (r *AMDRootCerts) ValidateAskX509() error {
278281
if r == nil {
279-
r = &DefaultRootCerts
282+
r = DefaultRootCerts["Milan"]
280283
}
281284
var cn string
282285
if r.Platform != "" {
@@ -285,21 +288,17 @@ func (r *AMDRootCerts) ValidateAskX509() error {
285288
if err := r.validateRootX509(r.AskX509, askX509Version, "ASK", cn); err != nil {
286289
return err
287290
}
288-
askSev := r.AskSev
289-
if askSev == nil {
290-
askSev = DefaultRootCerts.AskSev
291-
}
292-
if r.noCrossCheckTestOnly {
293-
return nil
291+
if r.AskSev != nil {
292+
return crossCheckSevX509(r.AskSev, r.AskX509)
294293
}
295-
return crossCheckSevX509(askSev, r.AskX509)
294+
return nil
296295
}
297296

298297
// ValidateArkX509 checks expected metadata about the ARK X.509 certificate. It does not verify the
299298
// cryptographic signatures.
300299
func (r *AMDRootCerts) ValidateArkX509() error {
301300
if r == nil {
302-
r = &DefaultRootCerts
301+
r = DefaultRootCerts["Milan"]
303302
}
304303
var cn string
305304
if r.Platform != "" {
@@ -308,14 +307,10 @@ func (r *AMDRootCerts) ValidateArkX509() error {
308307
if err := r.validateRootX509(r.ArkX509, arkX509Version, "ARK", cn); err != nil {
309308
return err
310309
}
311-
arkSev := r.ArkSev
312-
if arkSev == nil {
313-
arkSev = DefaultRootCerts.ArkSev
314-
}
315-
if r.noCrossCheckTestOnly {
316-
return nil
310+
if r.ArkSev != nil {
311+
return crossCheckSevX509(r.ArkSev, r.ArkX509)
317312
}
318-
return crossCheckSevX509(arkSev, r.ArkX509)
313+
return nil
319314
}
320315

321316
// Checks some steps of AMD SEV API Appendix B.3
@@ -335,7 +330,7 @@ func validateRootSev(subject, issuer *abi.AskCert, version, keyUsage uint32, sub
335330
// This covers steps 1, 2, and 5
336331
func (r *AMDRootCerts) ValidateAskSev() error {
337332
if r == nil {
338-
r = &DefaultRootCerts
333+
r = DefaultRootCerts["Milan"]
339334
}
340335
return validateRootSev(r.AskSev, r.ArkSev, askVersion, askKeyUsage, "ASK", "ARK")
341336
}
@@ -344,19 +339,19 @@ func (r *AMDRootCerts) ValidateAskSev() error {
344339
// This covers steps 5, 6, 9, and 11.
345340
func (r *AMDRootCerts) ValidateArkSev() error {
346341
if r == nil {
347-
r = &DefaultRootCerts
342+
r = DefaultRootCerts["Milan"]
348343
}
349344
return validateRootSev(r.ArkSev, r.ArkSev, arkVersion, arkKeyUsage, "ARK", "ARK")
350345
}
351346

352347
// ValidateX509 will validate the x509 certificates of the ASK and ARK.
353348
func (r *AMDRootCerts) ValidateX509() error {
354-
if err := r.ValidateAskX509(); err != nil {
355-
return fmt.Errorf("ASK validation error: %v", err)
356-
}
357349
if err := r.ValidateArkX509(); err != nil {
358350
return fmt.Errorf("ARK validation error: %v", err)
359351
}
352+
if err := r.ValidateAskX509(); err != nil {
353+
return fmt.Errorf("ASK validation error: %v", err)
354+
}
360355
return nil
361356
}
362357

@@ -448,7 +443,7 @@ func (r *AMDRootCerts) validateVcekCertificatePlatformSpecifics(cert *x509.Certi
448443
// VcekDER checks that the VCEK certificate matches expected fields
449444
// from the KDS specification and also that its certificate chain matches
450445
// hardcoded trusted root certificates from AMD.
451-
func VcekDER(vcek []byte, ask []byte, ark []byte) (*x509.Certificate, *AMDRootCerts, error) {
446+
func VcekDER(vcek []byte, ask []byte, ark []byte, options *Options) (*x509.Certificate, *AMDRootCerts, error) {
452447
vcekCert, err := x509.ParseCertificate(vcek)
453448
if err != nil {
454449
return nil, nil, fmt.Errorf("could not interpret VCEK DER bytes: %v", err)
@@ -457,21 +452,34 @@ func VcekDER(vcek []byte, ask []byte, ark []byte) (*x509.Certificate, *AMDRootCe
457452
if err != nil {
458453
return nil, nil, err
459454
}
460-
root := &AMDRootCerts{
461-
Platform: vcekProductMap[exts.ProductName],
462-
// Allow tests to disable crosscheck on test-only certificates.
463-
noCrossCheckTestOnly: DefaultRootCerts.noCrossCheckTestOnly,
464-
}
465-
if err := root.FromDER(ask, ark); err != nil {
466-
return nil, nil, err
467-
}
468-
if err := root.ValidateX509(); err != nil {
469-
return nil, nil, err
455+
roots := options.TrustedRoots
456+
platform := vcekProductMap[exts.ProductName]
457+
if roots == nil {
458+
root := &AMDRootCerts{
459+
Platform: platform,
460+
// Require that the root matches embedded root certs.
461+
AskSev: DefaultRootCerts[platform].AskSev,
462+
ArkSev: DefaultRootCerts[platform].ArkSev,
463+
}
464+
if err := root.FromDER(ask, ark); err != nil {
465+
return nil, nil, err
466+
}
467+
if err := root.ValidateX509(); err != nil {
468+
return nil, nil, err
469+
}
470+
roots = map[string][]*AMDRootCerts{
471+
platform: []*AMDRootCerts{root},
472+
}
470473
}
471-
if err := root.validateVcekCertificatePlatformSpecifics(vcekCert); err != nil {
472-
return nil, nil, err
474+
var lastErr error
475+
for _, platformRoot := range roots[platform] {
476+
if err := platformRoot.validateVcekCertificatePlatformSpecifics(vcekCert); err != nil {
477+
lastErr = err
478+
continue
479+
}
480+
return vcekCert, platformRoot, nil
473481
}
474-
return vcekCert, root, nil
482+
return nil, nil, fmt.Errorf("VCEK could not be verified by any trusted roots. Last error: %v", lastErr)
475483
}
476484

477485
// SnpReportSignature verifies the attestation report's signature based on the report's
@@ -509,13 +517,17 @@ type Options struct {
509517
// Getter takes a URL and returns the body of its contents. By default uses http.Get and returns
510518
// the body.
511519
Getter HTTPSGetter
520+
// TrustedRoots specifies the ARK and ASK certificates to trust when checking the VCEK. If nil,
521+
// then verification will fall back on embedded AMD-published root certificates.
522+
// Maps the platform name to an array of allowed roots.
523+
TrustedRoots map[string][]*AMDRootCerts
512524
}
513525

514526
// SnpAttestation verifies the protobuf representation of an attestation report's signature based
515527
// on the report's SignatureAlgo, provided the certificate chain is valid.
516528
func SnpAttestation(attestation *spb.Attestation, options *Options) error {
517529
chain := attestation.GetCertificateChain()
518-
vcek, root, err := VcekDER(chain.GetVcekCert(), chain.GetAskCert(), chain.GetArkCert())
530+
vcek, root, err := VcekDER(chain.GetVcekCert(), chain.GetAskCert(), chain.GetArkCert(), options)
519531
if err != nil {
520532
return err
521533
}

verify/verify_test.go

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,28 +58,30 @@ func initSigner() {
5858
func TestEmbeddedCertsAppendixB3Expectations(t *testing.T) {
5959
// https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf
6060
// Appendix B.1
61-
if err := DefaultRootCerts.ValidateAskSev(); err != nil {
62-
t.Errorf("Embedded ASK failed validation: %v", err)
63-
}
64-
if err := DefaultRootCerts.ValidateArkSev(); err != nil {
65-
t.Errorf("Embedded ARK failed validation: %v", err)
61+
for _, root := range DefaultRootCerts {
62+
if err := root.ValidateAskSev(); err != nil {
63+
t.Errorf("Embedded ASK failed validation: %v", err)
64+
}
65+
if err := root.ValidateArkSev(); err != nil {
66+
t.Errorf("Embedded ARK failed validation: %v", err)
67+
}
6668
}
6769
}
6870

6971
func TestFakeCertsKDSExpectations(t *testing.T) {
7072
signMu.Do(initSigner)
7173
root := AMDRootCerts{
72-
Platform: platform,
73-
ArkX509: signer.Ark,
74-
AskX509: signer.Ask,
75-
noCrossCheckTestOnly: true,
76-
}
77-
if err := root.ValidateAskX509(); err != nil {
78-
t.Errorf("fake ASK validation error: %v", err)
74+
Platform: platform,
75+
ArkX509: signer.Ark,
76+
AskX509: signer.Ask,
77+
// No ArkSev or AskSev intentionally for test certs.
7978
}
8079
if err := root.ValidateArkX509(); err != nil {
8180
t.Errorf("fake ARK validation error: %v", err)
8281
}
82+
if err := root.ValidateAskX509(); err != nil {
83+
t.Errorf("fake ASK validation error: %v", err)
84+
}
8385
}
8486

8587
func TestParseVcekCert(t *testing.T) {
@@ -162,7 +164,6 @@ func TestSnpReportSignature(t *testing.T) {
162164

163165
func TestKdsMetadataLogic(t *testing.T) {
164166
signMu.Do(initSigner)
165-
DefaultRootCerts.noCrossCheckTestOnly = true
166167
asn1Zero, _ := asn1.Marshal(0)
167168
productName, _ := asn1.Marshal("Cookie-B0")
168169
var hwid [64]byte
@@ -284,7 +285,19 @@ func TestKdsMetadataLogic(t *testing.T) {
284285
t.Errorf("%+v.CertChain() errored unexpectedly: %v", tc.builder, err)
285286
continue
286287
}
287-
vcek, _, err := VcekDER(newSigner.Vcek.Raw, newSigner.Ask.Raw, newSigner.Ark.Raw)
288+
// Trust the test-generated root if the test should pass. Otherwise, other root logic
289+
// won't get tested.
290+
options := &Options{TrustedRoots: map[string][]*AMDRootCerts{
291+
"Milan": []*AMDRootCerts{&AMDRootCerts{
292+
Platform: "Milan",
293+
ArkX509: newSigner.Ark,
294+
AskX509: newSigner.Ask,
295+
}},
296+
}}
297+
if tc.wantErr != "" {
298+
options = &Options{}
299+
}
300+
vcek, _, err := VcekDER(newSigner.Vcek.Raw, newSigner.Ask.Raw, newSigner.Ark.Raw, options)
288301
if err == nil && tc.wantErr != "" {
289302
t.Errorf("%s: VcekDER(...) = %+v did not error as expected.", tc.name, vcek)
290303
}
@@ -382,13 +395,20 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) {
382395
t.Error(err)
383396
}
384397
defer d.Close()
398+
// Trust the test device's root certs.
399+
options := &Options{TrustedRoots: map[string][]*AMDRootCerts{
400+
"Milan": []*AMDRootCerts{&AMDRootCerts{
401+
Platform: "Milan",
402+
ArkX509: d.Signer.Ark,
403+
AskX509: d.Signer.Ask,
404+
}}}}
385405
for _, tc := range tests {
386406
ereport, err := sg.GetExtendedReport(d, tc.Input)
387407
if err != tc.WantErr {
388408
t.Fatalf("%s: GetExtendedReport(d, %v) = %v, %v. Want err: %v", tc.Name, tc.Input, ereport, err, tc.WantErr)
389409
}
390410
if tc.WantErr == nil {
391-
if err := SnpAttestation(ereport, &Options{}); err != nil {
411+
if err := SnpAttestation(ereport, options); err != nil {
392412
t.Errorf("SnpAttestation(%v) errored unexpectedly: %v", ereport, err)
393413
}
394414
}

0 commit comments

Comments
 (0)