/* Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ package measurements import ( "context" "fmt" "net/http" "github.com/edgelesssys/constellation/v2/internal/api/versionsapi" "github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/sigstore" "github.com/edgelesssys/constellation/v2/internal/sigstore/keyselect" ) // RekorError is returned when verifying measurements with Rekor fails. type RekorError struct { err error } // Error returns the error message. func (e *RekorError) Error() string { return fmt.Sprintf("verifying measurements with Rekor failed: %s", e.err) } // Unwrap returns the wrapped error. func (e *RekorError) Unwrap() error { return e.err } // VerifyFetcher is a high-level fetcher that fetches measurements and verifies them. type VerifyFetcher struct { client *http.Client newCosignVerifier cosignVerifierConstructor rekor rekorVerifier } // NewVerifyFetcher creates a new MeasurementFetcher. func NewVerifyFetcher(newCosignVerifier func([]byte) (sigstore.Verifier, error), rekor rekorVerifier, client *http.Client) *VerifyFetcher { return &VerifyFetcher{ newCosignVerifier: newCosignVerifier, rekor: rekor, client: client, } } // FetchAndVerifyMeasurements fetches and verifies measurements for the given version and attestation variant. func (m *VerifyFetcher) FetchAndVerifyMeasurements(ctx context.Context, image string, csp cloudprovider.Provider, attestationVariant variant.Variant, noVerify bool, ) (M, error) { version, err := versionsapi.NewVersionFromShortPath(image, versionsapi.VersionKindImage) if err != nil { return nil, fmt.Errorf("parsing image version: %w", err) } publicKey, err := keyselect.CosignPublicKeyForVersion(version) if err != nil { return nil, fmt.Errorf("getting public key: %w", err) } cosign, err := m.newCosignVerifier(publicKey) if err != nil { return nil, fmt.Errorf("creating cosign verifier: %w", err) } measurementsURL, signatureURL, err := versionsapi.MeasurementURL(version) if err != nil { return nil, err } var fetchedMeasurements M if noVerify { if err := fetchedMeasurements.FetchNoVerify( ctx, m.client, measurementsURL, version, csp, attestationVariant, ); err != nil { return nil, fmt.Errorf("fetching measurements: %w", err) } } else { hash, err := fetchedMeasurements.FetchAndVerify( ctx, m.client, cosign, measurementsURL, signatureURL, version, csp, attestationVariant, ) if err != nil { return nil, fmt.Errorf("fetching and verifying measurements: %w", err) } if err := sigstore.VerifyWithRekor(ctx, publicKey, m.rekor, hash); err != nil { return nil, &RekorError{err: err} } } return fetchedMeasurements, nil } type cosignVerifierConstructor func([]byte) (sigstore.Verifier, error) type rekorVerifier interface { SearchByHash(context.Context, string) ([]string, error) VerifyEntry(context.Context, string, string) error }