From 1993486f5bfeb4982e22487571317be88940ad28 Mon Sep 17 00:00:00 2001 From: Dionna Glaze Date: Tue, 25 Feb 2025 22:50:25 +0000 Subject: [PATCH] Turin TCB support --- kds/kds.go | 463 ++++++++++++++++++++++++++++++------ kds/kds_test.go | 48 +++- proto/check.proto | 4 +- proto/check/check.pb.go | 81 ++++--- proto/fakekds/fakekds.pb.go | 2 +- proto/sevsnp/sevsnp.pb.go | 12 +- validate/validate.go | 77 ++++-- validate/validate_test.go | 20 +- verify/verify.go | 1 + 9 files changed, 561 insertions(+), 147 deletions(-) diff --git a/kds/kds.go b/kds/kds.go index b27db7a..fe4790c 100644 --- a/kds/kds.go +++ b/kds/kds.go @@ -59,11 +59,15 @@ var ( OidSpl7 = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 3704, 1, 3, 7}) // OidUcodeSpl is the x509v3 extension for V[CL]EK microcode security patch level. OidUcodeSpl = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 3704, 1, 3, 8}) + // OidFmcSpl is the x509v3 extension for FMC security patch level. + OidFmcSpl = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 3704, 1, 3, 9}) // OidHwid is the x509v3 extension for VCEK certificate associated hardware identifier. OidHwid = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 3704, 1, 4}) // OidCspID is the x509v3 extension for a VLEK certificate's Cloud Service Provider's // origin TLS key's certificate's subject key's CommonName. OidCspID = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 3704, 1, 5}) + // TurinHWIDSize is the number of bytes (octets) used to identify a chip for its hwid. + TurinHWIDSize = 8 authorityKeyOid = asn1.ObjectIdentifier([]int{2, 5, 29, 35}) // Short forms of the asn1 Object identifiers to use in map lookups, since []int are invalid key @@ -78,6 +82,7 @@ var ( kdsSpl6 = kdsOID{major: 3, minor: 6} kdsSpl7 = kdsOID{major: 3, minor: 7} kdsUcodeSpl = kdsOID{major: 3, minor: 8} + kdsFmcSpl = kdsOID{major: 3, minor: 9} kdsHwid = kdsOID{major: 4} kdsCspID = kdsOID{major: 5} @@ -90,7 +95,7 @@ var ( uint1 = &wrapperspb.UInt32Value{Value: 1} uint2 = &wrapperspb.UInt32Value{Value: 2} // Chip manufacturers assign stepping versions strings that are - // to describe a stepping number for a particular model chip. There is no way + // to describe a stepping number for a particular family chip. There is no way // other than documentation to map a stepping number to a stepping version and // vice versa. steppingDecoder = map[string]*pb.SevProduct{ @@ -117,6 +122,35 @@ var ( // TCBVersion is a 64-bit bitfield of different security patch levels of AMD firmware and microcode. type TCBVersion uint64 +// TCBVersionTurin is a 64-bit bitfield of different security patch levels of AMD firmware and microcode +// on Turin family chips. +type TCBVersionTurin uint64 + +// TCBVersionI is an interface for represeting the bit pattern of all TCB components' security patch +// levels. +type TCBVersionI interface { + Decompose() TCBPartsI + Raw() uint64 + tcbArgs() string +} + +// TCBPartsI is an interface for representing the decomposition of security patch levels of TCB components +// of AMD chips' SEV-SNP support. +type TCBPartsI interface { + Compose() (TCBVersionI, error) + LE(TCBPartsI) (bool, error) + // Builder methods for populating values from certificate extensions. + SetFmcSpl(byte) TCBPartsI + SetBlSpl(byte) TCBPartsI + SetTeeSpl(byte) TCBPartsI + SetSnpSpl(byte) TCBPartsI + SetSpl4(byte) TCBPartsI + SetSpl5(byte) TCBPartsI + SetSpl6(byte) TCBPartsI + SetSpl7(byte) TCBPartsI + SetUcodeSpl(byte) TCBPartsI +} + // Extensions represents the information stored in the KDS-specified x509 extensions of a V{C,L}EK // certificate. type Extensions struct { @@ -125,9 +159,11 @@ type Extensions struct { // The host driver knows the difference between primary and secondary HWID. // Primary vs secondary is irrelevant to verification. Must be nil or // abi.ChipIDSize long. - HWID []byte - TCBVersion TCBVersion - CspID string + HWID []byte + // TCBVersion is only usable for Milan/Genoa. Use TCBVersionI for a generic representation. + TCBVersion TCBVersion + TCBVersionI TCBVersionI + CspID string } func oidTokdsOID(id asn1.ObjectIdentifier) (kdsOID, error) { @@ -164,6 +200,9 @@ func oidTokdsOID(id asn1.ObjectIdentifier) (kdsOID, error) { if id.Equal(OidUcodeSpl) { return kdsUcodeSpl, nil } + if id.Equal(OidFmcSpl) { + return kdsFmcSpl, nil + } if id.Equal(OidCspID) { return kdsCspID, nil } @@ -191,6 +230,7 @@ func kdsOidMap(cert *x509.Certificate) (map[kdsOID]*pkix.Extension, error) { // TCBParts represents all TCB field values in a given uint64 representation of // an AMD secure processor firmware TCB version. +// This structure is valid only for Milan and Genoa type TCBParts struct { // BlSpl is the bootloader security patch level. BlSpl uint8 @@ -210,35 +250,192 @@ type TCBParts struct { UcodeSpl uint8 } +// SetBlSpl sets the bootloader SPL in the TCB version decomposition. +func (t *TCBParts) SetBlSpl(spl byte) TCBPartsI { + t.BlSpl = spl + return t +} + +// SetBlSpl sets the bootloader SPL in the TCB version decomposition. +func (t *TCBPartsTurin) SetBlSpl(spl byte) TCBPartsI { + t.BlSpl = spl + return t +} + +// SetFmcSpl sets the FPGA Mezzanine Card SPL in the TCB version decomposition. +func (t *TCBParts) SetFmcSpl(spl byte) TCBPartsI { return t } + +// SetFmcSpl sets the FPGA Mezzanine Card SPL in the TCB version decomposition. +func (t *TCBPartsTurin) SetFmcSpl(spl byte) TCBPartsI { + t.FmcSpl = spl + return t +} + +// SetTeeSpl sets the TEE SPL in the TCB version decomposition. +func (t *TCBParts) SetTeeSpl(spl byte) TCBPartsI { + t.TeeSpl = spl + return t +} + +// SetTeeSpl sets the TEE SPL in the TCB version decomposition. +func (t *TCBPartsTurin) SetTeeSpl(spl byte) TCBPartsI { + t.TeeSpl = spl + return t +} + +// SetSnpSpl sets the SNP firmware SPL in the TCB version decomposition. +func (t *TCBParts) SetSnpSpl(spl byte) TCBPartsI { + t.TeeSpl = spl + return t +} + +// SetSnpSpl sets the SNP firmware SPL in the TCB version decomposition. +func (t *TCBPartsTurin) SetSnpSpl(spl byte) TCBPartsI { + t.TeeSpl = spl + return t +} + +// SetSpl4 sets the SPL4 value in the TCB version decomposition. +func (t *TCBParts) SetSpl4(spl byte) TCBPartsI { + t.Spl4 = spl + return t +} + +// SetSpl4 sets the SPL4 value in the TCB version decomposition. +func (t *TCBPartsTurin) SetSpl4(spl byte) TCBPartsI { return t } + +// SetSpl5 sets the SPL5 value in the TCB version decomposition. +func (t *TCBParts) SetSpl5(spl byte) TCBPartsI { + t.Spl5 = spl + return t +} + +// SetSpl5 sets the SPL5 value in the TCB version decomposition. +func (t *TCBPartsTurin) SetSpl5(spl byte) TCBPartsI { + t.Spl5 = spl + return t +} + +// SetSpl6 sets the SPL6 value in the TCB version decomposition. +func (t *TCBParts) SetSpl6(spl byte) TCBPartsI { + t.Spl6 = spl + return t +} + +// SetSpl6 sets the SPL5 value in the TCB version decomposition. +func (t *TCBPartsTurin) SetSpl6(spl byte) TCBPartsI { + t.Spl6 = spl + return t +} + +// SetSpl7 sets the SPL7 value in the TCB version decomposition. +func (t *TCBParts) SetSpl7(spl byte) TCBPartsI { + t.Spl7 = spl + return t +} + +// SetSpl7 sets the SPL7 value in the TCB version decomposition. +func (t *TCBPartsTurin) SetSpl7(spl byte) TCBPartsI { + t.Spl7 = spl + return t +} + +// SetUcodeSpl sets the microcode SPL value in the TCB version decomposition. +func (t *TCBParts) SetUcodeSpl(spl byte) TCBPartsI { + t.UcodeSpl = spl + return t +} + +// SetUcodeSpl sets the microcode SPL value in the TCB version decomposition. +func (t *TCBPartsTurin) SetUcodeSpl(spl byte) TCBPartsI { + t.UcodeSpl = spl + return t +} + +// TCBPartsTurin represents all the TCB field values in a given uint64 representation +// of an AMD secure processor firmware TCB version. +// This structure is valid only for Turin. +type TCBPartsTurin struct { + // Fmc is the FPGA Mezzanine Card security patch level. + FmcSpl uint8 + // BlSpl is the bootloader security patch level. + BlSpl uint8 + // TeeSpl is the TEE security patch level. + TeeSpl uint8 + // SnpSpl is the SNP security patch level. + SnpSpl uint8 + // Spl5 is reserved. + Spl5 uint8 + // Spl6 is reserved. + Spl6 uint8 + // Spl7 is reserved. + Spl7 uint8 + // UcodeSpl is the microcode security patch level. + UcodeSpl uint8 +} + +// Only UcodeSpl may be 0-255. All others must be 0-127. +func check127(name string, value uint8) error { + if value > 127 { + return fmt.Errorf("%s TCB part is %d. Expect 0-127", name, value) + } + return nil +} + // ComposeTCBParts returns an SEV-SNP TCB_VERSION from OID mapping values. The spl4-spl7 fields are // reserved, but the KDS specification designates them as 4 byte-sized fields. func ComposeTCBParts(parts TCBParts) (TCBVersion, error) { - // Only UcodeSpl may be 0-255. All others must be 0-127. - check127 := func(name string, value uint8) error { - if value > 127 { - return fmt.Errorf("%s TCB part is %d. Expect 0-127", name, value) - } - return nil + v, err := (&parts).Compose() + if err != nil { + return 0, err + } + return v.(TCBVersion), nil +} + +// ComposeTCBParts returns an SEV-SNP TCB_VERSION from OID mapping values appropriate to chip family. +func (t *TCBParts) Compose() (TCBVersionI, error) { + if err := multierr.Combine(check127("SnpSpl", t.SnpSpl), + check127("Spl7", t.Spl7), + check127("Spl6", t.Spl6), + check127("Spl5", t.Spl5), + check127("Spl4", t.Spl4), + check127("TeeSpl", t.TeeSpl), + check127("BlSpl", t.BlSpl), + ); err != nil { + return TCBVersion(0), err } - if err := multierr.Combine(check127("SnpSpl", parts.SnpSpl), - check127("Spl7", parts.Spl7), - check127("Spl6", parts.Spl6), - check127("Spl5", parts.Spl5), - check127("Spl4", parts.Spl4), - check127("TeeSpl", parts.TeeSpl), - check127("BlSpl", parts.BlSpl), + return TCBVersion( + (uint64(t.UcodeSpl) << 56) | + (uint64(t.SnpSpl) << 48) | + (uint64(t.Spl7) << 40) | + (uint64(t.Spl6) << 32) | + (uint64(t.Spl5) << 24) | + (uint64(t.Spl4) << 16) | + (uint64(t.TeeSpl) << 8) | + (uint64(t.BlSpl) << 0)), nil +} + +// ComposeTCBParts returns an SEV-SNP TCB_VERSION from OID mapping values appropriate to chip family. +func (t *TCBPartsTurin) Compose() (TCBVersionI, error) { + if err := multierr.Combine(check127("Spl7", t.Spl7), + check127("Spl6", t.Spl6), + check127("Spl5", t.Spl5), + check127("SnpSpl", t.SnpSpl), + check127("TeeSpl", t.TeeSpl), + check127("BlSpl", t.BlSpl), + check127("FmcSpl", t.FmcSpl), ); err != nil { return TCBVersion(0), err } return TCBVersion( - (uint64(parts.UcodeSpl) << 56) | - (uint64(parts.SnpSpl) << 48) | - (uint64(parts.Spl7) << 40) | - (uint64(parts.Spl6) << 32) | - (uint64(parts.Spl5) << 24) | - (uint64(parts.Spl4) << 16) | - (uint64(parts.TeeSpl) << 8) | - (uint64(parts.BlSpl) << 0)), nil + (uint64(t.UcodeSpl) << 56) | + (uint64(t.Spl7) << 48) | + (uint64(t.Spl6) << 40) | + (uint64(t.Spl5) << 32) | + (uint64(t.SnpSpl) << 24) | + (uint64(t.TeeSpl) << 16) | + (uint64(t.BlSpl) << 8) | + (uint64(t.FmcSpl) << 0)), nil } // DecomposeTCBVersion interprets the byte components of the AMD representation of the @@ -256,16 +453,74 @@ func DecomposeTCBVersion(tcb TCBVersion) TCBParts { } } +// DecomposeTCBVersion interprets the byte components of the AMD representation of the +// platform security patch levels into a struct appropriate for the chip family. +func (tcb TCBVersion) Decompose() TCBPartsI { + p := DecomposeTCBVersion(tcb) + return &p +} + +// Raw returns a TCBVersionI as its underlying uint64 representation. +func (tcb TCBVersion) Raw() uint64 { + return uint64(tcb) +} + +// DecomposeTCBVersion interprets the byte components of the AMD representation of the +// platform security patch levels into a struct appropriate for the chip family.. +func (tcb TCBVersionTurin) Decompose() TCBPartsI { + return &TCBPartsTurin{ + UcodeSpl: uint8((uint64(tcb) >> 56) & 0xff), + Spl7: uint8((uint64(tcb) >> 48) & 0xff), + Spl6: uint8((uint64(tcb) >> 40) & 0xff), + Spl5: uint8((uint64(tcb) >> 32) & 0xff), + SnpSpl: uint8((uint64(tcb) >> 24) & 0xff), + TeeSpl: uint8((uint64(tcb) >> 16) & 0xff), + BlSpl: uint8((uint64(tcb) >> 8) & 0xff), + FmcSpl: uint8((uint64(tcb) >> 0) & 0xff), + } +} + +// Raw returns a TCBVersionI as its underlying uint64 representation. +func (tcb TCBVersionTurin) Raw() uint64 { + return uint64(tcb) +} + // TCBPartsLE returns true iff all TCB components of tcb0 are <= the corresponding tcb1 components. func TCBPartsLE(tcb0, tcb1 TCBParts) bool { - return (tcb0.UcodeSpl <= tcb1.UcodeSpl) && - (tcb0.SnpSpl <= tcb1.SnpSpl) && - (tcb0.Spl7 <= tcb1.Spl7) && - (tcb0.Spl6 <= tcb1.Spl6) && - (tcb0.Spl5 <= tcb1.Spl5) && - (tcb0.Spl4 <= tcb1.Spl4) && - (tcb0.TeeSpl <= tcb1.TeeSpl) && - (tcb0.BlSpl <= tcb1.BlSpl) + le, _ := (&tcb0).LE(&tcb1) + return le +} + +func (t *TCBParts) LE(t1 TCBPartsI) (bool, error) { + tcb1, ok := t1.(*TCBParts) + if !ok { + return false, fmt.Errorf("TCB parts are incomparable types. Got %T, want %T", t1, t) + } + return (t.UcodeSpl <= tcb1.UcodeSpl) && + (t.SnpSpl <= tcb1.SnpSpl) && + (t.Spl7 <= tcb1.Spl7) && + (t.Spl6 <= tcb1.Spl6) && + (t.Spl5 <= tcb1.Spl5) && + (t.Spl4 <= tcb1.Spl4) && + (t.TeeSpl <= tcb1.TeeSpl) && + (t.BlSpl <= tcb1.BlSpl), nil +} + +// LE returns true iff all TCB components of tcb0 are <= the corresponding tcb1 components. +// It is an error to compare TCB parts that are different types. +func (t *TCBPartsTurin) LE(t1 TCBPartsI) (bool, error) { + t1t, ok := t1.(*TCBPartsTurin) + if !ok { + return false, fmt.Errorf("TCB parts are incomparable types. Got %T, want %T", t1, t) + } + return (t.UcodeSpl <= t1t.UcodeSpl) && + (t.Spl7 <= t1t.Spl7) && + (t.Spl6 <= t1t.Spl6) && + (t.Spl5 <= t1t.Spl5) && + (t.SnpSpl <= t1t.SnpSpl) && + (t.TeeSpl <= t1t.TeeSpl) && + (t.BlSpl <= t1t.BlSpl) && + (t.FmcSpl <= t1t.FmcSpl), nil } func asn1U8(ext *pkix.Extension, field string, out *uint8) error { @@ -340,9 +595,10 @@ func kdsOidMapToExtensions(exts map[kdsOID]*pkix.Extension) (*Extensions, error) if err := asn1IA5String(exts[kdsProductName1], "ProductName1", &result.ProductName); err != nil { return nil, err } + productLine := ProductLineOfProductName(result.ProductName) hwidExt, ok := exts[kdsHwid] if ok { - octet, err := asn1OctetString(hwidExt, "HWID", 64) + octet, err := asn1OctetString(hwidExt, "HWID", hwidSize(productLine)) if err != nil { return nil, err } @@ -357,7 +613,7 @@ func kdsOidMapToExtensions(exts map[kdsOID]*pkix.Extension) (*Extensions, error) return nil, fmt.Errorf("certificate has both HWID (%s) and CSP_ID (%s) extensions", hex.EncodeToString(result.HWID), result.CspID) } } - var blspl, snpspl, teespl, spl4, spl5, spl6, spl7, ucodespl uint8 + var blspl, snpspl, teespl, spl4, spl5, spl6, spl7, ucodespl, fmcspl uint8 if err := asn1U8(exts[kdsBlSpl], "BlSpl", &blspl); err != nil { return nil, err } @@ -367,7 +623,7 @@ func kdsOidMapToExtensions(exts map[kdsOID]*pkix.Extension) (*Extensions, error) if err := asn1U8(exts[kdsSnpSpl], "SnpSpl", &snpspl); err != nil { return nil, err } - if err := asn1U8(exts[kdsSpl4], "Spl4", &spl4); err != nil { + if err := asn1U8(exts[kdsSpl4], "Spl4", &spl4); err != nil && productLine != "Turin" { return nil, err } if err := asn1U8(exts[kdsSpl5], "Spl5", &spl5); err != nil { @@ -382,20 +638,26 @@ func kdsOidMapToExtensions(exts map[kdsOID]*pkix.Extension) (*Extensions, error) if err := asn1U8(exts[kdsUcodeSpl], "UcodeSpl", &ucodespl); err != nil { return nil, err } - tcb, err := ComposeTCBParts(TCBParts{ - BlSpl: blspl, - SnpSpl: snpspl, - TeeSpl: teespl, - Spl4: spl4, - Spl5: spl5, - Spl6: spl6, - Spl7: spl7, - UcodeSpl: ucodespl, - }) + if err := asn1U8(exts[kdsFmcSpl], "FmcSpl", &fmcspl); err != nil && productLine == "Turin" { + return nil, err + } + t := newTCBParts(productLine).SetBlSpl(blspl). + SetTeeSpl(teespl). + SetSnpSpl(snpspl). + SetSpl4(spl4). + SetSpl5(spl5). + SetSpl6(spl6). + SetSpl7(spl7). + SetUcodeSpl(ucodespl). + SetFmcSpl(fmcspl) + tcb, err := t.Compose() if err != nil { return nil, err } - result.TCBVersion = tcb + if productLine != "Turin" { + result.TCBVersion = TCBVersion(tcb.Raw()) + } + result.TCBVersionI = tcb return &result, nil } @@ -426,7 +688,8 @@ func VcekCertificateExtensions(cert *x509.Certificate) (*Extensions, error) { if exts.CspID != "" { return nil, fmt.Errorf("unexpected CSP_ID in VCEK certificate: %s", exts.CspID) } - if len(exts.HWID) != abi.ChipIDSize { + // This is a bit lax since it doesn't have the context of the product line. + if len(exts.HWID) != abi.ChipIDSize && len(exts.HWID) != TurinHWIDSize { return nil, fmt.Errorf("missing HWID extension for VCEK certificate") } return exts, nil @@ -512,24 +775,25 @@ func ProductCertChainURL(s abi.ReportSigner, productLine string) string { // VCEKCertURL returns the AMD KDS URL for retrieving the VCEK on a given product // at a given TCB version. The hwid is the CHIP_ID field in an attestation report. +// +// Deprecated: Prefer TCBVersionI method VCEKCertQuery. func VCEKCertURL(productLine string, hwid []byte, tcb TCBVersion) string { - parts := DecomposeTCBVersion(tcb) - return fmt.Sprintf("%s/%s?blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d", - productBaseURL(abi.VcekReportSigner, productLine), - hex.EncodeToString(hwid), + return VCEKCertQuery(productLine, hwid, tcb) +} + +func (t TCBVersion) tcbArgs() string { + parts := DecomposeTCBVersion(t) + return fmt.Sprintf("blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d", parts.BlSpl, parts.TeeSpl, parts.SnpSpl, - parts.UcodeSpl, - ) + parts.UcodeSpl) } -// VLEKCertURL returns the GET URL for retrieving a VLEK certificate, but without the necessary -// CSP secret in the HTTP headers that makes the request validate to the KDS. -func VLEKCertURL(productLine string, tcb TCBVersion) string { - parts := DecomposeTCBVersion(tcb) - return fmt.Sprintf("%s/cert?blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d", - productBaseURL(abi.VlekReportSigner, productLine), +func (t TCBVersionTurin) tcbArgs() string { + parts := t.Decompose().(*TCBPartsTurin) + return fmt.Sprintf("fmcSPL=%d&blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d", + parts.FmcSpl, parts.BlSpl, parts.TeeSpl, parts.SnpSpl, @@ -537,6 +801,30 @@ func VLEKCertURL(productLine string, tcb TCBVersion) string { ) } +// VCEKCertQuery returns the AMD KDS URL for retrieving the VCEK on a given product +// at a given TCB version. The hwid is the CHIP_ID field in an attestation report. +func VCEKCertQuery(productLine string, hwid []byte, tcb TCBVersionI) string { + return fmt.Sprintf("%s/%s?%s", + productBaseURL(abi.VcekReportSigner, productLine), + hex.EncodeToString(hwid), + tcb.tcbArgs()) +} + +// VLEKCertURL returns the GET URL for retrieving a VLEK certificate, but without the necessary +// CSP secret in the HTTP headers that makes the request validate to the KDS. +// +// Deprecated: Prefer VLEKCertQuery. +func VLEKCertURL(productLine string, tcb TCBVersion) string { + return VLEKCertQuery(productLine, tcb) +} + +// VLEKCertQuery returns the GET URL for retrieving a VLEK certificate, but without the necessary +// CSP secret in the HTTP headers that makes the request validate to the KDS. +func VLEKCertQuery(productLine string, tcb TCBVersionI) string { + return fmt.Sprintf("%s/cert?%s", + productBaseURL(abi.VlekReportSigner, productLine), tcb.tcbArgs()) +} + // VCEKCert represents the attestation report components represented in a KDS VCEK certificate // request URL. type VCEKCert struct { @@ -639,23 +927,26 @@ func ParseProductCertChainURL(kdsurl string) (string, CertFunction, error) { return parsed.productLine, parsed.function, nil } -func parseTCBURL(u *url.URL) (uint64, error) { +func parseTCBURL(t TCBPartsI, u *url.URL) (uint64, error) { values, err := url.ParseQuery(u.RawQuery) if err != nil { return 0, fmt.Errorf("invalid AMD KDS URL query %q: %v", u.RawQuery, err) } - parts := TCBParts{} for key, valuelist := range values { - var setter func(number uint8) + var setter func(number uint8) TCBPartsI switch key { case "blSPL": - setter = func(number uint8) { parts.BlSpl = number } + setter = t.SetBlSpl case "teeSPL": - setter = func(number uint8) { parts.TeeSpl = number } + setter = t.SetTeeSpl case "snpSPL": - setter = func(number uint8) { parts.SnpSpl = number } + setter = t.SetSnpSpl case "ucodeSPL": - setter = func(number uint8) { parts.UcodeSpl = number } + setter = t.SetUcodeSpl + case "ucodSPL": // for Turin. Could be a spec typo. + setter = t.SetUcodeSpl + case "fmcSPL": + setter = t.SetFmcSpl default: return 0, fmt.Errorf("unexpected KDS TCB version URL argument %q", key) } @@ -667,11 +958,33 @@ func parseTCBURL(u *url.URL) (uint64, error) { setter(uint8(number)) } } - tcb, err := ComposeTCBParts(parts) + tcb, err := t.Compose() if err != nil { return 0, fmt.Errorf("invalid AMD KDS TCB arguments: %v", err) } - return uint64(tcb), err + return tcb.Raw(), err +} + +func newTCBParts(productLine string) TCBPartsI { + switch productLine { + case "Milan": + return &TCBParts{} + case "Genoa": + return &TCBParts{} + default: + return &TCBPartsTurin{} + } +} + +func hwidSize(productLine string) int { + switch productLine { + case "Milan": + return abi.ChipIDSize + case "Genoa": + return abi.ChipIDSize + default: + return TurinHWIDSize + } } // ParseVCEKCertURL returns the attestation report components represented in the given KDS VCEK @@ -691,13 +1004,13 @@ func ParseVCEKCertURL(kdsurl string) (VCEKCert, error) { if err != nil { return result, fmt.Errorf("hwid component of KDS URL is not a hex string: %q", parsed.simpleURL.Path) } - if len(hwid) != abi.ChipIDSize { - return result, fmt.Errorf("hwid component of KDS URL has size %d, want %d", len(hwid), abi.ChipIDSize) + wantSize := hwidSize(parsed.productLine) + if len(hwid) != wantSize { + return result, fmt.Errorf("hwid component of KDS URL has size %d, want %d", len(hwid), wantSize) } result.HWID = hwid - - result.TCB, err = parseTCBURL(parsed.simpleURL) + result.TCB, err = parseTCBURL(newTCBParts(parsed.productLine), parsed.simpleURL) return result, err } @@ -718,7 +1031,7 @@ func ParseVLEKCertURL(kdsurl string) (VLEKCert, error) { return result, fmt.Errorf("vlek function is %q, want 'cert'", parsed.simpleURL.Path) } - result.TCB, err = parseTCBURL(parsed.simpleURL) + result.TCB, err = parseTCBURL(newTCBParts(parsed.productLine), parsed.simpleURL) return result, err } @@ -840,6 +1153,8 @@ func ParseProductLine(productLine string) (*pb.SevProduct, error) { // ParseProductName returns the KDS project input value, and the model, stepping numbers represented // by a given V[CL]EK productName extension value, or an error. +// +// Deprecated: External product representation is not necessary on newer SNP firmware. func ParseProductName(productName string, key abi.ReportSigner) (*pb.SevProduct, error) { switch key { case abi.VcekReportSigner: diff --git a/kds/kds_test.go b/kds/kds_test.go index 01931d7..04865aa 100644 --- a/kds/kds_test.go +++ b/kds/kds_test.go @@ -36,10 +36,22 @@ func TestProductCertChainURL(t *testing.T) { } } -func TestVCEKCertURL(t *testing.T) { +func hwid64() []byte { hwid := make([]byte, abi.ChipIDSize) hwid[0] = 0xfe hwid[abi.ChipIDSize-1] = 0xc0 + return hwid +} + +func hwid8() []byte { + hwid := make([]byte, TurinHWIDSize) + hwid[0] = 0xfe + hwid[TurinHWIDSize-1] = 0xc0 + return hwid +} + +func TestVCEKCertURL(t *testing.T) { + hwid := hwid64() got := VCEKCertURL("Milan", hwid, TCBVersion(0)) want := "https://kdsintf.amd.com/vcek/v1/Milan/fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0?blSPL=0&teeSPL=0&snpSPL=0&ucodeSPL=0" if got != want { @@ -47,6 +59,40 @@ func TestVCEKCertURL(t *testing.T) { } } +func TestVCEKCertQueryTurin(t *testing.T) { + tcs := []struct { + name string + hwid []byte + productLine string + tcb TCBVersionI + want string + }{ + { + name: "milan", + hwid: hwid64(), + productLine: "Milan", + tcb: TCBVersion(0), + want: "https://kdsintf.amd.com/vcek/v1/Milan/fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0?blSPL=0&teeSPL=0&snpSPL=0&ucodeSPL=0", + }, + { + name: "turin", + hwid: hwid8(), + productLine: "Turin", + tcb: TCBVersionTurin(1 | (2 << 56)), + want: "https://kdsintf.amd.com/vcek/v1/Turin/fe000000000000c0?fmcSPL=1&blSPL=0&teeSPL=0&snpSPL=0&ucodeSPL=2", + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + got := VCEKCertQuery(tc.productLine, tc.hwid, tc.tcb) + + if got != tc.want { + t.Errorf("VCEKCertQuery(%q, %v, 0x%x) = %q, want %q", tc.productLine, tc.hwid, tc.tcb.Raw(), got, tc.want) + } + }) + } +} + func TestParseProductBaseURL(t *testing.T) { tcs := []struct { name string diff --git a/proto/check.proto b/proto/check.proto index 59c380f..1582d03 100644 --- a/proto/check.proto +++ b/proto/check.proto @@ -42,7 +42,7 @@ message Policy { bytes host_data = 12; // Should be 32 bytes long bytes report_id = 13; // Should be 32 bytes long bytes report_id_ma = 14; // Should be 32 bytes long - bytes chip_id = 15; // Should be 64 bytes long + bytes chip_id = 15; // Should be either 64 or 8 bytes long uint32 minimum_build = 16; string minimum_version = 17; // Should be "maj.min", both should be 0-255. bool permit_provisional_firmware = 18; @@ -53,6 +53,8 @@ message Policy { repeated bytes trusted_id_key_hashes = 23; // The expected product that generated the attestation report. Stepping optional. sevsnp.SevProduct product = 24; + uint64 minimum_tcb_turin = 25; + uint64 minimum_launch_tcb_turin = 26; } // RootOfTrust represents configuration for which hardware root of trust diff --git a/proto/check/check.pb.go b/proto/check/check.pb.go index 50d5649..64614ea 100644 --- a/proto/check/check.pb.go +++ b/proto/check/check.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v5.27.2 +// protoc v3.12.4 // source: check.proto // Package check represents an attestation validation policy. @@ -62,7 +62,7 @@ type Policy struct { HostData []byte `protobuf:"bytes,12,opt,name=host_data,json=hostData,proto3" json:"host_data,omitempty"` // Should be 32 bytes long ReportId []byte `protobuf:"bytes,13,opt,name=report_id,json=reportId,proto3" json:"report_id,omitempty"` // Should be 32 bytes long ReportIdMa []byte `protobuf:"bytes,14,opt,name=report_id_ma,json=reportIdMa,proto3" json:"report_id_ma,omitempty"` // Should be 32 bytes long - ChipId []byte `protobuf:"bytes,15,opt,name=chip_id,json=chipId,proto3" json:"chip_id,omitempty"` // Should be 64 bytes long + ChipId []byte `protobuf:"bytes,15,opt,name=chip_id,json=chipId,proto3" json:"chip_id,omitempty"` // Should be either 64 or 8 bytes long MinimumBuild uint32 `protobuf:"varint,16,opt,name=minimum_build,json=minimumBuild,proto3" json:"minimum_build,omitempty"` MinimumVersion string `protobuf:"bytes,17,opt,name=minimum_version,json=minimumVersion,proto3" json:"minimum_version,omitempty"` // Should be "maj.min", both should be 0-255. PermitProvisionalFirmware bool `protobuf:"varint,18,opt,name=permit_provisional_firmware,json=permitProvisionalFirmware,proto3" json:"permit_provisional_firmware,omitempty"` @@ -72,7 +72,9 @@ type Policy struct { TrustedIdKeys [][]byte `protobuf:"bytes,22,rep,name=trusted_id_keys,json=trustedIdKeys,proto3" json:"trusted_id_keys,omitempty"` TrustedIdKeyHashes [][]byte `protobuf:"bytes,23,rep,name=trusted_id_key_hashes,json=trustedIdKeyHashes,proto3" json:"trusted_id_key_hashes,omitempty"` // The expected product that generated the attestation report. Stepping optional. - Product *sevsnp.SevProduct `protobuf:"bytes,24,opt,name=product,proto3" json:"product,omitempty"` + Product *sevsnp.SevProduct `protobuf:"bytes,24,opt,name=product,proto3" json:"product,omitempty"` + MinimumTcbTurin uint64 `protobuf:"varint,25,opt,name=minimum_tcb_turin,json=minimumTcbTurin,proto3" json:"minimum_tcb_turin,omitempty"` + MinimumLaunchTcbTurin uint64 `protobuf:"varint,26,opt,name=minimum_launch_tcb_turin,json=minimumLaunchTcbTurin,proto3" json:"minimum_launch_tcb_turin,omitempty"` } func (x *Policy) Reset() { @@ -275,6 +277,20 @@ func (x *Policy) GetProduct() *sevsnp.SevProduct { return nil } +func (x *Policy) GetMinimumTcbTurin() uint64 { + if x != nil { + return x.MinimumTcbTurin + } + return 0 +} + +func (x *Policy) GetMinimumLaunchTcbTurin() uint64 { + if x != nil { + return x.MinimumLaunchTcbTurin + } + return 0 +} + // RootOfTrust represents configuration for which hardware root of trust // certificates to use for verifying attestation report signatures. type RootOfTrust struct { @@ -445,7 +461,7 @@ var file_check_proto_rawDesc = []byte{ 0x68, 0x65, 0x63, 0x6b, 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, 0x1a, 0x0c, 0x73, 0x65, 0x76, 0x73, 0x6e, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xda, 0x07, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2a, 0x0a, + 0x74, 0x6f, 0x22, 0xbf, 0x08, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x67, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x76, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x47, 0x75, 0x65, 0x73, 0x74, 0x53, 0x76, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x6f, 0x6c, @@ -506,31 +522,38 @@ var file_check_proto_rawDesc = []byte{ 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x49, 0x64, 0x4b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x18, 0x18, 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, 0x22, - 0xdb, 0x01, 0x0a, 0x0b, 0x52, 0x6f, 0x6f, 0x74, 0x4f, 0x66, 0x54, 0x72, 0x75, 0x73, 0x74, 0x12, - 0x1c, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x12, 0x25, 0x0a, - 0x0e, 0x63, 0x61, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x61, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x50, - 0x61, 0x74, 0x68, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x61, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, 0x62, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x63, 0x72, 0x6c, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x43, 0x72, 0x6c, 0x12, - 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, - 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x67, 0x0a, - 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0d, 0x72, 0x6f, 0x6f, 0x74, 0x5f, - 0x6f, 0x66, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x52, 0x6f, 0x6f, 0x74, 0x4f, 0x66, 0x54, 0x72, 0x75, - 0x73, 0x74, 0x52, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x4f, 0x66, 0x54, 0x72, 0x75, 0x73, 0x74, 0x12, - 0x25, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0d, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x2c, 0x5a, 0x2a, 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, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x12, + 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x74, 0x63, 0x62, 0x5f, 0x74, + 0x75, 0x72, 0x69, 0x6e, 0x18, 0x19, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x69, + 0x6d, 0x75, 0x6d, 0x54, 0x63, 0x62, 0x54, 0x75, 0x72, 0x69, 0x6e, 0x12, 0x37, 0x0a, 0x18, 0x6d, + 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x5f, 0x74, 0x63, + 0x62, 0x5f, 0x74, 0x75, 0x72, 0x69, 0x6e, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x6d, + 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x4c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x54, 0x63, 0x62, 0x54, + 0x75, 0x72, 0x69, 0x6e, 0x22, 0xdb, 0x01, 0x0a, 0x0b, 0x52, 0x6f, 0x6f, 0x74, 0x4f, 0x66, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x61, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x61, 0x62, 0x75, + 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x61, 0x62, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x61, + 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x5f, 0x63, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x43, 0x72, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, + 0x64, 0x69, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, + 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4c, 0x69, + 0x6e, 0x65, 0x22, 0x67, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0d, + 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6f, 0x66, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x52, 0x6f, 0x6f, 0x74, + 0x4f, 0x66, 0x54, 0x72, 0x75, 0x73, 0x74, 0x52, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x4f, 0x66, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x2c, 0x5a, 0x2a, 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, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/proto/fakekds/fakekds.pb.go b/proto/fakekds/fakekds.pb.go index 0d331d6..3df0a70 100644 --- a/proto/fakekds/fakekds.pb.go +++ b/proto/fakekds/fakekds.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v5.27.2 +// protoc v3.12.4 // source: fakekds.proto package fakekds diff --git a/proto/sevsnp/sevsnp.pb.go b/proto/sevsnp/sevsnp.pb.go index 05fb8b1..3ffb608 100644 --- a/proto/sevsnp/sevsnp.pb.go +++ b/proto/sevsnp/sevsnp.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v5.27.2 +// protoc v3.12.4 // source: sevsnp.proto // Package sevsnp represents an SEV-SNP attestation report and its certificate @@ -24,9 +24,9 @@ package sevsnp import ( + wrappers "github.com/golang/protobuf/ptypes/wrappers" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" reflect "reflect" sync "sync" ) @@ -476,8 +476,8 @@ type SevProduct struct { Name SevProduct_SevProductName `protobuf:"varint,1,opt,name=name,proto3,enum=sevsnp.SevProduct_SevProductName" json:"name,omitempty"` // Deprecated: Marked as deprecated in sevsnp.proto. - Stepping uint32 `protobuf:"varint,2,opt,name=stepping,proto3" json:"stepping,omitempty"` // Must be a 4-bit number - MachineStepping *wrapperspb.UInt32Value `protobuf:"bytes,3,opt,name=machine_stepping,json=machineStepping,proto3" json:"machine_stepping,omitempty"` + Stepping uint32 `protobuf:"varint,2,opt,name=stepping,proto3" json:"stepping,omitempty"` // Must be a 4-bit number + MachineStepping *wrappers.UInt32Value `protobuf:"bytes,3,opt,name=machine_stepping,json=machineStepping,proto3" json:"machine_stepping,omitempty"` } func (x *SevProduct) Reset() { @@ -527,7 +527,7 @@ func (x *SevProduct) GetStepping() uint32 { return 0 } -func (x *SevProduct) GetMachineStepping() *wrapperspb.UInt32Value { +func (x *SevProduct) GetMachineStepping() *wrappers.UInt32Value { if x != nil { return x.MachineStepping } @@ -738,7 +738,7 @@ var file_sevsnp_proto_goTypes = []interface{}{ (*SevProduct)(nil), // 3: sevsnp.SevProduct (*Attestation)(nil), // 4: sevsnp.Attestation nil, // 5: sevsnp.CertificateChain.ExtrasEntry - (*wrapperspb.UInt32Value)(nil), // 6: google.protobuf.UInt32Value + (*wrappers.UInt32Value)(nil), // 6: google.protobuf.UInt32Value } var file_sevsnp_proto_depIdxs = []int32{ 5, // 0: sevsnp.CertificateChain.extras:type_name -> sevsnp.CertificateChain.ExtrasEntry diff --git a/validate/validate.go b/validate/validate.go index 5a67c2d..29c8f9c 100644 --- a/validate/validate.go +++ b/validate/validate.go @@ -62,10 +62,17 @@ type Options struct { // where the MSB is the major number and the LSB is the minor number. MinimumVersion uint16 // MinimumTCB is the component-wise minimum TCB reported in the attestation report. This - // does not include the LaunchTCB. + // does not include the LaunchTCB. Specific to Milan and Genoa models. MinimumTCB kds.TCBParts - // MinimumLaunchTCB is the component-wise minimum for the attestation report LaunchTCB. + // MinimumTCBTurin is the component-wise minimum TCB reported in the attestation report, specific + // to the chip family, but following the Turin TCB structure. This does not include the LaunchTCB. + MinimumTCBTurin kds.TCBPartsI + // MinimumLaunchTCB is the component-wise minimum for the attestation report LaunchTCB, specific + // to Milan and Genoa models. MinimumLaunchTCB kds.TCBParts + // MinimumLaunchTCB2 is the component-wise minimum for the attestation report LaunchTCB, + // specific to the chip family, but following the Turin TCB structure. + MinimumLaunchTCBTurin kds.TCBPartsI // PermitProvisionalFirmware if true, allows the committed TCB, build, and API values to be less // than or equal to the current values. If false, committed and current values must be equal. PermitProvisionalFirmware bool @@ -242,6 +249,8 @@ func PolicyToOptions(policy *cpb.Policy) (*Options, error) { PlatformInfo: platformInfo, MinimumTCB: kds.DecomposeTCBVersion(kds.TCBVersion(policy.GetMinimumTcb())), MinimumLaunchTCB: kds.DecomposeTCBVersion(kds.TCBVersion(policy.GetMinimumLaunchTcb())), + MinimumTCBTurin: kds.TCBVersionTurin(policy.GetMinimumTcbTurin()).Decompose(), + MinimumLaunchTCBTurin: kds.TCBVersionTurin(policy.GetMinimumLaunchTcbTurin()).Decompose(), MinimumBuild: uint8(policy.GetMinimumBuild()), MinimumVersion: minVersion, RequireAuthorKey: policy.GetRequireAuthorKey(), @@ -325,7 +334,7 @@ func validateVerbatimFields(report *spb.Report, options *Options) error { // partDescription combines a TCB decomposition with a short description. It enables concise // comparisons with high quality error messages. type partDescription struct { - parts kds.TCBParts + parts kds.TCBPartsI desc string } @@ -347,26 +356,33 @@ type reportTcbDescriptions struct { cert partDescription } -func getReportTcbs(report *spb.Report, certTcb kds.TCBVersion) *reportTcbDescriptions { +func getReportTcbs(report *spb.Report, certTcb kds.TCBVersionI) *reportTcbDescriptions { + var toTcbVersion func(uint64) kds.TCBVersionI + product := abi.SevProductFromCpuid1Eax(report.GetCpuid1EaxFms()) + if product.Name == spb.SevProduct_SEV_PRODUCT_TURIN { + toTcbVersion = func(tcb uint64) kds.TCBVersionI { return kds.TCBVersionTurin(tcb) } + } else { + toTcbVersion = func(tcb uint64) kds.TCBVersionI { return kds.TCBVersion(tcb) } + } return &reportTcbDescriptions{ reported: partDescription{ - parts: kds.DecomposeTCBVersion(kds.TCBVersion(report.GetReportedTcb())), + parts: toTcbVersion(report.GetReportedTcb()).Decompose(), desc: "report's REPORTED_TCB", }, current: partDescription{ - parts: kds.DecomposeTCBVersion(kds.TCBVersion(report.GetCurrentTcb())), + parts: toTcbVersion(report.GetCurrentTcb()).Decompose(), desc: "report's CURRENT_TCB", }, committed: partDescription{ - parts: kds.DecomposeTCBVersion(kds.TCBVersion(report.GetCommittedTcb())), + parts: toTcbVersion(report.GetCommittedTcb()).Decompose(), desc: "report's COMMITTED_TCB", }, launch: partDescription{ - parts: kds.DecomposeTCBVersion(kds.TCBVersion(report.GetLaunchTcb())), + parts: toTcbVersion(report.GetLaunchTcb()).Decompose(), desc: "report's LAUNCH_TCB", }, cert: partDescription{ - parts: kds.DecomposeTCBVersion(certTcb), + parts: certTcb.Decompose(), desc: "TCB of the V[CL]EK certificate", }, } @@ -376,47 +392,58 @@ func getReportTcbs(report *spb.Report, certTcb kds.TCBVersion) *reportTcbDescrip type policyTcbDescriptions struct { // The validator policy's specified minimum TCB for both reported minimum partDescription - // The validator policy's sp - minLaunch partDescription + // The validator policy's specified minimum launch TCB. + minLaunch partDescription + minimumTurin partDescription + minLaunchTurin partDescription } func getPolicyTcbs(options *Options) *policyTcbDescriptions { return &policyTcbDescriptions{ minimum: partDescription{ - parts: options.MinimumTCB, - desc: "policy minimum TCB", + parts: &options.MinimumTCB, + desc: "policy minimum TCB when on Milan or Genoa", }, minLaunch: partDescription{ - parts: options.MinimumLaunchTCB, - desc: "policy minimum launch TCB", + parts: &options.MinimumLaunchTCB, + desc: "policy minimum launch TCB when on Milan or Genoa", + }, + minimumTurin: partDescription{ + parts: options.MinimumTCBTurin, + desc: "policy minimum TCB when on Turin", + }, + minLaunchTurin: partDescription{ + parts: options.MinimumLaunchTCBTurin, + desc: "policy minimum launch TCB when on Turin", }, } } // tcbNeError return an error if the two TCBs are not equal func tcbNeError(left, right partDescription) error { - ltcb, _ := kds.ComposeTCBParts(left.parts) - rtcb, _ := kds.ComposeTCBParts(right.parts) - if ltcb == rtcb { + lrle, err1 := left.parts.LE(right.parts) + rlle, err2 := right.parts.LE(left.parts) + if err1 == nil && err2 == nil && lrle && rlle { return nil } - return fmt.Errorf("the %s 0x%x does not match the %s 0x%x", left.desc, ltcb, right.desc, rtcb) + return fmt.Errorf("the %s %v does not match the %s %v (%v, %v)", left.desc, left.parts, right.desc, right.parts, err1, err2) } // tcbGtError returns an error if wantLower is greater than (in part) wantHigher. It enforces // the property wantLower <= wantHigher. func tcbGtError(wantLower, wantHigher partDescription) error { - if kds.TCBPartsLE(wantLower.parts, wantHigher.parts) { - return nil + le, err := wantLower.parts.LE(wantHigher.parts) + if err != nil || !le { + return fmt.Errorf("the %s %+v is lower than the %s %+v in at least one component", + wantHigher.desc, wantHigher.parts, wantLower.desc, wantLower.parts) } - return fmt.Errorf("the %s %+v is lower than the %s %+v in at least one component", - wantHigher.desc, wantHigher.parts, wantLower.desc, wantLower.parts) + return nil } // validateTcb returns an error if the TCB values present in the report and V[CL]EK certificate do not // obey expected relationships with respect to the given validation policy, or with respect to // internal consistency checks. -func validateTcb(report *spb.Report, certTcb kds.TCBVersion, options *Options) error { +func validateTcb(report *spb.Report, certTcb kds.TCBVersionI, options *Options) error { reportTcbs := getReportTcbs(report, certTcb) policyTcbs := getPolicyTcbs(options) @@ -687,7 +714,7 @@ func SnpAttestation(attestation *spb.Attestation, options *Options) error { if err := multierr.Combine( validatePolicy(report.GetPolicy(), options.GuestPolicy), validateVerbatimFields(report, options), - validateTcb(report, exts.TCBVersion, options), + validateTcb(report, exts.TCBVersionI, options), validateVersion(report, options), validatePlatformInfo(report.GetPlatformInfo(), options.PlatformInfo), validateKeys(report, options)); err != nil { diff --git a/validate/validate_test.go b/validate/validate_test.go index 29b4e32..ad7cca9 100644 --- a/validate/validate_test.go +++ b/validate/validate_test.go @@ -111,10 +111,10 @@ func TestValidateSnpAttestation(t *testing.T) { CurrentMajor: uint32(opts.currentMajor), CurrentMinor: uint32(opts.currentMinor), PlatformInfo: 1, - CommittedTcb: uint64(committedTcb), - CurrentTcb: uint64(currentTcb), - LaunchTcb: uint64(launchTcb), - ReportedTcb: uint64(reportedTcb), + CommittedTcb: committedTcb.Raw(), + CurrentTcb: currentTcb.Raw(), + LaunchTcb: launchTcb.Raw(), + ReportedTcb: reportedTcb.Raw(), Signature: make([]byte, abi.SignatureSize), } reportRaw, err := abi.ReportToAbiBytes(reportpb) @@ -387,8 +387,6 @@ func TestValidateSnpAttestation(t *testing.T) { }, wantErr: "report ID key not trusted: ffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee", }, - // TODO(dionnaglaze): test varies ways provisional firmware shows up. - // {name: "Provisional firmware"}, { name: "accepted provisional by build", attestation: attestationb1455, @@ -477,10 +475,12 @@ func TestValidateSnpAttestation(t *testing.T) { } for _, tc := range tests { - if err := SnpAttestation(tc.attestation, tc.opts); (err == nil && tc.wantErr != "") || - (err != nil && (tc.wantErr == "" || !strings.Contains(err.Error(), tc.wantErr))) { - t.Errorf("%s: SnpAttestation(%v) errored unexpectedly. Got '%v', want '%s'", tc.name, tc.attestation, err, tc.wantErr) - } + t.Run(tc.name, func(t *testing.T) { + if err := SnpAttestation(tc.attestation, tc.opts); (err == nil && tc.wantErr != "") || + (err != nil && (tc.wantErr == "" || !strings.Contains(err.Error(), tc.wantErr))) { + t.Errorf("%s: SnpAttestation(%v) errored unexpectedly. Got '%v', want '%s'", tc.name, tc.attestation, err, tc.wantErr) + } + }) } } diff --git a/verify/verify.go b/verify/verify.go index 72a04f5..2be7e13 100644 --- a/verify/verify.go +++ b/verify/verify.go @@ -875,6 +875,7 @@ func GetAttestationFromReport(report *spb.Report, options *Options) (*spb.Attest case abi.VlekReportSigner: exts, _ = kds.VlekCertificateExtensions(parse(result.CertificateChain.VlekCert)) } + // Relevant for v2 reports only. if exts != nil && report.GetCpuid1EaxFms() == 0 { product, _ := kds.ParseProductName(exts.ProductName, info.SigningKey) setProduct(result, product)