From 1cf267155e1c07089b3a2cad53d9ae884b07dc51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wei=C3=9Fe?= Date: Tue, 9 Jul 2024 11:00:26 +0200 Subject: [PATCH] Add compare command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Weiße --- .../cli/client/reporter.go | 40 ++++---- .../api/attestationconfigapi/cli/compare.go | 93 +++++++++++++++++++ 2 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 internal/api/attestationconfigapi/cli/compare.go diff --git a/internal/api/attestationconfigapi/cli/client/reporter.go b/internal/api/attestationconfigapi/cli/client/reporter.go index 7e40d6a3e..295c7b2b7 100644 --- a/internal/api/attestationconfigapi/cli/client/reporter.go +++ b/internal/api/attestationconfigapi/cli/client/reporter.go @@ -31,6 +31,25 @@ func reportVersionDir(attestation variant.Variant) string { return path.Join(attestationconfigapi.AttestationURLPath, attestation.String(), cachedVersionsSubDir) } +// IsInputNewerThanOtherVersion compares the input version with the other version and returns true if the input version is newer. +// This function panics if the input versions are not TDX or SEV-SNP versions. +func IsInputNewerThanOtherVersion(variant variant.Variant, inputVersion, otherVersion any) bool { + var result bool + actionForVariant(variant, + func() { + input := inputVersion.(attestationconfigapi.TDXVersion) + other := otherVersion.(attestationconfigapi.TDXVersion) + result = isInputNewerThanOtherTDXVersion(input, other) + }, + func() { + input := inputVersion.(attestationconfigapi.SEVSNPVersion) + other := otherVersion.(attestationconfigapi.SEVSNPVersion) + result = isInputNewerThanOtherSEVSNPVersion(input, other) + }, + ) + return result +} + // UploadLatestVersion saves the given version to the cache, determines the smallest // TCB version in the cache among the last cacheWindowSize versions and updates // the latest version in the API if there is an update. @@ -90,7 +109,7 @@ func (c Client) UploadLatestVersion( } c.log.Info(fmt.Sprintf("Found minimal version: %+v with date: %s", minVersion, minDate)) - if !isInputNewerThanOtherVersion(attestationVariant, minVersion, latestVersionInAPI) { + if !IsInputNewerThanOtherVersion(attestationVariant, minVersion, latestVersionInAPI) { c.log.Info(fmt.Sprintf("Input version: %+v is not newer than latest API version: %+v. Skipping list update", minVersion, latestVersionInAPI)) return ErrNoNewerVersion } @@ -200,7 +219,7 @@ func findMinimalVersion[T attestationconfigapi.TDXVersion | attestationconfigapi // If the current minimal version has newer versions than the one we just fetched, // update the minimal version to the older version. - if isInputNewerThanOtherVersion(variant, *minimalVersion, obj.getVersion()) { + if IsInputNewerThanOtherVersion(variant, *minimalVersion, obj.getVersion()) { v := obj.getVersion().(T) minimalVersion = &v minimalDate = date @@ -210,23 +229,6 @@ func findMinimalVersion[T attestationconfigapi.TDXVersion | attestationconfigapi return *minimalVersion, minimalDate, nil } -func isInputNewerThanOtherVersion(variant variant.Variant, inputVersion, otherVersion any) bool { - var result bool - actionForVariant(variant, - func() { - input := inputVersion.(attestationconfigapi.TDXVersion) - other := otherVersion.(attestationconfigapi.TDXVersion) - result = isInputNewerThanOtherTDXVersion(input, other) - }, - func() { - input := inputVersion.(attestationconfigapi.SEVSNPVersion) - other := otherVersion.(attestationconfigapi.SEVSNPVersion) - result = isInputNewerThanOtherSEVSNPVersion(input, other) - }, - ) - return result -} - type apiVersionObject struct { version string `json:"-"` variant variant.Variant `json:"-"` diff --git a/internal/api/attestationconfigapi/cli/compare.go b/internal/api/attestationconfigapi/cli/compare.go new file mode 100644 index 000000000..5741765c7 --- /dev/null +++ b/internal/api/attestationconfigapi/cli/compare.go @@ -0,0 +1,93 @@ +package main + +import ( + "fmt" + "slices" + + "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli/client" + "github.com/edgelesssys/constellation/v2/internal/attestation/variant" + "github.com/edgelesssys/constellation/v2/internal/file" + "github.com/edgelesssys/constellation/v2/internal/verify" + "github.com/google/go-tdx-guest/proto/tdx" + "github.com/spf13/afero" + "github.com/spf13/cobra" +) + +func newCompareCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "compare {aws-sev-snp|azure-sev-snp|azure-tdx|gcp-sev-snp} [FILE] [FILE] ...", + Short: "Compare two or more attestation reports and return the lowest version", + Long: "Compare two or more attestation reports and return the lowest version.", + Example: "cli compare azure-sev-snp report1.json report2.json", + Args: cobra.MatchAll(cobra.MinimumNArgs(3), isAttestationVariant(0)), + RunE: runCompare, + } + + return cmd +} + +func runCompare(cmd *cobra.Command, args []string) error { + variant, err := variant.FromString(args[0]) + if err != nil { + return fmt.Errorf("parsing variant: %w", err) + } + + return compare(cmd, variant, args[1:], file.NewHandler(afero.NewOsFs())) +} + +func compare(cmd *cobra.Command, attestationVariant variant.Variant, files []string, fs file.Handler) (retErr error) { + if !slices.Contains([]variant.Variant{variant.AWSSEVSNP{}, variant.AzureSEVSNP{}, variant.GCPSEVSNP{}, variant.AzureTDX{}}, attestationVariant) { + return fmt.Errorf("variant %s not supported", attestationVariant) + } + + lowestVersion, err := compareVersions(attestationVariant, files, fs) + if err != nil { + return fmt.Errorf("comparing versions: %w", err) + } + + cmd.Println(lowestVersion) + return nil +} + +func compareVersions(attestationVariant variant.Variant, files []string, fs file.Handler) (string, error) { + readReport := readSNPReport + if attestationVariant.Equal(variant.AzureTDX{}) { + readReport = readTDXReport + } + + lowestVersion := files[0] + lowestReport, err := readReport(files[0], fs) + if err != nil { + return "", fmt.Errorf("reading tdx report: %w", err) + } + + for _, file := range files[1:] { + report, err := readReport(file, fs) + if err != nil { + return "", fmt.Errorf("reading tdx report: %w", err) + } + + if client.IsInputNewerThanOtherVersion(attestationVariant, lowestReport, report) { + lowestVersion = file + lowestReport = report + } + } + + return lowestVersion, nil +} + +func readSNPReport(file string, fs file.Handler) (any, error) { + var report verify.Report + if err := fs.ReadJSON(file, &report); err != nil { + return nil, fmt.Errorf("reading snp report: %w", err) + } + return convertTCBVersionToSNPVersion(report.SNPReport.LaunchTCB), nil +} + +func readTDXReport(file string, fs file.Handler) (any, error) { + var report *tdx.QuoteV4 + if err := fs.ReadJSON(file, &report); err != nil { + return nil, fmt.Errorf("reading tdx report: %w", err) + } + return convertQuoteToTDXVersion(report), nil +}