Implement support for "latest" placeholders for Azure TDX

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2024-06-12 10:24:16 +02:00 committed by Daniel Weiße
parent a34493caa6
commit 9159b60331
16 changed files with 410 additions and 267 deletions

View File

@ -6,7 +6,7 @@ go_library(
srcs = [ srcs = [
"attestationconfigapi.go", "attestationconfigapi.go",
"fetcher.go", "fetcher.go",
"snp.go", "version.go",
], ],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi", importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
@ -22,7 +22,7 @@ go_test(
name = "attestationconfigapi_test", name = "attestationconfigapi_test",
srcs = [ srcs = [
"fetcher_test.go", "fetcher_test.go",
"snp_test.go", "version_test.go",
], ],
embed = [":attestationconfigapi"], embed = [":attestationconfigapi"],
deps = [ deps = [

View File

@ -20,6 +20,7 @@ go_library(
deps = [ deps = [
"//internal/api/attestationconfigapi", "//internal/api/attestationconfigapi",
"//internal/api/attestationconfigapi/cli/client", "//internal/api/attestationconfigapi/cli/client",
"//internal/api/fetcher",
"//internal/attestation/variant", "//internal/attestation/variant",
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/constants", "//internal/constants",

View File

@ -22,27 +22,27 @@ const AttestationURLPath = "constellation/v1/attestation"
// SEVSNPVersion tracks the latest version of each component for SEV-SNP. // SEVSNPVersion tracks the latest version of each component for SEV-SNP.
type SEVSNPVersion struct { type SEVSNPVersion struct {
// Bootloader is the latest version of the SEV-SNP bootloader. // Bootloader is the latest version of the SEV-SNP bootloader.
Bootloader uint8 `json:"bootloader,omitempty"` Bootloader uint8 `json:"bootloader"`
// TEE is the latest version of the SEV-SNP TEE. // TEE is the latest version of the SEV-SNP TEE.
TEE uint8 `json:"tee,omitempty"` TEE uint8 `json:"tee"`
// SNP is the latest version of the SEV-SNP SNP. // SNP is the latest version of the SEV-SNP SNP.
SNP uint8 `json:"snp,omitempty"` SNP uint8 `json:"snp"`
// Microcode is the latest version of the SEV-SNP microcode. // Microcode is the latest version of the SEV-SNP microcode.
Microcode uint8 `json:"microcode,omitempty"` Microcode uint8 `json:"microcode"`
} }
// TDXVersion tracks the latest version of each component for TDX. // TDXVersion tracks the latest version of each component for TDX.
type TDXVersion struct { type TDXVersion struct {
// QESVN is the latest QE security version number. // QESVN is the latest QE security version number.
QESVN uint16 `json:"qeSVN,omitempty"` QESVN uint16 `json:"qeSVN"`
// PCESVN is the latest PCE security version number. // PCESVN is the latest PCE security version number.
PCESVN uint16 `json:"pceSVN,omitempty"` PCESVN uint16 `json:"pceSVN"`
// TEETCBSVN are the latest component-wise security version numbers for the TEE. // TEETCBSVN are the latest component-wise security version numbers for the TEE.
TEETCBSVN [16]byte `json:"teeTCBSVN,omitempty"` TEETCBSVN [16]byte `json:"teeTCBSVN"`
// QEVendorID is the latest QE vendor ID. // QEVendorID is the latest QE vendor ID.
QEVendorID [16]byte `json:"qeVendorID,omitempty"` QEVendorID [16]byte `json:"qeVendorID"`
// XFAM is the latest XFAM field. // XFAM is the latest XFAM field.
XFAM [8]byte `json:"xfam,omitempty"` XFAM [8]byte `json:"xfam"`
} }
// VersionAPIEntry is the request to get the version information of the specific version in the config api. // VersionAPIEntry is the request to get the version information of the specific version in the config api.

View File

@ -103,14 +103,14 @@ func (v *Validator) validateQuote(tdxQuote *tdx.QuoteV4) error {
if err := validate.TdxQuote(tdxQuote, &validate.Options{ if err := validate.TdxQuote(tdxQuote, &validate.Options{
HeaderOptions: validate.HeaderOptions{ HeaderOptions: validate.HeaderOptions{
MinimumQeSvn: v.cfg.QESVN, MinimumQeSvn: v.cfg.QESVN.Value,
MinimumPceSvn: v.cfg.PCESVN, MinimumPceSvn: v.cfg.PCESVN.Value,
QeVendorID: v.cfg.QEVendorID, QeVendorID: v.cfg.QEVendorID.Value,
}, },
TdQuoteBodyOptions: validate.TdQuoteBodyOptions{ TdQuoteBodyOptions: validate.TdQuoteBodyOptions{
MinimumTeeTcbSvn: v.cfg.TEETCBSVN, MinimumTeeTcbSvn: v.cfg.TEETCBSVN.Value,
MrSeam: v.cfg.MRSeam, MrSeam: v.cfg.MRSeam,
Xfam: v.cfg.XFAM, Xfam: v.cfg.XFAM.Value,
}, },
}); err != nil { }); err != nil {
return err return err

View File

@ -63,6 +63,7 @@ go_test(
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/config/instancetypes", "//internal/config/instancetypes",
"//internal/constants", "//internal/constants",
"//internal/encoding",
"//internal/file", "//internal/file",
"//internal/semver", "//internal/semver",
"//internal/versions", "//internal/versions",

View File

@ -9,47 +9,49 @@ package config
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"math"
"strconv"
"strings" "strings"
"github.com/edgelesssys/constellation/v2/internal/encoding"
) )
const placeholderVersionValue = 0 type versionValue interface {
encoding.HexBytes | uint8 | uint16
}
func placeholderVersionValue[T versionValue]() T {
var placeholder T
return placeholder
}
// NewLatestPlaceholderVersion returns the latest version with a placeholder version value. // NewLatestPlaceholderVersion returns the latest version with a placeholder version value.
func NewLatestPlaceholderVersion() AttestationVersion { func NewLatestPlaceholderVersion[T versionValue]() AttestationVersion[T] {
return AttestationVersion{ return AttestationVersion[T]{
Value: placeholderVersionValue, Value: placeholderVersionValue[T](),
WantLatest: true, WantLatest: true,
} }
} }
// AttestationVersion is a type that represents a version of a SNP. // AttestationVersion holds version information.
type AttestationVersion struct { type AttestationVersion[T versionValue] struct {
Value uint8 Value T
WantLatest bool WantLatest bool
} }
// MarshalYAML implements a custom marshaller to resolve "latest" values. // MarshalYAML implements a custom marshaller to write "latest" as the type's value, if set.
func (v AttestationVersion) MarshalYAML() (any, error) { func (v AttestationVersion[T]) MarshalYAML() (any, error) {
if v.WantLatest { if v.WantLatest {
return "latest", nil return "latest", nil
} }
return v.Value, nil return v.Value, nil
} }
// UnmarshalYAML implements a custom unmarshaller to resolve "atest" values. // UnmarshalYAML implements a custom unmarshaller to resolve "latest" values.
func (v *AttestationVersion) UnmarshalYAML(unmarshal func(any) error) error { func (v *AttestationVersion[T]) UnmarshalYAML(unmarshal func(any) error) error {
var rawUnmarshal string return v.unmarshal(unmarshal)
if err := unmarshal(&rawUnmarshal); err != nil {
return fmt.Errorf("raw unmarshal: %w", err)
}
return v.parseRawUnmarshal(rawUnmarshal)
} }
// MarshalJSON implements a custom marshaller to resolve "latest" values. // MarshalJSON implements a custom marshaller to write "latest" as the type's value, if set.
func (v AttestationVersion) MarshalJSON() ([]byte, error) { func (v AttestationVersion[T]) MarshalJSON() ([]byte, error) {
if v.WantLatest { if v.WantLatest {
return json.Marshal("latest") return json.Marshal("latest")
} }
@ -57,39 +59,31 @@ func (v AttestationVersion) MarshalJSON() ([]byte, error) {
} }
// UnmarshalJSON implements a custom unmarshaller to resolve "latest" values. // UnmarshalJSON implements a custom unmarshaller to resolve "latest" values.
func (v *AttestationVersion) UnmarshalJSON(data []byte) (err error) { func (v *AttestationVersion[T]) UnmarshalJSON(data []byte) (err error) {
// JSON has two distinct ways to represent numbers and strings. return v.unmarshal(func(a any) error {
// This means we cannot simply unmarshal to string, like with YAML. return json.Unmarshal(data, a)
// Unmarshalling to `any` causes Go to unmarshal numbers to float64. })
// Therefore, try to unmarshal to string, and then to int, instead of using type assertions. }
// unmarshal takes care of unmarshalling the value from YAML or JSON.
func (v *AttestationVersion[T]) unmarshal(unmarshal func(any) error) error {
// Start by trying to unmarshal to the distinct type
var distinctType T
if err := unmarshal(&distinctType); err == nil {
v.Value = distinctType
return nil
}
var unmarshalString string var unmarshalString string
if err := json.Unmarshal(data, &unmarshalString); err != nil { if err := unmarshal(&unmarshalString); err != nil {
var unmarshalInt int64 return fmt.Errorf("failed unmarshalling to %T or string: %w", distinctType, err)
if err := json.Unmarshal(data, &unmarshalInt); err != nil {
return fmt.Errorf("unable to unmarshal to string or int: %w", err)
}
unmarshalString = strconv.FormatInt(unmarshalInt, 10)
} }
return v.parseRawUnmarshal(unmarshalString) if strings.ToLower(unmarshalString) == "latest" {
}
func (v *AttestationVersion) parseRawUnmarshal(str string) error {
if strings.HasPrefix(str, "0") && len(str) != 1 {
return fmt.Errorf("no format with prefixed 0 (octal, hexadecimal) allowed: %s", str)
}
if strings.ToLower(str) == "latest" {
v.WantLatest = true v.WantLatest = true
v.Value = placeholderVersionValue v.Value = placeholderVersionValue[T]()
} else { return nil
ui, err := strconv.ParseUint(str, 10, 8)
if err != nil {
return fmt.Errorf("invalid version value: %s", str)
}
if ui > math.MaxUint8 {
return fmt.Errorf("integer value is out ouf uint8 range: %d", ui)
}
v.Value = uint8(ui)
} }
return nil
return fmt.Errorf("failed unmarshalling to %T or string: invalid value: %s", distinctType, unmarshalString)
} }

View File

@ -7,204 +7,307 @@ SPDX-License-Identifier: AGPL-3.0-only
package config package config
import ( import (
"bytes"
"encoding/json" "encoding/json"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/edgelesssys/constellation/v2/internal/encoding"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
func TestVersionMarshalYAML(t *testing.T) { func TestVersionMarshalYAML(t *testing.T) {
tests := map[string]struct { testCasesUint8 := map[string]struct {
sut AttestationVersion sut AttestationVersion[uint8]
want string want string
}{ }{
"isLatest resolves to latest": { "version with latest writes latest": {
sut: AttestationVersion{ sut: AttestationVersion[uint8]{
Value: 1, Value: 1,
WantLatest: true, WantLatest: true,
}, },
want: "latest\n", want: "latest\n",
}, },
"value 5 resolves to 5": { "value 5 writes 5": {
sut: AttestationVersion{ sut: AttestationVersion[uint8]{
Value: 5, Value: 5,
WantLatest: false, WantLatest: false,
}, },
want: "5\n", want: "5\n",
}, },
} }
for name, tc := range tests { for name, tc := range testCasesUint8 {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
require := require.New(t) assert := assert.New(t)
bt, err := yaml.Marshal(tc.sut) bt, err := yaml.Marshal(tc.sut)
require.NoError(err) assert.NoError(err)
require.Equal(tc.want, string(bt)) assert.Equal(tc.want, string(bt))
})
}
testCasesUint16 := map[string]struct {
sut AttestationVersion[uint16]
want string
}{
"version with latest writes latest": {
sut: AttestationVersion[uint16]{
Value: 1,
WantLatest: true,
},
want: "latest\n",
},
"value 5 writes 5": {
sut: AttestationVersion[uint16]{
Value: 5,
WantLatest: false,
},
want: "5\n",
},
}
for name, tc := range testCasesUint16 {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
bt, err := yaml.Marshal(tc.sut)
assert.NoError(err)
assert.Equal(tc.want, string(bt))
})
}
testCasesHexBytes := map[string]struct {
sut AttestationVersion[encoding.HexBytes]
want string
}{
"version with latest writes latest": {
sut: AttestationVersion[encoding.HexBytes]{
Value: encoding.HexBytes(bytes.Repeat([]byte("0"), 16)),
WantLatest: true,
},
want: "latest\n",
},
"value 5 writes 5": {
sut: AttestationVersion[encoding.HexBytes]{
Value: encoding.HexBytes(bytes.Repeat([]byte("A"), 16)),
WantLatest: false,
},
want: "\"41414141414141414141414141414141\"\n",
},
}
for name, tc := range testCasesHexBytes {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
bt, err := yaml.Marshal(tc.sut)
assert.NoError(err)
assert.Equal(tc.want, string(bt))
}) })
} }
} }
func TestVersionUnmarshalYAML(t *testing.T) { func TestVersionUnmarshal(t *testing.T) {
tests := map[string]struct { testCasesUint8 := map[string]struct {
sut string yamlData string
want AttestationVersion jsonData string
wantErr bool want AttestationVersion[uint8]
wantErr bool
}{ }{
"latest resolves to isLatest": { "latest resolves to isLatest": {
sut: "latest", yamlData: "latest",
want: AttestationVersion{ jsonData: "\"latest\"",
want: AttestationVersion[uint8]{
Value: 0, Value: 0,
WantLatest: true, WantLatest: true,
}, },
wantErr: false, wantErr: false,
}, },
"1 resolves to value 1": { "1 resolves to value 1": {
sut: "1", yamlData: "1",
want: AttestationVersion{ jsonData: "1",
want: AttestationVersion[uint8]{
Value: 1, Value: 1,
WantLatest: false, WantLatest: false,
}, },
wantErr: false, wantErr: false,
}, },
"max uint8+1 errors": { "max uint8+1 errors": {
sut: "256", yamlData: "256",
wantErr: true, jsonData: "256",
wantErr: true,
}, },
"-1 errors": { "-1 errors": {
sut: "-1", yamlData: "-1",
wantErr: true, jsonData: "-1",
}, wantErr: true,
"2.6 errors": {
sut: "2.6",
wantErr: true,
},
"2.0 errors": {
sut: "2.0",
wantErr: true,
},
"hex format is invalid": {
sut: "0x10",
wantErr: true,
},
"octal format is invalid": {
sut: "010",
wantErr: true,
}, },
"0 resolves to value 0": { "0 resolves to value 0": {
sut: "0", yamlData: "0",
want: AttestationVersion{ jsonData: "0",
want: AttestationVersion[uint8]{
Value: 0, Value: 0,
WantLatest: false, WantLatest: false,
}, },
}, },
"00 errors": {
sut: "00",
wantErr: true,
},
} }
for name, tc := range tests { for name, tc := range testCasesUint8 {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
require := require.New(t) assert := assert.New(t)
var sut AttestationVersion {
err := yaml.Unmarshal([]byte(tc.sut), &sut) var sut AttestationVersion[uint8]
if tc.wantErr { err := yaml.Unmarshal([]byte(tc.yamlData), &sut)
require.Error(err) if tc.wantErr {
return assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.want, sut)
}
}
{
var sut AttestationVersion[uint8]
err := json.Unmarshal([]byte(tc.jsonData), &sut)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.want, sut)
}
} }
require.NoError(err)
require.Equal(tc.want, sut)
}) })
} }
}
func TestVersionUnmarshalJSON(t *testing.T) { testCasesUint16 := map[string]struct {
tests := map[string]struct { yamlData string
sut string jsonData string
want AttestationVersion want AttestationVersion[uint16]
wantErr bool wantErr bool
}{ }{
"latest resolves to isLatest": { "latest resolves to isLatest": {
sut: `"latest"`, yamlData: "latest",
want: AttestationVersion{ jsonData: "\"latest\"",
want: AttestationVersion[uint16]{
Value: 0, Value: 0,
WantLatest: true, WantLatest: true,
}, },
wantErr: false,
}, },
"1 resolves to value 1": { "1 resolves to value 1": {
sut: "1", yamlData: "1",
want: AttestationVersion{ jsonData: "1",
want: AttestationVersion[uint16]{
Value: 1, Value: 1,
WantLatest: false, WantLatest: false,
}, },
wantErr: false,
}, },
"quoted number resolves to value": { "max uint16+1 errors": {
sut: `"1"`, yamlData: "65536",
want: AttestationVersion{ jsonData: "65536",
Value: 1, wantErr: true,
WantLatest: false,
},
},
"quoted float errors": {
sut: `"1.0"`,
wantErr: true,
},
"max uint8+1 errors": {
sut: "256",
wantErr: true,
}, },
"-1 errors": { "-1 errors": {
sut: "-1", yamlData: "-1",
wantErr: true, jsonData: "-1",
}, wantErr: true,
"2.6 errors": {
sut: "2.6",
wantErr: true,
},
"2.0 errors": {
sut: "2.0",
wantErr: true,
},
"hex format is invalid": {
sut: "0x10",
wantErr: true,
},
"octal format is invalid": {
sut: "010",
wantErr: true,
}, },
"0 resolves to value 0": { "0 resolves to value 0": {
sut: "0", yamlData: "0",
want: AttestationVersion{ jsonData: "0",
want: AttestationVersion[uint16]{
Value: 0, Value: 0,
WantLatest: false, WantLatest: false,
}, },
}, },
"quoted 0 resolves to value 0": {
sut: `"0"`,
want: AttestationVersion{
Value: 0,
WantLatest: false,
},
},
"00 errors": {
sut: "00",
wantErr: true,
},
} }
for name, tc := range tests { for name, tc := range testCasesUint16 {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
require := require.New(t) assert := assert.New(t)
var sut AttestationVersion {
err := json.Unmarshal([]byte(tc.sut), &sut) var sut AttestationVersion[uint16]
if tc.wantErr { err := yaml.Unmarshal([]byte(tc.yamlData), &sut)
require.Error(err) if tc.wantErr {
return assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.want, sut)
}
}
{
var sut AttestationVersion[uint16]
err := json.Unmarshal([]byte(tc.jsonData), &sut)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.want, sut)
}
}
})
}
testCasesHexBytes := map[string]struct {
yamlData string
jsonData string
want AttestationVersion[encoding.HexBytes]
wantErr bool
}{
"latest resolves to isLatest": {
yamlData: "latest",
jsonData: "\"latest\"",
want: AttestationVersion[encoding.HexBytes]{
Value: encoding.HexBytes(nil),
WantLatest: true,
},
wantErr: false,
},
"hex string resolves to correctly": {
yamlData: "41414141414141414141414141414141",
jsonData: "\"41414141414141414141414141414141\"",
want: AttestationVersion[encoding.HexBytes]{
Value: encoding.HexBytes(bytes.Repeat([]byte("A"), 16)),
WantLatest: false,
},
wantErr: false,
},
"invalid hex string": {
yamlData: "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",
jsonData: "\"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\"",
wantErr: true,
},
"non hex data": {
yamlData: "-15",
jsonData: "-15",
wantErr: true,
},
}
for name, tc := range testCasesHexBytes {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
{
var sut AttestationVersion[encoding.HexBytes]
err := yaml.Unmarshal([]byte(tc.yamlData), &sut)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.want, sut)
}
}
{
var sut AttestationVersion[encoding.HexBytes]
err := json.Unmarshal([]byte(tc.jsonData), &sut)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.want, sut)
}
} }
require.NoError(err)
require.Equal(tc.want, sut)
}) })
} }
} }

View File

@ -22,10 +22,10 @@ var _ svnResolveMarshaller = &AWSSEVSNP{}
func DefaultForAWSSEVSNP() *AWSSEVSNP { func DefaultForAWSSEVSNP() *AWSSEVSNP {
return &AWSSEVSNP{ return &AWSSEVSNP{
Measurements: measurements.DefaultsFor(cloudprovider.AWS, variant.AWSSEVSNP{}), Measurements: measurements.DefaultsFor(cloudprovider.AWS, variant.AWSSEVSNP{}),
BootloaderVersion: NewLatestPlaceholderVersion(), BootloaderVersion: NewLatestPlaceholderVersion[uint8](),
TEEVersion: NewLatestPlaceholderVersion(), TEEVersion: NewLatestPlaceholderVersion[uint8](),
SNPVersion: NewLatestPlaceholderVersion(), SNPVersion: NewLatestPlaceholderVersion[uint8](),
MicrocodeVersion: NewLatestPlaceholderVersion(), MicrocodeVersion: NewLatestPlaceholderVersion[uint8](),
AMDRootKey: mustParsePEM(arkPEM), AMDRootKey: mustParsePEM(arkPEM),
} }
} }

View File

@ -28,10 +28,10 @@ var (
func DefaultForAzureSEVSNP() *AzureSEVSNP { func DefaultForAzureSEVSNP() *AzureSEVSNP {
return &AzureSEVSNP{ return &AzureSEVSNP{
Measurements: measurements.DefaultsFor(cloudprovider.Azure, variant.AzureSEVSNP{}), Measurements: measurements.DefaultsFor(cloudprovider.Azure, variant.AzureSEVSNP{}),
BootloaderVersion: NewLatestPlaceholderVersion(), BootloaderVersion: NewLatestPlaceholderVersion[uint8](),
TEEVersion: NewLatestPlaceholderVersion(), TEEVersion: NewLatestPlaceholderVersion[uint8](),
SNPVersion: NewLatestPlaceholderVersion(), SNPVersion: NewLatestPlaceholderVersion[uint8](),
MicrocodeVersion: NewLatestPlaceholderVersion(), MicrocodeVersion: NewLatestPlaceholderVersion[uint8](),
FirmwareSignerConfig: SNPFirmwareSignerConfig{ FirmwareSignerConfig: SNPFirmwareSignerConfig{
AcceptedKeyDigests: idkeydigest.DefaultList(), AcceptedKeyDigests: idkeydigest.DefaultList(),
EnforcementPolicy: idkeydigest.MAAFallback, EnforcementPolicy: idkeydigest.MAAFallback,
@ -142,14 +142,14 @@ func DefaultForAzureTDX() *AzureTDX {
return &AzureTDX{ return &AzureTDX{
Measurements: measurements.DefaultsFor(cloudprovider.Azure, variant.AzureTDX{}), Measurements: measurements.DefaultsFor(cloudprovider.Azure, variant.AzureTDX{}),
// TODO(AB#3798): Enable latest versioning for Azure TDX // TODO(AB#3798): Enable latest versioning for Azure TDX
QESVN: 0, QESVN: NewLatestPlaceholderVersion[uint16](),
PCESVN: 0, PCESVN: NewLatestPlaceholderVersion[uint16](),
TEETCBSVN: encoding.HexBytes{0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, TEETCBSVN: NewLatestPlaceholderVersion[encoding.HexBytes](),
QEVendorID: encoding.HexBytes{0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07}, QEVendorID: NewLatestPlaceholderVersion[encoding.HexBytes](),
// Don't set a default for MRSEAM as it effectively prevents upgrading the SEAM module // Don't set a default for MRSEAM as it effectively prevents upgrading the SEAM module
// Quote verification still makes sure the module comes from Intel (through MRSIGNERSEAM), and is not of a lower version than expected // Quote verification still makes sure the module comes from Intel (through MRSIGNERSEAM), and is not of a lower version than expected
// MRSeam: nil, // MRSeam: nil,
XFAM: encoding.HexBytes{0xe7, 0x18, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00}, XFAM: NewLatestPlaceholderVersion[encoding.HexBytes](),
IntelRootKey: mustParsePEM(tdxRootPEM), IntelRootKey: mustParsePEM(tdxRootPEM),
} }
@ -179,9 +179,43 @@ func (c AzureTDX) EqualTo(other AttestationCfg) (bool, error) {
return c.Measurements.EqualTo(otherCfg.Measurements), nil return c.Measurements.EqualTo(otherCfg.Measurements), nil
} }
// FetchAndSetLatestVersionNumbers fetches the latest version numbers from the configapi and sets them.
func (c *AzureTDX) FetchAndSetLatestVersionNumbers(ctx context.Context, fetcher attestationconfigapi.Fetcher) error {
// Only talk to the API if at least one version number is set to latest.
if !(c.PCESVN.WantLatest || c.QESVN.WantLatest || c.TEETCBSVN.WantLatest || c.QEVendorID.WantLatest || c.XFAM.WantLatest) {
return nil
}
versions, err := fetcher.FetchLatestVersion(ctx, variant.AzureTDX{})
if err != nil {
return fmt.Errorf("fetching latest TCB versions from configapi: %w", err)
}
// set values and keep WantLatest flag
if c.PCESVN.WantLatest {
c.PCESVN.Value = versions.PCESVN
}
if c.QESVN.WantLatest {
c.QESVN.Value = versions.QESVN
}
if c.TEETCBSVN.WantLatest {
c.TEETCBSVN.Value = versions.TEETCBSVN[:]
}
if c.QEVendorID.WantLatest {
c.QEVendorID.Value = versions.QEVendorID[:]
}
if c.XFAM.WantLatest {
c.XFAM.Value = versions.XFAM[:]
}
return nil
}
func (c *AzureTDX) getToMarshallLatestWithResolvedVersions() AttestationCfg { func (c *AzureTDX) getToMarshallLatestWithResolvedVersions() AttestationCfg {
cp := *c cp := *c
// TODO: We probably want to support "latest" pseudo versioning for Azure TDX cp.PCESVN.WantLatest = false
// But we should decide on which claims can be reliably used for attestation first cp.QESVN.WantLatest = false
cp.TEETCBSVN.WantLatest = false
cp.QEVendorID.WantLatest = false
cp.XFAM.WantLatest = false
return &cp return &cp
} }

View File

@ -468,18 +468,22 @@ func New(fileHandler file.Handler, name string, fetcher attestationconfigapi.Fet
return nil, err return nil, err
} }
// Replace "latest" placeholders for attestation version numbers with the actual latest version numbers from config API
if azure := c.Attestation.AzureSEVSNP; azure != nil { if azure := c.Attestation.AzureSEVSNP; azure != nil {
if err := azure.FetchAndSetLatestVersionNumbers(context.Background(), fetcher); err != nil { if err := azure.FetchAndSetLatestVersionNumbers(context.Background(), fetcher); err != nil {
return c, err return c, err
} }
} }
if azure := c.Attestation.AzureTDX; azure != nil {
if err := azure.FetchAndSetLatestVersionNumbers(context.Background(), fetcher); err != nil {
return c, err
}
}
if aws := c.Attestation.AWSSEVSNP; aws != nil { if aws := c.Attestation.AWSSEVSNP; aws != nil {
if err := aws.FetchAndSetLatestVersionNumbers(context.Background(), fetcher); err != nil { if err := aws.FetchAndSetLatestVersionNumbers(context.Background(), fetcher); err != nil {
return c, err return c, err
} }
} }
if gcp := c.Attestation.GCPSEVSNP; gcp != nil { if gcp := c.Attestation.GCPSEVSNP; gcp != nil {
if err := gcp.FetchAndSetLatestVersionNumbers(context.Background(), fetcher); err != nil { if err := gcp.FetchAndSetLatestVersionNumbers(context.Background(), fetcher); err != nil {
return c, err return c, err
@ -993,16 +997,16 @@ type GCPSEVSNP struct {
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"` Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
// description: | // description: |
// Lowest acceptable bootloader version. // Lowest acceptable bootloader version.
BootloaderVersion AttestationVersion `json:"bootloaderVersion" yaml:"bootloaderVersion"` BootloaderVersion AttestationVersion[uint8] `json:"bootloaderVersion" yaml:"bootloaderVersion"`
// description: | // description: |
// Lowest acceptable TEE version. // Lowest acceptable TEE version.
TEEVersion AttestationVersion `json:"teeVersion" yaml:"teeVersion"` TEEVersion AttestationVersion[uint8] `json:"teeVersion" yaml:"teeVersion"`
// description: | // description: |
// Lowest acceptable SEV-SNP version. // Lowest acceptable SEV-SNP version.
SNPVersion AttestationVersion `json:"snpVersion" yaml:"snpVersion"` SNPVersion AttestationVersion[uint8] `json:"snpVersion" yaml:"snpVersion"`
// description: | // description: |
// Lowest acceptable microcode version. // Lowest acceptable microcode version.
MicrocodeVersion AttestationVersion `json:"microcodeVersion" yaml:"microcodeVersion"` MicrocodeVersion AttestationVersion[uint8] `json:"microcodeVersion" yaml:"microcodeVersion"`
// description: | // description: |
// AMD Root Key certificate used to verify the SEV-SNP certificate chain. // AMD Root Key certificate used to verify the SEV-SNP certificate chain.
AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"` AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"`
@ -1080,16 +1084,16 @@ type AWSSEVSNP struct {
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"` Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
// description: | // description: |
// Lowest acceptable bootloader version. // Lowest acceptable bootloader version.
BootloaderVersion AttestationVersion `json:"bootloaderVersion" yaml:"bootloaderVersion"` BootloaderVersion AttestationVersion[uint8] `json:"bootloaderVersion" yaml:"bootloaderVersion"`
// description: | // description: |
// Lowest acceptable TEE version. // Lowest acceptable TEE version.
TEEVersion AttestationVersion `json:"teeVersion" yaml:"teeVersion"` TEEVersion AttestationVersion[uint8] `json:"teeVersion" yaml:"teeVersion"`
// description: | // description: |
// Lowest acceptable SEV-SNP version. // Lowest acceptable SEV-SNP version.
SNPVersion AttestationVersion `json:"snpVersion" yaml:"snpVersion"` SNPVersion AttestationVersion[uint8] `json:"snpVersion" yaml:"snpVersion"`
// description: | // description: |
// Lowest acceptable microcode version. // Lowest acceptable microcode version.
MicrocodeVersion AttestationVersion `json:"microcodeVersion" yaml:"microcodeVersion"` MicrocodeVersion AttestationVersion[uint8] `json:"microcodeVersion" yaml:"microcodeVersion"`
// description: | // description: |
// AMD Root Key certificate used to verify the SEV-SNP certificate chain. // AMD Root Key certificate used to verify the SEV-SNP certificate chain.
AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"` AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"`
@ -1112,16 +1116,16 @@ type AzureSEVSNP struct {
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"` Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
// description: | // description: |
// Lowest acceptable bootloader version. // Lowest acceptable bootloader version.
BootloaderVersion AttestationVersion `json:"bootloaderVersion" yaml:"bootloaderVersion"` BootloaderVersion AttestationVersion[uint8] `json:"bootloaderVersion" yaml:"bootloaderVersion"`
// description: | // description: |
// Lowest acceptable TEE version. // Lowest acceptable TEE version.
TEEVersion AttestationVersion `json:"teeVersion" yaml:"teeVersion"` TEEVersion AttestationVersion[uint8] `json:"teeVersion" yaml:"teeVersion"`
// description: | // description: |
// Lowest acceptable SEV-SNP version. // Lowest acceptable SEV-SNP version.
SNPVersion AttestationVersion `json:"snpVersion" yaml:"snpVersion"` SNPVersion AttestationVersion[uint8] `json:"snpVersion" yaml:"snpVersion"`
// description: | // description: |
// Lowest acceptable microcode version. // Lowest acceptable microcode version.
MicrocodeVersion AttestationVersion `json:"microcodeVersion" yaml:"microcodeVersion"` MicrocodeVersion AttestationVersion[uint8] `json:"microcodeVersion" yaml:"microcodeVersion"`
// description: | // description: |
// Configuration for validating the firmware signature. // Configuration for validating the firmware signature.
FirmwareSignerConfig SNPFirmwareSignerConfig `json:"firmwareSignerConfig" yaml:"firmwareSignerConfig"` FirmwareSignerConfig SNPFirmwareSignerConfig `json:"firmwareSignerConfig" yaml:"firmwareSignerConfig"`
@ -1147,22 +1151,22 @@ type AzureTDX struct {
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"` Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
// description: | // description: |
// Minimum required QE security version number (SVN). // Minimum required QE security version number (SVN).
QESVN uint16 `json:"qeSVN" yaml:"qeSVN"` QESVN AttestationVersion[uint16] `json:"qeSVN" yaml:"qeSVN"`
// description: | // description: |
// Minimum required PCE security version number (SVN). // Minimum required PCE security version number (SVN).
PCESVN uint16 `json:"pceSVN" yaml:"pceSVN"` PCESVN AttestationVersion[uint16] `json:"pceSVN" yaml:"pceSVN"`
// description: | // description: |
// Component-wise minimum required 16 byte hex-encoded TEE_TCB security version number (SVN). // Component-wise minimum required 16 byte hex-encoded TEE_TCB security version number (SVN).
TEETCBSVN encoding.HexBytes `json:"teeTCBSVN" yaml:"teeTCBSVN"` TEETCBSVN AttestationVersion[encoding.HexBytes] `json:"teeTCBSVN" yaml:"teeTCBSVN"`
// description: | // description: |
// Expected 16 byte hex-encoded QE_VENDOR_ID field. // Expected 16 byte hex-encoded QE_VENDOR_ID field.
QEVendorID encoding.HexBytes `json:"qeVendorID" yaml:"qeVendorID"` QEVendorID AttestationVersion[encoding.HexBytes] `json:"qeVendorID" yaml:"qeVendorID"`
// description: | // description: |
// Expected 48 byte hex-encoded MR_SEAM value. // Expected 48 byte hex-encoded MR_SEAM value.
MRSeam encoding.HexBytes `json:"mrSeam,omitempty" yaml:"mrSeam,omitempty"` MRSeam encoding.HexBytes `json:"mrSeam,omitempty" yaml:"mrSeam,omitempty"`
// description: | // description: |
// Expected 8 byte hex-encoded XFAM field. // Expected 8 byte hex-encoded XFAM field.
XFAM encoding.HexBytes `json:"xfam" yaml:"xfam"` XFAM AttestationVersion[encoding.HexBytes] `json:"xfam" yaml:"xfam"`
// description: | // description: |
// Intel Root Key certificate used to verify the TDX certificate chain. // Intel Root Key certificate used to verify the TDX certificate chain.
IntelRootKey Certificate `json:"intelRootKey" yaml:"intelRootKey"` IntelRootKey Certificate `json:"intelRootKey" yaml:"intelRootKey"`

View File

@ -70,10 +70,10 @@ func TestGetAttestationConfigMarshalsNumericalVersion(t *testing.T) {
var mp map[string]interface{} var mp map[string]interface{}
require.NoError(yaml.Unmarshal(bt, &mp)) require.NoError(yaml.Unmarshal(bt, &mp))
assert := assert.New(t) assert := assert.New(t)
assert.Equal(placeholderVersionValue, mp["microcodeVersion"]) assert.EqualValues(placeholderVersionValue[uint8](), mp["microcodeVersion"])
assert.Equal(placeholderVersionValue, mp["teeVersion"]) assert.EqualValues(placeholderVersionValue[uint8](), mp["teeVersion"])
assert.Equal(placeholderVersionValue, mp["snpVersion"]) assert.EqualValues(placeholderVersionValue[uint8](), mp["snpVersion"])
assert.Equal(placeholderVersionValue, mp["bootloaderVersion"]) assert.EqualValues(placeholderVersionValue[uint8](), mp["bootloaderVersion"])
} }
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
@ -99,19 +99,19 @@ func TestNew(t *testing.T) {
wantResult: func() *Config { wantResult: func() *Config {
conf := Default() conf := Default()
modifyConfigForAzureToPassValidate(conf) modifyConfigForAzureToPassValidate(conf)
conf.Attestation.AzureSEVSNP.MicrocodeVersion = AttestationVersion{ conf.Attestation.AzureSEVSNP.MicrocodeVersion = AttestationVersion[uint8]{
Value: testCfg.Microcode, Value: testCfg.Microcode,
WantLatest: true, WantLatest: true,
} }
conf.Attestation.AzureSEVSNP.TEEVersion = AttestationVersion{ conf.Attestation.AzureSEVSNP.TEEVersion = AttestationVersion[uint8]{
Value: 2, Value: 2,
WantLatest: false, WantLatest: false,
} }
conf.Attestation.AzureSEVSNP.BootloaderVersion = AttestationVersion{ conf.Attestation.AzureSEVSNP.BootloaderVersion = AttestationVersion[uint8]{
Value: 1, Value: 1,
WantLatest: false, WantLatest: false,
} }
conf.Attestation.AzureSEVSNP.SNPVersion = AttestationVersion{ conf.Attestation.AzureSEVSNP.SNPVersion = AttestationVersion[uint8]{
Value: testCfg.SNP, Value: testCfg.SNP,
WantLatest: true, WantLatest: true,
} }

View File

@ -22,10 +22,10 @@ var _ svnResolveMarshaller = &GCPSEVSNP{}
func DefaultForGCPSEVSNP() *GCPSEVSNP { func DefaultForGCPSEVSNP() *GCPSEVSNP {
return &GCPSEVSNP{ return &GCPSEVSNP{
Measurements: measurements.DefaultsFor(cloudprovider.GCP, variant.GCPSEVSNP{}), Measurements: measurements.DefaultsFor(cloudprovider.GCP, variant.GCPSEVSNP{}),
BootloaderVersion: NewLatestPlaceholderVersion(), BootloaderVersion: NewLatestPlaceholderVersion[uint8](),
TEEVersion: NewLatestPlaceholderVersion(), TEEVersion: NewLatestPlaceholderVersion[uint8](),
SNPVersion: NewLatestPlaceholderVersion(), SNPVersion: NewLatestPlaceholderVersion[uint8](),
MicrocodeVersion: NewLatestPlaceholderVersion(), MicrocodeVersion: NewLatestPlaceholderVersion[uint8](),
AMDRootKey: mustParsePEM(arkPEM), AMDRootKey: mustParsePEM(arkPEM),
} }
} }

View File

@ -415,19 +415,19 @@ func V3ToV4(path string, fileHandler file.Handler) error {
case cfgV3.Attestation.AzureSEVSNP != nil: case cfgV3.Attestation.AzureSEVSNP != nil:
cfgV4.Attestation.AzureSEVSNP = &config.AzureSEVSNP{ cfgV4.Attestation.AzureSEVSNP = &config.AzureSEVSNP{
Measurements: cfgV3.Attestation.AzureSEVSNP.Measurements, Measurements: cfgV3.Attestation.AzureSEVSNP.Measurements,
BootloaderVersion: config.AttestationVersion{ BootloaderVersion: config.AttestationVersion[uint8]{
Value: cfgV3.Attestation.AzureSEVSNP.BootloaderVersion.Value, Value: cfgV3.Attestation.AzureSEVSNP.BootloaderVersion.Value,
WantLatest: cfgV3.Attestation.AzureSEVSNP.BootloaderVersion.WantLatest, WantLatest: cfgV3.Attestation.AzureSEVSNP.BootloaderVersion.WantLatest,
}, },
TEEVersion: config.AttestationVersion{ TEEVersion: config.AttestationVersion[uint8]{
Value: cfgV3.Attestation.AzureSEVSNP.TEEVersion.Value, Value: cfgV3.Attestation.AzureSEVSNP.TEEVersion.Value,
WantLatest: cfgV3.Attestation.AzureSEVSNP.TEEVersion.WantLatest, WantLatest: cfgV3.Attestation.AzureSEVSNP.TEEVersion.WantLatest,
}, },
SNPVersion: config.AttestationVersion{ SNPVersion: config.AttestationVersion[uint8]{
Value: cfgV3.Attestation.AzureSEVSNP.SNPVersion.Value, Value: cfgV3.Attestation.AzureSEVSNP.SNPVersion.Value,
WantLatest: cfgV3.Attestation.AzureSEVSNP.SNPVersion.WantLatest, WantLatest: cfgV3.Attestation.AzureSEVSNP.SNPVersion.WantLatest,
}, },
MicrocodeVersion: config.AttestationVersion{ MicrocodeVersion: config.AttestationVersion[uint8]{
Value: cfgV3.Attestation.AzureSEVSNP.MicrocodeVersion.Value, Value: cfgV3.Attestation.AzureSEVSNP.MicrocodeVersion.Value,
WantLatest: cfgV3.Attestation.AzureSEVSNP.MicrocodeVersion.WantLatest, WantLatest: cfgV3.Attestation.AzureSEVSNP.MicrocodeVersion.WantLatest,
}, },

View File

@ -32,6 +32,7 @@ go_library(
"//internal/constellation/helm", "//internal/constellation/helm",
"//internal/constellation/kubecmd", "//internal/constellation/kubecmd",
"//internal/constellation/state", "//internal/constellation/state",
"//internal/encoding",
"//internal/file", "//internal/file",
"//internal/grpc/dialer", "//internal/grpc/dialer",
"//internal/imagefetcher", "//internal/imagefetcher",

View File

@ -162,17 +162,18 @@ func (d *AttestationDataSource) Read(ctx context.Context, req datasource.ReadReq
insecureFetch := data.Insecure.ValueBool() insecureFetch := data.Insecure.ValueBool()
snpVersions := attestationconfigapi.VersionAPIEntry{} latestVersions := attestationconfigapi.VersionAPIEntry{}
if attestationVariant.Equal(variant.AzureSEVSNP{}) || if attestationVariant.Equal(variant.AWSSEVSNP{}) ||
attestationVariant.Equal(variant.AWSSEVSNP{}) || attestationVariant.Equal(variant.AzureSEVSNP{}) ||
attestationVariant.Equal(variant.AzureTDX{}) ||
attestationVariant.Equal(variant.GCPSEVSNP{}) { attestationVariant.Equal(variant.GCPSEVSNP{}) {
snpVersions, err = d.fetcher.FetchLatestVersion(ctx, attestationVariant) latestVersions, err = d.fetcher.FetchLatestVersion(ctx, attestationVariant)
if err != nil { if err != nil {
resp.Diagnostics.AddError("Fetching SNP Version numbers", err.Error()) resp.Diagnostics.AddError("Fetching SNP Version numbers", err.Error())
return return
} }
} }
tfAttestation, err := convertToTfAttestation(attestationVariant, snpVersions) tfAttestation, err := convertToTfAttestation(attestationVariant, latestVersions)
if err != nil { if err != nil {
resp.Diagnostics.AddError("Converting attestation", err.Error()) resp.Diagnostics.AddError("Converting attestation", err.Error())
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/encoding"
) )
// naming schema: // naming schema:
@ -110,12 +111,12 @@ func convertFromTfAttestationCfg(tfAttestation attestationAttribute, attestation
attestationConfig = &config.AzureTDX{ attestationConfig = &config.AzureTDX{
Measurements: c11nMeasurements, Measurements: c11nMeasurements,
QESVN: tfAttestation.TDX.QESVN, QESVN: newVersion(tfAttestation.TDX.QESVN),
PCESVN: tfAttestation.TDX.PCESVN, PCESVN: newVersion(tfAttestation.TDX.PCESVN),
TEETCBSVN: teeTCBSVN, TEETCBSVN: newVersion(encoding.HexBytes(teeTCBSVN)),
QEVendorID: qeVendorID, QEVendorID: newVersion(encoding.HexBytes(qeVendorID)),
MRSeam: mrSeam, MRSeam: mrSeam,
XFAM: xfam, XFAM: newVersion(encoding.HexBytes(xfam)),
IntelRootKey: rootKey, IntelRootKey: rootKey,
} }
case variant.GCPSEVES{}: case variant.GCPSEVES{}:
@ -137,13 +138,9 @@ func convertFromTfAttestationCfg(tfAttestation attestationAttribute, attestation
} }
// convertToTfAttestationCfg converts the constellation attestation config to the related terraform structs. // convertToTfAttestationCfg converts the constellation attestation config to the related terraform structs.
func convertToTfAttestation(attVar variant.Variant, snpVersions attestationconfigapi.VersionAPIEntry) (tfAttestation attestationAttribute, err error) { func convertToTfAttestation(attVar variant.Variant, latestVersions attestationconfigapi.VersionAPIEntry) (tfAttestation attestationAttribute, err error) {
tfAttestation = attestationAttribute{ tfAttestation = attestationAttribute{
Variant: attVar.String(), Variant: attVar.String(),
BootloaderVersion: snpVersions.Bootloader,
TEEVersion: snpVersions.TEE,
SNPVersion: snpVersions.SNP,
MicrocodeVersion: snpVersions.Microcode,
} }
switch attVar { switch attVar {
@ -153,6 +150,10 @@ func convertToTfAttestation(attVar variant.Variant, snpVersions attestationconfi
return tfAttestation, err return tfAttestation, err
} }
tfAttestation.AMDRootKey = certStr tfAttestation.AMDRootKey = certStr
tfAttestation.BootloaderVersion = latestVersions.Bootloader
tfAttestation.TEEVersion = latestVersions.TEE
tfAttestation.SNPVersion = latestVersions.SNP
tfAttestation.MicrocodeVersion = latestVersions.Microcode
case variant.GCPSEVSNP{}: case variant.GCPSEVSNP{}:
certStr, err := certAsString(config.DefaultForGCPSEVSNP().AMDRootKey) certStr, err := certAsString(config.DefaultForGCPSEVSNP().AMDRootKey)
@ -160,6 +161,10 @@ func convertToTfAttestation(attVar variant.Variant, snpVersions attestationconfi
return tfAttestation, err return tfAttestation, err
} }
tfAttestation.AMDRootKey = certStr tfAttestation.AMDRootKey = certStr
tfAttestation.BootloaderVersion = latestVersions.Bootloader
tfAttestation.TEEVersion = latestVersions.TEE
tfAttestation.SNPVersion = latestVersions.SNP
tfAttestation.MicrocodeVersion = latestVersions.Microcode
case variant.AzureSEVSNP{}: case variant.AzureSEVSNP{}:
certStr, err := certAsString(config.DefaultForAzureSEVSNP().AMDRootKey) certStr, err := certAsString(config.DefaultForAzureSEVSNP().AMDRootKey)
@ -167,6 +172,10 @@ func convertToTfAttestation(attVar variant.Variant, snpVersions attestationconfi
return tfAttestation, err return tfAttestation, err
} }
tfAttestation.AMDRootKey = certStr tfAttestation.AMDRootKey = certStr
tfAttestation.BootloaderVersion = latestVersions.Bootloader
tfAttestation.TEEVersion = latestVersions.TEE
tfAttestation.SNPVersion = latestVersions.SNP
tfAttestation.MicrocodeVersion = latestVersions.Microcode
firmwareCfg := config.DefaultForAzureSEVSNP().FirmwareSignerConfig firmwareCfg := config.DefaultForAzureSEVSNP().FirmwareSignerConfig
tfFirmwareCfg, err := convertToTfFirmwareCfg(firmwareCfg) tfFirmwareCfg, err := convertToTfFirmwareCfg(firmwareCfg)
@ -174,24 +183,19 @@ func convertToTfAttestation(attVar variant.Variant, snpVersions attestationconfi
return tfAttestation, err return tfAttestation, err
} }
tfAttestation.AzureSNPFirmwareSignerConfig = tfFirmwareCfg tfAttestation.AzureSNPFirmwareSignerConfig = tfFirmwareCfg
case variant.AzureTDX{}: case variant.AzureTDX{}:
tdxCfg := config.DefaultForAzureTDX() certStr, err := certAsString(config.DefaultForAzureTDX().IntelRootKey)
certStr, err := certAsString(tdxCfg.IntelRootKey)
if err != nil { if err != nil {
return tfAttestation, err return tfAttestation, err
} }
tfAttestation.TDX.IntelRootKey = certStr
tfAttestation.TDX.PCESVN = latestVersions.PCESVN
tfAttestation.TDX.QESVN = latestVersions.QESVN
tfAttestation.TDX.TEETCBSVN = hex.EncodeToString(latestVersions.TEETCBSVN[:])
tfAttestation.TDX.QEVendorID = hex.EncodeToString(latestVersions.QEVendorID[:])
tfAttestation.TDX.XFAM = hex.EncodeToString(latestVersions.XFAM[:])
tfTdxCfg := tdxConfigAttribute{
IntelRootKey: certStr,
// TODO(AB#3798): Load these values dynamically from our attestation API
QESVN: tdxCfg.QESVN,
PCESVN: tdxCfg.PCESVN,
TEETCBSVN: hex.EncodeToString(tdxCfg.TEETCBSVN),
QEVendorID: hex.EncodeToString(tdxCfg.QEVendorID),
MRSeam: hex.EncodeToString(tdxCfg.MRSeam),
XFAM: hex.EncodeToString(tdxCfg.XFAM),
}
tfAttestation.TDX = tfTdxCfg
case variant.GCPSEVES{}, variant.QEMUVTPM{}: case variant.GCPSEVES{}, variant.QEMUVTPM{}:
// no additional fields // no additional fields
default: default:
@ -251,8 +255,8 @@ func convertToTfMeasurements(m measurements.M) map[string]measurementAttribute {
return tfMeasurements return tfMeasurements
} }
func newVersion(v uint8) config.AttestationVersion { func newVersion[T uint8 | uint16 | encoding.HexBytes](v T) config.AttestationVersion[T] {
return config.AttestationVersion{ return config.AttestationVersion[T]{
Value: v, Value: v,
} }
} }