Skip to content

Commit b5aceed

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 b5aceed

File tree

7 files changed

+150
-46
lines changed

7 files changed

+150
-46
lines changed

kds/kds.go

Lines changed: 108 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,14 @@ func (v TCBVersion) String() string {
149153
}
150154
}
151155

156+
func TCBVersionStructFromTCBVersion(v TCBVersion) TCBVersionStruct {
157+
return TCBVersionStruct{Version: TCBStructVersion0, TCB: uint64(v)}
158+
}
159+
160+
func TCBVersionStructToTCBVersion(v TCBVersionStruct) uint64 {
161+
return v.TCB
162+
}
163+
152164
// Extensions represents the information stored in the KDS-specified x509 extensions of a V{C,L}EK
153165
// certificate.
154166
type Extensions struct {
@@ -158,9 +170,11 @@ type Extensions struct {
158170
// Primary vs secondary is irrelevant to verification. The length of this
159171
// field depends on the TCB version; 64 bytes for version 0 and
160172
// 8 bytes for version 1
161-
HWID []byte
162-
TCBVersion TCBVersion
163-
CspID string
173+
HWID []byte
174+
// Deprecated: use TCBVersionStruct instead
175+
TCBVersion TCBVersion
176+
TCBVersionStruct TCBVersionStruct
177+
CspID string
164178
}
165179

166180
func oidTokdsOID(id asn1.ObjectIdentifier) (kdsOID, error) {
@@ -250,10 +264,8 @@ type TCBParts struct {
250264
FmcSpl uint8
251265
}
252266

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{}
267+
func composeTCBPartsToTCBVersionStruct(parts TCBParts) (TCBVersionStruct, error) {
268+
tcbVersion := TCBVersionStruct{}
257269
// Only UcodeSpl may be 0-255. All others must be 0-127.
258270
check127 := func(name string, value uint8) error {
259271
if value > 127 {
@@ -298,9 +310,26 @@ func ComposeTCBParts(parts TCBParts) (TCBVersion, error) {
298310
return tcbVersion, nil
299311
}
300312

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 {
313+
// ComposeTCBParts returns an SEV-SNP TCB_VERSION from OID mapping values. The spl4-spl7 fields are
314+
// reserved, but the KDS specification designates them as 4 byte-sized fields.
315+
// Deprecated: use ComposeTCBPartsToTCBVersionStruct instead
316+
func ComposeTCBParts(parts TCBParts) (TCBVersion, error) {
317+
tcb, err := composeTCBPartsToTCBVersionStruct(parts)
318+
if err != nil {
319+
return TCBVersion(0), err
320+
}
321+
322+
return TCBVersion(tcb.TCB), nil
323+
}
324+
325+
// ComposeTCBPartsToTCBVersionStruct returns an SEV-SNP TCB_VERSION from OID mapping
326+
// values as a TCBVersionStruct. The spl4-spl7 fields are reserved, but the
327+
// KDS specification designates them as 4 byte-sized fields.
328+
func ComposeTCBPartsToTCBVersionStruct(parts TCBParts) (TCBVersionStruct, error) {
329+
return composeTCBPartsToTCBVersionStruct(parts)
330+
}
331+
332+
func decomposeTCBVersionStruct(tcb TCBVersionStruct) TCBParts {
304333
switch tcb.Version {
305334
case TCBStructVersion1:
306335
return TCBParts{
@@ -326,6 +355,34 @@ func DecomposeTCBVersion(tcb TCBVersion) TCBParts {
326355
}
327356
}
328357

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

476-
tcb, err := ComposeTCBParts(TCBParts{
533+
tcb, err := ComposeTCBPartsToTCBVersionStruct(TCBParts{
477534
Version: result.StructVersion,
478535
BlSpl: blspl,
479536
SnpSpl: snpspl,
@@ -488,7 +545,8 @@ func kdsOidMapToExtensions(exts map[kdsOID]*pkix.Extension) (*Extensions, error)
488545
if err != nil {
489546
return nil, err
490547
}
491-
result.TCBVersion = tcb
548+
result.TCBVersion = TCBVersion(tcb.TCB)
549+
result.TCBVersionStruct = tcb
492550
return &result, nil
493551
}
494552

@@ -603,10 +661,14 @@ func ProductCertChainURL(s abi.ReportSigner, productLine string) string {
603661
return fmt.Sprintf("%s/cert_chain", productBaseURL(s, productLine))
604662
}
605663

606-
// VCEKCertURL returns the AMD KDS URL for retrieving the VCEK on a given product
664+
// VCEKCertQuery returns the AMD KDS URL for retrieving the VCEK on a given product
607665
// 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)
666+
func VCEKCertQuery(productLine string, hwid []byte, tcb TCBVersionStruct) (string, error) {
667+
parts, err := DecomposeTCBVersionStruct(tcb)
668+
if err != nil {
669+
return "", err
670+
}
671+
610672
switch parts.Version {
611673
case TCBStructVersion1:
612674
hwidv1 := hwid[0:TCBHwIDLenVersion1]
@@ -618,7 +680,7 @@ func VCEKCertURL(productLine string, hwid []byte, tcb TCBVersion) string {
618680
parts.SnpSpl,
619681
parts.UcodeSpl,
620682
parts.FmcSpl,
621-
)
683+
), nil
622684
default:
623685
return fmt.Sprintf("%s/%s?blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d",
624686
productBaseURL(abi.VcekReportSigner, productLine),
@@ -627,14 +689,26 @@ func VCEKCertURL(productLine string, hwid []byte, tcb TCBVersion) string {
627689
parts.TeeSpl,
628690
parts.SnpSpl,
629691
parts.UcodeSpl,
630-
)
692+
), nil
631693
}
632694
}
633695

634-
// VLEKCertURL returns the GET URL for retrieving a VLEK certificate, but without the necessary
696+
// VCEKCertURL returns the AMD KDS URL for retrieving the VCEK on a given product
697+
// at a given TCB version. The hwid is the CHIP_ID field in an attestation report.
698+
// Deprecated: use VCEKCertQuery instead
699+
func VCEKCertURL(productLine string, hwid []byte, tcb TCBVersion) string {
700+
url, _ := VCEKCertQuery(productLine, hwid, TCBVersionStructFromTCBVersion(tcb))
701+
return url
702+
}
703+
704+
// VLEKCertQuery returns the GET URL for retrieving a VLEK certificate, but without the necessary
635705
// 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)
706+
func VLEKCertQuery(productLine string, tcb TCBVersionStruct) (string, error) {
707+
parts, err := DecomposeTCBVersionStruct(tcb)
708+
if err != nil {
709+
return "", err
710+
}
711+
638712
switch parts.Version {
639713
case TCBStructVersion1:
640714
return fmt.Sprintf("%s/cert?blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d&fmcSPL=%d",
@@ -644,18 +718,26 @@ func VLEKCertURL(productLine string, tcb TCBVersion) string {
644718
parts.SnpSpl,
645719
parts.UcodeSpl,
646720
parts.FmcSpl,
647-
)
721+
), nil
648722
default:
649723
return fmt.Sprintf("%s/cert?blSPL=%d&teeSPL=%d&snpSPL=%d&ucodeSPL=%d",
650724
productBaseURL(abi.VlekReportSigner, productLine),
651725
parts.BlSpl,
652726
parts.TeeSpl,
653727
parts.SnpSpl,
654728
parts.UcodeSpl,
655-
)
729+
), nil
656730
}
657731
}
658732

733+
// VLEKCertURL returns the GET URL for retrieving a VLEK certificate, but without the necessary
734+
// CSP secret in the HTTP headers that makes the request validate to the KDS.
735+
// Deprecated: use VLEKCertQuery instead
736+
func VLEKCertURL(productLine string, tcb TCBVersion) string {
737+
url, _ := VLEKCertQuery(productLine, TCBVersionStructFromTCBVersion(tcb))
738+
return url
739+
}
740+
659741
// VCEKCert represents the attestation report components represented in a KDS VCEK certificate
660742
// request URL.
661743
type VCEKCert struct {
@@ -788,7 +870,7 @@ func parseTCBURL(u *url.URL, tcbVersion uint8) (uint64, error) {
788870
setter(uint8(number))
789871
}
790872
}
791-
tcb, err := ComposeTCBParts(parts)
873+
tcb, err := ComposeTCBPartsToTCBVersionStruct(parts)
792874
if err != nil {
793875
return 0, fmt.Errorf("invalid AMD KDS TCB arguments: %v", err)
794876
}

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: 26 additions & 9 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(),
@@ -379,25 +389,32 @@ func getReportTcbs(report *spb.Report, certTcb kds.TCBVersion) *reportTcbDescrip
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
}

validate/validate_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ func TestValidateSnpAttestation(t *testing.T) {
8080
committedMinor uint8
8181
}
8282
makeReport := func(reportData [64]byte, opts testOptions) [labi.SnpReportRespReportSize]byte {
83-
currentTcb, currerr := kds.ComposeTCBParts(opts.currentTcb)
84-
reportedTcb, reportederr := kds.ComposeTCBParts(opts.reportedTcb)
85-
committedTcb, committederr := kds.ComposeTCBParts(opts.committedTcb)
86-
launchTcb, launcherr := kds.ComposeTCBParts(opts.launchTcb)
83+
currentTcb, currerr := kds.ComposeTCBPartsToTCBVersionStruct(opts.currentTcb)
84+
reportedTcb, reportederr := kds.ComposeTCBPartsToTCBVersionStruct(opts.reportedTcb)
85+
committedTcb, committederr := kds.ComposeTCBPartsToTCBVersionStruct(opts.committedTcb)
86+
launchTcb, launcherr := kds.ComposeTCBPartsToTCBVersionStruct(opts.launchTcb)
8787
if err := multierr.Combine(currerr,
8888
reportederr,
8989
committederr,

0 commit comments

Comments
 (0)