Enable CLI verify with JSON output for Azure TDX

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2024-06-11 13:44:07 +02:00
parent 574a0812b0
commit 3e91f1a53f
No known key found for this signature in database
GPG key ID: 7DD3015F3DDE4B9C
5 changed files with 53 additions and 23 deletions

View file

@ -111,6 +111,9 @@ go_library(
"@io_k8s_sigs_yaml//:yaml", "@io_k8s_sigs_yaml//:yaml",
"@org_golang_x_mod//semver", "@org_golang_x_mod//semver",
"@org_golang_google_grpc//:grpc", "@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({ ] + select({
"@io_bazel_rules_go//go/platform:android_amd64": [ "@io_bazel_rules_go//go/platform:android_amd64": [
"@org_golang_x_sys//unix", "@org_golang_x_sys//unix",

View file

@ -21,10 +21,9 @@ import (
"strconv" "strconv"
"strings" "strings"
tpmProto "github.com/google/go-tpm-tools/proto/tpm"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/atls" "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/choose"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/snp" "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/grpc/dialer"
"github.com/edgelesssys/constellation/v2/internal/verify" "github.com/edgelesssys/constellation/v2/internal/verify"
"github.com/edgelesssys/constellation/v2/verify/verifyproto" "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/afero"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@ -189,8 +192,9 @@ func (c *verifyCmd) verify(cmd *cobra.Command, verifyClient verifyClient, config
case "json": case "json":
if !(attConfig.GetVariant().Equal(variant.AzureSEVSNP{}) || if !(attConfig.GetVariant().Equal(variant.AzureSEVSNP{}) ||
attConfig.GetVariant().Equal(variant.AWSSEVSNP{}) || attConfig.GetVariant().Equal(variant.AWSSEVSNP{}) ||
attConfig.GetVariant().Equal(variant.GCPSEVSNP{})) { attConfig.GetVariant().Equal(variant.GCPSEVSNP{}) ||
return errors.New("json output is only supported for SEV-SNP") 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) 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. // formatJSON returns the json formatted attestation doc.
func formatJSON(ctx context.Context, docString string, attestationCfg config.AttestationCfg, log debugLog, func formatJSON(ctx context.Context, docString string, attestationCfg config.AttestationCfg, log debugLog,
) (string, error) { ) (string, error) {
var doc attestationDoc var doc vtpm.AttestationDocument
if err := json.Unmarshal([]byte(docString), &doc); err != nil { if err := json.Unmarshal([]byte(docString), &doc); err != nil {
return "", fmt.Errorf("unmarshalling attestation document: %w", err) 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 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) return "", fmt.Errorf("unmarshalling instance info: %w", err)
} }
report, err := verify.NewReport(ctx, instanceInfo, attestationCfg, log) 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) jsonBytes, err := json.Marshal(report)
return string(jsonBytes), err 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. // format returns the formatted attestation doc.
func formatDefault(ctx context.Context, docString string, attestationCfg config.AttestationCfg, log debugLog, func formatDefault(ctx context.Context, docString string, attestationCfg config.AttestationCfg, log debugLog,
) (string, error) { ) (string, error) {
b := &strings.Builder{} b := &strings.Builder{}
b.WriteString("Attestation Document:\n") b.WriteString("Attestation Document:\n")
var doc attestationDoc var doc vtpm.AttestationDocument
if err := json.Unmarshal([]byte(docString), &doc); err != nil { if err := json.Unmarshal([]byte(docString), &doc); err != nil {
return "", fmt.Errorf("unmarshal attestation document: %w", err) return "", fmt.Errorf("unmarshal attestation document: %w", err)
} }
@ -330,18 +368,6 @@ func parseQuotes(b *strings.Builder, quotes []*tpmProto.Quote, expectedPCRs meas
return nil 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 { type constellationVerifier struct {
dialer grpcInsecureDialer dialer grpcInsecureDialer
log debugLog log debugLog

View file

@ -90,7 +90,7 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, _
return nil, fmt.Errorf("getting quote: %w", err) return nil, fmt.Errorf("getting quote: %w", err)
} }
instanceInfo := instanceInfo{ instanceInfo := InstanceInfo{
AttestationReport: quote, AttestationReport: quote,
RuntimeData: runtimeData, RuntimeData: runtimeData,
} }

View file

@ -19,7 +19,8 @@ More specifically:
*/ */
package tdx package tdx
type instanceInfo struct { // InstanceInfo wraps the TDX report with additional Azure specific runtime data.
type InstanceInfo struct {
AttestationReport []byte AttestationReport []byte
RuntimeData []byte RuntimeData []byte
} }

View file

@ -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) { 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 { if err := json.Unmarshal(attDoc.InstanceInfo, &instanceInfo); err != nil {
return nil, err return nil, err
} }