diff --git a/abi/abi.go b/abi/abi.go index a1dbd10..f8c1cba 100644 --- a/abi/abi.go +++ b/abi/abi.go @@ -117,14 +117,17 @@ const ( ExtraPlatformInfoV0Size = 8 // CpuidProductMask keeps only the SevProduct-relevant bits from the CPUID(1).EAX result. - CpuidProductMask = 0x0fff0f0f + CpuidProductMask = 0x0fff0fff extendedFamilyShift = 20 extendedModelShift = 16 familyShift = 8 - sevExtendedFamily = 0xA - sevFamily = 0xF - milanExtendedModel = 0 - genoaExtendedModel = 1 + modelShift = 4 + // Combined extended values + zen3zen4Family = 0x19 + zen5Family = 0x1A + milanModel = 0 | 1 + genoaModel = (1 << 4) | 1 + turinModel = 2 // ReportVersion2 is set by the SNP API specification // https://web.archive.org/web/20231222054111if_/http://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56860.pdf @@ -487,9 +490,7 @@ func ReportToProto(data []uint8) (*pb.Report, error) { mbzLo := 0x188 if r.Version == ReportVersion3 { mbzLo = 0x18B - r.CpuidFamId = []byte{data[0x188]} - r.CpuidModId = []byte{data[0x189]} - r.CpuidStep = []byte{data[0x18A]} + r.Cpuid1EaxFms = FmsToCpuid1Eax(data[0x188], data[0x189], data[0x18A]) } if err := mbz(data, mbzLo, 0x1A0); err != nil { @@ -635,9 +636,10 @@ func ReportToAbiBytes(r *pb.Report) ([]byte, error) { // Add CPUID information if this is a version 3 report. if r.Version == ReportVersion3 { - data[0x188] = r.CpuidFamId[0] - data[0x189] = r.CpuidModId[0] - data[0x18A] = r.CpuidStep[0] + family, model, stepping := FmsFromCpuid1Eax(r.Cpuid1EaxFms) + data[0x188] = family + data[0x189] = model + data[0x18A] = stepping } copy(data[0x1A0:0x1E0], r.ChipId[:]) @@ -903,59 +905,106 @@ func (c *CertTable) Proto() *pb.CertificateChain { // See assembly implementations in cpuid_*.s var cpuid func(op uint32) (eax, ebx, ecx, edx uint32) -// SevProductFromCpuid1Eax returns the SevProduct that is represented by cpuid(1).eax. -func SevProductFromCpuid1Eax(eax uint32) *pb.SevProduct { +// FmsToCpuid1Eax returns the masked CPUID_1_EAX value that represents the given +// family, model, stepping (FMS) values. +func FmsToCpuid1Eax(family, model, stepping byte) uint32 { + var extendedFamily byte + + familyID := family + if family >= 0xf { + extendedFamily = family - 0xf + familyID = 0xf + } + extendedModel := model >> 4 + modelID := model & 0xf + return (uint32(extendedFamily) << extendedFamilyShift) | + (uint32(extendedModel) << extendedModelShift) | + (uint32(familyID) << familyShift) | + (uint32(modelID) << modelShift) | + (uint32(stepping & 0xf)) +} + +// FmsFromCpuid1Eax returns the family, model, stepping (FMS) values extracted from a +// CPUID_1_EAX value. +func FmsFromCpuid1Eax(eax uint32) (byte, byte, byte) { // 31:28 reserved // 27:20 Extended Family ID - extendedFamily := (eax >> extendedFamilyShift) & 0xff + extendedFamily := byte((eax >> extendedFamilyShift) & 0xff) // 19:16 Extended Model ID - extendedModel := (eax >> extendedModelShift) & 0xf + extendedModel := byte((eax >> extendedModelShift) & 0xf) // 15:14 reserved // 11:8 Family ID - family := (eax >> familyShift) & 0xf + familyID := byte((eax >> familyShift) & 0xf) + // 7:4 Model + modelID := byte((eax >> modelShift) & 0xf) // 3:0 Stepping - stepping := eax & 0xf + family := extendedFamily + familyID + model := (extendedModel << 4) | modelID + stepping := byte(eax & 0xf) + return family, model, stepping +} + +// SevProductFromCpuid1Eax returns the SevProduct that is represented by cpuid(1).eax. +func SevProductFromCpuid1Eax(eax uint32) *pb.SevProduct { + family, model, stepping := FmsFromCpuid1Eax(eax) // Ah, Fh, {0h,1h} values from the KDS specification, // section "Determining the Product Name". var productName pb.SevProduct_SevProductName + unknown := func() { + productName = pb.SevProduct_SEV_PRODUCT_UNKNOWN + stepping = 0 // Reveal nothing. + } // Product information specified by processor programming reference publications. - if extendedFamily == sevExtendedFamily && family == sevFamily { - switch extendedModel { - case milanExtendedModel: + switch family { + case zen3zen4Family: + switch model { + case milanModel: productName = pb.SevProduct_SEV_PRODUCT_MILAN - case genoaExtendedModel: + case genoaModel: productName = pb.SevProduct_SEV_PRODUCT_GENOA default: - productName = pb.SevProduct_SEV_PRODUCT_UNKNOWN - stepping = 0 // Reveal nothing. + unknown() + } + case zen5Family: + switch model { + case turinModel: + productName = pb.SevProduct_SEV_PRODUCT_TURIN + default: + unknown() } + default: + unknown() } return &pb.SevProduct{ Name: productName, - MachineStepping: &wrapperspb.UInt32Value{Value: stepping}, + MachineStepping: &wrapperspb.UInt32Value{Value: uint32(stepping)}, } } // MaskedCpuid1EaxFromSevProduct returns the Cpuid1Eax value expected from the given product // when masked with CpuidProductMask. func MaskedCpuid1EaxFromSevProduct(product *pb.SevProduct) uint32 { - var stepping uint32 + if product == nil { + return 0 + } + var family, model, stepping byte if product.MachineStepping != nil { - stepping = product.MachineStepping.Value & 0xf + stepping = byte(product.MachineStepping.Value & 0xf) } - extendedFamily := uint32(sevExtendedFamily) << extendedFamilyShift - family := uint32(sevFamily) << familyShift - - var extendedModel uint32 switch product.Name { case pb.SevProduct_SEV_PRODUCT_MILAN: - extendedModel = milanExtendedModel + family = zen3zen4Family + model = milanModel case pb.SevProduct_SEV_PRODUCT_GENOA: - extendedModel = genoaExtendedModel + family = zen3zen4Family + model = genoaModel + case pb.SevProduct_SEV_PRODUCT_TURIN: + family = zen5Family + model = turinModel default: return 0 } - return extendedFamily | family | stepping | (extendedModel << extendedModelShift) + return FmsToCpuid1Eax(family, model, stepping) } // SevProduct returns the SEV product enum for the CPU that runs this diff --git a/abi/abi_test.go b/abi/abi_test.go index de56e99..e64239a 100644 --- a/abi/abi_test.go +++ b/abi/abi_test.go @@ -17,6 +17,7 @@ package abi import ( "bytes" "encoding/hex" + "fmt" "math/rand" "runtime" "strings" @@ -61,9 +62,7 @@ var ( author_key_digest: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' report_id: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' report_id_ma: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - cpuid_fam_id: '\x00\x00' - cpuid_mod_id: '\x00\x00' - cpuid_step: '\x00\x00' + cpuid1eax_fms: 0 chip_id: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' signature: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' ` @@ -396,17 +395,26 @@ func TestSevProduct(t *testing.T) { MachineStepping: &wrapperspb.UInt32Value{Value: 0}, }, }, + { + eax: 0x00b00f21, + want: &spb.SevProduct{ + Name: spb.SevProduct_SEV_PRODUCT_TURIN, + MachineStepping: &wrapperspb.UInt32Value{Value: 1}, + }, + }, } for _, tc := range tcs { - cpuid = func(uint32) (uint32, uint32, uint32, uint32) { return tc.eax, 0, 0, 0 } - got := SevProduct() - if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" { - t.Errorf("SevProduct() = %+v, want %+v. Diff: %s", got, tc.want, diff) - } - got2 := SevProductFromCpuid1Eax(tc.eax) - if diff := cmp.Diff(got2, got, protocmp.Transform()); diff != "" { - t.Errorf("SevProductFromCpuid1Eax(0x%x) = %+v, want %+v. Diff: %s", tc.eax, got2, tc.want, diff) - } + t.Run(fmt.Sprintf("EAX_0x%x", tc.eax), func(t *testing.T) { + cpuid = func(uint32) (uint32, uint32, uint32, uint32) { return tc.eax, 0, 0, 0 } + got := SevProduct() + if diff := cmp.Diff(got, tc.want, protocmp.Transform()); diff != "" { + t.Errorf("SevProduct() = %+v, want %+v. Diff: %s", got, tc.want, diff) + } + got2 := SevProductFromCpuid1Eax(tc.eax) + if diff := cmp.Diff(got2, got, protocmp.Transform()); diff != "" { + t.Errorf("SevProductFromCpuid1Eax(0x%x) = %+v, want %+v. Diff: %s", tc.eax, got2, tc.want, diff) + } + }) } } @@ -446,9 +454,9 @@ func TestExtendedPlatformCertTable(t *testing.T) { eax uint32 stepping uint32 }{ - {name: "Genoa-B2 cruft", pname: spb.SevProduct_SEV_PRODUCT_GENOA, eax: 0x00a10f12, stepping: 2}, - {name: "Milan-B1 cruft", pname: spb.SevProduct_SEV_PRODUCT_MILAN, eax: 0x00a00f11, stepping: 1}, - {name: "Milan-B0", pname: spb.SevProduct_SEV_PRODUCT_MILAN, eax: 0x00a00f00, stepping: 0}, + {name: "Genoa-B2", pname: spb.SevProduct_SEV_PRODUCT_GENOA, eax: 0x00a10f12, stepping: 2}, + {name: "Milan-B1", pname: spb.SevProduct_SEV_PRODUCT_MILAN, eax: 0x00a00f11, stepping: 1}, + {name: "Milan-B0", pname: spb.SevProduct_SEV_PRODUCT_MILAN, eax: 0x00a00f10, stepping: 0}, } for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { diff --git a/go.mod b/go.mod index c3534b8..1495bfd 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/google/go-sev-guest go 1.19 require ( + github.com/golang/protobuf v1.5.0 github.com/google/go-cmp v0.5.7 github.com/google/go-configfs-tsm v0.2.2 github.com/google/logger v1.1.1 diff --git a/go.sum b/go.sum index 83d5d8c..3554e90 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-configfs-tsm v0.2.2 h1:YnJ9rXIOj5BYD7/0DNnzs8AOp7UcvjfTvt215EWcs98= @@ -18,6 +21,7 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/kds/kds.go b/kds/kds.go index 7b44a91..b27db7a 100644 --- a/kds/kds.go +++ b/kds/kds.go @@ -99,9 +99,19 @@ var ( "Genoa-B0": {Name: pb.SevProduct_SEV_PRODUCT_GENOA, MachineStepping: uint0}, "Genoa-B1": {Name: pb.SevProduct_SEV_PRODUCT_GENOA, MachineStepping: uint1}, "Genoa-B2": {Name: pb.SevProduct_SEV_PRODUCT_GENOA, MachineStepping: uint2}, + "Turin-B0": {Name: pb.SevProduct_SEV_PRODUCT_TURIN, MachineStepping: uint0}, + "Turin-B1": {Name: pb.SevProduct_SEV_PRODUCT_TURIN, MachineStepping: uint1}, } milanSteppingVersions = []string{"B0", "B1"} genoaSteppingVersions = []string{"B0", "B1", "B2"} + turinSteppingVersions = []string{"B0", "B1"} + + // ProductLineCpuid associates the CPUID_1_EAX value (Stepping 0) to its AMD product name. + ProductLineCpuid = map[uint32]string{ + 0x00a00f10: "Milan", + 0x00a10f10: "Genoa", + 0x00b00f20: "Turin", + } ) // TCBVersion is a 64-bit bitfield of different security patch levels of AMD firmware and microcode. @@ -731,6 +741,8 @@ func ProductLine(product *pb.SevProduct) string { return "Milan" case pb.SevProduct_SEV_PRODUCT_GENOA: return "Genoa" + case pb.SevProduct_SEV_PRODUCT_TURIN: + return "Turin" default: return "Unknown" } @@ -785,12 +797,22 @@ func ProductName(product *pb.SevProduct) string { if int(stepping) >= len(genoaSteppingVersions) { return "unmappedGenoaStepping" } - return fmt.Sprintf("Milan-%s", genoaSteppingVersions[stepping]) + return fmt.Sprintf("Genoa-%s", genoaSteppingVersions[stepping]) + case pb.SevProduct_SEV_PRODUCT_TURIN: + if int(stepping) >= len(turinSteppingVersions) { + return "unmappedTurinStepping" + } + return fmt.Sprintf("Turin-%s", turinSteppingVersions[stepping]) default: return "Unknown" } } +// ProductLineFromFms returns the product name used in the KDS endpoint to fetch VCEK certificates. +func ProductLineFromFms(fms uint32) string { + return ProductLine(abi.SevProductFromCpuid1Eax(fms)) +} + // ParseProduct returns the SevProductName for a product name without the stepping suffix. // // Deprecated: Use ParseProductLine @@ -809,6 +831,8 @@ func ParseProductLine(productLine string) (*pb.SevProduct, error) { return &pb.SevProduct{Name: pb.SevProduct_SEV_PRODUCT_MILAN}, nil case "Genoa": return &pb.SevProduct{Name: pb.SevProduct_SEV_PRODUCT_GENOA}, nil + case "Turin": + return &pb.SevProduct{Name: pb.SevProduct_SEV_PRODUCT_TURIN}, nil default: return nil, fmt.Errorf("unknown AMD SEV product: %q", productLine) } diff --git a/proto/fakekds.proto b/proto/fakekds.proto index 3cdfef7..a9e8c46 100644 --- a/proto/fakekds.proto +++ b/proto/fakekds.proto @@ -26,6 +26,7 @@ message Certificates { bytes chip_id = 1; // Should be 64 bytes map tcb_certs = 2; string hostname = 3; + uint32 fms = 4; } repeated ChipTCBCerts chip_certs = 1; } diff --git a/proto/fakekds/fakekds.pb.go b/proto/fakekds/fakekds.pb.go index 073ff73..0d331d6 100644 --- a/proto/fakekds/fakekds.pb.go +++ b/proto/fakekds/fakekds.pb.go @@ -92,6 +92,7 @@ type Certificates_ChipTCBCerts struct { ChipId []byte `protobuf:"bytes,1,opt,name=chip_id,json=chipId,proto3" json:"chip_id,omitempty"` // Should be 64 bytes TcbCerts map[uint64][]byte `protobuf:"bytes,2,rep,name=tcb_certs,json=tcbCerts,proto3" json:"tcb_certs,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` + Fms uint32 `protobuf:"varint,4,opt,name=fms,proto3" json:"fms,omitempty"` } func (x *Certificates_ChipTCBCerts) Reset() { @@ -147,16 +148,23 @@ func (x *Certificates_ChipTCBCerts) GetHostname() string { return "" } +func (x *Certificates_ChipTCBCerts) GetFms() uint32 { + if x != nil { + return x.Fms + } + return 0 +} + var File_fakekds_proto protoreflect.FileDescriptor var file_fakekds_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x07, 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, 0x22, 0xa3, 0x02, 0x0a, 0x0c, 0x43, 0x65, 0x72, + 0x07, 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, 0x22, 0xb5, 0x02, 0x0a, 0x0c, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x63, 0x68, 0x69, 0x70, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x43, 0x68, 0x69, 0x70, 0x54, 0x43, 0x42, 0x43, 0x65, 0x72, 0x74, - 0x73, 0x52, 0x09, 0x63, 0x68, 0x69, 0x70, 0x43, 0x65, 0x72, 0x74, 0x73, 0x1a, 0xcf, 0x01, 0x0a, + 0x73, 0x52, 0x09, 0x63, 0x68, 0x69, 0x70, 0x43, 0x65, 0x72, 0x74, 0x73, 0x1a, 0xe1, 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x69, 0x70, 0x54, 0x43, 0x42, 0x43, 0x65, 0x72, 0x74, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x68, 0x69, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x68, 0x69, 0x70, 0x49, 0x64, 0x12, 0x4d, 0x0a, 0x09, 0x74, 0x63, 0x62, 0x5f, 0x63, 0x65, @@ -166,14 +174,15 @@ var file_fakekds_proto_rawDesc = []byte{ 0x62, 0x43, 0x65, 0x72, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x74, 0x63, 0x62, 0x43, 0x65, 0x72, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 0x1a, 0x3b, 0x0a, 0x0d, 0x54, 0x63, 0x62, 0x43, 0x65, 0x72, 0x74, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x2e, - 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x67, 0x6f, 0x2d, 0x73, 0x65, 0x76, 0x2d, 0x67, 0x75, 0x65, 0x73, 0x74, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, + 0x66, 0x6d, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x54, 0x63, 0x62, 0x43, 0x65, 0x72, 0x74, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x67, 0x6f, 0x2d, 0x73, 0x65, 0x76, 0x2d, 0x67, 0x75, 0x65, + 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x6b, 0x64, 0x73, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/sevsnp.proto b/proto/sevsnp.proto index 2ef34dc..8bc6e3d 100644 --- a/proto/sevsnp.proto +++ b/proto/sevsnp.proto @@ -56,9 +56,7 @@ message Report { uint64 launch_tcb = 27; bytes signature = 28; // Should be 512 bytes long - bytes cpuid_fam_id = 29; // 1 byte combined Extended Family ID and Family ID - bytes cpuid_mod_id = 30; // 1 byte combined Extended Model and Model fields - bytes cpuid_step = 31; // 1 byte Stepping + uint32 cpuid1eax_fms = 29; // The cpuid(1).eax & 0x0fff0fff representation of family/model/stepping } message CertificateChain { @@ -93,6 +91,7 @@ message SevProduct { SEV_PRODUCT_UNKNOWN = 0; SEV_PRODUCT_MILAN = 1; SEV_PRODUCT_GENOA = 2; + SEV_PRODUCT_TURIN = 3; } SevProductName name = 1; diff --git a/proto/sevsnp/sevsnp.pb.go b/proto/sevsnp/sevsnp.pb.go index 5790cff..05fb8b1 100644 --- a/proto/sevsnp/sevsnp.pb.go +++ b/proto/sevsnp/sevsnp.pb.go @@ -14,8 +14,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 -// protoc v5.28.3 +// protoc-gen-go v1.31.0 +// protoc v5.27.2 // source: sevsnp.proto // Package sevsnp represents an SEV-SNP attestation report and its certificate @@ -44,6 +44,7 @@ const ( SevProduct_SEV_PRODUCT_UNKNOWN SevProduct_SevProductName = 0 SevProduct_SEV_PRODUCT_MILAN SevProduct_SevProductName = 1 SevProduct_SEV_PRODUCT_GENOA SevProduct_SevProductName = 2 + SevProduct_SEV_PRODUCT_TURIN SevProduct_SevProductName = 3 ) // Enum value maps for SevProduct_SevProductName. @@ -52,11 +53,13 @@ var ( 0: "SEV_PRODUCT_UNKNOWN", 1: "SEV_PRODUCT_MILAN", 2: "SEV_PRODUCT_GENOA", + 3: "SEV_PRODUCT_TURIN", } SevProduct_SevProductName_value = map[string]int32{ "SEV_PRODUCT_UNKNOWN": 0, "SEV_PRODUCT_MILAN": 1, "SEV_PRODUCT_GENOA": 2, + "SEV_PRODUCT_TURIN": 3, } ) @@ -124,10 +127,8 @@ type Report struct { CommittedMinor uint32 `protobuf:"varint,25,opt,name=committed_minor,json=committedMinor,proto3" json:"committed_minor,omitempty"` CommittedMajor uint32 `protobuf:"varint,26,opt,name=committed_major,json=committedMajor,proto3" json:"committed_major,omitempty"` LaunchTcb uint64 `protobuf:"varint,27,opt,name=launch_tcb,json=launchTcb,proto3" json:"launch_tcb,omitempty"` - Signature []byte `protobuf:"bytes,28,opt,name=signature,proto3" json:"signature,omitempty"` // Should be 512 bytes long - CpuidFamId []byte `protobuf:"bytes,29,opt,name=cpuid_fam_id,json=cpuidFamId,proto3" json:"cpuid_fam_id,omitempty"` // 1 byte combined Extended Family ID and Family ID - CpuidModId []byte `protobuf:"bytes,30,opt,name=cpuid_mod_id,json=cpuidModId,proto3" json:"cpuid_mod_id,omitempty"` // 1 byte combined Extended Model and Model fields - CpuidStep []byte `protobuf:"bytes,31,opt,name=cpuid_step,json=cpuidStep,proto3" json:"cpuid_step,omitempty"` // 1 byte Stepping + Signature []byte `protobuf:"bytes,28,opt,name=signature,proto3" json:"signature,omitempty"` // Should be 512 bytes long + Cpuid1EaxFms uint32 `protobuf:"varint,29,opt,name=cpuid1eax_fms,json=cpuid1eaxFms,proto3" json:"cpuid1eax_fms,omitempty"` // The cpuid(1).eax & 0x0fff0fff representation of family/model/stepping } func (x *Report) Reset() { @@ -358,25 +359,11 @@ func (x *Report) GetSignature() []byte { return nil } -func (x *Report) GetCpuidFamId() []byte { +func (x *Report) GetCpuid1EaxFms() uint32 { if x != nil { - return x.CpuidFamId + return x.Cpuid1EaxFms } - return nil -} - -func (x *Report) GetCpuidModId() []byte { - if x != nil { - return x.CpuidModId - } - return nil -} - -func (x *Report) GetCpuidStep() []byte { - if x != nil { - return x.CpuidStep - } - return nil + return 0 } type CertificateChain struct { @@ -616,7 +603,7 @@ var file_sevsnp_proto_rawDesc = []byte{ 0x0a, 0x0c, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8b, 0x08, 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcd, 0x07, 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x76, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, @@ -675,62 +662,59 @@ var file_sevsnp_proto_rawDesc = []byte{ 0x63, 0x62, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x54, 0x63, 0x62, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x63, 0x70, 0x75, 0x69, 0x64, 0x5f, 0x66, 0x61, 0x6d, 0x5f, 0x69, - 0x64, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x70, 0x75, 0x69, 0x64, 0x46, 0x61, - 0x6d, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x63, 0x70, 0x75, 0x69, 0x64, 0x5f, 0x6d, 0x6f, 0x64, - 0x5f, 0x69, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x70, 0x75, 0x69, 0x64, - 0x4d, 0x6f, 0x64, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x70, 0x75, 0x69, 0x64, 0x5f, 0x73, - 0x74, 0x65, 0x70, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x70, 0x75, 0x69, 0x64, - 0x53, 0x74, 0x65, 0x70, 0x22, 0xa4, 0x02, 0x0a, 0x10, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x63, 0x65, - 0x6b, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x76, 0x63, - 0x65, 0x6b, 0x43, 0x65, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6c, 0x65, 0x6b, 0x5f, 0x63, - 0x65, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x76, 0x6c, 0x65, 0x6b, 0x43, - 0x65, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x6b, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x6b, 0x43, 0x65, 0x72, 0x74, 0x12, 0x19, - 0x0a, 0x08, 0x61, 0x72, 0x6b, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x07, 0x61, 0x72, 0x6b, 0x43, 0x65, 0x72, 0x74, 0x12, 0x27, 0x0a, 0x0d, 0x66, 0x69, 0x72, - 0x6d, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x43, 0x65, - 0x72, 0x74, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x78, 0x74, 0x72, 0x61, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, 0x43, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x45, 0x78, 0x74, - 0x72, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x65, 0x78, 0x74, 0x72, 0x61, 0x73, - 0x1a, 0x39, 0x0a, 0x0b, 0x45, 0x78, 0x74, 0x72, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x85, 0x02, 0x0a, 0x0a, - 0x53, 0x65, 0x76, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x12, 0x35, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x73, 0x65, 0x76, 0x73, 0x6e, - 0x70, 0x2e, 0x53, 0x65, 0x76, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x2e, 0x53, 0x65, 0x76, - 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x1e, 0x0a, 0x08, 0x73, 0x74, 0x65, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x73, 0x74, 0x65, 0x70, 0x70, 0x69, 0x6e, - 0x67, 0x12, 0x47, 0x0a, 0x10, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x74, 0x65, - 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, - 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x6d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x53, 0x74, 0x65, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x22, 0x57, 0x0a, 0x0e, 0x53, 0x65, - 0x76, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x13, - 0x53, 0x45, 0x56, 0x5f, 0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, - 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x45, 0x56, 0x5f, 0x50, 0x52, 0x4f, - 0x44, 0x55, 0x43, 0x54, 0x5f, 0x4d, 0x49, 0x4c, 0x41, 0x4e, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, - 0x53, 0x45, 0x56, 0x5f, 0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x5f, 0x47, 0x45, 0x4e, 0x4f, - 0x41, 0x10, 0x02, 0x22, 0xaa, 0x01, 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x45, 0x0a, 0x11, 0x63, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, - 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x52, 0x10, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, 0x53, 0x65, 0x76, - 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, - 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x67, 0x6f, 0x2d, 0x73, 0x65, 0x76, 0x2d, 0x67, 0x75, 0x65, - 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x70, 0x75, 0x69, 0x64, 0x31, 0x65, 0x61, 0x78, 0x5f, 0x66, + 0x6d, 0x73, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x63, 0x70, 0x75, 0x69, 0x64, 0x31, + 0x65, 0x61, 0x78, 0x46, 0x6d, 0x73, 0x22, 0xa4, 0x02, 0x0a, 0x10, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x76, + 0x63, 0x65, 0x6b, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, + 0x76, 0x63, 0x65, 0x6b, 0x43, 0x65, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x6c, 0x65, 0x6b, + 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x76, 0x6c, 0x65, + 0x6b, 0x43, 0x65, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x6b, 0x5f, 0x63, 0x65, 0x72, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x73, 0x6b, 0x43, 0x65, 0x72, 0x74, + 0x12, 0x19, 0x0a, 0x08, 0x61, 0x72, 0x6b, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x61, 0x72, 0x6b, 0x43, 0x65, 0x72, 0x74, 0x12, 0x27, 0x0a, 0x0d, 0x66, + 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, + 0x43, 0x65, 0x72, 0x74, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x78, 0x74, 0x72, 0x61, 0x73, 0x18, 0x07, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x2e, 0x45, + 0x78, 0x74, 0x72, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x65, 0x78, 0x74, 0x72, + 0x61, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x45, 0x78, 0x74, 0x72, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9c, 0x02, + 0x0a, 0x0a, 0x53, 0x65, 0x76, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x12, 0x35, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x73, 0x65, 0x76, + 0x73, 0x6e, 0x70, 0x2e, 0x53, 0x65, 0x76, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x2e, 0x53, + 0x65, 0x76, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x08, 0x73, 0x74, 0x65, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x73, 0x74, 0x65, 0x70, 0x70, + 0x69, 0x6e, 0x67, 0x12, 0x47, 0x0a, 0x10, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x73, + 0x74, 0x65, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x6d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x65, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x22, 0x6e, 0x0a, 0x0e, + 0x53, 0x65, 0x76, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x17, + 0x0a, 0x13, 0x53, 0x45, 0x56, 0x5f, 0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x5f, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x45, 0x56, 0x5f, 0x50, + 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x5f, 0x4d, 0x49, 0x4c, 0x41, 0x4e, 0x10, 0x01, 0x12, 0x15, + 0x0a, 0x11, 0x53, 0x45, 0x56, 0x5f, 0x50, 0x52, 0x4f, 0x44, 0x55, 0x43, 0x54, 0x5f, 0x47, 0x45, + 0x4e, 0x4f, 0x41, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x45, 0x56, 0x5f, 0x50, 0x52, 0x4f, + 0x44, 0x55, 0x43, 0x54, 0x5f, 0x54, 0x55, 0x52, 0x49, 0x4e, 0x10, 0x03, 0x22, 0xaa, 0x01, 0x0a, + 0x0b, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x06, + 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, + 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x06, 0x72, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x12, 0x45, 0x0a, 0x11, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x10, 0x63, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x07, 0x70, + 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, + 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, 0x53, 0x65, 0x76, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, + 0x52, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x67, + 0x6f, 0x2d, 0x73, 0x65, 0x76, 0x2d, 0x67, 0x75, 0x65, 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/testing/client/client.go b/testing/client/client.go index 2804037..c90a982 100644 --- a/testing/client/client.go +++ b/testing/client/client.go @@ -22,6 +22,7 @@ import ( "github.com/google/go-sev-guest/client" test "github.com/google/go-sev-guest/testing" "github.com/google/go-sev-guest/verify/trust" + "google.golang.org/protobuf/proto" ) // SkipUnmockableTestCase returns whether we have to skip a mocked failure test case on real hardware. @@ -130,6 +131,17 @@ func GetSevQuoteProvider(tcs []test.TestCase, opts *test.DeviceOptions, tb testi }, }, }, + "Genoa": { + { + Product: "Genoa", // TODO(Issue#114): Remove + ProductLine: "Genoa", + ProductCerts: &trust.ProductCerts{ + Ask: sevQp.Device.Signer.Ask, + Ark: sevQp.Device.Signer.Ark, + Asvk: sevQp.Device.Signer.Asvk, + }, + }, + }, } badSnpRoot := map[string][]*trust.AMDRootCerts{ "Milan": { @@ -144,6 +156,18 @@ func GetSevQuoteProvider(tcs []test.TestCase, opts *test.DeviceOptions, tb testi }, }, }, + "Genoa": { + { + Product: "Genoa", // TODO(Issue#114): Remove + ProductLine: "Genoa", + ProductCerts: &trust.ProductCerts{ + // No ASK, oops. + Ask: sevQp.Device.Signer.Ark, + Ark: sevQp.Device.Signer.Ark, + Asvk: sevQp.Device.Signer.Ark, + }, + }, + }, } fakekds, err := test.FakeKDSFromSigner(sevQp.Device.Signer) if err != nil { @@ -152,6 +176,11 @@ func GetSevQuoteProvider(tcs []test.TestCase, opts *test.DeviceOptions, tb testi return sevQp, goodSnpRoot, badSnpRoot, fakekds } + // If requested to use a different product than on the machine, fail. + if opts.Product != nil && !proto.Equal(abi.SevProduct(), opts.Product) { + return nil, nil, nil, nil + } + client, err := client.GetQuoteProvider() if err != nil { tb.Fatalf("Failed to open SEV guest device: %v", err) diff --git a/testing/fake_certs.go b/testing/fake_certs.go index abc1741..ccae055 100644 --- a/testing/fake_certs.go +++ b/testing/fake_certs.go @@ -110,8 +110,9 @@ type AmdSigner struct { Keys *AmdKeys // This identity does not match AMD's notion of an HWID. It is purely to combine expectations of // report data -> KDS URL construction for the fake KDS implementation. - HWID [abi.ChipIDSize]byte - TCB kds.TCBVersion + HWID [abi.ChipIDSize]byte + TCB kds.TCBVersion + Product *spb.SevProduct } // AmdKeys encapsulates the key chain of ARK through ASK down to VCEK. diff --git a/testing/fakekds.go b/testing/fakekds.go index f843779..30bb0da 100644 --- a/testing/fakekds.go +++ b/testing/fakekds.go @@ -23,6 +23,7 @@ import ( "strings" "testing" + "github.com/google/go-sev-guest/abi" "github.com/google/go-sev-guest/kds" kpb "github.com/google/go-sev-guest/proto/fakekds" "github.com/google/go-sev-guest/verify/trust" @@ -75,14 +76,14 @@ type RootBundle struct { type FakeKDS struct { Certs *kpb.Certificates // Two CERTIFICATE PEMs for ASK, then ARK or ASVK then ARK, per product - RootBundles map[string]RootBundle + RootBundles map[string]*RootBundle } // FakeKDSFromFile returns a FakeKDS from a path to a serialized fakekds.Certificates message. func FakeKDSFromFile(path string) (*FakeKDS, error) { result := &FakeKDS{ Certs: &kpb.Certificates{}, - RootBundles: map[string]RootBundle{ + RootBundles: map[string]*RootBundle{ "Milan": { VcekBundle: string(trust.AskArkMilanVcekBytes), VlekBundle: string(trust.AskArkMilanVlekBytes), @@ -110,14 +111,17 @@ func FakeKDSFromFile(path string) (*FakeKDS, error) { // AMD KDS REST API expectations. func FakeKDSFromSigner(signer *AmdSigner) (*FakeKDS, error) { certs := &kpb.Certificates{} + rootBundles := map[string]*RootBundle{} certs.ChipCerts = []*kpb.Certificates_ChipTCBCerts{ { ChipId: signer.HWID[:], TcbCerts: map[uint64][]byte{ uint64(signer.TCB): signer.Vcek.Raw, }, + Fms: abi.MaskedCpuid1EaxFromSevProduct(signer.Product), }, } + productLine := kds.ProductLine(signer.Product) b := &strings.Builder{} if err := multierr.Combine( @@ -126,8 +130,7 @@ func FakeKDSFromSigner(signer *AmdSigner) (*FakeKDS, error) { ); err != nil { return nil, fmt.Errorf("could not encode VCEK root certificates: %v", err) } - vcekBundle := b.String() - var vlekBundle string + rootBundles[productLine] = &RootBundle{VcekBundle: b.String()} if signer.Asvk != nil { b := &strings.Builder{} if err := multierr.Combine( @@ -136,15 +139,10 @@ func FakeKDSFromSigner(signer *AmdSigner) (*FakeKDS, error) { ); err != nil { return nil, fmt.Errorf("could not encode VLEK root certificates: %v", err) } - vlekBundle = b.String() - } - return &FakeKDS{ - Certs: certs, - RootBundles: map[string]RootBundle{"Milan": { - VcekBundle: vcekBundle, - VlekBundle: vlekBundle, - }}, - }, nil + rootBundles[productLine].VlekBundle = b.String() + } + + return &FakeKDS{Certs: certs, RootBundles: rootBundles}, nil } // FindChipTcbCerts returns the TcbCerts associated with the given chipID in the database if they @@ -200,7 +198,7 @@ func GetKDS(t testing.TB) trust.HTTPSGetter { } fakeKds := &FakeKDS{ Certs: &kpb.Certificates{}, - RootBundles: map[string]RootBundle{"Milan": { + RootBundles: map[string]*RootBundle{"Milan": { VcekBundle: string(trust.AskArkMilanVcekBytes), VlekBundle: string(trust.AskArkMilanVlekBytes), }, diff --git a/testing/test_cases.go b/testing/test_cases.go index 0d6c84b..83b5bd8 100644 --- a/testing/test_cases.go +++ b/testing/test_cases.go @@ -25,6 +25,7 @@ import ( "github.com/google/go-sev-guest/kds" spb "github.com/google/go-sev-guest/proto/sevsnp" "github.com/google/logger" + "google.golang.org/protobuf/encoding/prototext" ) // userZeros defines a ReportData example that is all zeros @@ -63,6 +64,25 @@ var userZeros12 = [64]byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2} +var userZeros13 = [64]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 3} +var userZeros14 = [64]byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 4} + // zeroReport is a textproto representing an unsigned report response to UserZeros. // The policy just sets the debug bit and bit 17 to 1, and the signature algo 1 is the encoding for // ECDSA P-384 with SHA-348. Every `bytes` field needs to be the correct length. @@ -124,9 +144,12 @@ chip_id: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x type TestReportOptions struct { ReportData []byte SignerInfo abi.SignerInfo + FMS uint32 + // If 0, then treated as 2. + Version uint32 } -// TestRawReport creates simple raw attestation report with the given REPORT_DATA. +// TestRawReport creates a simple V2 raw attestation report with the given REPORT_DATA. // We can't sign the report with AMD keys, and verification isn't the client's responsibility, so // we keep the signature zeros. // Similarly, we leave the randomly-generated fields zero. @@ -134,18 +157,37 @@ func TestRawReport(reportData [64]byte) [labi.SnpReportRespReportSize]byte { return CreateRawReport(&TestReportOptions{ReportData: reportData[:]}) } +// TestRawReportV3 creates simple V3 raw attestation report with the given REPORT_DATA and CPUID_1_EAX. +// We can't sign the report with AMD keys, and verification isn't the client's responsibility, so +// we keep the signature zeros. +// Similarly, we leave the randomly-generated fields zero. +func TestRawReportV3(reportData [64]byte, fms uint32) [labi.SnpReportRespReportSize]byte { + return CreateRawReport(&TestReportOptions{ReportData: reportData[:], FMS: fms, Version: 3}) +} + // CreateRawReport creates simple raw attestation report with the given configurable data in options. // We can't sign the report with AMD keys, and verification isn't the client's responsibility, so // we keep the signature zeros. // Similarly, we leave the randomly-generated fields zero. func CreateRawReport(opts *TestReportOptions) [labi.SnpReportRespReportSize]byte { var r [labi.SnpReportRespReportSize]byte - // Set Version to 2 - binary.LittleEndian.PutUint32(r[0x00:0x04], 2) + // Set Version to 2 if option is 0 + Version := uint32(2) + if opts.Version != 0 { + Version = opts.Version + } + binary.LittleEndian.PutUint32(r[0x00:0x04], Version) binary.LittleEndian.PutUint64(r[0x08:0x10], abi.SnpPolicyToBytes(abi.SnpPolicy{Debug: true})) // Signature algorithm ECC P-384 with SHA-384 is encoded as 1. binary.LittleEndian.PutUint32(r[0x34:0x38], 1) binary.LittleEndian.PutUint32(r[0x48:0x4C], abi.ComposeSignerInfo(opts.SignerInfo)) + // Family, model, stepping + if opts.Version > 2 { + family, model, stepping := abi.FmsFromCpuid1Eax(opts.FMS) + r[0x188] = family + r[0x189] = model + r[0x18A] = stepping + } // Place user data in its report location. copy(r[0x50:0x90], opts.ReportData) return r @@ -206,6 +248,12 @@ type TestCase struct { // TestCases returns common test cases for get_report. func TestCases() []TestCase { zeroRaw := TestRawReport(userZeros) + milanReportV3Raw := TestRawReportV3(userZeros13, 0x00a00f10) + genoaReportV3Raw := TestRawReportV3(userZeros14, 0x00a10f10) + milanReportV3proto, _ := abi.ReportToProto(milanReportV3Raw[:]) + genoaReportV3proto, _ := abi.ReportToProto(genoaReportV3Raw[:]) + milanReportV3, _ := prototext.MarshalOptions{Multiline: true}.Marshal(milanReportV3proto) + genoaReportV3, _ := prototext.MarshalOptions{Multiline: true}.Marshal(genoaReportV3proto) oneRaw := TestRawReport(userZeros1) vlekRaw := CreateRawReport(&TestReportOptions{ ReportData: userZeros12[:], @@ -237,6 +285,18 @@ func TestCases() []TestCase { FwErr: abi.ResourceLimit, WantErr: "input/output error", // All firmware errors get translated to EIO in configfs }, + { + Name: "zeros milan v3", + Input: userZeros13, + Output: milanReportV3Raw, + OutputProto: string(milanReportV3), + }, + { + Name: "zeros genoa v3", + Input: userZeros14, + Output: genoaReportV3Raw, + OutputProto: string(genoaReportV3), + }, } } diff --git a/verify/trust/ask_ark_turin_vcek.pem b/verify/trust/ask_ark_turin_vcek.pem new file mode 100644 index 0000000..b98ed53 --- /dev/null +++ b/verify/trust/ask_ark_turin_vcek.pem @@ -0,0 +1,74 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +zpacMwFusA== +-----END CERTIFICATE----- diff --git a/verify/trust/ask_ark_turin_vlek.pem b/verify/trust/ask_ark_turin_vlek.pem new file mode 100644 index 0000000..00533d7 --- /dev/null +++ b/verify/trust/ask_ark_turin_vlek.pem @@ -0,0 +1,76 @@ +-----BEGIN CERTIFICATE----- +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 +SUxr +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +zpacMwFusA== +-----END CERTIFICATE----- + diff --git a/verify/trust/trust.go b/verify/trust/trust.go index 8d4c214..6ea231d 100644 --- a/verify/trust/trust.go +++ b/verify/trust/trust.go @@ -59,6 +59,16 @@ var ( //go:embed ask_ark_genoa_vlek.pem AskArkGenoaVlekBytes []byte + // AskArkTurinVcekBytes is a CA bundle for VCEK certs on Turin. + // source: https://kdsintf.amd.com/vcek/v1/Turin/cert_chain + //go:embed ask_ark_turin_vcek.pem + AskArkTurinVcekBytes []byte + + // AskArkTurinVlekBytes is a CA bundle for VLEK certs on Turin. + // source: https://kdsintf.amd.com/vcek/v1/Turin/cert_chain + //go:embed ask_ark_turin_vlek.pem + AskArkTurinVlekBytes []byte + // A cache of product certificate KDS results per product. prodCacheMu sync.Mutex productLineCertCache map[string]*ProductCerts @@ -378,8 +388,12 @@ func init() { genoaCerts := new(AMDRootCerts) genoaCerts.FromKDSCertBytes(AskArkGenoaVcekBytes) genoaCerts.ProductLine = "Genoa" + turinCerts := new(AMDRootCerts) + turinCerts.ProductLine = "Turin" + turinCerts.FromKDSCertBytes(AskArkTurinVcekBytes) DefaultRootCerts = map[string]*AMDRootCerts{ "Milan": milanCerts, "Genoa": genoaCerts, + "Turin": turinCerts, } } diff --git a/verify/verify.go b/verify/verify.go index 7eafbad..df7b4a5 100644 --- a/verify/verify.go +++ b/verify/verify.go @@ -468,7 +468,7 @@ func checkProductName(got, want *spb.SevProduct, key abi.ReportSigner) error { // decodeCerts checks that the V[CL]EK certificate matches expected fields // from the KDS specification and also that its certificate chain matches // hardcoded trusted root certificates from AMD. -func decodeCerts(chain *spb.CertificateChain, key abi.ReportSigner, options *Options) (*x509.Certificate, *trust.AMDRootCerts, error) { +func decodeCerts(chain *spb.CertificateChain, key abi.ReportSigner, knownProductLine string, options *Options) (*x509.Certificate, *trust.AMDRootCerts, error) { var ek []byte switch key { case abi.VcekReportSigner: @@ -489,15 +489,19 @@ func decodeCerts(chain *spb.CertificateChain, key abi.ReportSigner, options *Opt } roots := options.TrustedRoots - product, err := kds.ParseProductName(exts.ProductName, key) - if err != nil { - return nil, nil, err - } + productLine := knownProductLine + // Relevant for v2 reports only. + if productLine == "" { + product, err := kds.ParseProductName(exts.ProductName, key) + if err != nil { + return nil, nil, err + } - productLine := kds.ProductLine(product) - // Ensure the extension product info matches expectations. - if err := checkProductName(product, options.Product, key); err != nil { - return nil, nil, err + productLine = kds.ProductLine(product) + // Ensure the extension product info matches expectations. + if err := checkProductName(product, options.Product, key); err != nil { + return nil, nil, err + } } if len(roots) == 0 { root := trust.AMDRootCertsProduct(productLine) @@ -574,7 +578,7 @@ type Options struct { TrustedRoots map[string][]*trust.AMDRootCerts // Product is a forced value for the attestation product name when verifying or retrieving // VCEK certificates. An attestation should carry the product of the reporting - // machine. + // machine. Only used for v2 attestation reports. Product *spb.SevProduct } @@ -673,7 +677,12 @@ func SnpAttestation(attestation *spb.Attestation, options *Options) error { return err } chain := attestation.GetCertificateChain() - endorsementKeyCert, root, err := decodeCerts(chain, info.SigningKey, options) + + var knownProductLine string + if fms := attestation.GetReport().GetCpuid1EaxFms(); fms != 0 { + knownProductLine = kds.ProductLineFromFms(fms) + } + endorsementKeyCert, root, err := decodeCerts(chain, info.SigningKey, knownProductLine, options) if err != nil { return err } @@ -719,10 +728,26 @@ func setProduct(attestation *spb.Attestation, product *spb.SevProduct) { attestation.Product = product } -// fillInAttestation uses AMD's KDS to populate any empty certificate field in the attestation's -// certificate chain. -func fillInAttestation(attestation *spb.Attestation, options *Options) error { - var productOverridden bool +// In version 2 attestation reports, there is no cpuid_1_eax information about the +// family/model/stepping of the chip, so it's difficult to derive the endpoint from which to +// fetch a VCEK certificate. +// In version 3 attestation reports, the information is present, so we can directly return +// the product line from those fields of the report. +// +// The result values are a product line string, a method of updating product information when there +// is no explicit product expectation from options, and a method of updating the product expectation +// when relevant. This can correct any inaccuracy about a stepping value. +// For v3 reports, these update functions are trivial, as there are no inaccuracies to correct when +// the information is directly in the attestation report. +func cpuidWorkaround(attestation *spb.Attestation, options *Options) (string, func([]byte) error, func() error, error) { + productUpdate := func([]byte) error { return nil } + updateExpectation := func() error { return nil } + + fms := attestation.GetReport().GetCpuid1EaxFms() + if fms != 0 { + return kds.ProductLineFromFms(fms), productUpdate, updateExpectation, nil + } + // ATTESTATION_REPORT v2 makes product determination difficult. product := getProduct(attestation) if product == nil { if options.Product != nil { @@ -731,12 +756,42 @@ func fillInAttestation(attestation *spb.Attestation, options *Options) error { logger.Warning("Attestation missing product information. KDS certificate may be invalid. Using default Milan-B1") attestation.Product = abi.DefaultSevProduct() } - productOverridden = true + productUpdate = func(vcek []byte) error { + cert, err := x509.ParseCertificate(vcek) + if err != nil { + return err + } + exts, err := kds.VcekCertificateExtensions(cert) + if err != nil { + return err + } + product, err = kds.ParseProductName(exts.ProductName, abi.VcekReportSigner) + if err != nil { + return err + } + return nil + } + } + updateExpectation = func() error { + // Pass along the expected product information for VcekDER. fillInAttestation will ensure + // that this is a noop if options.Product began as non-nil. + return updateProductExpectation(&options.Product, product) } + + return kds.ProductLine(product), productUpdate, updateExpectation, nil +} + +// fillInAttestation uses AMD's KDS to populate any empty certificate field in the attestation's +// certificate chain. +func fillInAttestation(attestation *spb.Attestation, options *Options) error { if options.DisableCertFetching { return nil } - productLine := kds.ProductLine(product) + productLine, productUpdate, updateExpectation, err := cpuidWorkaround(attestation, options) + if err != nil { + return err + } + getter := options.Getter if getter == nil { getter = trust.DefaultHTTPSGetter() @@ -777,19 +832,8 @@ func fillInAttestation(attestation *spb.Attestation, options *Options) error { chain.VcekCert = vcek // An attempt was made with defaults or the option's product, so now use // the VCEK cert to determine the real product info. - if productOverridden { - cert, err := x509.ParseCertificate(vcek) - if err != nil { - return err - } - exts, err := kds.VcekCertificateExtensions(cert) - if err != nil { - return err - } - product, err = kds.ParseProductName(exts.ProductName, abi.VcekReportSigner) - if err != nil { - return err - } + if err := productUpdate(vcek); err != nil { + return err } } case abi.VlekReportSigner: @@ -800,9 +844,7 @@ func fillInAttestation(attestation *spb.Attestation, options *Options) error { } } - // Pass along the expected product information for VcekDER. fillInAttestation will ensure - // that this is a noop if options.Product began as non-nil. - return updateProductExpectation(&options.Product, product) + return updateExpectation() } // GetAttestationFromReport uses AMD's Key Distribution Service (KDS) to download the certificate @@ -830,7 +872,7 @@ func GetAttestationFromReport(report *spb.Report, options *Options) (*spb.Attest case abi.VlekReportSigner: exts, _ = kds.VlekCertificateExtensions(parse(result.CertificateChain.VlekCert)) } - if exts != nil { + if exts != nil && report.GetCpuid1EaxFms() == 0 { product, _ := kds.ParseProductName(exts.ProductName, info.SigningKey) setProduct(result, product) } diff --git a/verify/verify_test.go b/verify/verify_test.go index 80aea89..ed16235 100644 --- a/verify/verify_test.go +++ b/verify/verify_test.go @@ -31,6 +31,7 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/google/go-sev-guest/abi" sg "github.com/google/go-sev-guest/client" "github.com/google/go-sev-guest/kds" @@ -83,19 +84,25 @@ func TestEmbeddedCertsAppendixB3Expectations(t *testing.T) { } func TestFakeCertsKDSExpectations(t *testing.T) { - signMu.Do(initSigner) - trust.ClearProductCertCache() - root := trust.AMDRootCertsProduct(test.GetProductLine()) - root.ProductCerts = &trust.ProductCerts{ - Ark: signer.Ark, - Ask: signer.Ask, - } - // No ArkSev or AskSev intentionally for test certs. - if err := validateArkX509(root); err != nil { - t.Errorf("fake ARK validation error: %v", err) - } - if err := validateAskX509(root); err != nil { - t.Errorf("fake ASK validation error: %v", err) + for _, productLine := range kds.ProductLineCpuid { + trust.ClearProductCertCache() + signer, err := test.DefaultTestOnlyCertChain(productLine+"-B0", time.Now()) + if err != nil { + t.Fatalf("no quote provider for productLine %s: %v", productLine, err) + } + + root := trust.AMDRootCertsProduct(productLine) + root.ProductCerts = &trust.ProductCerts{ + Ark: signer.Ark, + Ask: signer.Ask, + } + // No ArkSev or AskSev intentionally for test certs. + if err := validateArkX509(root); err != nil { + t.Errorf("fake ARK validation error: %v", err) + } + if err := validateAskX509(root); err != nil { + t.Errorf("fake ASK validation error: %v", err) + } } } @@ -151,14 +158,27 @@ func TestVerifyVcekCert(t *testing.T) { func TestSnpReportSignature(t *testing.T) { tests := test.TestCases() now := time.Date(2022, time.May, 3, 9, 0, 0, 0, time.UTC) - qp, err := test.TcQuoteProvider(tests, &test.DeviceOptions{Now: now, Product: abi.DefaultSevProduct()}) - if err != nil { - t.Fatal(err) + qps := map[uint32]*test.QuoteProvider{} + for fms, productLine := range kds.ProductLineCpuid { + p, _ := kds.ParseProductLine(productLine) + if p == nil { + t.Fatal("productLine parsing failed") + } + qp, err := test.TcQuoteProvider(tests, &test.DeviceOptions{Now: now, Product: p}) + if err != nil { + t.Fatal(err) + } + qps[fms] = qp } for _, tc := range tests { if testclient.SkipUnmockableTestCase(&tc) { continue } + fms := fmsFromReport(t, tc.Output[:]) + qp := qps[fms&^0xf] + if qp == nil { + t.Fatalf("No quote provider for fms 0x%x", fms) + } // Does the Raw report match expectations? rawcombo, err := qp.GetRawQuote(tc.Input) if !test.Match(err, tc.WantErr) || (tc.WantErr == "" && len(rawcombo) < abi.ReportSize) { @@ -168,8 +188,8 @@ func TestSnpReportSignature(t *testing.T) { raw := rawcombo[:abi.ReportSize] got := abi.SignedComponent(raw) want := abi.SignedComponent(tc.Output[:]) - if !bytes.Equal(got, want) { - t.Errorf("%s: GetRawReport(%v) = %v, want %v", tc.Name, tc.Input, got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("%s: GetRawReport(%v) = %v, want %v\nDiff (-want, +got): %s", tc.Name, tc.Input, got, want, diff) } key := qp.Device.Signer.Vcek if tc.EK == test.KeyChoiceVlek { @@ -326,7 +346,7 @@ func TestKdsMetadataLogic(t *testing.T) { options = &Options{Product: abi.DefaultSevProduct()} } vcekPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: newSigner.Vcek.Raw}) - vcek, _, err := decodeCerts(&spb.CertificateChain{VcekCert: vcekPem, AskCert: newSigner.Ask.Raw, ArkCert: newSigner.Ark.Raw}, abi.VcekReportSigner, options) + vcek, _, err := decodeCerts(&spb.CertificateChain{VcekCert: vcekPem, AskCert: newSigner.Ask.Raw, ArkCert: newSigner.Ark.Raw}, abi.VcekReportSigner, "", options) if !test.Match(err, tc.wantErr) { t.Errorf("%s: decodeCerts(...) = %+v, %v did not error as expected. Want %q", tc.name, vcek, err, tc.wantErr) } @@ -411,13 +431,105 @@ func TestCRLRootValidity(t *testing.T) { } } +type reportGetter func(sg.QuoteProvider, [64]byte) (*spb.Attestation, error) +type reportGetterProfile struct { + name string + getter reportGetter + skipVlek bool + skipNoCache bool + badRootErr string + vlekOnly bool + vlekErr string + vlekBadRootErr string +} +type providerCache struct { + tcs []test.TestCase + opts *test.DeviceOptions + entries map[uint32]*providerData +} +type providerData struct { + qp sg.QuoteProvider + badRoots map[string][]*trust.AMDRootCerts + opts *Options +} + +func (p *providerCache) getStore() map[uint32]*providerData { + if p.entries == nil { + p.entries = map[uint32]*providerData{} + } + return p.entries +} + +func (p *providerCache) forceProvider(t testing.TB, fms uint32) *providerData { + store := p.getStore() + if data, ok := store[fms]; ok { + return data + } + dopts := *p.opts + dopts.Product = abi.SevProductFromCpuid1Eax(fms) + qp, goodRoots, badRoots, kds := testclient.GetSevQuoteProvider(p.tcs, &dopts, t) + + // Trust the test device's root certs. + options := &Options{ + TrustedRoots: goodRoots, + Getter: kds, + Product: dopts.Product, + DisableCertFetching: *requireCache && !sg.UseDefaultSevGuest(), + } + data := &providerData{ + qp: qp, + badRoots: badRoots, + opts: options, + } + store[fms] = data + return data +} + +func fullQuoteTest(t *testing.T, pd *providerData, getReport *reportGetterProfile, tc *test.TestCase) { + // On real hardware, skip tests that represent being on a different platform. + if pd.qp == nil { + t.Skip() + return + } + ereport, err := getReport.getter(pd.qp, tc.Input) + if !test.Match(err, tc.WantErr) { + t.Fatalf("(d, %v) = %v, %v. Want err: %v", tc.Input, ereport, err, tc.WantErr) + } + if tc.WantErr != "" { + return + } + var wantAttestationErr string + if tc.EK == test.KeyChoiceVlek && getReport.vlekErr != "" { + wantAttestationErr = getReport.vlekErr + } + if err := SnpAttestation(ereport, pd.opts); !test.Match(err, wantAttestationErr) { + t.Errorf("SnpAttestation(%v) = %v. Want err: %q", ereport, err, wantAttestationErr) + } + + wantBad := getReport.badRootErr + if tc.EK == test.KeyChoiceVlek && getReport.vlekBadRootErr != "" { + wantBad = getReport.vlekBadRootErr + } + badOptions := &Options{TrustedRoots: pd.badRoots, Getter: pd.opts.Getter, Product: pd.opts.Product} + if err := SnpAttestation(ereport, badOptions); !test.Match(err, wantBad) { + t.Errorf("SnpAttestation(_) bad root test errored unexpectedly: %v, want %s", + err, wantBad) + } +} + +func fmsFromReport(t testing.TB, report []byte) uint32 { + fms := abi.FmsToCpuid1Eax(report[0x188], report[0x189], report[0x18A]) + if fms == 0 { + fms = abi.MaskedCpuid1EaxFromSevProduct(test.GetProduct(t)) + } + return fms +} + // TestOpenGetExtendedReportVerifyClose tests the SnpAttestation function for the deprecated ioctl // API. func TestOpenGetExtendedReportVerifyClose(t *testing.T) { trust.ClearProductCertCache() tests := test.TestCases() - qp, goodRoots, badRoots, kds := testclient.GetSevQuoteProvider(tests, &test.DeviceOptions{Now: time.Now()}, t) - type reportGetter func(sg.QuoteProvider, [64]byte) (*spb.Attestation, error) reportOnly := func(qp sg.QuoteProvider, input [64]byte) (*spb.Attestation, error) { attestation, err := sg.GetQuoteProto(qp, input) if err != nil { @@ -425,16 +537,7 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { } return &spb.Attestation{Report: attestation.Report}, nil } - reportGetters := []struct { - name string - getter reportGetter - skipVlek bool - skipNoCache bool - badRootErr string - vlekOnly bool - vlekErr string - vlekBadRootErr string - }{ + reportGetters := []*reportGetterProfile{ { name: "GetExtendedReport", getter: sg.GetQuoteProto, @@ -461,8 +564,11 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { attestation.CertificateChain = &spb.CertificateChain{} } chain := attestation.CertificateChain - info, _ := abi.ParseSignerInfo(attestation.GetReport().GetSignerInfo()) - if sg.UseDefaultSevGuest() && info.SigningKey == abi.VlekReportSigner { + // Forge VLEK signer info since all test cases assume VCEK. + attestation.Report.SignerInfo = abi.ComposeSignerInfo(abi.SignerInfo{ + SigningKey: abi.VlekReportSigner, + }) + if sg.UseDefaultSevGuest() { if td, ok := qp.(*test.QuoteProvider); ok { chain.VlekCert = td.Device.Signer.Vlek.Raw } @@ -476,14 +582,7 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { skipNoCache: true, }, } - // Trust the test device's root certs. - options := &Options{ - TrustedRoots: goodRoots, - Getter: kds, - Product: test.GetProduct(t), - DisableCertFetching: *requireCache && !sg.UseDefaultSevGuest(), - } - badOptions := &Options{TrustedRoots: badRoots, Getter: kds, Product: test.GetProduct(t)} + providerCache := &providerCache{tcs: test.TestCases(), opts: &test.DeviceOptions{Now: time.Now()}} for _, tc := range tests { if testclient.SkipUnmockableTestCase(&tc) { t.Run(tc.Name, func(t *testing.T) { t.Skip() }) @@ -491,6 +590,7 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { } for _, getReport := range reportGetters { t.Run(tc.Name+"_"+getReport.name, func(t *testing.T) { + trust.ClearProductCertCache() if getReport.skipVlek && tc.EK == test.KeyChoiceVlek { t.Skip() return @@ -503,28 +603,10 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { t.Skip() return } - ereport, err := getReport.getter(qp, tc.Input) - if !test.Match(err, tc.WantErr) { - t.Fatalf("(d, %v) = %v, %v. Want err: %v", tc.Input, ereport, err, tc.WantErr) - } - if tc.WantErr == "" { - var wantAttestationErr string - if tc.EK == test.KeyChoiceVlek && getReport.vlekErr != "" { - wantAttestationErr = getReport.vlekErr - } - if err := SnpAttestation(ereport, options); !test.Match(err, wantAttestationErr) { - t.Errorf("SnpAttestation(%v) = %v. Want err: %q", ereport, err, wantAttestationErr) - } - - wantBad := getReport.badRootErr - if tc.EK == test.KeyChoiceVlek && getReport.vlekBadRootErr != "" { - wantBad = getReport.vlekBadRootErr - } - if err := SnpAttestation(ereport, badOptions); !test.Match(err, wantBad) { - t.Errorf("SnpAttestation(_) bad root test errored unexpectedly: %v, want %s", - err, wantBad) - } - } + // If the test case is for a v3 report and the products don't align with + // the expected product, skip. + fms := fmsFromReport(t, tc.Output[:]) + fullQuoteTest(t, providerCache.forceProvider(t, fms), getReport, &tc) }) } } @@ -534,15 +616,7 @@ func TestOpenGetExtendedReportVerifyClose(t *testing.T) { func TestGetQuoteProviderVerify(t *testing.T) { trust.ClearProductCertCache() tests := test.TestCases() - qp, goodRoots, badRoots, kds := testclient.GetSevQuoteProvider(tests, &test.DeviceOptions{Now: time.Now()}, t) - // Trust the test device's root certs. - options := &Options{ - TrustedRoots: goodRoots, - Getter: kds, - Product: test.GetProduct(t), - DisableCertFetching: *requireCache && !sg.UseDefaultSevGuest(), - } - badOptions := &Options{TrustedRoots: badRoots, Getter: kds, Product: test.GetProduct(t)} + providerCache := &providerCache{tcs: tests, opts: &test.DeviceOptions{Now: time.Now()}} for _, tc := range tests { // configfs-tsm doesn't support the key choice parameter for getting an attestation report, and // it doesn't return firmware error codes. @@ -551,7 +625,12 @@ func TestGetQuoteProviderVerify(t *testing.T) { continue } t.Run(tc.Name+"_", func(t *testing.T) { - reportcerts, err := qp.GetRawQuote(tc.Input) + pd := providerCache.forceProvider(t, fmsFromReport(t, tc.Output[:])) + if pd.qp == nil { + t.Skip() + return + } + reportcerts, err := pd.qp.GetRawQuote(tc.Input) ereport, _ := abi.ReportCertsToProto(reportcerts) if tc.FwErr != abi.Success { if err == nil { @@ -562,10 +641,10 @@ func TestGetQuoteProviderVerify(t *testing.T) { } if tc.WantErr == "" { var wantAttestationErr string - if err := SnpAttestation(ereport, options); !test.Match(err, wantAttestationErr) { + if err := SnpAttestation(ereport, pd.opts); !test.Match(err, wantAttestationErr) { t.Errorf("SnpAttestation(%v) = %v. Want err: %q", ereport, err, wantAttestationErr) } - + badOptions := &Options{TrustedRoots: pd.badRoots, Getter: pd.opts.Getter, Product: pd.opts.Product} wantBad := "error verifying VCEK certificate" if err := SnpAttestation(ereport, badOptions); !test.Match(err, wantBad) { t.Errorf("SnpAttestation(_) bad root test errored unexpectedly: %v, want %s", @@ -728,3 +807,65 @@ func TestKDSCertBackdated(t *testing.T) { cert.NotBefore.Format(time.RFC3339), now.Format(time.RFC3339)) } } + +func TestV3KDSProduct(t *testing.T) { + var tcs []test.TestCase + for _, tc := range test.TestCases() { + if tc.Output[0] == 3 { + t.Logf("picked %s", tc.Name) + tcs = append(tcs, tc) + } + } + if len(tcs) == 0 { + t.Fatalf("no test cases") + } + getter := test.SimpleGetter(map[string][]byte{ + "https://kdsintf.amd.com/vcek/v1/Milan/00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000?blSPL=0&teeSPL=0&snpSPL=0&ucodeSPL=0": []byte("milancert"), + "https://kdsintf.amd.com/vcek/v1/Milan/cert_chain": trust.AskArkMilanVcekBytes, + "https://kdsintf.amd.com/vcek/v1/Genoa/00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000?blSPL=0&teeSPL=0&snpSPL=0&ucodeSPL=0": []byte("genoacert"), + "https://kdsintf.amd.com/vcek/v1/Genoa/cert_chain": trust.AskArkGenoaVcekBytes, + }) + options := &Options{ + TrustedRoots: map[string][]*trust.AMDRootCerts{}, + Now: time.Date(1, time.January, 5, 0, 0, 0, 0, time.UTC), + Product: abi.DefaultSevProduct(), + Getter: getter, + } + for _, productLine := range []string{"Milan", "Genoa"} { + r := trust.AMDRootCertsProduct(productLine) + r.ProductCerts = &trust.ProductCerts{ + Ark: signer.Ark, + Ask: signer.Ask, + } + options.TrustedRoots[productLine] = []*trust.AMDRootCerts{r} + } + var gotGenoa, gotMilan bool + for _, tc := range tcs { + t.Run(tc.Name, func(t *testing.T) { + report, _ := abi.ReportToProto(tc.Output[:]) + a := &spb.Attestation{Report: report} + if err := fillInAttestation(a, options); err != nil { + t.Fatalf("fillInAttestation(%v, %v) = %v, want nil", a, options, err) + } + var want []byte + switch report.Cpuid1EaxFms { + case 0x00a00f10: + want = []byte("milancert") + gotMilan = true + case 0x00a10f10: + want = []byte("genoacert") + gotGenoa = true + } + got := a.CertificateChain.VcekCert + if !bytes.Equal(got, want) { + t.Fatalf("certificate is %v, want %v", got, want) + } + }) + } + if !gotMilan { + t.Errorf("missed Milan case") + } + if !gotGenoa { + t.Errorf("missed Genoa case") + } +}