Skip to content

Commit ab70387

Browse files
committed
fix(kds/TCBVersion): address backwards compatibility
- address backwards compatibility issues raised in the review - revert changes to: - kds/kds_test.go - testing/fakekds.go Signed-off-by: Jagannathan Raman <jraman567@gmail.com>
1 parent 98c5cb5 commit ab70387

File tree

7 files changed

+155
-49
lines changed

7 files changed

+155
-49
lines changed

kds/kds.go

Lines changed: 110 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,20 @@ var (
129129
}
130130
)
131131

132-
// TCBVersion represents the SEV-SNP TCB_VERSION
133-
type TCBVersion struct {
132+
// TCBVersion is a 64-bit bitfield of different security patch levels of AMD firmware and microcode.
133+
// Deprecated: use TCBVersionStruct instead
134+
type TCBVersion uint64
135+
136+
// TCBVersionStruct represents the SEV-SNP TCB_VERSION
137+
type TCBVersionStruct struct {
134138
// Version identifies the version of this structure as defined in V[C,L]EK x509 certificate extensions
135139
Version uint8
136140
// TCB is a 64-bit bitfield of different security patch levels of AMD firmware and microcode.
137141
TCB uint64
138142
}
139143

140144
// String returns the version of the TCB in string format
141-
func (v TCBVersion) String() string {
145+
func (v TCBVersionStruct) String() string {
142146
switch v.Version {
143147
case TCBStructVersion0:
144148
return fmt.Sprintf("Milan or Genoa TCB: 0x%x", v.TCB)
@@ -149,6 +153,16 @@ func (v TCBVersion) String() string {
149153
}
150154
}
151155

156+
// TCBVersionStructFromTCBVersion converts an object of type TCBVersion to TCBVersionStruct
157+
func TCBVersionStructFromTCBVersion(v TCBVersion) TCBVersionStruct {
158+
return TCBVersionStruct{Version: TCBStructVersion0, TCB: uint64(v)}
159+
}
160+
161+
// TCBVersionStructToTCBVersion converts an object of type TCBVersionStruct to TCBVersion
162+
func TCBVersionStructToTCBVersion(v TCBVersionStruct) uint64 {
163+
return v.TCB
164+
}
165+
152166
// Extensions represents the information stored in the KDS-specified x509 extensions of a V{C,L}EK
153167
// certificate.
154168
type Extensions struct {
@@ -158,9 +172,11 @@ type Extensions struct {
158172
// Primary vs secondary is irrelevant to verification. The length of this
159173
// field depends on the TCB version; 64 bytes for version 0 and
160174
// 8 bytes for version 1
161-
HWID []byte
162-
TCBVersion TCBVersion
163-
CspID string
175+
HWID []byte
176+
// Deprecated: use TCBVersionStruct instead
177+
TCBVersion TCBVersion
178+
TCBVersionStruct TCBVersionStruct
179+
CspID string
164180
}
165181

166182
func oidTokdsOID(id asn1.ObjectIdentifier) (kdsOID, error) {
@@ -250,10 +266,8 @@ type TCBParts struct {
250266
FmcSpl uint8
251267
}
252268

253-
// ComposeTCBParts returns an SEV-SNP TCB_VERSION from OID mapping values. The spl4-spl7 fields are
254-
// reserved, but the KDS specification designates them as 4 byte-sized fields.
255-
func ComposeTCBParts(parts TCBParts) (TCBVersion, error) {
256-
tcbVersion := TCBVersion{}
269+
func composeTCBPartsToTCBVersionStruct(parts TCBParts) (TCBVersionStruct, error) {
270+
tcbVersion := TCBVersionStruct{}
257271
// Only UcodeSpl may be 0-255. All others must be 0-127.
258272
check127 := func(name string, value uint8) error {
259273
if value > 127 {
@@ -298,9 +312,26 @@ func ComposeTCBParts(parts TCBParts) (TCBVersion, error) {
298312
return tcbVersion, nil
299313
}
300314

301-
// DecomposeTCBVersion interprets the byte components of the AMD representation of the
302-
// platform security patch levels into a struct.
303-
func DecomposeTCBVersion(tcb TCBVersion) TCBParts {
315+
// ComposeTCBParts returns an SEV-SNP TCB_VERSION from OID mapping values. The spl4-spl7 fields are
316+
// reserved, but the KDS specification designates them as 4 byte-sized fields.
317+
// Deprecated: use ComposeTCBPartsToTCBVersionStruct instead
318+
func ComposeTCBParts(parts TCBParts) (TCBVersion, error) {
319+
tcb, err := composeTCBPartsToTCBVersionStruct(parts)
320+
if err != nil {
321+
return TCBVersion(0), err
322+
}
323+
324+
return TCBVersion(tcb.TCB), nil
325+
}
326+
327+
// ComposeTCBPartsToTCBVersionStruct returns an SEV-SNP TCB_VERSION from OID mapping
328+
// values as a TCBVersionStruct. The spl4-spl7 fields are reserved, but the
329+
// KDS specification designates them as 4 byte-sized fields.
330+
func ComposeTCBPartsToTCBVersionStruct(parts TCBParts) (TCBVersionStruct, error) {
331+
return composeTCBPartsToTCBVersionStruct(parts)
332+
}
333+
334+
func decomposeTCBVersionStruct(tcb TCBVersionStruct) TCBParts {
304335
switch tcb.Version {
305336
case TCBStructVersion1:
306337
return TCBParts{
@@ -326,6 +357,34 @@ func DecomposeTCBVersion(tcb TCBVersion) TCBParts {
326357
}
327358
}
328359

360+
// DecomposeTCBVersion interprets the byte components of the AMD representation of the
361+
// platform security patch levels into a struct.
362+
// Deprecated: use DecomposeTCBVersionStruct instead
363+
func DecomposeTCBVersion(tcb TCBVersion) TCBParts {
364+
parts, _ := DecomposeTCBVersionStruct(tcb)
365+
return parts
366+
}
367+
368+
// DecomposeTCBVersionStruct accepts SEV-SNP TCB version as TCBVersion,
369+
// TCBVersionStruct or uint64 and decodes various security patch
370+
// levels from them.
371+
func DecomposeTCBVersionStruct(t interface{}) (TCBParts, error) {
372+
var tcb TCBVersionStruct
373+
374+
switch v := t.(type) {
375+
case TCBVersionStruct:
376+
tcb = v
377+
case TCBVersion:
378+
tcb = TCBVersionStructFromTCBVersion(v)
379+
case uint64:
380+
tcb = TCBVersionStruct{Version: TCBStructVersion0, TCB: v}
381+
default:
382+
return TCBParts{}, fmt.Errorf("unsupported TCB type: %v", t)
383+
}
384+
385+
return decomposeTCBVersionStruct(tcb), nil
386+
}
387+
329388
// TCBPartsLE returns true iff all TCB components of tcb0 are <= the corresponding tcb1 components.
330389
func TCBPartsLE(tcb0, tcb1 TCBParts) bool {
331390
return (tcb0.UcodeSpl <= tcb1.UcodeSpl) &&
@@ -473,7 +532,7 @@ func kdsOidMapToExtensions(exts map[kdsOID]*pkix.Extension) (*Extensions, error)
473532
}
474533
}
475534

476-
tcb, err := ComposeTCBParts(TCBParts{
535+
tcb, err := ComposeTCBPartsToTCBVersionStruct(TCBParts{
477536
Version: result.StructVersion,
478537
BlSpl: blspl,
479538
SnpSpl: snpspl,
@@ -488,7 +547,8 @@ func kdsOidMapToExtensions(exts map[kdsOID]*pkix.Extension) (*Extensions, error)
488547
if err != nil {
489548
return nil, err
490549
}
491-
result.TCBVersion = tcb
550+
result.TCBVersion = TCBVersion(tcb.TCB)
551+
result.TCBVersionStruct = tcb
492552
return &result, nil
493553
}
494554

@@ -603,10 +663,14 @@ func ProductCertChainURL(s abi.ReportSigner, productLine string) string {
603663
return fmt.Sprintf("%s/cert_chain", productBaseURL(s, productLine))
604664
}
605665

606-
// VCEKCertURL returns the AMD KDS URL for retrieving the VCEK on a given product
666+
// VCEKCertQuery returns the AMD KDS URL for retrieving the VCEK on a given product
607667
// at a given TCB version. The hwid is the CHIP_ID field in an attestation report.
608-
func VCEKCertURL(productLine string, hwid []byte, tcb TCBVersion) string {
609-
parts := DecomposeTCBVersion(tcb)
668+
func VCEKCertQuery(productLine string, hwid []byte, tcb TCBVersionStruct) (string, error) {
669+
parts, err := DecomposeTCBVersionStruct(tcb)
670+
if err != nil {
671+
return "", err
672+
}
673+
610674
switch parts.Version {
611675
case TCBStructVersion1:
612676
hwidv1 := hwid[0:TCBHwIDLenVersion1]
@@ -618,7 +682,7 @@ func VCEKCertURL(productLine string, hwid []byte, tcb TCBVersion) string {
618682
parts.SnpSpl,
619683
parts.UcodeSpl,
620684
parts.FmcSpl,
621-
)
685+
), nil
622686
default:
623687
return fmt.Sprintf("%s/%s?blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d",
624688
productBaseURL(abi.VcekReportSigner, productLine),
@@ -627,14 +691,26 @@ func VCEKCertURL(productLine string, hwid []byte, tcb TCBVersion) string {
627691
parts.TeeSpl,
628692
parts.SnpSpl,
629693
parts.UcodeSpl,
630-
)
694+
), nil
631695
}
632696
}
633697

634-
// VLEKCertURL returns the GET URL for retrieving a VLEK certificate, but without the necessary
698+
// VCEKCertURL returns the AMD KDS URL for retrieving the VCEK on a given product
699+
// at a given TCB version. The hwid is the CHIP_ID field in an attestation report.
700+
// Deprecated: use VCEKCertQuery instead
701+
func VCEKCertURL(productLine string, hwid []byte, tcb TCBVersion) string {
702+
url, _ := VCEKCertQuery(productLine, hwid, TCBVersionStructFromTCBVersion(tcb))
703+
return url
704+
}
705+
706+
// VLEKCertQuery returns the GET URL for retrieving a VLEK certificate, but without the necessary
635707
// CSP secret in the HTTP headers that makes the request validate to the KDS.
636-
func VLEKCertURL(productLine string, tcb TCBVersion) string {
637-
parts := DecomposeTCBVersion(tcb)
708+
func VLEKCertQuery(productLine string, tcb TCBVersionStruct) (string, error) {
709+
parts, err := DecomposeTCBVersionStruct(tcb)
710+
if err != nil {
711+
return "", err
712+
}
713+
638714
switch parts.Version {
639715
case TCBStructVersion1:
640716
return fmt.Sprintf("%s/cert?blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d&fmcSPL=%d",
@@ -644,18 +720,26 @@ func VLEKCertURL(productLine string, tcb TCBVersion) string {
644720
parts.SnpSpl,
645721
parts.UcodeSpl,
646722
parts.FmcSpl,
647-
)
723+
), nil
648724
default:
649725
return fmt.Sprintf("%s/cert?blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d",
650726
productBaseURL(abi.VlekReportSigner, productLine),
651727
parts.BlSpl,
652728
parts.TeeSpl,
653729
parts.SnpSpl,
654730
parts.UcodeSpl,
655-
)
731+
), nil
656732
}
657733
}
658734

735+
// VLEKCertURL returns the GET URL for retrieving a VLEK certificate, but without the necessary
736+
// CSP secret in the HTTP headers that makes the request validate to the KDS.
737+
// Deprecated: use VLEKCertQuery instead
738+
func VLEKCertURL(productLine string, tcb TCBVersion) string {
739+
url, _ := VLEKCertQuery(productLine, TCBVersionStructFromTCBVersion(tcb))
740+
return url
741+
}
742+
659743
// VCEKCert represents the attestation report components represented in a KDS VCEK certificate
660744
// request URL.
661745
type VCEKCert struct {
@@ -788,7 +872,7 @@ func parseTCBURL(u *url.URL, tcbVersion uint8) (uint64, error) {
788872
setter(uint8(number))
789873
}
790874
}
791-
tcb, err := ComposeTCBParts(parts)
875+
tcb, err := ComposeTCBPartsToTCBVersionStruct(parts)
792876
if err != nil {
793877
return 0, fmt.Errorf("invalid AMD KDS TCB arguments: %v", err)
794878
}

kds/kds_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2022-2025 Google LLC
1+
// Copyright 2022 Google LLC
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -40,7 +40,7 @@ func TestVCEKCertURL(t *testing.T) {
4040
hwid := make([]byte, abi.ChipIDSize)
4141
hwid[0] = 0xfe
4242
hwid[abi.ChipIDSize-1] = 0xc0
43-
got := VCEKCertURL("Milan", hwid, TCBVersion{Version: 0, TCB: 0})
43+
got := VCEKCertURL("Milan", hwid, TCBVersion(0))
4444
want := "https://kdsintf.amd.com/vcek/v1/Milan/fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0?blSPL=0&teeSPL=0&snpSPL=0&ucodeSPL=0"
4545
if got != want {
4646
t.Errorf("VCEKCertURL(\"Milan\", %v, 0) = %q, want %q", hwid, got, want)
@@ -139,7 +139,7 @@ func TestParseVCEKCertURL(t *testing.T) {
139139
}{
140140
{
141141
name: "happy path",
142-
url: VCEKCertURL("Milan", hwid, TCBVersion{Version: 0, TCB: 0}),
142+
url: VCEKCertURL("Milan", hwid, TCBVersion(0)),
143143
want: func() VCEKCert {
144144
c := VCEKCertProduct("Milan")
145145
c.HWID = hwid

testing/fakekds.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2022-2025 Google LLC
1+
// Copyright 2022 Google LLC
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -120,7 +120,7 @@ func FakeKDSFromSigner(signer *AmdSigner) (*FakeKDS, error) {
120120
{
121121
ChipId: signer.HWID[:],
122122
TcbCerts: map[uint64][]byte{
123-
signer.TCB.TCB: signer.Vcek.Raw,
123+
uint64(signer.TCB): signer.Vcek.Raw,
124124
},
125125
Fms: abi.MaskedCpuid1EaxFromSevProduct(signer.Product),
126126
},

tools/lib/report/report.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func asBin(report *spb.Attestation) ([]byte, error) {
122122
}
123123

124124
func tcbBreakdown(tcbVersion uint8, tcb uint64) string {
125-
parts := kds.DecomposeTCBVersion(kds.TCBVersion{Version: tcbVersion, TCB: tcb})
125+
parts, _ := kds.DecomposeTCBVersionStruct(kds.TCBVersionStruct{Version: tcbVersion, TCB: tcb})
126126
switch tcbVersion {
127127
case kds.TCBStructVersion1:
128128
return fmt.Sprintf("0x%x:{ucode: %d, snp: %d, tee: %d, bl: %d, fmc: %d}",

validate/validate.go

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,16 @@ func PolicyToOptions(policy *cpb.Policy) (*Options, error) {
240240
return nil, fmt.Errorf("unknown product %q", policy.Product.Name)
241241
}
242242

243+
minTCBParts, err := kds.DecomposeTCBVersionStruct(kds.TCBVersionStruct{Version: tcbVersion, TCB: policy.GetMinimumTcb()})
244+
if err != nil {
245+
return nil, err
246+
}
247+
248+
minLaunchTCBParts, err := kds.DecomposeTCBVersionStruct(kds.TCBVersionStruct{Version: tcbVersion, TCB: policy.GetMinimumLaunchTcb()})
249+
if err != nil {
250+
return nil, err
251+
}
252+
243253
opts := &Options{
244254
MinimumGuestSvn: policy.GetMinimumGuestSvn(),
245255
GuestPolicy: guestPolicy,
@@ -252,8 +262,8 @@ func PolicyToOptions(policy *cpb.Policy) (*Options, error) {
252262
HostData: policy.GetHostData(),
253263
ReportData: policy.GetReportData(),
254264
PlatformInfo: platformInfo,
255-
MinimumTCB: kds.DecomposeTCBVersion(kds.TCBVersion{Version: tcbVersion, TCB: policy.GetMinimumTcb()}),
256-
MinimumLaunchTCB: kds.DecomposeTCBVersion(kds.TCBVersion{Version: tcbVersion, TCB: policy.GetMinimumLaunchTcb()}),
265+
MinimumTCB: minTCBParts,
266+
MinimumLaunchTCB: minLaunchTCBParts,
257267
MinimumBuild: uint8(policy.GetMinimumBuild()),
258268
MinimumVersion: minVersion,
259269
RequireAuthorKey: policy.GetRequireAuthorKey(),
@@ -372,32 +382,39 @@ type reportTcbDescriptions struct {
372382
cert partDescription
373383
}
374384

375-
func getReportTcbs(report *spb.Report, certTcb kds.TCBVersion) *reportTcbDescriptions {
385+
func getReportTcbs(report *spb.Report, certTcb kds.TCBVersionStruct) *reportTcbDescriptions {
376386
fms := report.GetCpuid1EaxFms()
377387
tcbVersion, err := kds.ProductLineToTCBVersion(kds.ProductLineFromFms(fms))
378388
if err != nil {
379389
fmt.Printf("error: defaulting to TCB version 0. failed to findTCB version %v", err)
380390
tcbVersion = kds.TCBStructVersion0
381391
}
392+
393+
reportedTCBParts, _ := kds.DecomposeTCBVersionStruct(kds.TCBVersionStruct{Version: tcbVersion, TCB: report.GetReportedTcb()})
394+
currentTCBParts, _ := kds.DecomposeTCBVersionStruct(kds.TCBVersionStruct{Version: tcbVersion, TCB: report.GetCurrentTcb()})
395+
committedTCBParts, _ := kds.DecomposeTCBVersionStruct(kds.TCBVersionStruct{Version: tcbVersion, TCB: report.GetCommittedTcb()})
396+
launchTCBParts, _ := kds.DecomposeTCBVersionStruct(kds.TCBVersionStruct{Version: tcbVersion, TCB: report.GetLaunchTcb()})
397+
certTCBParts, _ := kds.DecomposeTCBVersionStruct(certTcb)
398+
382399
return &reportTcbDescriptions{
383400
reported: partDescription{
384-
parts: kds.DecomposeTCBVersion(kds.TCBVersion{Version: tcbVersion, TCB: report.GetReportedTcb()}),
401+
parts: reportedTCBParts,
385402
desc: "report's REPORTED_TCB",
386403
},
387404
current: partDescription{
388-
parts: kds.DecomposeTCBVersion(kds.TCBVersion{Version: tcbVersion, TCB: report.GetCurrentTcb()}),
405+
parts: currentTCBParts,
389406
desc: "report's CURRENT_TCB",
390407
},
391408
committed: partDescription{
392-
parts: kds.DecomposeTCBVersion(kds.TCBVersion{Version: tcbVersion, TCB: report.GetCommittedTcb()}),
409+
parts: committedTCBParts,
393410
desc: "report's COMMITTED_TCB",
394411
},
395412
launch: partDescription{
396-
parts: kds.DecomposeTCBVersion(kds.TCBVersion{Version: tcbVersion, TCB: report.GetLaunchTcb()}),
413+
parts: launchTCBParts,
397414
desc: "report's LAUNCH_TCB",
398415
},
399416
cert: partDescription{
400-
parts: kds.DecomposeTCBVersion(certTcb),
417+
parts: certTCBParts,
401418
desc: "TCB of the V[CL]EK certificate",
402419
},
403420
}
@@ -426,8 +443,8 @@ func getPolicyTcbs(options *Options) *policyTcbDescriptions {
426443

427444
// tcbNeError return an error if the two TCBs are not equal
428445
func tcbNeError(left, right partDescription) error {
429-
ltcb, _ := kds.ComposeTCBParts(left.parts)
430-
rtcb, _ := kds.ComposeTCBParts(right.parts)
446+
ltcb, _ := kds.ComposeTCBPartsToTCBVersionStruct(left.parts)
447+
rtcb, _ := kds.ComposeTCBPartsToTCBVersionStruct(right.parts)
431448
if ltcb == rtcb {
432449
return nil
433450
}
@@ -447,7 +464,7 @@ func tcbGtError(wantLower, wantHigher partDescription) error {
447464
// validateTcb returns an error if the TCB values present in the report and V[CL]EK certificate do not
448465
// obey expected relationships with respect to the given validation policy, or with respect to
449466
// internal consistency checks.
450-
func validateTcb(report *spb.Report, certTcb kds.TCBVersion, options *Options) error {
467+
func validateTcb(report *spb.Report, certTcb kds.TCBVersionStruct, options *Options) error {
451468
reportTcbs := getReportTcbs(report, certTcb)
452469
policyTcbs := getPolicyTcbs(options)
453470

@@ -730,7 +747,7 @@ func SnpAttestation(attestation *spb.Attestation, options *Options) error {
730747
if err := multierr.Combine(
731748
validatePolicy(report.GetPolicy(), options.GuestPolicy),
732749
validateVerbatimFields(report, options),
733-
validateTcb(report, exts.TCBVersion, options),
750+
validateTcb(report, exts.TCBVersionStruct, options),
734751
validateVersion(report, options),
735752
validatePlatformInfo(report.GetPlatformInfo(), options.PlatformInfo),
736753
validateKeys(report, options)); err != nil {

0 commit comments

Comments
 (0)