mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-01-23 05:41:19 -05:00
Implement support for "latest" placeholders for Azure TDX
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
parent
a34493caa6
commit
9159b60331
@ -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 = [
|
||||||
|
@ -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",
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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"`
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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",
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user