mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 23:19:39 -05:00
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:
parent
e0f52b4acd
commit
9d99d05826
@ -114,6 +114,8 @@ go_library(
|
|||||||
"@com_github_google_go_tdx_guest//abi",
|
"@com_github_google_go_tdx_guest//abi",
|
||||||
"@com_github_google_go_tdx_guest//proto/tdx",
|
"@com_github_google_go_tdx_guest//proto/tdx",
|
||||||
"//internal/attestation/azure/tdx",
|
"//internal/attestation/azure/tdx",
|
||||||
|
"@com_github_google_go_sev_guest//proto/sevsnp",
|
||||||
|
"@com_github_google_go_tpm_tools//proto/attest",
|
||||||
] + 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",
|
||||||
|
@ -38,8 +38,10 @@ import (
|
|||||||
"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-sev-guest/proto/sevsnp"
|
||||||
"github.com/google/go-tdx-guest/abi"
|
"github.com/google/go-tdx-guest/abi"
|
||||||
"github.com/google/go-tdx-guest/proto/tdx"
|
"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"
|
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"
|
||||||
@ -191,16 +193,22 @@ func (c *verifyCmd) verify(cmd *cobra.Command, verifyClient verifyClient, config
|
|||||||
switch c.flags.output {
|
switch c.flags.output {
|
||||||
case "json":
|
case "json":
|
||||||
attDocOutput, err = formatJSON(cmd.Context(), rawAttestationDoc, attConfig, c.log)
|
attDocOutput, err = formatJSON(cmd.Context(), rawAttestationDoc, attConfig, c.log)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("printing attestation document: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
case "raw":
|
case "raw":
|
||||||
attDocOutput = fmt.Sprintf("Attestation Document:\n%s\n", rawAttestationDoc)
|
attDocOutput = fmt.Sprintf("Attestation Document:\n%s\n", rawAttestationDoc)
|
||||||
|
|
||||||
case "":
|
case "":
|
||||||
attDocOutput, err = formatDefault(cmd.Context(), rawAttestationDoc, attConfig, c.log)
|
attDocOutput, err = formatDefault(cmd.Context(), rawAttestationDoc, attConfig, c.log)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("printing attestation document: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid output value for formatter: %s", c.flags.output)
|
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.Println(attDocOutput)
|
||||||
cmd.PrintErrln("Verification OK")
|
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.
|
// 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) {
|
) (string, error) {
|
||||||
var doc vtpm.AttestationDocument
|
doc, err := unmarshalAttDoc(docString, attestationCfg.GetVariant())
|
||||||
if err := json.Unmarshal([]byte(docString), &doc); err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unmarshalling attestation document: %w", err)
|
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.
|
// 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) {
|
) (string, error) {
|
||||||
b := &strings.Builder{}
|
b := &strings.Builder{}
|
||||||
b.WriteString("Attestation Document:\n")
|
b.WriteString("Attestation Document:\n")
|
||||||
|
|
||||||
var doc vtpm.AttestationDocument
|
doc, err := unmarshalAttDoc(docString, attestationCfg.GetVariant())
|
||||||
if err := json.Unmarshal([]byte(docString), &doc); err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unmarshal attestation document: %w", err)
|
return "", fmt.Errorf("unmarshalling attestation document: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := parseQuotes(b, doc.Attestation.Quotes, attestationCfg.GetMeasurements()); err != nil {
|
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.
|
// Verify retrieves an attestation statement from the Constellation and verifies it using the validator.
|
||||||
func (v *constellationVerifier) Verify(
|
func (v *constellationVerifier) Verify(
|
||||||
ctx context.Context, endpoint string, req *verifyproto.GetAttestationRequest, validator atls.Validator,
|
ctx context.Context, endpoint string, req *verifyproto.GetAttestationRequest, validator atls.Validator,
|
||||||
) (string, error) {
|
) ([]byte, error) {
|
||||||
v.log.Debug(fmt.Sprintf("Dialing endpoint: %q", endpoint))
|
v.log.Debug(fmt.Sprintf("Dialing endpoint: %q", endpoint))
|
||||||
conn, err := v.dialer.DialInsecure(ctx, endpoint)
|
conn, err := v.dialer.DialInsecure(ctx, endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("dialing init server: %w", err)
|
return nil, fmt.Errorf("dialing init server: %w", err)
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
@ -383,24 +391,24 @@ func (v *constellationVerifier) Verify(
|
|||||||
v.log.Debug("Sending attestation request")
|
v.log.Debug("Sending attestation request")
|
||||||
resp, err := client.GetAttestation(ctx, req)
|
resp, err := client.GetAttestation(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("getting attestation: %w", err)
|
return nil, fmt.Errorf("getting attestation: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.log.Debug("Verifying attestation")
|
v.log.Debug("Verifying attestation")
|
||||||
signedData, err := validator.Validate(ctx, resp.Attestation, req.Nonce)
|
signedData, err := validator.Validate(ctx, resp.Attestation, req.Nonce)
|
||||||
if err != nil {
|
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)) {
|
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 {
|
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 {
|
type grpcInsecureDialer interface {
|
||||||
@ -515,3 +523,26 @@ func decodeMeasurement(encoded string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return decoded, nil
|
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
|
||||||
|
}
|
||||||
|
@ -208,18 +208,20 @@ func TestVerify(t *testing.T) {
|
|||||||
|
|
||||||
func TestFormatDefault(t *testing.T) {
|
func TestFormatDefault(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
doc string
|
doc []byte
|
||||||
|
attCfg config.AttestationCfg
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"invalid doc": {
|
"invalid doc": {
|
||||||
doc: "invalid",
|
doc: []byte("invalid"),
|
||||||
|
attCfg: &config.AzureSEVSNP{},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, tc := range testCases {
|
for name, tc := range testCases {
|
||||||
t.Run(name, func(t *testing.T) {
|
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 {
|
if tc.wantErr {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
} else {
|
} else {
|
||||||
@ -313,9 +315,9 @@ type stubVerifyClient struct {
|
|||||||
endpoint string
|
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
|
c.endpoint = endpoint
|
||||||
return "", c.verifyErr
|
return nil, c.verifyErr
|
||||||
}
|
}
|
||||||
|
|
||||||
type stubVerifyAPI struct {
|
type stubVerifyAPI struct {
|
||||||
|
2
go.mod
2
go.mod
@ -79,7 +79,7 @@ require (
|
|||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/google/go-sev-guest v0.11.1
|
github.com/google/go-sev-guest v0.11.1
|
||||||
github.com/google/go-tdx-guest v0.3.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/go-tpm-tools v0.4.4
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/googleapis/gax-go/v2 v2.12.4
|
github.com/googleapis/gax-go/v2 v2.12.4
|
||||||
|
4
go.sum
4
go.sum
@ -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-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 h1:gl0KvjdsD4RrJzyLefDOvFOUH3NAJri/3qvaL5m83Iw=
|
||||||
github.com/google/go-tdx-guest v0.3.1/go.mod h1:/rc3d7rnPykOPuY8U9saMyEps0PZDThLk/RygXm04nE=
|
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 h1:0pGc4X//bAlmZzMKf8iz6IsDo1nYTbYJ6FZN/rg4zdM=
|
||||||
github.com/google/go-tpm v0.9.1-0.20240510201744-5c2f0887e003/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
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 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
|
||||||
github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
|
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=
|
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
||||||
|
Loading…
Reference in New Issue
Block a user