mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-23 22:49:43 -05:00
attestation: add SNP-based attestation for aws-sev-snp (#1916)
* config: move AMD root key to global constant * attestation: add SNP based attestation for aws * Always enable SNP, regardless of attestation type. * Make AWSNitroTPM default again There exists a bug in AWS SNP implementation where sometimes a host might not be able to produce valid SNP reports. Since we have to wait for AWS to fix this we are merging SNP attestation as opt-in feature.
This commit is contained in:
parent
94b21e11ad
commit
c7d12055d1
@ -2769,8 +2769,9 @@ def go_dependencies():
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable_global",
|
||||
importpath = "github.com/google/go-sev-guest",
|
||||
sum = "h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2btFtk0=",
|
||||
version = "v0.6.1",
|
||||
replace = "github.com/derpsteb/go-sev-guest",
|
||||
sum = "h1:rqEp/ttS4sPC6dNwdiX0A9smWyyPxGqa/0sqJhXDzTg=",
|
||||
version = "v0.0.0-20230612061930-77cc6c19fa1a",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_google_go_tpm",
|
||||
|
@ -143,7 +143,9 @@ func (c *Creator) createAWS(ctx context.Context, cl terraformClient, opts Create
|
||||
IAMProfileControlPlane: opts.Config.Provider.AWS.IAMProfileControlPlane,
|
||||
IAMProfileWorkerNodes: opts.Config.Provider.AWS.IAMProfileWorkerNodes,
|
||||
Debug: opts.Config.IsDebugCluster(),
|
||||
EnableSNP: opts.Config.GetAttestationConfig().GetVariant().Equal(variant.AWSSEVSNP{}),
|
||||
// We always want to use SNP machines. If the users decides to use NitroTPM attestation,
|
||||
// they will at least have runtime encryption.
|
||||
EnableSNP: true,
|
||||
}
|
||||
|
||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.AWS.String())), &vars); err != nil {
|
||||
|
@ -233,6 +233,7 @@ func parseTerraformUpgradeVars(cmd *cobra.Command, conf *config.Config, fetcher
|
||||
IAMProfileControlPlane: conf.Provider.AWS.IAMProfileControlPlane,
|
||||
IAMProfileWorkerNodes: conf.Provider.AWS.IAMProfileWorkerNodes,
|
||||
Debug: conf.IsDebugCluster(),
|
||||
// TODO (AB#3235): decide how to handle EnableSNP during upgrades.
|
||||
}
|
||||
return targets, vars, nil
|
||||
case cloudprovider.Azure:
|
||||
|
@ -75,7 +75,7 @@ constellation config generate {aws|azure|gcp|openstack|qemu|stackit} [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-a, --attestation string attestation variant to use {aws-sev-snp|aws-nitro-tpm|azure-sev-snp|azure-trustedlaunch|gcp-sev-es|qemu-vtpm}. If not specified, the default for the cloud provider is used
|
||||
-a, --attestation string attestation variant to use {aws-nitro-tpm|aws-sev-snp|azure-sev-snp|azure-trustedlaunch|gcp-sev-es|qemu-vtpm}. If not specified, the default for the cloud provider is used
|
||||
-f, --file string path to output file, or '-' for stdout (default "constellation-conf.yaml")
|
||||
-h, --help help for generate
|
||||
-k, --kubernetes string Kubernetes version to use in format MAJOR.MINOR (default "v1.26")
|
||||
|
4
go.mod
4
go.mod
@ -33,6 +33,8 @@ replace (
|
||||
|
||||
replace (
|
||||
github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api => ./operators/constellation-node-operator/api
|
||||
// We need to extend this PR and merge it to go back to upstream: https://github.com/google/go-sev-guest/pull/47
|
||||
github.com/google/go-sev-guest => github.com/derpsteb/go-sev-guest v0.0.0-20230612061930-77cc6c19fa1a
|
||||
github.com/google/go-tpm => github.com/thomasten/go-tpm v0.0.0-20230222180349-bb3cc5560299
|
||||
github.com/google/go-tpm-tools => github.com/daniel-weisse/go-tpm-tools v0.0.0-20230612131025-c1ddd5ded590
|
||||
)
|
||||
@ -229,7 +231,7 @@ require (
|
||||
github.com/google/go-attestation v0.4.4-0.20221011162210-17f9c05652a9 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-containerregistry v0.14.1-0.20230409045903-ed5c185df419 // indirect
|
||||
github.com/google/go-sev-guest v0.6.1 // indirect
|
||||
github.com/google/go-sev-guest v0.6.1
|
||||
github.com/google/go-tspi v0.3.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/logger v1.1.1 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -394,6 +394,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/derpsteb/go-sev-guest v0.0.0-20230612061930-77cc6c19fa1a h1:rqEp/ttS4sPC6dNwdiX0A9smWyyPxGqa/0sqJhXDzTg=
|
||||
github.com/derpsteb/go-sev-guest v0.0.0-20230612061930-77cc6c19fa1a/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q=
|
||||
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
@ -699,8 +701,6 @@ github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOm
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
|
||||
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
|
||||
github.com/google/go-sev-guest v0.6.1 h1:NajHkAaLqN9/aW7bCFSUplUMtDgk2+HcN7jC2btFtk0=
|
||||
github.com/google/go-sev-guest v0.6.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q=
|
||||
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
||||
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||
|
@ -4,6 +4,7 @@ load("//bazel/go:go_test.bzl", "go_test")
|
||||
go_library(
|
||||
name = "snp",
|
||||
srcs = [
|
||||
"errors.go",
|
||||
"issuer.go",
|
||||
"snp.go",
|
||||
"validator.go",
|
||||
@ -15,9 +16,9 @@ go_library(
|
||||
"//internal/attestation/variant",
|
||||
"//internal/attestation/vtpm",
|
||||
"//internal/config",
|
||||
"@com_github_aws_aws_sdk_go_v2_config//:config",
|
||||
"@com_github_aws_aws_sdk_go_v2_feature_ec2_imds//:imds",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_ec2//:ec2",
|
||||
"@com_github_google_go_sev_guest//abi",
|
||||
"@com_github_google_go_sev_guest//client",
|
||||
"@com_github_google_go_sev_guest//verify",
|
||||
"@com_github_google_go_tpm//tpm2",
|
||||
"@com_github_google_go_tpm_tools//client",
|
||||
"@com_github_google_go_tpm_tools//proto/attest",
|
||||
@ -30,6 +31,7 @@ go_test(
|
||||
"issuer_test.go",
|
||||
"validator_test.go",
|
||||
],
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":snp"],
|
||||
# keep
|
||||
gotags = select({
|
||||
@ -39,10 +41,7 @@ go_test(
|
||||
deps = [
|
||||
"//internal/attestation/simulator",
|
||||
"//internal/attestation/vtpm",
|
||||
"@com_github_aws_aws_sdk_go_v2_feature_ec2_imds//:imds",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_ec2//:ec2",
|
||||
"@com_github_aws_aws_sdk_go_v2_service_ec2//types",
|
||||
"@com_github_aws_smithy_go//middleware",
|
||||
"//internal/constants",
|
||||
"@com_github_google_go_tpm_tools//client",
|
||||
"@com_github_google_go_tpm_tools//proto/attest",
|
||||
"@com_github_stretchr_testify//assert",
|
||||
|
48
internal/attestation/aws/snp/errors.go
Normal file
48
internal/attestation/aws/snp/errors.go
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package snp
|
||||
|
||||
import "fmt"
|
||||
|
||||
// decodeError is used to signal an error during decoding of a public key.
|
||||
// It only wrapps an error.
|
||||
type decodeError struct {
|
||||
inner error
|
||||
}
|
||||
|
||||
// newDecodeError an error in a DecodeError.
|
||||
func newDecodeError(err error) *decodeError {
|
||||
return &decodeError{inner: err}
|
||||
}
|
||||
|
||||
func (e *decodeError) Error() string {
|
||||
return fmt.Sprintf("error decoding public key: %v", e.inner)
|
||||
}
|
||||
|
||||
func (e *decodeError) Unwrap() error {
|
||||
return e.inner
|
||||
}
|
||||
|
||||
// validationError is used to signal an invalid SNP report.
|
||||
// It only wrapps an error.
|
||||
// Used during testing to error conditions more precisely.
|
||||
type validationError struct {
|
||||
inner error
|
||||
}
|
||||
|
||||
// newValidationError wraps an error in a ValidationError.
|
||||
func newValidationError(err error) *validationError {
|
||||
return &validationError{inner: err}
|
||||
}
|
||||
|
||||
func (e *validationError) Error() string {
|
||||
return e.inner.Error()
|
||||
}
|
||||
|
||||
func (e *validationError) Unwrap() error {
|
||||
return e.inner
|
||||
}
|
@ -8,15 +8,17 @@ package snp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
|
||||
sevclient "github.com/google/go-sev-guest/client"
|
||||
"github.com/google/go-tpm-tools/client"
|
||||
tpmclient "github.com/google/go-tpm-tools/client"
|
||||
)
|
||||
@ -33,7 +35,7 @@ func NewIssuer(log attestation.Logger) *Issuer {
|
||||
Issuer: vtpm.NewIssuer(
|
||||
vtpm.OpenVTPM,
|
||||
getAttestationKey,
|
||||
getInstanceInfo(imds.New(imds.Options{})),
|
||||
getInstanceInfo,
|
||||
log,
|
||||
),
|
||||
}
|
||||
@ -49,18 +51,44 @@ func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) {
|
||||
return tpmAk, nil
|
||||
}
|
||||
|
||||
// getInstanceInfo returns information about the current instance using the aws Metadata SDK.
|
||||
// getInstanceInfo generates an extended SNP report, i.e. the report and any loaded certificates.
|
||||
// Report generation is triggered by sending ioctl syscalls to the SNP guest device, the AMD PSP generates the report.
|
||||
// The returned bytes will be written into the attestation document.
|
||||
func getInstanceInfo(client awsMetaData) func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) {
|
||||
return func(ctx context.Context, _ io.ReadWriteCloser, _ []byte) ([]byte, error) {
|
||||
ec2InstanceIdentityOutput, err := client.GetInstanceIdentityDocument(ctx, &imds.GetInstanceIdentityDocumentInput{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching instance identity document: %w", err)
|
||||
}
|
||||
return json.Marshal(ec2InstanceIdentityOutput.InstanceIdentityDocument)
|
||||
func getInstanceInfo(_ context.Context, tpm io.ReadWriteCloser, _ []byte) ([]byte, error) {
|
||||
tpmAk, err := client.AttestationKeyRSA(tpm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating RSA Endorsement key: %w", err)
|
||||
}
|
||||
|
||||
encoded, err := x509.MarshalPKIXPublicKey(tpmAk.PublicKey())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshalling public key: %w", err)
|
||||
}
|
||||
|
||||
akDigest := sha512.Sum512(encoded)
|
||||
|
||||
device, err := sevclient.OpenDevice()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("opening sev device: %w", err)
|
||||
}
|
||||
defer device.Close()
|
||||
|
||||
report, certs, err := sevclient.GetRawExtendedReportAtVmpl(device, akDigest, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting extended report: %w", err)
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(instanceInfo{Report: report, Certs: certs})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshalling instance info: %w", err)
|
||||
}
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
type awsMetaData interface {
|
||||
GetInstanceIdentityDocument(context.Context, *imds.GetInstanceIdentityDocumentInput, ...func(*imds.Options)) (*imds.GetInstanceIdentityDocumentOutput, error)
|
||||
type instanceInfo struct {
|
||||
// Report contains the marshalled AMD SEV-SNP Report.
|
||||
Report []byte
|
||||
// Certs contains the PEM encoded VLEK and ASK certificates, queried from the AMD PSP of the issuing party.
|
||||
Certs []byte
|
||||
}
|
||||
|
@ -7,12 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package snp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
|
||||
tpmclient "github.com/google/go-tpm-tools/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -27,7 +23,7 @@ func TestGetAttestationKey(t *testing.T) {
|
||||
require.NoError(err)
|
||||
defer tpm.Close()
|
||||
|
||||
// create the attestation ket in RSA format
|
||||
// create the attestation key in RSA format
|
||||
tpmAk, err := tpmclient.AttestationKeyRSA(tpm)
|
||||
assert.NoError(err)
|
||||
assert.NotNil(tpmAk)
|
||||
@ -40,79 +36,3 @@ func TestGetAttestationKey(t *testing.T) {
|
||||
// if everything worked fine, tpmAk and getAk are the same key
|
||||
assert.Equal(tpmAk, getAk)
|
||||
}
|
||||
|
||||
func TestGetInstanceInfo(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
client stubMetadataAPI
|
||||
wantErr bool
|
||||
}{
|
||||
"invalid region": {
|
||||
client: stubMetadataAPI{
|
||||
instanceDoc: imds.InstanceIdentityDocument{
|
||||
Region: "invalid-region",
|
||||
},
|
||||
instanceErr: errors.New("failed"),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"valid region": {
|
||||
client: stubMetadataAPI{
|
||||
instanceDoc: imds.InstanceIdentityDocument{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid imageID": {
|
||||
client: stubMetadataAPI{
|
||||
instanceDoc: imds.InstanceIdentityDocument{
|
||||
ImageID: "ami-fail",
|
||||
},
|
||||
instanceErr: errors.New("failed"),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"valid imageID": {
|
||||
client: stubMetadataAPI{
|
||||
instanceDoc: imds.InstanceIdentityDocument{
|
||||
ImageID: "ami-09e7c7f5617a47830",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
tpm, err := simulator.OpenSimulatedTPM()
|
||||
assert.NoError(err)
|
||||
defer tpm.Close()
|
||||
|
||||
instanceInfoFunc := getInstanceInfo(&tc.client)
|
||||
assert.NotNil(instanceInfoFunc)
|
||||
|
||||
info, err := instanceInfoFunc(context.Background(), tpm, nil)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Nil(info)
|
||||
} else {
|
||||
assert.Nil(err)
|
||||
assert.NotNil(info)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type stubMetadataAPI struct {
|
||||
instanceDoc imds.InstanceIdentityDocument
|
||||
instanceErr error
|
||||
}
|
||||
|
||||
func (c *stubMetadataAPI) GetInstanceIdentityDocument(context.Context, *imds.GetInstanceIdentityDocumentInput, ...func(*imds.Options)) (*imds.GetInstanceIdentityDocumentOutput, error) {
|
||||
output := &imds.InstanceIdentityDocument{}
|
||||
|
||||
return &imds.GetInstanceIdentityDocumentOutput{
|
||||
InstanceIdentityDocument: *output,
|
||||
ResultMetadata: middleware.Metadata{},
|
||||
}, c.instanceErr
|
||||
}
|
||||
|
@ -36,6 +36,10 @@ Thus, the hypervisor is still included in the trusted computing base.
|
||||
|
||||
This section explains abbreviations used in SNP implementation.
|
||||
|
||||
- Platform Security Processor (PSP)
|
||||
|
||||
- Certificate Revocation List (CRL)
|
||||
|
||||
- Attestation Key (AK)
|
||||
|
||||
- AMD Root Key (ARK)
|
||||
|
@ -7,95 +7,213 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package snp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
awsConfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/google/go-sev-guest/abi"
|
||||
"github.com/google/go-sev-guest/verify"
|
||||
"github.com/google/go-tpm-tools/proto/attest"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
)
|
||||
|
||||
// Validator for AWS TPM attestation.
|
||||
type Validator struct {
|
||||
// Embed variant to identify the Validator using varaint.OID().
|
||||
variant.AWSSEVSNP
|
||||
// Embed validator to implement Validate method for aTLS handshake.
|
||||
*vtpm.Validator
|
||||
getDescribeClient func(context.Context, string) (awsMetadataAPI, error)
|
||||
// AMD root key. Root of trust for the ASK used during report validation.
|
||||
ark *x509.Certificate
|
||||
// kdsClient gets an ASK from somewhere. kdsClient is required for testing.
|
||||
kdsClient askGetter
|
||||
// reportValidator validates a SNP report. reportValidator is required for testing.
|
||||
reportValidator snpReportValidator
|
||||
}
|
||||
|
||||
// NewValidator create a new Validator structure and returns it.
|
||||
func NewValidator(cfg *config.AWSSEVSNP, log attestation.Logger) *Validator {
|
||||
v := &Validator{}
|
||||
v := &Validator{
|
||||
ark: (*x509.Certificate)(&cfg.AMDRootKey),
|
||||
kdsClient: kdsClient{http.DefaultClient},
|
||||
reportValidator: awsValidator{},
|
||||
}
|
||||
|
||||
v.Validator = vtpm.NewValidator(
|
||||
cfg.Measurements,
|
||||
getTrustedKey,
|
||||
v.tpmEnabled,
|
||||
v.getTrustedKey,
|
||||
func(vtpm.AttestationDocument, *attest.MachineState) error { return nil },
|
||||
log,
|
||||
)
|
||||
v.getDescribeClient = getEC2Client
|
||||
return v
|
||||
}
|
||||
|
||||
// getTrustedKeys return the public area of the provides attestation key.
|
||||
// Normally, here the trust of this key should be verified, but currently AWS does not provide this feature.
|
||||
func getTrustedKey(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) {
|
||||
// Normally, the key should be verified here, but currently AWS does not provide means to do so.
|
||||
func (v *Validator) getTrustedKey(ctx context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) {
|
||||
// Copied from https://github.com/edgelesssys/constellation/blob/main/internal/attestation/qemu/validator.go
|
||||
pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, newDecodeError(err)
|
||||
}
|
||||
|
||||
pubKey, err := pubArea.Key()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting public key: %w", err)
|
||||
}
|
||||
|
||||
akDigest, err := sha512sum(pubKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calculating hash of attestation key: %w", err)
|
||||
}
|
||||
|
||||
if err := v.reportValidator.validate(ctx, attDoc, v.kdsClient, v.ark, akDigest); err != nil {
|
||||
return nil, fmt.Errorf("validating SNP report: %w", err)
|
||||
}
|
||||
|
||||
return pubArea.Key()
|
||||
}
|
||||
|
||||
// tpmEnabled verifies if the virtual machine has the tpm2.0 feature enabled.
|
||||
func (v *Validator) tpmEnabled(attestation vtpm.AttestationDocument, _ *attest.MachineState) error {
|
||||
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/verify-nitrotpm-support-on-ami.html
|
||||
// 1. Get the vm's ami (from IdentiTyDocument.imageId)
|
||||
// 2. Check the value of key "TpmSupport": {"Value": "v2.0"}"
|
||||
ctx := context.Background()
|
||||
|
||||
idDocument := imds.InstanceIdentityDocument{}
|
||||
err := json.Unmarshal(attestation.InstanceInfo, &idDocument)
|
||||
// sha512sum PEM-encodes a public key and calculates the SHA512 hash of the encoded key.
|
||||
func sha512sum(key crypto.PublicKey) ([64]byte, error) {
|
||||
pub, err := x509.MarshalPKIXPublicKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
return [64]byte{}, fmt.Errorf("marshalling public key: %w", err)
|
||||
}
|
||||
|
||||
imageID := idDocument.ImageID
|
||||
|
||||
client, err := v.getDescribeClient(ctx, idDocument.Region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Currently, there seems to be a problem with retrieving image attributes directly.
|
||||
// Alternatively, parse it from the general output.
|
||||
imageOutput, err := client.DescribeImages(ctx, &ec2.DescribeImagesInput{ImageIds: []string{imageID}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if imageOutput.Images[0].TpmSupport == "v2.0" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("iam image %s does not support TPM v2.0", imageID)
|
||||
return sha512.Sum512(pub), nil
|
||||
}
|
||||
|
||||
func getEC2Client(ctx context.Context, region string) (awsMetadataAPI, error) {
|
||||
client, err := awsConfig.LoadDefaultConfig(ctx, awsConfig.WithRegion(region))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ec2.NewFromConfig(client), nil
|
||||
// Validate a given SNP report.
|
||||
type snpReportValidator interface {
|
||||
validate(ctx context.Context, attestation vtpm.AttestationDocument, kdsClient askGetter, ark *x509.Certificate, ak [64]byte) error
|
||||
}
|
||||
|
||||
type awsMetadataAPI interface {
|
||||
DescribeImages(ctx context.Context, params *ec2.DescribeImagesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeImagesOutput, error)
|
||||
// Validation logic for the AWS SNP implementation.
|
||||
type awsValidator struct{}
|
||||
|
||||
// validate the report by checking if it has a valid VLEK signature.
|
||||
// The certificate chain ARK -> ASK -> VLEK is also validated.
|
||||
// Checks that the report's userData matches the connection's userData.
|
||||
func (awsValidator) validate(ctx context.Context, attestation vtpm.AttestationDocument, kdsClient askGetter, ark *x509.Certificate, akDigest [64]byte) error {
|
||||
var info instanceInfo
|
||||
if err := json.Unmarshal(attestation.InstanceInfo, &info); err != nil {
|
||||
return newValidationError(fmt.Errorf("unmarshalling instance info: %w", err))
|
||||
}
|
||||
|
||||
vlek, err := getVLEK(info.Certs)
|
||||
if err != nil {
|
||||
return newValidationError(fmt.Errorf("parsing certificates from certtable %x: %w", info.Certs, err))
|
||||
}
|
||||
|
||||
ask, err := kdsClient.getASK(ctx)
|
||||
if err != nil {
|
||||
return newValidationError(fmt.Errorf("getting ASK: %w", err))
|
||||
}
|
||||
|
||||
if err := ask.CheckSignatureFrom(ark); err != nil {
|
||||
return newValidationError(fmt.Errorf("verifying ASK signature: %w", err))
|
||||
}
|
||||
if err := vlek.CheckSignatureFrom(ask); err != nil {
|
||||
return newValidationError(fmt.Errorf("verifying VLEK signature: %w", err))
|
||||
}
|
||||
|
||||
if err := verify.SnpReportSignature(info.Report, vlek); err != nil {
|
||||
return newValidationError(fmt.Errorf("verifying snp report signature: %w", err))
|
||||
}
|
||||
|
||||
report, err := abi.ReportToProto(info.Report)
|
||||
if err != nil {
|
||||
return newValidationError(fmt.Errorf("unmarshalling SNP report: %w", err))
|
||||
}
|
||||
|
||||
if !bytes.Equal(report.GetReportData(), akDigest[:]) {
|
||||
return newValidationError(errors.New("SNP report and attestation statement contain mismatching attestation keys"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getVLEK parses the certificate table included in an extended SNP report
|
||||
// and returns the VLEK certificate.
|
||||
func getVLEK(certs []byte) (vlek *x509.Certificate, err error) {
|
||||
certTable := abi.CertTable{}
|
||||
if err = certTable.Unmarshal(certs); err != nil {
|
||||
return nil, fmt.Errorf("unmarshalling SNP certificate table: %v", err)
|
||||
}
|
||||
|
||||
vlekRaw, err := certTable.GetByGUIDString(abi.VlekGUID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting VLEK certificate: %v", err)
|
||||
}
|
||||
|
||||
vlek, err = x509.ParseCertificate(vlekRaw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing certificate: %w", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Query the AMD key distribution service for an AMD signing key.
|
||||
type kdsClient struct {
|
||||
httpClient
|
||||
}
|
||||
|
||||
type httpClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// getASK requests the current certificate chain from the AMD KDS API and returns the ASK.
|
||||
// There is no information on how to handle CRLs in the official AMD docs.
|
||||
// Once github.com/google/go-sev-guest adds support to check CRLs for VLEK-based certificate chains
|
||||
// we can check CRLs here.
|
||||
func (k kdsClient) getASK(ctx context.Context) (*x509.Certificate, error) {
|
||||
// If there are multiple CPU generations (and with that different API paths to call) in the future,
|
||||
// we can select the correct path to call based on the information contained in the SNP report.
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://kdsintf.amd.com/vlek/v1/Milan/cert_chain", nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := k.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("requesting ASK: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
pemChain, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading response body: %w", err)
|
||||
}
|
||||
|
||||
// certificate chain starts with ASK. We hardcode the ARK, so ignore that.
|
||||
decodedASK, _ := pem.Decode(pemChain)
|
||||
if decodedASK == nil {
|
||||
return nil, errors.New("no PEM data found")
|
||||
}
|
||||
|
||||
ask, err := x509.ParseCertificate(decodedASK.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing ASK: %w", err)
|
||||
}
|
||||
|
||||
return ask, nil
|
||||
}
|
||||
|
||||
// askGetter gets an ASK from somewhere.
|
||||
type askGetter interface {
|
||||
getASK(ctx context.Context) (*x509.Certificate, error)
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -51,7 +51,7 @@ const (
|
||||
)
|
||||
|
||||
var providerAttestationMapping = map[cloudprovider.Provider][]Variant{
|
||||
cloudprovider.AWS: {AWSSEVSNP{}, AWSNitroTPM{}},
|
||||
cloudprovider.AWS: {AWSNitroTPM{}, AWSSEVSNP{}},
|
||||
cloudprovider.Azure: {AzureSEVSNP{}, AzureTrustedLaunch{}},
|
||||
cloudprovider.GCP: {GCPSEVES{}},
|
||||
cloudprovider.QEMU: {QEMUVTPM{}},
|
||||
|
@ -17,6 +17,7 @@ go_library(
|
||||
"@com_github_google_go_tpm_tools//proto/attest",
|
||||
"@com_github_google_go_tpm_tools//proto/tpm",
|
||||
"@com_github_google_go_tpm_tools//server",
|
||||
"@org_golang_google_protobuf//encoding/protojson",
|
||||
],
|
||||
)
|
||||
|
||||
@ -26,6 +27,7 @@ go_test(
|
||||
"attestation_test.go",
|
||||
"vtpm_test.go",
|
||||
],
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":vtpm"],
|
||||
# keep
|
||||
gotags = select({
|
||||
@ -37,6 +39,7 @@ go_test(
|
||||
"//internal/attestation/measurements",
|
||||
"//internal/attestation/simulator",
|
||||
"//internal/logger",
|
||||
"@com_github_google_go_sev_guest//proto/sevsnp",
|
||||
"@com_github_google_go_tpm//tpm2",
|
||||
"@com_github_google_go_tpm_tools//client",
|
||||
"@com_github_google_go_tpm_tools//proto/attest",
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
tpmProto "github.com/google/go-tpm-tools/proto/tpm"
|
||||
tpmServer "github.com/google/go-tpm-tools/server"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
@ -76,6 +77,51 @@ type AttestationDocument struct {
|
||||
UserData []byte
|
||||
}
|
||||
|
||||
type auxAttestationDocument struct {
|
||||
// Attestation contains the TPM event log, PCR values and quotes, and public key of the key used to sign the attestation.
|
||||
Attestation json.RawMessage
|
||||
// InstanceInfo is used to verify the provided public key.
|
||||
InstanceInfo []byte
|
||||
// arbitrary data, quoted by the TPM.
|
||||
UserData []byte
|
||||
}
|
||||
|
||||
// MarshalJSON is needed as the proto definition of attest.Attestation uses protobuf's oneof feature.
|
||||
// That feature is not handled correctly by encoding/json.
|
||||
func (a AttestationDocument) MarshalJSON() ([]byte, error) {
|
||||
attestation, err := protojson.Marshal(a.Attestation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshaling attestation: %w", err)
|
||||
}
|
||||
|
||||
aux := auxAttestationDocument{
|
||||
Attestation: attestation,
|
||||
InstanceInfo: a.InstanceInfo,
|
||||
UserData: a.UserData,
|
||||
}
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON is needed as the proto definition of attest.Attestation uses protobuf's oneof feature.
|
||||
// That feature is not handled correctly by encoding/json.
|
||||
func (a *AttestationDocument) UnmarshalJSON(b []byte) error {
|
||||
aux := auxAttestationDocument{}
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return fmt.Errorf("unmarshaling AttestationDocument: %w", err)
|
||||
}
|
||||
|
||||
attestation := &attest.Attestation{}
|
||||
if err := protojson.Unmarshal(aux.Attestation, attestation); err != nil {
|
||||
return fmt.Errorf("unmarshaling attestation: %w", err)
|
||||
}
|
||||
|
||||
a.Attestation = attestation
|
||||
a.InstanceInfo = aux.InstanceInfo
|
||||
a.UserData = aux.UserData
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Issuer handles issuing of TPM based attestation documents.
|
||||
type Issuer struct {
|
||||
openTPM TPMOpenFunc
|
||||
|
File diff suppressed because one or more lines are too long
@ -27,7 +27,7 @@ func TestUnmarshalAttestationConfig(t *testing.T) {
|
||||
cfg AttestationCfg
|
||||
}{
|
||||
"AWSSEVSNP": {
|
||||
cfg: &AWSSEVSNP{Measurements: measurements.DefaultsFor(cloudprovider.AWS, variant.AWSSEVSNP{}), LaunchMeasurement: measurements.PlaceHolderMeasurement(48)},
|
||||
cfg: DefaultForAWSSEVSNP(),
|
||||
},
|
||||
"AWSNitroTPM": {
|
||||
cfg: &AWSNitroTPM{Measurements: measurements.DefaultsFor(cloudprovider.AWS, variant.AWSNitroTPM{})},
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
)
|
||||
|
||||
// DefaultForAzureSEVSNP returns the default configuration for Azure SEV-SNP attestation.
|
||||
@ -32,7 +33,7 @@ func DefaultForAzureSEVSNP() *AzureSEVSNP {
|
||||
EnforcementPolicy: idkeydigest.MAAFallback,
|
||||
},
|
||||
// AMD root key. Received from the AMD Key Distribution System API (KDS).
|
||||
AMDRootKey: mustParsePEM(`-----BEGIN CERTIFICATE-----\nMIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy\nMTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg\nW41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta\n1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2\nSzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0\n60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05\ngmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg\nbKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs\n+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi\nQi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ\neTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18\nfHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j\nWhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI\nrFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG\nKWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG\nSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI\nAWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel\nETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw\nSTjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK\ndHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq\nzT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp\nKGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e\npmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq\nHnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh\n3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn\nJZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH\nCViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4\nAFZEAwoKCQ==\n-----END CERTIFICATE-----\n`),
|
||||
AMDRootKey: mustParsePEM(constants.AMDRootKey),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@ All config relevant definitions, parsing and validation functions should go here
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -358,7 +357,7 @@ func Default() *Config {
|
||||
// AWS uses aws-nitro-tpm as attestation variant
|
||||
// AWS will have aws-sev-snp as attestation variant
|
||||
Attestation: AttestationConfig{
|
||||
AWSSEVSNP: &AWSSEVSNP{Measurements: measurements.DefaultsFor(cloudprovider.AWS, variant.AWSSEVSNP{}), LaunchMeasurement: measurements.WithAllBytes(0x00, measurements.Enforce, measurements.PCRMeasurementLength)},
|
||||
AWSSEVSNP: DefaultForAWSSEVSNP(),
|
||||
AWSNitroTPM: &AWSNitroTPM{Measurements: measurements.DefaultsFor(cloudprovider.AWS, variant.AWSNitroTPM{})},
|
||||
AzureSEVSNP: DefaultForAzureSEVSNP(),
|
||||
AzureTrustedLaunch: &AzureTrustedLaunch{Measurements: measurements.DefaultsFor(cloudprovider.Azure, variant.AzureTrustedLaunch{})},
|
||||
@ -788,9 +787,22 @@ type AWSSEVSNP struct {
|
||||
// description: |
|
||||
// Expected TPM measurements.
|
||||
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
|
||||
// TODO (derpsteb): reenable launchMeasurement once we have a way to generate the expected value dynamically.
|
||||
// description: |
|
||||
// Expected launch measurement in SNP report.
|
||||
LaunchMeasurement measurements.Measurement `json:"launchMeasurement" yaml:"launchMeasurement" validate:"required"`
|
||||
// Expected launch measurement in SNP report. Not in use right now.
|
||||
// LaunchMeasurement measurements.Measurement `json:"launchMeasurement" yaml:"launchMeasurement" validate:"required"`
|
||||
// description: |
|
||||
// AMD Root Key certificate used to verify the SEV-SNP certificate chain.
|
||||
AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"`
|
||||
}
|
||||
|
||||
// DefaultForAWSSEVSNP provides a valid default configuration for AWS SEV-SNP attestation.
|
||||
func DefaultForAWSSEVSNP() *AWSSEVSNP {
|
||||
return &AWSSEVSNP{
|
||||
Measurements: measurements.DefaultsFor(cloudprovider.AWS, variant.AWSSEVSNP{}),
|
||||
// LaunchMeasurement: measurements.PlaceHolderMeasurement(48),
|
||||
AMDRootKey: mustParsePEM(constants.AMDRootKey),
|
||||
}
|
||||
}
|
||||
|
||||
// GetVariant returns aws-sev-snp as the variant.
|
||||
@ -814,12 +826,13 @@ func (c AWSSEVSNP) EqualTo(other AttestationCfg) (bool, error) {
|
||||
if !ok {
|
||||
return false, fmt.Errorf("cannot compare %T with %T", c, other)
|
||||
}
|
||||
if !bytes.Equal(c.LaunchMeasurement.Expected, otherCfg.LaunchMeasurement.Expected) {
|
||||
return false, nil
|
||||
}
|
||||
if c.LaunchMeasurement.ValidationOpt != otherCfg.LaunchMeasurement.ValidationOpt {
|
||||
return false, nil
|
||||
}
|
||||
// TODO (derpsteb): reenable launchMeasurement once we have a way to generate the expected value dynamically.
|
||||
// if !bytes.Equal(c.LaunchMeasurement.Expected, otherCfg.LaunchMeasurement.Expected) {
|
||||
// return false, nil
|
||||
// }
|
||||
// if c.LaunchMeasurement.ValidationOpt != otherCfg.LaunchMeasurement.ValidationOpt {
|
||||
// return false, nil
|
||||
// }
|
||||
|
||||
return c.Measurements.EqualTo(otherCfg.Measurements), nil
|
||||
}
|
||||
|
@ -478,11 +478,11 @@ func init() {
|
||||
AWSSEVSNPDoc.Fields[0].Note = ""
|
||||
AWSSEVSNPDoc.Fields[0].Description = "Expected TPM measurements."
|
||||
AWSSEVSNPDoc.Fields[0].Comments[encoder.LineComment] = "Expected TPM measurements."
|
||||
AWSSEVSNPDoc.Fields[1].Name = "launchMeasurement"
|
||||
AWSSEVSNPDoc.Fields[1].Type = "Measurement"
|
||||
AWSSEVSNPDoc.Fields[1].Name = "amdRootKey"
|
||||
AWSSEVSNPDoc.Fields[1].Type = "Certificate"
|
||||
AWSSEVSNPDoc.Fields[1].Note = ""
|
||||
AWSSEVSNPDoc.Fields[1].Description = "Expected launch measurement in SNP report."
|
||||
AWSSEVSNPDoc.Fields[1].Comments[encoder.LineComment] = "Expected launch measurement in SNP report."
|
||||
AWSSEVSNPDoc.Fields[1].Description = "TODO (derpsteb): reenable launchMeasurement once we have a way to generate the expected value dynamically.\ndescription: |\n Expected launch measurement in SNP report. Not in use right now.\nLaunchMeasurement measurements.Measurement `json:\"launchMeasurement\" yaml:\"launchMeasurement\" validate:\"required\"`\ndescription: |\n AMD Root Key certificate used to verify the SEV-SNP certificate chain.\n"
|
||||
AWSSEVSNPDoc.Fields[1].Comments[encoder.LineComment] = "TODO (derpsteb): reenable launchMeasurement once we have a way to generate the expected value dynamically."
|
||||
|
||||
AWSNitroTPMDoc.Type = "AWSNitroTPM"
|
||||
AWSNitroTPMDoc.Comments[encoder.LineComment] = "AWSNitroTPM is the configuration for AWS Nitro TPM attestation."
|
||||
|
@ -483,11 +483,11 @@ func TestConfig_UpdateMeasurements(t *testing.T) {
|
||||
{ // AWS
|
||||
conf := Default()
|
||||
conf.RemoveProviderAndAttestationExcept(cloudprovider.AWS)
|
||||
for k := range conf.Attestation.AWSSEVSNP.Measurements {
|
||||
delete(conf.Attestation.AWSSEVSNP.Measurements, k)
|
||||
for k := range conf.Attestation.AWSNitroTPM.Measurements {
|
||||
delete(conf.Attestation.AWSNitroTPM.Measurements, k)
|
||||
}
|
||||
conf.UpdateMeasurements(newMeasurements)
|
||||
assert.Equal(newMeasurements, conf.Attestation.AWSSEVSNP.Measurements)
|
||||
assert.Equal(newMeasurements, conf.Attestation.AWSNitroTPM.Measurements)
|
||||
}
|
||||
{ // Azure
|
||||
conf := Default()
|
||||
|
@ -218,6 +218,13 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELcPl4Ik+qZuH4K049wksoXK/Os3Z
|
||||
b92PDCpM7FZAINQF88s1TZS/HmRXYk62UJ4eqPduvUnJmXhNikhLbMi6fw==
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
|
||||
//
|
||||
// AMD SEV-SNP.
|
||||
//
|
||||
|
||||
// AMDRootKey is the root certificate for signatures from the AMD SEV-SNP PKI.
|
||||
AMDRootKey = `-----BEGIN CERTIFICATE-----\nMIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy\nMTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg\nW41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta\n1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2\nSzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0\n60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05\ngmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg\nbKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs\n+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi\nQi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ\neTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18\nfHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j\nWhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI\nrFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG\nKWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG\nSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI\nAWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel\nETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw\nSTjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK\ndHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq\nzT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp\nKGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e\npmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq\nHnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh\n3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn\nJZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH\nCViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4\nAFZEAwoKCQ==\n-----END CERTIFICATE-----\n`
|
||||
)
|
||||
|
||||
// VersionInfo returns the version of a binary.
|
||||
|
Loading…
Reference in New Issue
Block a user