Enable versions API to handle TDX versions

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2024-06-11 16:25:24 +02:00 committed by Daniel Weiße
parent fbddbc9867
commit a34493caa6
13 changed files with 253 additions and 245 deletions

View File

@ -204,18 +204,8 @@ func (f stubVerifyFetcher) FetchAndVerifyMeasurements(_ context.Context, _ strin
type stubAttestationFetcher struct{} type stubAttestationFetcher struct{}
func (f stubAttestationFetcher) FetchSEVSNPVersionList(_ context.Context, _ attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) { func (f stubAttestationFetcher) FetchLatestVersion(_ context.Context, _ variant.Variant) (attestationconfigapi.VersionAPIEntry, error) {
return attestationconfigapi.SEVSNPVersionList{}, nil return attestationconfigapi.VersionAPIEntry{
}
func (f stubAttestationFetcher) FetchSEVSNPVersion(_ context.Context, _ attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{
SEVSNPVersion: testCfg,
}, nil
}
func (f stubAttestationFetcher) FetchLatestVersion(_ context.Context, _ variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{
SEVSNPVersion: testCfg, SEVSNPVersion: testCfg,
}, nil }, nil
} }

View File

@ -171,14 +171,6 @@ type stubConfigFetcher struct {
fetchLatestErr error fetchLatestErr error
} }
func (s *stubConfigFetcher) FetchSEVSNPVersion(context.Context, attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) { func (s *stubConfigFetcher) FetchLatestVersion(context.Context, variant.Variant) (attestationconfigapi.VersionAPIEntry, error) {
panic("not implemented") return attestationconfigapi.VersionAPIEntry{}, s.fetchLatestErr
}
func (s *stubConfigFetcher) FetchSEVSNPVersionList(context.Context, attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) {
panic("not implemented")
}
func (s *stubConfigFetcher) FetchLatestVersion(context.Context, variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{}, s.fetchLatestErr
} }

View File

@ -80,20 +80,20 @@ func (a Client) DeleteSEVSNPVersion(ctx context.Context, attestation variant.Var
} }
// List returns the list of versions for the given attestation variant. // List returns the list of versions for the given attestation variant.
func (a Client) List(ctx context.Context, attestation variant.Variant) (attestationconfigapi.SEVSNPVersionList, error) { func (a Client) List(ctx context.Context, attestation variant.Variant) (attestationconfigapi.VersionList, error) {
if !attestation.Equal(variant.AzureSEVSNP{}) && if !attestation.Equal(variant.AzureSEVSNP{}) &&
!attestation.Equal(variant.AWSSEVSNP{}) && !attestation.Equal(variant.AWSSEVSNP{}) &&
!attestation.Equal(variant.GCPSEVSNP{}) { !attestation.Equal(variant.GCPSEVSNP{}) {
return attestationconfigapi.SEVSNPVersionList{}, fmt.Errorf("unsupported attestation variant: %s", attestation) return attestationconfigapi.VersionList{}, fmt.Errorf("unsupported attestation variant: %s", attestation)
} }
versions, err := apiclient.Fetch(ctx, a.s3Client, attestationconfigapi.SEVSNPVersionList{Variant: attestation}) versions, err := apiclient.Fetch(ctx, a.s3Client, attestationconfigapi.VersionList{Variant: attestation})
if err != nil { if err != nil {
var notFoundErr *apiclient.NotFoundError var notFoundErr *apiclient.NotFoundError
if errors.As(err, &notFoundErr) { if errors.As(err, &notFoundErr) {
return attestationconfigapi.SEVSNPVersionList{Variant: attestation}, nil return attestationconfigapi.VersionList{Variant: attestation}, nil
} }
return attestationconfigapi.SEVSNPVersionList{}, err return attestationconfigapi.VersionList{}, err
} }
versions.Variant = attestation versions.Variant = attestation
@ -101,10 +101,10 @@ func (a Client) List(ctx context.Context, attestation variant.Variant) (attestat
return versions, nil return versions, nil
} }
func (a Client) deleteSEVSNPVersion(versions attestationconfigapi.SEVSNPVersionList, versionStr string) (ops []crudCmd, err error) { func (a Client) deleteSEVSNPVersion(versions attestationconfigapi.VersionList, versionStr string) (ops []crudCmd, err error) {
versionStr = versionStr + ".json" versionStr = versionStr + ".json"
ops = append(ops, deleteCmd{ ops = append(ops, deleteCmd{
apiObject: attestationconfigapi.SEVSNPVersionAPI{ apiObject: attestationconfigapi.VersionAPIEntry{
Variant: versions.Variant, Variant: versions.Variant,
Version: versionStr, Version: versionStr,
}, },
@ -121,7 +121,7 @@ func (a Client) deleteSEVSNPVersion(versions attestationconfigapi.SEVSNPVersionL
return ops, nil return ops, nil
} }
func (a Client) constructUploadCmd(attestation variant.Variant, version attestationconfigapi.SEVSNPVersion, versionNames attestationconfigapi.SEVSNPVersionList, date time.Time) []crudCmd { func (a Client) constructUploadCmd(attestation variant.Variant, version attestationconfigapi.SEVSNPVersion, versionNames attestationconfigapi.VersionList, date time.Time) []crudCmd {
if !attestation.Equal(versionNames.Variant) { if !attestation.Equal(versionNames.Variant) {
return nil return nil
} }
@ -130,7 +130,7 @@ func (a Client) constructUploadCmd(attestation variant.Variant, version attestat
var res []crudCmd var res []crudCmd
res = append(res, putCmd{ res = append(res, putCmd{
apiObject: attestationconfigapi.SEVSNPVersionAPI{Version: dateStr, Variant: attestation, SEVSNPVersion: version}, apiObject: attestationconfigapi.VersionAPIEntry{Version: dateStr, Variant: attestation, SEVSNPVersion: version},
signer: a.signer, signer: a.signer,
}) })
@ -144,19 +144,19 @@ func (a Client) constructUploadCmd(attestation variant.Variant, version attestat
return res return res
} }
func removeVersion(list attestationconfigapi.SEVSNPVersionList, versionStr string) (removedVersions attestationconfigapi.SEVSNPVersionList, err error) { func removeVersion(list attestationconfigapi.VersionList, versionStr string) (removedVersions attestationconfigapi.VersionList, err error) {
versions := list.List versions := list.List
for i, v := range versions { for i, v := range versions {
if v == versionStr { if v == versionStr {
if i == len(versions)-1 { if i == len(versions)-1 {
removedVersions = attestationconfigapi.SEVSNPVersionList{List: versions[:i], Variant: list.Variant} removedVersions = attestationconfigapi.VersionList{List: versions[:i], Variant: list.Variant}
} else { } else {
removedVersions = attestationconfigapi.SEVSNPVersionList{List: append(versions[:i], versions[i+1:]...), Variant: list.Variant} removedVersions = attestationconfigapi.VersionList{List: append(versions[:i], versions[i+1:]...), Variant: list.Variant}
} }
return removedVersions, nil return removedVersions, nil
} }
} }
return attestationconfigapi.SEVSNPVersionList{}, fmt.Errorf("version %s not found in list %v", versionStr, versions) return attestationconfigapi.VersionList{}, fmt.Errorf("version %s not found in list %v", versionStr, versions)
} }
type crudCmd interface { type crudCmd interface {

View File

@ -21,11 +21,11 @@ func TestUploadAzureSEVSNP(t *testing.T) {
} }
version := attestationconfigapi.SEVSNPVersion{} version := attestationconfigapi.SEVSNPVersion{}
date := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC) date := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC)
ops := sut.constructUploadCmd(variant.AzureSEVSNP{}, version, attestationconfigapi.SEVSNPVersionList{List: []string{"2021-01-01-01-01.json", "2019-01-01-01-01.json"}, Variant: variant.AzureSEVSNP{}}, date) ops := sut.constructUploadCmd(variant.AzureSEVSNP{}, version, attestationconfigapi.VersionList{List: []string{"2021-01-01-01-01.json", "2019-01-01-01-01.json"}, Variant: variant.AzureSEVSNP{}}, date)
dateStr := "2023-01-01-01-01.json" dateStr := "2023-01-01-01-01.json"
assert := assert.New(t) assert := assert.New(t)
assert.Contains(ops, putCmd{ assert.Contains(ops, putCmd{
apiObject: attestationconfigapi.SEVSNPVersionAPI{ apiObject: attestationconfigapi.VersionAPIEntry{
Variant: variant.AzureSEVSNP{}, Variant: variant.AzureSEVSNP{},
Version: dateStr, Version: dateStr,
SEVSNPVersion: version, SEVSNPVersion: version,
@ -33,7 +33,7 @@ func TestUploadAzureSEVSNP(t *testing.T) {
signer: fakeSigner{}, signer: fakeSigner{},
}) })
assert.Contains(ops, putCmd{ assert.Contains(ops, putCmd{
apiObject: attestationconfigapi.SEVSNPVersionList{Variant: variant.AzureSEVSNP{}, List: []string{"2023-01-01-01-01.json", "2021-01-01-01-01.json", "2019-01-01-01-01.json"}}, apiObject: attestationconfigapi.VersionList{Variant: variant.AzureSEVSNP{}, List: []string{"2023-01-01-01-01.json", "2021-01-01-01-01.json", "2019-01-01-01-01.json"}},
signer: fakeSigner{}, signer: fakeSigner{},
}) })
} }
@ -42,20 +42,20 @@ func TestDeleteAzureSEVSNPVersions(t *testing.T) {
sut := Client{ sut := Client{
bucketID: "bucket", bucketID: "bucket",
} }
versions := attestationconfigapi.SEVSNPVersionList{List: []string{"2023-01-01.json", "2021-01-01.json", "2019-01-01.json"}} versions := attestationconfigapi.VersionList{List: []string{"2023-01-01.json", "2021-01-01.json", "2019-01-01.json"}}
ops, err := sut.deleteSEVSNPVersion(versions, "2021-01-01") ops, err := sut.deleteSEVSNPVersion(versions, "2021-01-01")
assert := assert.New(t) assert := assert.New(t)
assert.NoError(err) assert.NoError(err)
assert.Contains(ops, deleteCmd{ assert.Contains(ops, deleteCmd{
apiObject: attestationconfigapi.SEVSNPVersionAPI{ apiObject: attestationconfigapi.VersionAPIEntry{
Version: "2021-01-01.json", Version: "2021-01-01.json",
}, },
}) })
assert.Contains(ops, putCmd{ assert.Contains(ops, putCmd{
apiObject: attestationconfigapi.SEVSNPVersionList{List: []string{"2023-01-01.json", "2019-01-01.json"}}, apiObject: attestationconfigapi.VersionList{List: []string{"2023-01-01.json", "2019-01-01.json"}},
}) })
} }

View File

@ -15,6 +15,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli/client" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli/client"
"github.com/edgelesssys/constellation/v2/internal/api/fetcher"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
@ -127,7 +128,8 @@ func uploadReport(ctx context.Context,
latestAPIVersionAPI, err := attestationconfigapi.NewFetcherWithCustomCDNAndCosignKey(cfg.url, cfg.cosignPublicKey).FetchLatestVersion(ctx, attestation) latestAPIVersionAPI, err := attestationconfigapi.NewFetcherWithCustomCDNAndCosignKey(cfg.url, cfg.cosignPublicKey).FetchLatestVersion(ctx, attestation)
if err != nil { if err != nil {
if errors.Is(err, attestationconfigapi.ErrNoVersionsFound) { var notFoundErr *fetcher.NotFoundError
if errors.As(err, &notFoundErr) {
log.Info("No versions found in API, but assuming that we are uploading the first version.") log.Info("No versions found in API, but assuming that we are uploading the first version.")
} else { } else {
return fmt.Errorf("fetching latest version: %w", err) return fmt.Errorf("fetching latest version: %w", err)

View File

@ -8,7 +8,6 @@ package attestationconfigapi
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
apifetcher "github.com/edgelesssys/constellation/v2/internal/api/fetcher" apifetcher "github.com/edgelesssys/constellation/v2/internal/api/fetcher"
@ -19,12 +18,9 @@ import (
const cosignPublicKey = constants.CosignPublicKeyReleases const cosignPublicKey = constants.CosignPublicKeyReleases
// ErrNoVersionsFound is returned if no versions are found.
var ErrNoVersionsFound = errors.New("no versions found")
// Fetcher fetches config API resources without authentication. // Fetcher fetches config API resources without authentication.
type Fetcher interface { type Fetcher interface {
FetchLatestVersion(ctx context.Context, attestation variant.Variant) (SEVSNPVersionAPI, error) FetchLatestVersion(ctx context.Context, attestation variant.Variant) (VersionAPIEntry, error)
} }
// fetcher fetches AttestationCfg API resources without authentication. // fetcher fetches AttestationCfg API resources without authentication.
@ -64,46 +60,43 @@ func newFetcherWithClientAndVerifier(client apifetcher.HTTPClient, cosignVerifie
} }
// FetchLatestVersion returns the latest versions of the given type. // FetchLatestVersion returns the latest versions of the given type.
func (f *fetcher) FetchLatestVersion(ctx context.Context, attesation variant.Variant) (res SEVSNPVersionAPI, err error) { func (f *fetcher) FetchLatestVersion(ctx context.Context, variant variant.Variant) (VersionAPIEntry, error) {
list, err := f.fetchVersionList(ctx, SEVSNPVersionList{Variant: attesation}) list, err := f.fetchVersionList(ctx, variant)
if err != nil { if err != nil {
return res, ErrNoVersionsFound return VersionAPIEntry{}, err
} }
getVersionRequest := SEVSNPVersionAPI{ // latest version is first in list
Version: list.List[0], // latest version is first in list return f.fetchVersion(ctx, list.List[0], variant)
Variant: attesation,
}
res, err = f.fetchVersion(ctx, getVersionRequest)
if err != nil {
return res, err
}
return
} }
// fetchVersionList fetches the version list information from the config API. // fetchVersionList fetches the version list information from the config API.
func (f *fetcher) fetchVersionList(ctx context.Context, list SEVSNPVersionList) (SEVSNPVersionList, error) { func (f *fetcher) fetchVersionList(ctx context.Context, variant variant.Variant) (VersionList, error) {
// TODO(derpsteb): Replace with FetchAndVerify once we move to v2 of the config API. // TODO(derpsteb): Replace with FetchAndVerify once we move to v2 of the config API and the list is saved as (.json) file.
fetchedList, err := apifetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, list) fetchedList, err := apifetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, VersionList{Variant: variant})
if err != nil { if err != nil {
return list, fmt.Errorf("fetching version list: %w", err) return VersionList{}, fmt.Errorf("fetching version list: %w", err)
} }
// Need to set this explicitly as the variant is not part of the marshalled JSON. // Set the attestation variant of the list as it is not part of the marshalled JSON retrieved by Fetch
fetchedList.Variant = list.Variant fetchedList.Variant = variant
return fetchedList, nil return fetchedList, nil
} }
// fetchVersion fetches the version information from the config API. // fetchVersion fetches the version information from the config API.
func (f *fetcher) fetchVersion(ctx context.Context, version SEVSNPVersionAPI) (SEVSNPVersionAPI, error) { func (f *fetcher) fetchVersion(ctx context.Context, version string, variant variant.Variant) (VersionAPIEntry, error) {
fetchedVersion, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, f.cdnURL, version, f.verifier) obj := VersionAPIEntry{
Version: version,
Variant: variant,
}
fetchedVersion, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, f.cdnURL, obj, f.verifier)
if err != nil { if err != nil {
return fetchedVersion, fmt.Errorf("fetching version %s: %w", version.Version, err) return VersionAPIEntry{}, fmt.Errorf("fetching version %q: %w", version, err)
} }
// Need to set this explicitly as the variant is not part of the marshalled JSON. // Set the attestation variant of the list as it is not part of the marshalled JSON retrieved by FetchAndVerify
fetchedVersion.Variant = version.Variant fetchedVersion.Variant = variant
return fetchedVersion, nil return fetchedVersion, nil
} }

View File

@ -22,82 +22,106 @@ import (
) )
func TestFetchLatestSEVSNPVersion(t *testing.T) { func TestFetchLatestSEVSNPVersion(t *testing.T) {
latestVersionSNP := VersionAPIEntry{
SEVSNPVersion: SEVSNPVersion{
Microcode: 93,
TEE: 0,
SNP: 6,
Bootloader: 2,
},
}
olderVersionSNP := VersionAPIEntry{
SEVSNPVersion: SEVSNPVersion{
Microcode: 1,
TEE: 0,
SNP: 1,
Bootloader: 1,
},
}
latestVersionTDX := VersionAPIEntry{
TDXVersion: TDXVersion{
QESVN: 2,
PCESVN: 3,
TEETCBSVN: [16]byte{4},
QEVendorID: [16]byte{5},
XFAM: [8]byte{6},
},
}
olderVersionTDX := VersionAPIEntry{
TDXVersion: TDXVersion{
QESVN: 1,
PCESVN: 2,
TEETCBSVN: [16]byte{3},
QEVendorID: [16]byte{4},
XFAM: [8]byte{5},
},
}
latestStr := "2023-06-11-14-09.json" latestStr := "2023-06-11-14-09.json"
olderStr := "2019-01-01-01-01.json" olderStr := "2019-01-01-01-01.json"
testcases := map[string]struct { testCases := map[string]struct {
fetcherVersions []string fetcherVersions []string
timeAtTest time.Time timeAtTest time.Time
wantErr bool wantErr bool
attestation variant.Variant attestation variant.Variant
expectedVersion func() SEVSNPVersionAPI expectedVersion VersionAPIEntry
olderVersion func() SEVSNPVersionAPI olderVersion VersionAPIEntry
latestVersion func() SEVSNPVersionAPI latestVersion VersionAPIEntry
}{ }{
"get latest version azure": { "get latest version azure-sev-snp": {
fetcherVersions: []string{latestStr, olderStr}, fetcherVersions: []string{latestStr, olderStr},
attestation: variant.AzureSEVSNP{}, attestation: variant.AzureSEVSNP{},
expectedVersion: func() SEVSNPVersionAPI { tmp := latestVersion; tmp.Variant = variant.AzureSEVSNP{}; return tmp }, expectedVersion: func() VersionAPIEntry { tmp := latestVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
olderVersion: func() SEVSNPVersionAPI { tmp := olderVersion; tmp.Variant = variant.AzureSEVSNP{}; return tmp }, olderVersion: func() VersionAPIEntry { tmp := olderVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
latestVersion: func() SEVSNPVersionAPI { tmp := latestVersion; tmp.Variant = variant.AzureSEVSNP{}; return tmp }, latestVersion: func() VersionAPIEntry { tmp := latestVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
}, },
"get latest version aws": { "get latest version aws-sev-snp": {
fetcherVersions: []string{latestStr, olderStr}, fetcherVersions: []string{latestStr, olderStr},
attestation: variant.AWSSEVSNP{}, attestation: variant.AWSSEVSNP{},
expectedVersion: func() SEVSNPVersionAPI { tmp := latestVersion; tmp.Variant = variant.AWSSEVSNP{}; return tmp }, expectedVersion: func() VersionAPIEntry { tmp := latestVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
olderVersion: func() SEVSNPVersionAPI { tmp := olderVersion; tmp.Variant = variant.AWSSEVSNP{}; return tmp }, olderVersion: func() VersionAPIEntry { tmp := olderVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
latestVersion: func() SEVSNPVersionAPI { tmp := latestVersion; tmp.Variant = variant.AWSSEVSNP{}; return tmp }, latestVersion: func() VersionAPIEntry { tmp := latestVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
},
"get latest version azure-tdx": {
fetcherVersions: []string{latestStr, olderStr},
attestation: variant.AzureTDX{},
expectedVersion: func() VersionAPIEntry { tmp := latestVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
olderVersion: func() VersionAPIEntry { tmp := olderVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
latestVersion: func() VersionAPIEntry { tmp := latestVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
}, },
} }
for name, tc := range testcases { for name, tc := range testCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
client := &http.Client{ client := &http.Client{
Transport: &fakeConfigAPIHandler{ Transport: &fakeConfigAPIHandler{
attestation: tc.attestation, attestation: tc.attestation,
versions: tc.fetcherVersions, versions: tc.fetcherVersions,
latestDate: latestStr, latestDate: latestStr,
latestVersion: tc.latestVersion(), latestVersion: tc.latestVersion,
olderDate: olderStr, olderDate: olderStr,
olderVersion: tc.olderVersion(), olderVersion: tc.olderVersion,
}, },
} }
fetcher := newFetcherWithClientAndVerifier(client, dummyVerifier{}, constants.CDNRepositoryURL) fetcher := newFetcherWithClientAndVerifier(client, stubVerifier{}, constants.CDNRepositoryURL)
res, err := fetcher.FetchLatestVersion(context.Background(), tc.attestation) res, err := fetcher.FetchLatestVersion(context.Background(), tc.attestation)
assert := assert.New(t) assert := assert.New(t)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
} else { } else {
assert.NoError(err) assert.NoError(err)
assert.Equal(tc.expectedVersion(), res) assert.Equal(tc.expectedVersion, res)
} }
}) })
} }
} }
var latestVersion = SEVSNPVersionAPI{
SEVSNPVersion: SEVSNPVersion{
Microcode: 93,
TEE: 0,
SNP: 6,
Bootloader: 2,
},
}
var olderVersion = SEVSNPVersionAPI{
SEVSNPVersion: SEVSNPVersion{
Microcode: 1,
TEE: 0,
SNP: 1,
Bootloader: 1,
},
}
type fakeConfigAPIHandler struct { type fakeConfigAPIHandler struct {
attestation variant.Variant attestation variant.Variant
versions []string versions []string
latestDate string latestDate string
latestVersion SEVSNPVersionAPI latestVersion VersionAPIEntry
olderDate string olderDate string
olderVersion SEVSNPVersionAPI olderVersion VersionAPIEntry
} }
// RoundTrip resolves the request and returns a dummy response. // RoundTrip resolves the request and returns a dummy response.
@ -148,8 +172,8 @@ func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, err
return nil, errors.New("no endpoint found") return nil, errors.New("no endpoint found")
} }
type dummyVerifier struct{} type stubVerifier struct{}
func (s dummyVerifier) VerifySignature(_, _ []byte) error { func (s stubVerifier) VerifySignature(_, _ []byte) error {
return nil return nil
} }

View File

@ -1,110 +0,0 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package attestationconfigapi
import (
"encoding/json"
"fmt"
"path"
"sort"
"strings"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
)
// AttestationURLPath is the URL path to the attestation versions.
const AttestationURLPath = "constellation/v1/attestation"
// SEVSNPVersion tracks the latest version of each component of the SEVSNP.
type SEVSNPVersion struct {
// Bootloader is the latest version of the SEVSNP bootloader.
Bootloader uint8 `json:"bootloader"`
// TEE is the latest version of the SEVSNP TEE.
TEE uint8 `json:"tee"`
// SNP is the latest version of the SEVSNP SNP.
SNP uint8 `json:"snp"`
// Microcode is the latest version of the SEVSNP microcode.
Microcode uint8 `json:"microcode"`
}
// SEVSNPVersionAPI is the request to get the version information of the specific version in the config api.
// Because variant is not part of the marshalled JSON, fetcher and client methods need to fill the variant property.
// Once we switch to v2 of the API we should embed the variant in the object.
// That would remove the possibility of some fetcher/client code forgetting to set the variant.
type SEVSNPVersionAPI struct {
Version string `json:"-"`
Variant variant.Variant `json:"-"`
SEVSNPVersion
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i SEVSNPVersionAPI) JSONPath() string {
return path.Join(AttestationURLPath, i.Variant.String(), i.Version)
}
// ValidateRequest validates the request.
func (i SEVSNPVersionAPI) ValidateRequest() error {
if !strings.HasSuffix(i.Version, ".json") {
return fmt.Errorf("version has no .json suffix")
}
return nil
}
// Validate is a No-Op at the moment.
func (i SEVSNPVersionAPI) Validate() error {
return nil
}
// SEVSNPVersionList is the request to list all versions in the config api.
// Because variant is not part of the marshalled JSON, fetcher and client methods need to fill the variant property.
// Once we switch to v2 of the API we could embed the variant in the object and remove some code from fetcher & client.
// That would remove the possibility of some fetcher/client code forgetting to set the variant.
type SEVSNPVersionList struct {
Variant variant.Variant
List []string
}
// MarshalJSON marshals the i's list property to JSON.
func (i SEVSNPVersionList) MarshalJSON() ([]byte, error) {
return json.Marshal(i.List)
}
// UnmarshalJSON unmarshals a list of strings into i's list property.
func (i *SEVSNPVersionList) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &i.List)
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i SEVSNPVersionList) JSONPath() string {
return path.Join(AttestationURLPath, i.Variant.String(), "list")
}
// ValidateRequest is a NoOp as there is no input.
func (i SEVSNPVersionList) ValidateRequest() error {
return nil
}
// SortReverse sorts the list of versions in reverse order.
func (i *SEVSNPVersionList) SortReverse() {
sort.Sort(sort.Reverse(sort.StringSlice(i.List)))
}
// AddVersion adds new to i's list and sorts the element in descending order.
func (i *SEVSNPVersionList) AddVersion(new string) {
i.List = append(i.List, new)
i.List = variant.RemoveDuplicate(i.List)
i.SortReverse()
}
// Validate validates the response.
func (i SEVSNPVersionList) Validate() error {
if len(i.List) < 1 {
return fmt.Errorf("no versions found in /list")
}
return nil
}

View File

@ -0,0 +1,127 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package attestationconfigapi
import (
"encoding/json"
"fmt"
"path"
"sort"
"strings"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
)
// AttestationURLPath is the URL path to the attestation versions.
const AttestationURLPath = "constellation/v1/attestation"
// SEVSNPVersion tracks the latest version of each component for SEV-SNP.
type SEVSNPVersion struct {
// Bootloader is the latest version of the SEV-SNP bootloader.
Bootloader uint8 `json:"bootloader,omitempty"`
// TEE is the latest version of the SEV-SNP TEE.
TEE uint8 `json:"tee,omitempty"`
// SNP is the latest version of the SEV-SNP SNP.
SNP uint8 `json:"snp,omitempty"`
// Microcode is the latest version of the SEV-SNP microcode.
Microcode uint8 `json:"microcode,omitempty"`
}
// TDXVersion tracks the latest version of each component for TDX.
type TDXVersion struct {
// QESVN is the latest QE security version number.
QESVN uint16 `json:"qeSVN,omitempty"`
// PCESVN is the latest PCE security version number.
PCESVN uint16 `json:"pceSVN,omitempty"`
// TEETCBSVN are the latest component-wise security version numbers for the TEE.
TEETCBSVN [16]byte `json:"teeTCBSVN,omitempty"`
// QEVendorID is the latest QE vendor ID.
QEVendorID [16]byte `json:"qeVendorID,omitempty"`
// XFAM is the latest XFAM field.
XFAM [8]byte `json:"xfam,omitempty"`
}
// VersionAPIEntry is the request to get the version information of the specific version in the config api.
//
// TODO: Because variant is not part of the marshalled JSON, fetcher and client methods need to fill the variant property.
// In API v2 we should embed the variant in the object and remove some code from fetcher & client.
// That would remove the possibility of some fetcher/client code forgetting to set the variant.
type VersionAPIEntry struct {
Version string `json:"-"`
Variant variant.Variant `json:"-"`
SEVSNPVersion
TDXVersion
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i VersionAPIEntry) JSONPath() string {
return path.Join(AttestationURLPath, i.Variant.String(), i.Version)
}
// ValidateRequest validates the request.
func (i VersionAPIEntry) ValidateRequest() error {
if !strings.HasSuffix(i.Version, ".json") {
return fmt.Errorf("version has no .json suffix")
}
return nil
}
// Validate is a No-Op at the moment.
func (i VersionAPIEntry) Validate() error {
return nil
}
// VersionList is the request to retrieve of all versions in the API for one attestation variant.
//
// TODO: Because variant is not part of the marshalled JSON, fetcher and client methods need to fill the variant property.
// In API v2 we should embed the variant in the object and remove some code from fetcher & client.
// That would remove the possibility of some fetcher/client code forgetting to set the variant.
type VersionList struct {
Variant variant.Variant
List []string
}
// MarshalJSON marshals the i's list property to JSON.
func (i VersionList) MarshalJSON() ([]byte, error) {
return json.Marshal(i.List)
}
// UnmarshalJSON unmarshals a list of strings into i's list property.
func (i *VersionList) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &i.List)
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i VersionList) JSONPath() string {
return path.Join(AttestationURLPath, i.Variant.String(), "list")
}
// ValidateRequest is a NoOp as there is no input.
func (i VersionList) ValidateRequest() error {
return nil
}
// SortReverse sorts the list of versions in reverse order.
func (i *VersionList) SortReverse() {
sort.Sort(sort.Reverse(sort.StringSlice(i.List)))
}
// AddVersion adds new to i's list and sorts the element in descending order.
func (i *VersionList) AddVersion(new string) {
i.List = append(i.List, new)
i.List = variant.RemoveDuplicate(i.List)
i.SortReverse()
}
// Validate validates the response.
func (i VersionList) Validate() error {
if len(i.List) < 1 {
return fmt.Errorf("no versions found in /list")
}
return nil
}

View File

@ -14,23 +14,23 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestSEVSNPVersionListMarshalUnmarshalJSON(t *testing.T) { func TestVersionListMarshalUnmarshalJSON(t *testing.T) {
tests := map[string]struct { tests := map[string]struct {
input SEVSNPVersionList input VersionList
output SEVSNPVersionList output VersionList
wantDiff bool wantDiff bool
}{ }{
"success": { "success": {
input: SEVSNPVersionList{List: []string{"v1", "v2"}}, input: VersionList{List: []string{"v1", "v2"}},
output: SEVSNPVersionList{List: []string{"v1", "v2"}}, output: VersionList{List: []string{"v1", "v2"}},
}, },
"variant is lost": { "variant is lost": {
input: SEVSNPVersionList{List: []string{"v1", "v2"}, Variant: variant.AzureSEVSNP{}}, input: VersionList{List: []string{"v1", "v2"}, Variant: variant.AzureSEVSNP{}},
output: SEVSNPVersionList{List: []string{"v1", "v2"}}, output: VersionList{List: []string{"v1", "v2"}},
}, },
"wrong order": { "wrong order": {
input: SEVSNPVersionList{List: []string{"v1", "v2"}}, input: VersionList{List: []string{"v1", "v2"}},
output: SEVSNPVersionList{List: []string{"v2", "v1"}}, output: VersionList{List: []string{"v2", "v1"}},
wantDiff: true, wantDiff: true,
}, },
} }
@ -40,7 +40,7 @@ func TestSEVSNPVersionListMarshalUnmarshalJSON(t *testing.T) {
inputRaw, err := tc.input.MarshalJSON() inputRaw, err := tc.input.MarshalJSON()
require.NoError(t, err) require.NoError(t, err)
var actual SEVSNPVersionList var actual VersionList
err = actual.UnmarshalJSON(inputRaw) err = actual.UnmarshalJSON(inputRaw)
require.NoError(t, err) require.NoError(t, err)
@ -53,7 +53,7 @@ func TestSEVSNPVersionListMarshalUnmarshalJSON(t *testing.T) {
} }
} }
func TestSEVSNPVersionListAddVersion(t *testing.T) { func TestVersionListAddVersion(t *testing.T) {
tests := map[string]struct { tests := map[string]struct {
versions []string versions []string
new string new string
@ -68,7 +68,7 @@ func TestSEVSNPVersionListAddVersion(t *testing.T) {
for name, tc := range tests { for name, tc := range tests {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
v := SEVSNPVersionList{List: tc.versions} v := VersionList{List: tc.versions}
v.AddVersion(tc.new) v.AddVersion(tc.new)
assert.Equal(t, tc.expected, v.List) assert.Equal(t, tc.expected, v.List)

View File

@ -1051,18 +1051,8 @@ func getConfigAsMap(conf *Config, t *testing.T) (res configMap) {
type stubAttestationFetcher struct{} type stubAttestationFetcher struct{}
func (f stubAttestationFetcher) FetchSEVSNPVersionList(_ context.Context, _ attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) { func (f stubAttestationFetcher) FetchLatestVersion(_ context.Context, _ variant.Variant) (attestationconfigapi.VersionAPIEntry, error) {
return attestationconfigapi.SEVSNPVersionList{}, nil return attestationconfigapi.VersionAPIEntry{
}
func (f stubAttestationFetcher) FetchSEVSNPVersion(_ context.Context, _ attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{
SEVSNPVersion: testCfg,
}, nil
}
func (f stubAttestationFetcher) FetchLatestVersion(_ context.Context, _ variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{
SEVSNPVersion: testCfg, SEVSNPVersion: testCfg,
}, nil }, nil
} }

View File

@ -162,7 +162,7 @@ func (d *AttestationDataSource) Read(ctx context.Context, req datasource.ReadReq
insecureFetch := data.Insecure.ValueBool() insecureFetch := data.Insecure.ValueBool()
snpVersions := attestationconfigapi.SEVSNPVersionAPI{} snpVersions := attestationconfigapi.VersionAPIEntry{}
if attestationVariant.Equal(variant.AzureSEVSNP{}) || if attestationVariant.Equal(variant.AzureSEVSNP{}) ||
attestationVariant.Equal(variant.AWSSEVSNP{}) || attestationVariant.Equal(variant.AWSSEVSNP{}) ||
attestationVariant.Equal(variant.GCPSEVSNP{}) { attestationVariant.Equal(variant.GCPSEVSNP{}) {

View File

@ -137,7 +137,7 @@ 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.SEVSNPVersionAPI) (tfAttestation attestationAttribute, err error) { func convertToTfAttestation(attVar variant.Variant, snpVersions attestationconfigapi.VersionAPIEntry) (tfAttestation attestationAttribute, err error) {
tfAttestation = attestationAttribute{ tfAttestation = attestationAttribute{
Variant: attVar.String(), Variant: attVar.String(),
BootloaderVersion: snpVersions.Bootloader, BootloaderVersion: snpVersions.Bootloader,