Skip to content

Commit 8da1394

Browse files
committed
Add product utility unit tests
Improves test coverage, but also expands GetAttestationFromReport to fill in the Product field of the Attestation message from the fetched VCEK/VLEK certificate's extension. Signed-off-by: Dionna Glaze <dionnaglaze@google.com>
1 parent 1a9dbbc commit 8da1394

File tree

5 files changed

+134
-14
lines changed

5 files changed

+134
-14
lines changed

abi/abi_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ import (
2121
"strings"
2222
"testing"
2323

24+
"github.com/google/go-cmp/cmp"
2425
spb "github.com/google/go-sev-guest/proto/sevsnp"
2526
"github.com/pborman/uuid"
2627
"google.golang.org/protobuf/encoding/prototext"
28+
"google.golang.org/protobuf/testing/protocmp"
29+
"google.golang.org/protobuf/types/known/wrapperspb"
2730
)
2831

2932
var emptyReport = `
@@ -267,3 +270,50 @@ func TestCertTableProto(t *testing.T) {
267270
t.Fatalf("Extras[%q] = %v, want %v", extraGUID, gotExtra, extraraw)
268271
}
269272
}
273+
274+
func TestSevProduct(t *testing.T) {
275+
oldCpuid := cpuid
276+
defer func() { cpuid = oldCpuid }()
277+
tcs := []struct {
278+
eax uint32
279+
want *spb.SevProduct
280+
}{
281+
{
282+
eax: 0x00a00f10,
283+
want: &spb.SevProduct{
284+
Name: spb.SevProduct_SEV_PRODUCT_MILAN,
285+
MachineStepping: &wrapperspb.UInt32Value{Value: 0}},
286+
},
287+
{
288+
eax: 0x00a00f11,
289+
want: &spb.SevProduct{
290+
Name: spb.SevProduct_SEV_PRODUCT_MILAN,
291+
MachineStepping: &wrapperspb.UInt32Value{Value: 1}},
292+
},
293+
{
294+
eax: 0x00a10f10,
295+
want: &spb.SevProduct{
296+
Name: spb.SevProduct_SEV_PRODUCT_GENOA,
297+
MachineStepping: &wrapperspb.UInt32Value{Value: 0}},
298+
},
299+
{
300+
eax: 0x00a10f12,
301+
want: &spb.SevProduct{
302+
Name: spb.SevProduct_SEV_PRODUCT_GENOA,
303+
MachineStepping: &wrapperspb.UInt32Value{Value: 2}},
304+
},
305+
{
306+
eax: 0x0b010f0,
307+
want: &spb.SevProduct{
308+
Name: spb.SevProduct_SEV_PRODUCT_UNKNOWN,
309+
MachineStepping: &wrapperspb.UInt32Value{Value: 0}},
310+
},
311+
}
312+
for _, tc := range tcs {
313+
cpuid = func(op uint32) (uint32, uint32, uint32, uint32) { return tc.eax, 0, 0, 0 }
314+
got := SevProduct()
315+
if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" {
316+
t.Errorf("SevProduct() = %+v, want %+v. Diff: %s", got, tc.want, diff)
317+
}
318+
}
319+
}

kds/kds.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,9 @@ func preEndorsementKeyCertificateExtensions(cert *x509.Certificate) (*Extensions
406406
// VcekCertificateExtensions returns the x509v3 extensions from the KDS specification of a VCEK
407407
// certificate interpreted into a struct type.
408408
func VcekCertificateExtensions(cert *x509.Certificate) (*Extensions, error) {
409+
if cert == nil {
410+
return nil, fmt.Errorf("cert cannot be nil")
411+
}
409412
exts, err := preEndorsementKeyCertificateExtensions(cert)
410413
if err != nil {
411414
return nil, err
@@ -422,6 +425,9 @@ func VcekCertificateExtensions(cert *x509.Certificate) (*Extensions, error) {
422425
// VlekCertificateExtensions returns the x509v3 extensions from the KDS specification of a VLEK
423426
// certificate interpreted into a struct type.
424427
func VlekCertificateExtensions(cert *x509.Certificate) (*Extensions, error) {
428+
if cert == nil {
429+
return nil, fmt.Errorf("cert cannot be nil")
430+
}
425431
exts, err := preEndorsementKeyCertificateExtensions(cert)
426432
if err != nil {
427433
return nil, err

kds/kds_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,30 @@ func TestProductName(t *testing.T) {
218218
},
219219
want: "badstepping",
220220
},
221+
{
222+
name: "unknown milan stepping",
223+
input: &pb.SevProduct{
224+
Name: pb.SevProduct_SEV_PRODUCT_MILAN,
225+
MachineStepping: &wrapperspb.UInt32Value{Value: 15},
226+
},
227+
want: "unmappedMilanStepping",
228+
},
229+
{
230+
name: "unknown genoa stepping",
231+
input: &pb.SevProduct{
232+
Name: pb.SevProduct_SEV_PRODUCT_GENOA,
233+
MachineStepping: &wrapperspb.UInt32Value{Value: 15},
234+
},
235+
want: "unmappedGenoaStepping",
236+
},
237+
{
238+
name: "unknown",
239+
input: &pb.SevProduct{
240+
Name: pb.SevProduct_SEV_PRODUCT_UNKNOWN,
241+
MachineStepping: &wrapperspb.UInt32Value{Value: 15},
242+
},
243+
want: "Unknown",
244+
},
221245
}
222246
for _, tc := range tcs {
223247
t.Run(tc.name, func(t *testing.T) {
@@ -266,6 +290,12 @@ func TestParseProductName(t *testing.T) {
266290
Name: pb.SevProduct_SEV_PRODUCT_GENOA,
267291
},
268292
},
293+
{
294+
name: "Unhandled report signer",
295+
input: "ignored",
296+
key: abi.NoneReportSigner,
297+
wantErr: "internal: unhandled reportSigner",
298+
},
269299
}
270300
for _, tc := range tcs {
271301
t.Run(tc.name, func(t *testing.T) {

verify/verify.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ func checkProductName(got, want *spb.SevProduct, key abi.ReportSigner) error {
456456
return fmt.Errorf("stepping value in VCEK certificate should not be nil")
457457
}
458458
if got.MachineStepping.Value != want.MachineStepping.Value {
459-
return fmt.Errorf("%v cert product stepping number %02X is not %02X",
459+
return fmt.Errorf("%v cert product stepping number 0x%X is not 0x%X",
460460
key, got.MachineStepping.Value, want.MachineStepping.Value)
461461
}
462462
}
@@ -659,11 +659,6 @@ func SnpAttestation(attestation *spb.Attestation, options *Options) error {
659659
if err := fillInAttestation(attestation, options); err != nil {
660660
return err
661661
}
662-
// Pass along the expected product information for VcekDER. fillInAttestation will ensure
663-
// that this is a noop if options.Product began as non-nil.
664-
if err := updateProductExpectation(&options.Product, attestation.Product); err != nil {
665-
return err
666-
}
667662

668663
report := attestation.GetReport()
669664
info, err := abi.ParseSignerInfo(report.GetSignerInfo())
@@ -758,7 +753,10 @@ func fillInAttestation(attestation *spb.Attestation, options *Options) error {
758753
return ErrMissingVlek
759754
}
760755
}
761-
return nil
756+
757+
// Pass along the expected product information for VcekDER. fillInAttestation will ensure
758+
// that this is a noop if options.Product began as non-nil.
759+
return updateProductExpectation(&options.Product, attestation.Product)
762760
}
763761

764762
// GetAttestationFromReport uses AMD's Key Distribution Service (KDS) to download the certificate
@@ -772,6 +770,23 @@ func GetAttestationFromReport(report *spb.Report, options *Options) (*spb.Attest
772770
if err := fillInAttestation(result, options); err != nil {
773771
return nil, err
774772
}
773+
// Attempt to fill in the product field of the attestation. Don't error at this
774+
// point since this is not validation.
775+
info, _ := abi.ParseSignerInfo(report.SignerInfo)
776+
var exts *kds.Extensions
777+
parse := func(der []byte) *x509.Certificate {
778+
out, _ := x509.ParseCertificate(der)
779+
return out
780+
}
781+
switch info.SigningKey {
782+
case abi.VcekReportSigner:
783+
exts, _ = kds.VcekCertificateExtensions(parse(result.CertificateChain.VcekCert))
784+
case abi.VlekReportSigner:
785+
exts, _ = kds.VlekCertificateExtensions(parse(result.CertificateChain.VlekCert))
786+
}
787+
if exts != nil {
788+
result.Product, _ = kds.ParseProductName(exts.ProductName, info.SigningKey)
789+
}
775790
return result, nil
776791
}
777792

verify/verify_test.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -557,13 +557,32 @@ func TestRealAttestationVerification(t *testing.T) {
557557
"https://kdsintf.amd.com/vcek/v1/Milan/3ac3fe21e13fb0990eb28a802e3fb6a29483a6b0753590c951bdd3b8e53786184ca39e359669a2b76a1936776b564ea464cdce40c05f63c9b610c5068b006b5d?blSPL=2&teeSPL=0&snpSPL=5&ucodeSPL=68": testdata.VcekBytes,
558558
},
559559
)
560-
if err := RawSnpReport(testdata.AttestationBytes, &Options{
561-
Getter: getter,
562-
Product: &pb.SevProduct{
563-
Name: pb.SevProduct_SEV_PRODUCT_MILAN,
564-
MachineStepping: &wrapperspb.UInt32Value{Value: 0},
565-
}}); err != nil {
566-
t.Error(err)
560+
tcs := []struct {
561+
name string
562+
product *pb.SevProduct
563+
wantErr string
564+
}{
565+
{
566+
name: "happy path",
567+
product: &pb.SevProduct{
568+
Name: pb.SevProduct_SEV_PRODUCT_MILAN,
569+
MachineStepping: &wrapperspb.UInt32Value{Value: 0},
570+
},
571+
},
572+
{
573+
name: "bad vcek stepping",
574+
product: &pb.SevProduct{
575+
Name: pb.SevProduct_SEV_PRODUCT_MILAN,
576+
MachineStepping: &wrapperspb.UInt32Value{Value: 12},
577+
},
578+
wantErr: "expected product stepping 12, got 0",
579+
},
580+
}
581+
for _, tc := range tcs {
582+
opts := &Options{Getter: getter, Product: tc.product}
583+
if err := RawSnpReport(testdata.AttestationBytes, opts); !test.Match(err, tc.wantErr) {
584+
t.Errorf("RawSnpReport(_, %+v) = %v errored unexpectedly. Want %q", opts, err, tc.wantErr)
585+
}
567586
}
568587
}
569588

0 commit comments

Comments
 (0)