cli: fix unmarshalling of sev-snp attestation documents in constellation verify (#3171)

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2024-06-17 13:38:59 +02:00 committed by GitHub
parent e0f52b4acd
commit 9d99d05826
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 60 additions and 25 deletions

View File

@ -114,6 +114,8 @@ go_library(
"@com_github_google_go_tdx_guest//abi",
"@com_github_google_go_tdx_guest//proto/tdx",
"//internal/attestation/azure/tdx",
"@com_github_google_go_sev_guest//proto/sevsnp",
"@com_github_google_go_tpm_tools//proto/attest",
] + select({
"@io_bazel_rules_go//go/platform:android_amd64": [
"@org_golang_x_sys//unix",

View File

@ -38,8 +38,10 @@ import (
"github.com/edgelesssys/constellation/v2/internal/verify"
"github.com/edgelesssys/constellation/v2/verify/verifyproto"
"github.com/google/go-sev-guest/proto/sevsnp"
"github.com/google/go-tdx-guest/abi"
"github.com/google/go-tdx-guest/proto/tdx"
"github.com/google/go-tpm-tools/proto/attest"
tpmProto "github.com/google/go-tpm-tools/proto/tpm"
"github.com/spf13/afero"
"github.com/spf13/cobra"
@ -191,16 +193,22 @@ func (c *verifyCmd) verify(cmd *cobra.Command, verifyClient verifyClient, config
switch c.flags.output {
case "json":
attDocOutput, err = formatJSON(cmd.Context(), rawAttestationDoc, attConfig, c.log)
if err != nil {
return fmt.Errorf("printing attestation document: %w", err)
}
case "raw":
attDocOutput = fmt.Sprintf("Attestation Document:\n%s\n", rawAttestationDoc)
case "":
attDocOutput, err = formatDefault(cmd.Context(), rawAttestationDoc, attConfig, c.log)
if err != nil {
return fmt.Errorf("printing attestation document: %w", err)
}
default:
return fmt.Errorf("invalid output value for formatter: %s", c.flags.output)
}
if err != nil {
return fmt.Errorf("printing attestation document: %w", err)
}
cmd.Println(attDocOutput)
cmd.PrintErrln("Verification OK")
@ -242,10 +250,10 @@ 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,
func formatJSON(ctx context.Context, docString []byte, attestationCfg config.AttestationCfg, log debugLog,
) (string, error) {
var doc vtpm.AttestationDocument
if err := json.Unmarshal([]byte(docString), &doc); err != nil {
doc, err := unmarshalAttDoc(docString, attestationCfg.GetVariant())
if err != nil {
return "", fmt.Errorf("unmarshalling attestation document: %w", err)
}
@ -299,14 +307,14 @@ func tdxFormatJSON(instanceInfoRaw []byte, attestationCfg config.AttestationCfg)
}
// format returns the formatted attestation doc.
func formatDefault(ctx context.Context, docString string, attestationCfg config.AttestationCfg, log debugLog,
func formatDefault(ctx context.Context, docString []byte, attestationCfg config.AttestationCfg, log debugLog,
) (string, error) {
b := &strings.Builder{}
b.WriteString("Attestation Document:\n")
var doc vtpm.AttestationDocument
if err := json.Unmarshal([]byte(docString), &doc); err != nil {
return "", fmt.Errorf("unmarshal attestation document: %w", err)
doc, err := unmarshalAttDoc(docString, attestationCfg.GetVariant())
if err != nil {
return "", fmt.Errorf("unmarshalling attestation document: %w", err)
}
if err := parseQuotes(b, doc.Attestation.Quotes, attestationCfg.GetMeasurements()); err != nil {
@ -370,11 +378,11 @@ type constellationVerifier struct {
// Verify retrieves an attestation statement from the Constellation and verifies it using the validator.
func (v *constellationVerifier) Verify(
ctx context.Context, endpoint string, req *verifyproto.GetAttestationRequest, validator atls.Validator,
) (string, error) {
) ([]byte, error) {
v.log.Debug(fmt.Sprintf("Dialing endpoint: %q", endpoint))
conn, err := v.dialer.DialInsecure(ctx, endpoint)
if err != nil {
return "", fmt.Errorf("dialing init server: %w", err)
return nil, fmt.Errorf("dialing init server: %w", err)
}
defer conn.Close()
@ -383,24 +391,24 @@ func (v *constellationVerifier) Verify(
v.log.Debug("Sending attestation request")
resp, err := client.GetAttestation(ctx, req)
if err != nil {
return "", fmt.Errorf("getting attestation: %w", err)
return nil, fmt.Errorf("getting attestation: %w", err)
}
v.log.Debug("Verifying attestation")
signedData, err := validator.Validate(ctx, resp.Attestation, req.Nonce)
if err != nil {
return "", fmt.Errorf("validating attestation: %w", err)
return nil, fmt.Errorf("validating attestation: %w", err)
}
if !bytes.Equal(signedData, []byte(constants.ConstellationVerifyServiceUserData)) {
return "", errors.New("signed data in attestation does not match expected user data")
return nil, errors.New("signed data in attestation does not match expected user data")
}
return string(resp.Attestation), nil
return resp.Attestation, nil
}
type verifyClient interface {
Verify(ctx context.Context, endpoint string, req *verifyproto.GetAttestationRequest, validator atls.Validator) (string, error)
Verify(ctx context.Context, endpoint string, req *verifyproto.GetAttestationRequest, validator atls.Validator) ([]byte, error)
}
type grpcInsecureDialer interface {
@ -515,3 +523,26 @@ func decodeMeasurement(encoded string) ([]byte, error) {
}
return decoded, nil
}
func unmarshalAttDoc(attDocJSON []byte, attestationVariant variant.Variant) (vtpm.AttestationDocument, error) {
attDoc := vtpm.AttestationDocument{
Attestation: &attest.Attestation{},
}
// Explicitly initialize this struct, as TeeAttestation
// is a "oneof" protobuf field, which needs an explicit
// type to be set to be unmarshaled correctly.
switch attestationVariant {
case variant.AzureTDX{}:
attDoc.Attestation.TeeAttestation = &attest.Attestation_TdxAttestation{
TdxAttestation: &tdx.QuoteV4{},
}
default:
attDoc.Attestation.TeeAttestation = &attest.Attestation_SevSnpAttestation{
SevSnpAttestation: &sevsnp.Attestation{},
}
}
err := json.Unmarshal(attDocJSON, &attDoc)
return attDoc, err
}

View File

@ -208,18 +208,20 @@ func TestVerify(t *testing.T) {
func TestFormatDefault(t *testing.T) {
testCases := map[string]struct {
doc string
doc []byte
attCfg config.AttestationCfg
wantErr bool
}{
"invalid doc": {
doc: "invalid",
doc: []byte("invalid"),
attCfg: &config.AzureSEVSNP{},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
_, err := formatDefault(context.Background(), tc.doc, nil, logger.NewTest(t))
_, err := formatDefault(context.Background(), tc.doc, tc.attCfg, logger.NewTest(t))
if tc.wantErr {
assert.Error(t, err)
} else {
@ -313,9 +315,9 @@ type stubVerifyClient struct {
endpoint string
}
func (c *stubVerifyClient) Verify(_ context.Context, endpoint string, _ *verifyproto.GetAttestationRequest, _ atls.Validator) (string, error) {
func (c *stubVerifyClient) Verify(_ context.Context, endpoint string, _ *verifyproto.GetAttestationRequest, _ atls.Validator) ([]byte, error) {
c.endpoint = endpoint
return "", c.verifyErr
return nil, c.verifyErr
}
type stubVerifyAPI struct {

2
go.mod
View File

@ -79,7 +79,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/go-sev-guest v0.11.1
github.com/google/go-tdx-guest v0.3.1
github.com/google/go-tpm v0.9.1-0.20240510201744-5c2f0887e003
github.com/google/go-tpm v0.9.1
github.com/google/go-tpm-tools v0.4.4
github.com/google/uuid v1.6.0
github.com/googleapis/gax-go/v2 v2.12.4

4
go.sum
View File

@ -420,8 +420,8 @@ github.com/google/go-sev-guest v0.11.1 h1:gnww4U8fHV5DCPz4gykr1s8SEX1fFNcxCBy+vv
github.com/google/go-sev-guest v0.11.1/go.mod h1:qBOfb+JmgsUI3aUyzQoGC13Kpp9zwLeWvuyXmA9q77w=
github.com/google/go-tdx-guest v0.3.1 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw=
github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE=
github.com/google/go-tpm v0.9.1-0.20240510201744-5c2f0887e003 h1:gfGQAIxsEEAuYuFvjCGpDnTwisMJOz+rUfJMkk4yTmc=
github.com/google/go-tpm v0.9.1-0.20240510201744-5c2f0887e003/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm v0.9.1 h1:0pGc4X//bAlmZzMKf8iz6IsDo1nYTbYJ6FZN/rg4zdM=
github.com/google/go-tpm v0.9.1/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=