From 3e91f1a53fb9fb8555247e24cac7c43d8bb45226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wei=C3=9Fe?= Date: Tue, 11 Jun 2024 13:44:07 +0200 Subject: [PATCH] Enable CLI verify with JSON output for Azure TDX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Weiße --- cli/internal/cmd/BUILD.bazel | 3 + cli/internal/cmd/verify.go | 66 ++++++++++++++------- internal/attestation/azure/tdx/issuer.go | 2 +- internal/attestation/azure/tdx/tdx.go | 3 +- internal/attestation/azure/tdx/validator.go | 2 +- 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/cli/internal/cmd/BUILD.bazel b/cli/internal/cmd/BUILD.bazel index 6f29a7965..c2decf328 100644 --- a/cli/internal/cmd/BUILD.bazel +++ b/cli/internal/cmd/BUILD.bazel @@ -111,6 +111,9 @@ go_library( "@io_k8s_sigs_yaml//:yaml", "@org_golang_x_mod//semver", "@org_golang_google_grpc//:grpc", + "@com_github_google_go_tdx_guest//abi", + "@com_github_google_go_tdx_guest//proto/tdx", + "//internal/attestation/azure/tdx", ] + select({ "@io_bazel_rules_go//go/platform:android_amd64": [ "@org_golang_x_sys//unix", diff --git a/cli/internal/cmd/verify.go b/cli/internal/cmd/verify.go index 9ba5ed546..39021a595 100644 --- a/cli/internal/cmd/verify.go +++ b/cli/internal/cmd/verify.go @@ -21,10 +21,9 @@ import ( "strconv" "strings" - tpmProto "github.com/google/go-tpm-tools/proto/tpm" - "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/atls" + azuretdx "github.com/edgelesssys/constellation/v2/internal/attestation/azure/tdx" "github.com/edgelesssys/constellation/v2/internal/attestation/choose" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/snp" @@ -38,6 +37,10 @@ import ( "github.com/edgelesssys/constellation/v2/internal/grpc/dialer" "github.com/edgelesssys/constellation/v2/internal/verify" "github.com/edgelesssys/constellation/v2/verify/verifyproto" + + "github.com/google/go-tdx-guest/abi" + "github.com/google/go-tdx-guest/proto/tdx" + tpmProto "github.com/google/go-tpm-tools/proto/tpm" "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -189,8 +192,9 @@ func (c *verifyCmd) verify(cmd *cobra.Command, verifyClient verifyClient, config case "json": if !(attConfig.GetVariant().Equal(variant.AzureSEVSNP{}) || attConfig.GetVariant().Equal(variant.AWSSEVSNP{}) || - attConfig.GetVariant().Equal(variant.GCPSEVSNP{})) { - return errors.New("json output is only supported for SEV-SNP") + attConfig.GetVariant().Equal(variant.GCPSEVSNP{}) || + attConfig.GetVariant().Equal(variant.AzureTDX{})) { + return errors.New("json output is only supported for SEV-SNP and TDX variants") } attDocOutput, err = formatJSON(cmd.Context(), rawAttestationDoc, attConfig, c.log) @@ -247,13 +251,24 @@ func (c *verifyCmd) validateEndpointFlag(cmd *cobra.Command, stateFile *state.St // formatJSON returns the json formatted attestation doc. func formatJSON(ctx context.Context, docString string, attestationCfg config.AttestationCfg, log debugLog, ) (string, error) { - var doc attestationDoc + var doc vtpm.AttestationDocument if err := json.Unmarshal([]byte(docString), &doc); err != nil { return "", fmt.Errorf("unmarshalling attestation document: %w", err) } + if (attestationCfg.GetVariant().Equal(variant.AWSSEVSNP{}) || + attestationCfg.GetVariant().Equal(variant.AzureSEVSNP{}) || + attestationCfg.GetVariant().Equal(variant.GCPSEVSNP{})) { + return snpFormatJSON(ctx, doc.InstanceInfo, attestationCfg, log) + } + + return tdxFormatJSON(doc.InstanceInfo, attestationCfg) +} + +func snpFormatJSON(ctx context.Context, instanceInfoRaw []byte, attestationCfg config.AttestationCfg, log debugLog, +) (string, error) { var instanceInfo snp.InstanceInfo - if err := json.Unmarshal(doc.InstanceInfo, &instanceInfo); err != nil { + if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil { return "", fmt.Errorf("unmarshalling instance info: %w", err) } report, err := verify.NewReport(ctx, instanceInfo, attestationCfg, log) @@ -262,17 +277,40 @@ func formatJSON(ctx context.Context, docString string, attestationCfg config.Att } jsonBytes, err := json.Marshal(report) - return string(jsonBytes), err } +func tdxFormatJSON(instanceInfoRaw []byte, attestationCfg config.AttestationCfg) (string, error) { + var rawQuote []byte + + if attestationCfg.GetVariant().Equal(variant.AzureTDX{}) { + var instanceInfo azuretdx.InstanceInfo + if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil { + return "", fmt.Errorf("unmarshalling instance info: %w", err) + } + rawQuote = instanceInfo.AttestationReport + } + + tdxQuote, err := abi.QuoteToProto(rawQuote) + if err != nil { + return "", fmt.Errorf("converting quote to proto: %w", err) + } + quote, ok := tdxQuote.(*tdx.QuoteV4) + if !ok { + return "", fmt.Errorf("unexpected quote type: %T", tdxQuote) + } + + quoteJSON, err := json.Marshal(quote) + return string(quoteJSON), err +} + // format returns the formatted attestation doc. func formatDefault(ctx context.Context, docString string, attestationCfg config.AttestationCfg, log debugLog, ) (string, error) { b := &strings.Builder{} b.WriteString("Attestation Document:\n") - var doc attestationDoc + var doc vtpm.AttestationDocument if err := json.Unmarshal([]byte(docString), &doc); err != nil { return "", fmt.Errorf("unmarshal attestation document: %w", err) } @@ -330,18 +368,6 @@ func parseQuotes(b *strings.Builder, quotes []*tpmProto.Quote, expectedPCRs meas return nil } -// attestationDoc is the attestation document returned by the verifier. -type attestationDoc struct { - Attestation struct { - AkPub string `json:"ak_pub"` - Quotes []*tpmProto.Quote `json:"quotes"` - EventLog string `json:"event_log"` - TeeAttestation interface{} `json:"TeeAttestation"` - } `json:"Attestation"` - InstanceInfo []byte `json:"InstanceInfo"` - UserData []byte `json:"UserData"` -} - type constellationVerifier struct { dialer grpcInsecureDialer log debugLog diff --git a/internal/attestation/azure/tdx/issuer.go b/internal/attestation/azure/tdx/issuer.go index e04b066a6..082616635 100644 --- a/internal/attestation/azure/tdx/issuer.go +++ b/internal/attestation/azure/tdx/issuer.go @@ -90,7 +90,7 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _ return nil, fmt.Errorf("getting quote: %w", err) } - instanceInfo := instanceInfo{ + instanceInfo := InstanceInfo{ AttestationReport: quote, RuntimeData: runtimeData, } diff --git a/internal/attestation/azure/tdx/tdx.go b/internal/attestation/azure/tdx/tdx.go index 815a43ae2..eaee6161a 100644 --- a/internal/attestation/azure/tdx/tdx.go +++ b/internal/attestation/azure/tdx/tdx.go @@ -19,7 +19,8 @@ More specifically: */ package tdx -type instanceInfo struct { +// InstanceInfo wraps the TDX report with additional Azure specific runtime data. +type InstanceInfo struct { AttestationReport []byte RuntimeData []byte } diff --git a/internal/attestation/azure/tdx/validator.go b/internal/attestation/azure/tdx/validator.go index 5b090dae9..02a8d3d6d 100644 --- a/internal/attestation/azure/tdx/validator.go +++ b/internal/attestation/azure/tdx/validator.go @@ -58,7 +58,7 @@ func NewValidator(cfg *config.AzureTDX, log attestation.Logger) *Validator { } func (v *Validator) getTrustedTPMKey(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) { - var instanceInfo instanceInfo + var instanceInfo InstanceInfo if err := json.Unmarshal(attDoc.InstanceInfo, &instanceInfo); err != nil { return nil, err }