2023-06-07 10:16:32 -04:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
|
|
|
package attestationconfigapi
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-09-25 05:53:02 -04:00
|
|
|
"errors"
|
2023-06-07 10:16:32 -04:00
|
|
|
"fmt"
|
|
|
|
|
|
|
|
apifetcher "github.com/edgelesssys/constellation/v2/internal/api/fetcher"
|
2023-11-14 04:03:01 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
2023-06-07 10:16:32 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
|
|
|
"github.com/edgelesssys/constellation/v2/internal/sigstore"
|
|
|
|
)
|
|
|
|
|
|
|
|
const cosignPublicKey = constants.CosignPublicKeyReleases
|
|
|
|
|
2023-09-25 05:53:02 -04:00
|
|
|
// ErrNoVersionsFound is returned if no versions are found.
|
|
|
|
var ErrNoVersionsFound = errors.New("no versions found")
|
|
|
|
|
2023-06-07 10:16:32 -04:00
|
|
|
// Fetcher fetches config API resources without authentication.
|
|
|
|
type Fetcher interface {
|
2023-11-14 04:03:01 -05:00
|
|
|
FetchSEVSNPVersion(ctx context.Context, version SEVSNPVersionAPI) (SEVSNPVersionAPI, error)
|
|
|
|
FetchSEVSNPVersionList(ctx context.Context, list SEVSNPVersionList) (SEVSNPVersionList, error)
|
2023-11-28 11:30:11 -05:00
|
|
|
FetchSEVSNPVersionLatest(ctx context.Context, attestation variant.Variant) (SEVSNPVersionAPI, error)
|
2023-06-07 10:16:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// fetcher fetches AttestationCfg API resources without authentication.
|
|
|
|
type fetcher struct {
|
|
|
|
apifetcher.HTTPClient
|
2023-09-25 05:53:02 -04:00
|
|
|
cdnURL string
|
2023-06-09 06:48:12 -04:00
|
|
|
verifier sigstore.Verifier
|
2023-06-07 10:16:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewFetcher returns a new apifetcher.
|
|
|
|
func NewFetcher() Fetcher {
|
2023-09-25 05:53:02 -04:00
|
|
|
return NewFetcherWithClient(apifetcher.NewHTTPClient(), constants.CDNRepositoryURL)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFetcherWithCustomCDNAndCosignKey returns a new fetcher with custom CDN URL.
|
|
|
|
func NewFetcherWithCustomCDNAndCosignKey(cdnURL, cosignKey string) Fetcher {
|
|
|
|
verifier, err := sigstore.NewCosignVerifier([]byte(cosignKey))
|
|
|
|
if err != nil {
|
|
|
|
// This relies on an embedded public key. If this key can not be validated, there is no way to recover from this.
|
|
|
|
panic(fmt.Errorf("creating cosign verifier: %w", err))
|
|
|
|
}
|
|
|
|
return newFetcherWithClientAndVerifier(apifetcher.NewHTTPClient(), verifier, cdnURL)
|
2023-06-07 10:16:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewFetcherWithClient returns a new fetcher with custom http client.
|
2023-09-25 05:53:02 -04:00
|
|
|
func NewFetcherWithClient(client apifetcher.HTTPClient, cdnURL string) Fetcher {
|
2023-08-01 10:48:13 -04:00
|
|
|
verifier, err := sigstore.NewCosignVerifier([]byte(cosignPublicKey))
|
|
|
|
if err != nil {
|
|
|
|
// This relies on an embedded public key. If this key can not be validated, there is no way to recover from this.
|
|
|
|
panic(fmt.Errorf("creating cosign verifier: %w", err))
|
|
|
|
}
|
2023-09-25 05:53:02 -04:00
|
|
|
return newFetcherWithClientAndVerifier(client, verifier, cdnURL)
|
2023-06-09 06:48:12 -04:00
|
|
|
}
|
|
|
|
|
2023-09-25 05:53:02 -04:00
|
|
|
func newFetcherWithClientAndVerifier(client apifetcher.HTTPClient, cosignVerifier sigstore.Verifier, url string) Fetcher {
|
|
|
|
return &fetcher{HTTPClient: client, verifier: cosignVerifier, cdnURL: url}
|
2023-06-07 10:16:32 -04:00
|
|
|
}
|
|
|
|
|
2023-11-14 04:03:01 -05:00
|
|
|
// FetchSEVSNPVersionList fetches the version list information from the config API.
|
|
|
|
func (f *fetcher) FetchSEVSNPVersionList(ctx context.Context, list SEVSNPVersionList) (SEVSNPVersionList, error) {
|
2023-09-08 15:15:02 -04:00
|
|
|
// TODO(derpsteb): Replace with FetchAndVerify once we move to v2 of the config API.
|
2023-11-14 04:03:01 -05:00
|
|
|
fetchedList, err := apifetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, list)
|
|
|
|
if err != nil {
|
|
|
|
return list, fmt.Errorf("fetching version list: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Need to set this explicitly as the variant is not part of the marshalled JSON.
|
|
|
|
fetchedList.variant = list.variant
|
|
|
|
|
|
|
|
return fetchedList, nil
|
2023-06-07 10:16:32 -04:00
|
|
|
}
|
|
|
|
|
2023-11-14 04:03:01 -05:00
|
|
|
// FetchSEVSNPVersion fetches the version information from the config API.
|
|
|
|
func (f *fetcher) FetchSEVSNPVersion(ctx context.Context, version SEVSNPVersionAPI) (SEVSNPVersionAPI, error) {
|
|
|
|
fetchedVersion, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, f.cdnURL, version, f.verifier)
|
2023-06-07 10:16:32 -04:00
|
|
|
if err != nil {
|
2023-11-14 04:03:01 -05:00
|
|
|
return fetchedVersion, fmt.Errorf("fetching version %s: %w", version.Version, err)
|
2023-06-07 10:16:32 -04:00
|
|
|
}
|
2023-11-14 04:03:01 -05:00
|
|
|
|
|
|
|
// Need to set this explicitly as the variant is not part of the marshalled JSON.
|
|
|
|
fetchedVersion.Variant = version.Variant
|
|
|
|
|
2023-06-07 10:16:32 -04:00
|
|
|
return fetchedVersion, nil
|
|
|
|
}
|
|
|
|
|
2023-11-14 04:03:01 -05:00
|
|
|
// FetchSEVSNPVersionLatest returns the latest versions of the given type.
|
|
|
|
func (f *fetcher) FetchSEVSNPVersionLatest(ctx context.Context, attesation variant.Variant) (res SEVSNPVersionAPI, err error) {
|
|
|
|
list, err := f.FetchSEVSNPVersionList(ctx, SEVSNPVersionList{variant: attesation})
|
2023-06-07 10:16:32 -04:00
|
|
|
if err != nil {
|
2023-09-25 05:53:02 -04:00
|
|
|
return res, ErrNoVersionsFound
|
2023-06-07 10:16:32 -04:00
|
|
|
}
|
2023-11-14 04:03:01 -05:00
|
|
|
|
|
|
|
getVersionRequest := SEVSNPVersionAPI{
|
|
|
|
Version: list.List()[0], // latest version is first in list
|
|
|
|
Variant: attesation,
|
2023-06-09 06:48:12 -04:00
|
|
|
}
|
2023-11-14 04:03:01 -05:00
|
|
|
res, err = f.FetchSEVSNPVersion(ctx, getVersionRequest)
|
2023-06-07 10:16:32 -04:00
|
|
|
if err != nil {
|
2023-09-25 05:53:02 -04:00
|
|
|
return res, err
|
2023-06-07 10:16:32 -04:00
|
|
|
}
|
2023-06-09 06:48:12 -04:00
|
|
|
return
|
|
|
|
}
|