azure: allow a set of idkeydigest values (#991)

This commit is contained in:
3u13r 2023-01-18 16:49:55 +01:00 committed by GitHub
parent a3db3c8424
commit 632090c21b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 360 additions and 197 deletions

View File

@ -14,7 +14,6 @@ import (
"strconv" "strconv"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/helm" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/helm"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/initserver"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi"
kubewaiter "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubeWaiter" kubewaiter "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubeWaiter"
@ -32,7 +31,6 @@ import (
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
gcpcloud "github.com/edgelesssys/constellation/v2/internal/cloud/gcp" gcpcloud "github.com/edgelesssys/constellation/v2/internal/cloud/gcp"
qemucloud "github.com/edgelesssys/constellation/v2/internal/cloud/qemu" qemucloud "github.com/edgelesssys/constellation/v2/internal/cloud/qemu"
"github.com/edgelesssys/constellation/v2/internal/cloud/vmtype"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl" "github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
@ -68,7 +66,7 @@ func main() {
var clusterInitJoiner clusterInitJoiner var clusterInitJoiner clusterInitJoiner
var metadataAPI metadataAPI var metadataAPI metadataAPI
var cloudLogger logging.CloudLogger var cloudLogger logging.CloudLogger
var issuer initserver.IssuerWrapper var issuer atls.Issuer
var openTPM vtpm.TPMOpenFunc var openTPM vtpm.TPMOpenFunc
var fs afero.Fs var fs afero.Fs
@ -84,7 +82,7 @@ func main() {
log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs") log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs")
} }
issuer = initserver.NewIssuerWrapper(aws.NewIssuer(), vmtype.Unknown, nil) issuer = aws.NewIssuer()
metadata, err := awscloud.New(ctx) metadata, err := awscloud.New(ctx)
if err != nil { if err != nil {
@ -110,7 +108,7 @@ func main() {
log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs") log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs")
} }
issuer = initserver.NewIssuerWrapper(gcp.NewIssuer(), vmtype.Unknown, nil) issuer = gcp.NewIssuer()
metadata, err := gcpcloud.New(ctx) metadata, err := gcpcloud.New(ctx)
if err != nil { if err != nil {
@ -138,11 +136,11 @@ func main() {
log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs") log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs")
} }
if idkeydigest, err := snp.GetIDKeyDigest(vtpm.OpenVTPM); err == nil { if _, err := snp.GetIDKeyDigest(vtpm.OpenVTPM); err == nil {
issuer = initserver.NewIssuerWrapper(snp.NewIssuer(), vmtype.AzureCVM, idkeydigest) issuer = snp.NewIssuer()
} else { } else {
// assume we are running in a trusted-launch VM // assume we are running in a trusted-launch VM
issuer = initserver.NewIssuerWrapper(trustedlaunch.NewIssuer(), vmtype.AzureTrustedLaunch, idkeydigest) issuer = trustedlaunch.NewIssuer()
} }
metadata, err := azurecloud.New(ctx) metadata, err := azurecloud.New(ctx)
@ -168,7 +166,7 @@ func main() {
log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs") log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs")
} }
issuer = initserver.NewIssuerWrapper(qemu.NewIssuer(), vmtype.Unknown, nil) issuer = qemu.NewIssuer()
cloudLogger = qemucloud.NewLogger() cloudLogger = qemucloud.NewLogger()
metadata := qemucloud.New() metadata := qemucloud.New()
@ -181,7 +179,7 @@ func main() {
openTPM = vtpm.OpenVTPM openTPM = vtpm.OpenVTPM
fs = afero.NewOsFs() fs = afero.NewOsFs()
default: default:
issuer = initserver.NewIssuerWrapper(atls.NewFakeIssuer(oid.Dummy{}), vmtype.Unknown, nil) issuer = atls.NewFakeIssuer(oid.Dummy{})
clusterInitJoiner = &clusterFake{} clusterInitJoiner = &clusterFake{}
metadataAPI = &providerMetadataFake{} metadataAPI = &providerMetadataFake{}
cloudLogger = &logging.NopLogger{} cloudLogger = &logging.NopLogger{}

View File

@ -16,6 +16,7 @@ import (
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/joinclient" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/joinclient"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/nodelock" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/nodelock"
"github.com/edgelesssys/constellation/v2/internal/atls"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
@ -24,7 +25,7 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
func run(issuerWrapper initserver.IssuerWrapper, tpm vtpm.TPMOpenFunc, fileHandler file.Handler, func run(issuer atls.Issuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler,
kube clusterInitJoiner, metadata metadataAPI, kube clusterInitJoiner, metadata metadataAPI,
bindIP, bindPort string, log *logger.Logger, bindIP, bindPort string, log *logger.Logger,
cloudLogger logging.CloudLogger, cloudLogger logging.CloudLogger,
@ -56,12 +57,12 @@ func run(issuerWrapper initserver.IssuerWrapper, tpm vtpm.TPMOpenFunc, fileHandl
} }
nodeLock := nodelock.New(tpm) nodeLock := nodelock.New(tpm)
initServer, err := initserver.New(context.Background(), nodeLock, kube, issuerWrapper, fileHandler, metadata, log) initServer, err := initserver.New(context.Background(), nodeLock, kube, issuer, fileHandler, metadata, log)
if err != nil { if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to create init server") log.With(zap.Error(err)).Fatalf("Failed to create init server")
} }
dialer := dialer.New(issuerWrapper, nil, &net.Dialer{}) dialer := dialer.New(issuer, nil, &net.Dialer{})
joinClient := joinclient.New(nodeLock, dialer, kube, metadata, log) joinClient := joinclient.New(nodeLock, dialer, kube, metadata, log)
cleaner := clean.New().With(initServer).With(joinClient) cleaner := clean.New().With(initServer).With(joinClient)

View File

@ -21,7 +21,7 @@ type clusterFake struct{}
// InitCluster fakes bootstrapping a new cluster with the current node being the master, returning the arguments required to join the cluster. // InitCluster fakes bootstrapping a new cluster with the current node being the master, returning the arguments required to join the cluster.
func (c *clusterFake) InitCluster( func (c *clusterFake) InitCluster(
context.Context, string, string, []byte, []uint32, bool, []byte, bool, context.Context, string, string, []byte, []uint32, bool, bool,
[]byte, bool, components.Components, *logger.Logger, []byte, bool, components.Components, *logger.Logger,
) ([]byte, error) { ) ([]byte, error) {
return []byte{}, nil return []byte{}, nil

View File

@ -17,7 +17,7 @@ import (
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/diskencryption" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/diskencryption"
"github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/atls"
"github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/cloud/vmtype" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp"
"github.com/edgelesssys/constellation/v2/internal/crypto" "github.com/edgelesssys/constellation/v2/internal/crypto"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/grpc/atlscredentials" "github.com/edgelesssys/constellation/v2/internal/grpc/atlscredentials"
@ -38,13 +38,13 @@ import (
// The server handles initialization calls from the CLI and initializes the // The server handles initialization calls from the CLI and initializes the
// Kubernetes cluster. // Kubernetes cluster.
type Server struct { type Server struct {
nodeLock locker nodeLock locker
initializer ClusterInitializer initializer ClusterInitializer
disk encryptedDisk disk encryptedDisk
fileHandler file.Handler fileHandler file.Handler
grpcServer serveStopper grpcServer serveStopper
cleaner cleaner cleaner cleaner
issuerWrapper IssuerWrapper issuer atls.Issuer
initSecretHash []byte initSecretHash []byte
@ -54,7 +54,7 @@ type Server struct {
} }
// New creates a new initialization server. // New creates a new initialization server.
func New(ctx context.Context, lock locker, kube ClusterInitializer, issuerWrapper IssuerWrapper, fh file.Handler, metadata MetadataAPI, log *logger.Logger) (*Server, error) { func New(ctx context.Context, lock locker, kube ClusterInitializer, issuer atls.Issuer, fh file.Handler, metadata MetadataAPI, log *logger.Logger) (*Server, error) {
log = log.Named("initServer") log = log.Named("initServer")
initSecretHash, err := metadata.InitSecretHash(ctx) initSecretHash, err := metadata.InitSecretHash(ctx)
@ -70,13 +70,13 @@ func New(ctx context.Context, lock locker, kube ClusterInitializer, issuerWrappe
disk: diskencryption.New(), disk: diskencryption.New(),
initializer: kube, initializer: kube,
fileHandler: fh, fileHandler: fh,
issuerWrapper: issuerWrapper, issuer: issuer,
log: log, log: log,
initSecretHash: initSecretHash, initSecretHash: initSecretHash,
} }
grpcServer := grpc.NewServer( grpcServer := grpc.NewServer(
grpc.Creds(atlscredentials.New(issuerWrapper, nil)), grpc.Creds(atlscredentials.New(issuer, nil)),
grpc.KeepaliveParams(keepalive.ServerParameters{Time: 15 * time.Second}), grpc.KeepaliveParams(keepalive.ServerParameters{Time: 15 * time.Second}),
log.Named("gRPC").GetServerUnaryInterceptor(), log.Named("gRPC").GetServerUnaryInterceptor(),
) )
@ -140,14 +140,16 @@ func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initpro
return nil, status.Errorf(codes.Internal, "persisting node state: %s", err) return nil, status.Errorf(codes.Internal, "persisting node state: %s", err)
} }
// Check if we are running on a CVM
_, isCVM := s.issuer.(*snp.Issuer)
kubeconfig, err := s.initializer.InitCluster(ctx, kubeconfig, err := s.initializer.InitCluster(ctx,
req.CloudServiceAccountUri, req.CloudServiceAccountUri,
req.KubernetesVersion, req.KubernetesVersion,
measurementSalt, measurementSalt,
req.EnforcedPcrs, req.EnforcedPcrs,
req.EnforceIdkeydigest, req.EnforceIdkeydigest,
s.issuerWrapper.IDKeyDigest(), isCVM,
s.issuerWrapper.VMType() == vmtype.AzureCVM,
req.HelmDeployments, req.HelmDeployments,
req.ConformanceMode, req.ConformanceMode,
components.NewComponentsFromInitProto(req.KubernetesComponents), components.NewComponentsFromInitProto(req.KubernetesComponents),
@ -193,33 +195,6 @@ func (s *Server) setupDisk(masterSecret, salt []byte) error {
return s.disk.UpdatePassphrase(string(diskKey)) return s.disk.UpdatePassphrase(string(diskKey))
} }
// IssuerWrapper adds VM type context to an issuer to distinguish between
// confidential and trusted launch VMs.
type IssuerWrapper struct {
atls.Issuer
vmType vmtype.VMType
idkeydigest []byte
}
// NewIssuerWrapper creates a new issuer with VM type context.
func NewIssuerWrapper(issuer atls.Issuer, vmType vmtype.VMType, idkeydigest []byte) IssuerWrapper {
return IssuerWrapper{
Issuer: issuer,
vmType: vmType,
idkeydigest: idkeydigest,
}
}
// VMType returns the VM type.
func (i *IssuerWrapper) VMType() vmtype.VMType {
return i.vmType
}
// IDKeyDigest returns the ID key digest.
func (i *IssuerWrapper) IDKeyDigest() []byte {
return i.idkeydigest
}
func deriveMeasurementValues(masterSecret, hkdfSalt []byte) (salt, clusterID []byte, err error) { func deriveMeasurementValues(masterSecret, hkdfSalt []byte) (salt, clusterID []byte, err error) {
salt, err = crypto.GenerateRandomBytes(crypto.RNGLengthDefault) salt, err = crypto.GenerateRandomBytes(crypto.RNGLengthDefault)
if err != nil { if err != nil {
@ -247,7 +222,6 @@ type ClusterInitializer interface {
measurementSalt []byte, measurementSalt []byte,
enforcedPcrs []uint32, enforcedPcrs []uint32,
enforceIDKeyDigest bool, enforceIDKeyDigest bool,
idKeyDigest []byte,
azureCVM bool, azureCVM bool,
helmDeployments []byte, helmDeployments []byte,
conformanceMode bool, conformanceMode bool,

View File

@ -16,9 +16,11 @@ import (
"time" "time"
"github.com/edgelesssys/constellation/v2/bootstrapper/initproto" "github.com/edgelesssys/constellation/v2/bootstrapper/initproto"
"github.com/edgelesssys/constellation/v2/internal/atls"
"github.com/edgelesssys/constellation/v2/internal/crypto/testvector" "github.com/edgelesssys/constellation/v2/internal/crypto/testvector"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/oid"
"github.com/edgelesssys/constellation/v2/internal/versions/components" "github.com/edgelesssys/constellation/v2/internal/versions/components"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -55,7 +57,7 @@ func TestNew(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
server, err := New(context.TODO(), newFakeLock(), &stubClusterInitializer{}, IssuerWrapper{}, fh, &tc.metadata, logger.NewTest(t)) server, err := New(context.TODO(), newFakeLock(), &stubClusterInitializer{}, atls.NewFakeIssuer(oid.Dummy{}), fh, &tc.metadata, logger.NewTest(t))
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
return return
@ -301,7 +303,7 @@ type stubClusterInitializer struct {
} }
func (i *stubClusterInitializer) InitCluster( func (i *stubClusterInitializer) InitCluster(
context.Context, string, string, []byte, []uint32, bool, []byte, bool, context.Context, string, string, []byte, []uint32, bool, bool,
[]byte, bool, components.Components, *logger.Logger, []byte, bool, components.Components, *logger.Logger,
) ([]byte, error) { ) ([]byte, error) {
return i.initClusterKubeconfig, i.initClusterErr return i.initClusterKubeconfig, i.initClusterErr

View File

@ -9,7 +9,6 @@ package kubernetes
import ( import (
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -89,7 +88,7 @@ func New(cloudProvider string, clusterUtil clusterUtil, configProvider configura
// InitCluster initializes a new Kubernetes cluster and applies pod network provider. // InitCluster initializes a new Kubernetes cluster and applies pod network provider.
func (k *KubeWrapper) InitCluster( func (k *KubeWrapper) InitCluster(
ctx context.Context, cloudServiceAccountURI, versionString string, measurementSalt []byte, enforcedPCRs []uint32, ctx context.Context, cloudServiceAccountURI, versionString string, measurementSalt []byte, enforcedPCRs []uint32,
enforceIDKeyDigest bool, idKeyDigest []byte, azureCVM bool, enforceIDKeyDigest bool, azureCVM bool,
helmReleasesRaw []byte, conformanceMode bool, kubernetesComponents components.Components, log *logger.Logger, helmReleasesRaw []byte, conformanceMode bool, kubernetesComponents components.Components, log *logger.Logger,
) ([]byte, error) { ) ([]byte, error) {
log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components") log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components")
@ -216,7 +215,6 @@ func (k *KubeWrapper) InitCluster(
} }
serviceConfig := constellationServicesConfig{ serviceConfig := constellationServicesConfig{
initialMeasurementsJSON: measurementsJSON, initialMeasurementsJSON: measurementsJSON,
idkeydigest: idKeyDigest,
measurementSalt: measurementSalt, measurementSalt: measurementSalt,
subnetworkPodCIDR: subnetworkPodCIDR, subnetworkPodCIDR: subnetworkPodCIDR,
cloudServiceAccountURI: cloudServiceAccountURI, cloudServiceAccountURI: cloudServiceAccountURI,
@ -484,12 +482,6 @@ func (k *KubeWrapper) setupExtraVals(ctx context.Context, serviceConfig constell
"subnetworkPodCIDR": serviceConfig.subnetworkPodCIDR, "subnetworkPodCIDR": serviceConfig.subnetworkPodCIDR,
} }
joinVals, ok := extraVals["join-service"].(map[string]any)
if !ok {
return nil, errors.New("invalid join-service values")
}
joinVals["idkeydigest"] = hex.EncodeToString(serviceConfig.idkeydigest)
subscriptionID, resourceGroup, err := azureshared.BasicsFromProviderID(instance.ProviderID) subscriptionID, resourceGroup, err := azureshared.BasicsFromProviderID(instance.ProviderID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -532,7 +524,6 @@ type ccmConfigGetter interface {
type constellationServicesConfig struct { type constellationServicesConfig struct {
initialMeasurementsJSON []byte initialMeasurementsJSON []byte
idkeydigest []byte
measurementSalt []byte measurementSalt []byte
subnetworkPodCIDR string subnetworkPodCIDR string
cloudServiceAccountURI string cloudServiceAccountURI string

View File

@ -257,7 +257,7 @@ func TestInitCluster(t *testing.T) {
_, err := kube.InitCluster( _, err := kube.InitCluster(
context.Background(), serviceAccountURI, string(tc.k8sVersion), context.Background(), serviceAccountURI, string(tc.k8sVersion),
nil, nil, false, nil, true, []byte("{}"), false, nil, logger.NewTest(t), nil, nil, false, true, []byte("{}"), false, nil, logger.NewTest(t),
) )
if tc.wantErr { if tc.wantErr {

View File

@ -18,6 +18,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp"
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch"
"github.com/edgelesssys/constellation/v2/internal/attestation/gcp" "github.com/edgelesssys/constellation/v2/internal/attestation/gcp"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/qemu" "github.com/edgelesssys/constellation/v2/internal/attestation/qemu"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
@ -30,7 +31,7 @@ import (
type Validator struct { type Validator struct {
provider cloudprovider.Provider provider cloudprovider.Provider
pcrs measurements.M pcrs measurements.M
idkeydigest []byte idkeydigests idkeydigest.IDKeyDigests
enforceIDKeyDigest bool enforceIDKeyDigest bool
azureCVM bool azureCVM bool
validator atls.Validator validator atls.Validator
@ -50,12 +51,8 @@ func NewValidator(provider cloudprovider.Provider, conf *config.Config) (*Valida
if v.provider == cloudprovider.Azure { if v.provider == cloudprovider.Azure {
v.azureCVM = *conf.Provider.Azure.ConfidentialVM v.azureCVM = *conf.Provider.Azure.ConfidentialVM
if v.azureCVM { if v.azureCVM {
idkeydigest, err := hex.DecodeString(conf.Provider.Azure.IDKeyDigest)
if err != nil {
return nil, fmt.Errorf("bad config: decoding idkeydigest from config: %w", err)
}
v.enforceIDKeyDigest = *conf.Provider.Azure.EnforceIDKeyDigest v.enforceIDKeyDigest = *conf.Provider.Azure.EnforceIDKeyDigest
v.idkeydigest = idkeydigest v.idkeydigests = conf.Provider.Azure.IDKeyDigests
} }
} }
@ -153,7 +150,7 @@ func (v *Validator) updateValidator(cmd *cobra.Command) {
v.validator = gcp.NewValidator(v.pcrs, log) v.validator = gcp.NewValidator(v.pcrs, log)
case cloudprovider.Azure: case cloudprovider.Azure:
if v.azureCVM { if v.azureCVM {
v.validator = snp.NewValidator(v.pcrs, v.idkeydigest, v.enforceIDKeyDigest, log) v.validator = snp.NewValidator(v.pcrs, v.idkeydigests, v.enforceIDKeyDigest, log)
} else { } else {
v.validator = trustedlaunch.NewValidator(v.pcrs, log) v.validator = trustedlaunch.NewValidator(v.pcrs, log)
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp"
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch"
"github.com/edgelesssys/constellation/v2/internal/attestation/gcp" "github.com/edgelesssys/constellation/v2/internal/attestation/gcp"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/qemu" "github.com/edgelesssys/constellation/v2/internal/attestation/qemu"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
@ -39,7 +40,7 @@ func TestNewValidator(t *testing.T) {
config *config.Config config *config.Config
pcrs measurements.M pcrs measurements.M
enforceIDKeyDigest bool enforceIDKeyDigest bool
idKeyDigest string digest idkeydigest.IDKeyDigests
azureCVM bool azureCVM bool
wantErr bool wantErr bool
}{ }{
@ -74,17 +75,9 @@ func TestNewValidator(t *testing.T) {
"set idkeydigest": { "set idkeydigest": {
provider: cloudprovider.Azure, provider: cloudprovider.Azure,
pcrs: testPCRs, pcrs: testPCRs,
idKeyDigest: "414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141", digest: idkeydigest.IDKeyDigests{[]byte("414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141")},
enforceIDKeyDigest: true, enforceIDKeyDigest: true,
}, },
"invalid idkeydigest": {
provider: cloudprovider.Azure,
pcrs: testPCRs,
idKeyDigest: "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414",
enforceIDKeyDigest: true,
azureCVM: true,
wantErr: true,
},
} }
for name, tc := range testCases { for name, tc := range testCases {
@ -96,7 +89,7 @@ func TestNewValidator(t *testing.T) {
conf.Provider.GCP = &config.GCPConfig{Measurements: tc.pcrs} conf.Provider.GCP = &config.GCPConfig{Measurements: tc.pcrs}
} }
if tc.provider == cloudprovider.Azure { if tc.provider == cloudprovider.Azure {
conf.Provider.Azure = &config.AzureConfig{Measurements: tc.pcrs, EnforceIDKeyDigest: &tc.enforceIDKeyDigest, IDKeyDigest: tc.idKeyDigest, ConfidentialVM: &tc.azureCVM} conf.Provider.Azure = &config.AzureConfig{Measurements: tc.pcrs, EnforceIDKeyDigest: &tc.enforceIDKeyDigest, IDKeyDigests: tc.digest, ConfidentialVM: &tc.azureCVM}
} }
if tc.provider == cloudprovider.QEMU { if tc.provider == cloudprovider.QEMU {
conf.Provider.QEMU = &config.QEMUConfig{Measurements: tc.pcrs} conf.Provider.QEMU = &config.QEMUConfig{Measurements: tc.pcrs}
@ -148,7 +141,7 @@ func TestValidatorV(t *testing.T) {
"azure cvm": { "azure cvm": {
provider: cloudprovider.Azure, provider: cloudprovider.Azure,
pcrs: newTestPCRs(), pcrs: newTestPCRs(),
wantVs: snp.NewValidator(newTestPCRs(), nil, false, nil), wantVs: snp.NewValidator(newTestPCRs(), idkeydigest.IDKeyDigests{}, false, nil),
azureCVM: true, azureCVM: true,
}, },
"azure trusted launch": { "azure trusted launch": {

View File

@ -9,7 +9,7 @@ data:
{{- if eq .Values.csp "Azure" }} {{- if eq .Values.csp "Azure" }}
{{/* ConfigMap.data is of type map[string]string. quote will not quote a quoted string. */}} {{/* ConfigMap.data is of type map[string]string. quote will not quote a quoted string. */}}
enforceIdKeyDigest: {{ .Values.enforceIdKeyDigest | quote }} enforceIdKeyDigest: {{ .Values.enforceIdKeyDigest | quote }}
idkeydigest: {{ .Values.idkeydigest | quote }} idkeydigests: {{ .Values.idkeydigests | mustToJson }}
{{- end }} {{- end }}
binaryData: binaryData:
measurementSalt: {{ .Values.measurementSalt }} measurementSalt: {{ .Values.measurementSalt }}

View File

@ -14,10 +14,10 @@
"description": "Whether or not idkeydigest should be enforced during attestation on azure.", "description": "Whether or not idkeydigest should be enforced during attestation on azure.",
"type": "boolean" "type": "boolean"
}, },
"idkeydigest": { "idkeydigests": {
"description": "Expected idkeydigest value for Azure SNP attestation.", "description": "List of expected idkeydigest values for Azure SNP attestation.",
"type": "string", "type": "string",
"examples": ["57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696"] "examples": ["[\"57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696\", \"0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3\"]"]
}, },
"image": { "image": {
"description": "Container image to use for the spawned pods.", "description": "Container image to use for the spawned pods.",
@ -40,7 +40,7 @@
"properties": { "csp": { "const": "azure" } }, "properties": { "csp": { "const": "azure" } },
"required": ["csp"] "required": ["csp"]
}, },
"then": { "required": ["enforceIdKeyDigest", "idkeydigest"] }, "then": { "required": ["enforceIdKeyDigest", "idkeydigests"] },
"title": "Values", "title": "Values",
"type": "object" "type": "object"
} }

View File

@ -405,6 +405,12 @@ func (i *ChartLoader) loadConstellationServicesValues(config *config.Config, mas
} }
joinServiceVals["enforceIdKeyDigest"] = config.EnforcesIDKeyDigest() joinServiceVals["enforceIdKeyDigest"] = config.EnforcesIDKeyDigest()
marshalledDigests, err := json.Marshal(config.IDKeyDigests())
if err != nil {
return nil, fmt.Errorf("marshalling id key digests: %w", err)
}
joinServiceVals["idkeydigests"] = string(marshalledDigests)
ccmVals, ok := values["ccm"].(map[string]any) ccmVals, ok := values["ccm"].(map[string]any)
if !ok { if !ok {
return nil, errors.New("invalid ccm values") return nil, errors.New("invalid ccm values")

View File

@ -356,7 +356,7 @@ func prepareAzureValues(values map[string]any) error {
if !ok { if !ok {
return errors.New("missing 'join-service' key") return errors.New("missing 'join-service' key")
} }
joinVals["idkeydigest"] = "baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad" joinVals["idkeydigests"] = "[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\", \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"]"
m := measurements.M{1: measurements.WithAllBytes(0xAA, false)} m := measurements.M{1: measurements.WithAllBytes(0xAA, false)}
mJSON, err := json.Marshal(m) mJSON, err := json.Marshal(m)
if err != nil { if err != nil {

View File

@ -6,6 +6,6 @@ metadata:
data: data:
measurements: "{\"1\":{\"expected\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"warnOnly\":false}}" measurements: "{\"1\":{\"expected\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"warnOnly\":false}}"
enforceIdKeyDigest: "true" enforceIdKeyDigest: "true"
idkeydigest: "baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad" idkeydigests: "[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\", \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"]"
binaryData: binaryData:
measurementSalt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA measurementSalt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

View File

@ -9,6 +9,8 @@ package snp
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
) )
type signatureError struct { type signatureError struct {
@ -48,7 +50,8 @@ func (e *vcekError) Error() string {
} }
type idKeyError struct { type idKeyError struct {
expectedValue []byte encounteredValue []byte
expectedValues idkeydigest.IDKeyDigests
} }
func (e *idKeyError) Unwrap() error { func (e *idKeyError) Unwrap() error {
@ -56,7 +59,7 @@ func (e *idKeyError) Unwrap() error {
} }
func (e *idKeyError) Error() string { func (e *idKeyError) Error() string {
return fmt.Sprintf("configured idkeydigest does not match reported idkeydigest: %x", e.expectedValue) return fmt.Sprintf("configured idkeydigests %x doesn't contain reported idkeydigest %x", e.expectedValues, e.encounteredValue)
} }
type versionError struct { type versionError struct {

View File

@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
internalCrypto "github.com/edgelesssys/constellation/v2/internal/crypto" internalCrypto "github.com/edgelesssys/constellation/v2/internal/crypto"
@ -42,11 +43,11 @@ type Validator struct {
} }
// NewValidator initializes a new Azure validator with the provided PCR values. // NewValidator initializes a new Azure validator with the provided PCR values.
func NewValidator(pcrs measurements.M, idKeyDigest []byte, enforceIDKeyDigest bool, log vtpm.AttestationLogger) *Validator { func NewValidator(pcrs measurements.M, idKeyDigests idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, log vtpm.AttestationLogger) *Validator {
return &Validator{ return &Validator{
Validator: vtpm.NewValidator( Validator: vtpm.NewValidator(
pcrs, pcrs,
getTrustedKey(&azureInstanceInfo{}, idKeyDigest, enforceIDKeyDigest, log), getTrustedKey(&azureInstanceInfo{}, idKeyDigests, enforceIDKeyDigest, log),
validateCVM, validateCVM,
vtpm.VerifyPKCS1v15, vtpm.VerifyPKCS1v15,
log, log,
@ -77,7 +78,7 @@ func reverseEndian(b []byte) {
// getTrustedKey establishes trust in the given public key. // getTrustedKey establishes trust in the given public key.
// It does so by verifying the SNP attestation statement in instanceInfo. // It does so by verifying the SNP attestation statement in instanceInfo.
func getTrustedKey( func getTrustedKey(
hclAk HCLAkValidator, idKeyDigest []byte, enforceIDKeyDigest bool, log vtpm.AttestationLogger, hclAk HCLAkValidator, idKeyDigest idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, log vtpm.AttestationLogger,
) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { ) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
var instanceInfo azureInstanceInfo var instanceInfo azureInstanceInfo
@ -95,7 +96,7 @@ func getTrustedKey(
return nil, fmt.Errorf("validating VCEK: %w", err) return nil, fmt.Errorf("validating VCEK: %w", err)
} }
if err = validateSNPReport(vcek, idKeyDigest, enforceIDKeyDigest, report, log); err != nil { if err := validateSNPReport(vcek, idKeyDigest, enforceIDKeyDigest, report, log); err != nil {
return nil, fmt.Errorf("validating SNP report: %w", err) return nil, fmt.Errorf("validating SNP report: %w", err)
} }
@ -143,7 +144,7 @@ func validateVCEK(vcekRaw []byte, certChain []byte) (*x509.Certificate, error) {
} }
func validateSNPReport( func validateSNPReport(
cert *x509.Certificate, expectedIDKeyDigest []byte, enforceIDKeyDigest bool, cert *x509.Certificate, expectedIDKeyDigests idkeydigest.IDKeyDigests, enforceIDKeyDigest bool,
report snpAttestationReport, log vtpm.AttestationLogger, report snpAttestationReport, log vtpm.AttestationLogger,
) error { ) error {
if report.Policy.Debug() { if report.Policy.Debug() {
@ -189,12 +190,20 @@ func validateSNPReport(
return &signatureError{err} return &signatureError{err}
} }
if !bytes.Equal(expectedIDKeyDigest, report.IDKeyDigest[:]) { hasExpectedIDKeyDigest := false
for _, digest := range expectedIDKeyDigests {
if bytes.Equal(digest, report.IDKeyDigest[:]) {
hasExpectedIDKeyDigest = true
break
}
}
if !hasExpectedIDKeyDigest {
if enforceIDKeyDigest { if enforceIDKeyDigest {
return &idKeyError{report.IDKeyDigest[:]} return &idKeyError{report.IDKeyDigest[:], expectedIDKeyDigests}
} }
if log != nil { if log != nil {
log.Warnf("Encountered different than configured IDKeyDigest value: %x", report.IDKeyDigest[:]) log.Warnf("configured idkeydigests %x doesn't contain reported idkeydigest %x", expectedIDKeyDigests, report.IDKeyDigest[:])
} }
} }

View File

@ -17,6 +17,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator" "github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/client"
@ -41,14 +42,16 @@ func TestTrustedKeyFromSNP(t *testing.T) {
defaultRuntimeData := "7b226b657973223a5b7b226b6964223a2248434c416b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d" defaultRuntimeData := "7b226b657973223a5b7b226b6964223a2248434c416b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d"
defaultVCEK := "-----BEGIN CERTIFICATE-----\nMIIFTDCCAvugAwIBAgIBADBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQCAgUA\noRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBATB7MRQwEgYD\nVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDASBgNVBAcMC1NhbnRhIENs\nYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5jZWQgTWljcm8gRGV2aWNl\nczESMBAGA1UEAwwJU0VWLU1pbGFuMB4XDTIyMDYyOTE2MzEzMFoXDTI5MDYyOTE2\nMzEzMFowejEUMBIGA1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVTMRQwEgYD\nVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFkdmFuY2Vk\nIE1pY3JvIERldmljZXMxETAPBgNVBAMMCFNFVi1WQ0VLMHYwEAYHKoZIzj0CAQYF\nK4EEACIDYgAEhPX8Cl9uA7PxqNGzeqamJNYJLx/VFE/s3+8qOWtaztKNcn1PaAI4\nndE+yaVfMHsiA8CLTylumpWXcVBHPYV9kPEVrtozhvrrT5Oii9OpZPYHJ7/WPVmM\nJ3K8/Iz3AshTo4IBFjCCARIwEAYJKwYBBAGceAEBBAMCAQAwFwYJKwYBBAGceAEC\nBAoWCE1pbGFuLUIwMBEGCisGAQQBnHgBAwEEAwIBAjARBgorBgEEAZx4AQMCBAMC\nAQAwEQYKKwYBBAGceAEDBAQDAgEAMBEGCisGAQQBnHgBAwUEAwIBADARBgorBgEE\nAZx4AQMGBAMCAQAwEQYKKwYBBAGceAEDBwQDAgEAMBEGCisGAQQBnHgBAwMEAwIB\nBjARBgorBgEEAZx4AQMIBAMCAV0wTQYJKwYBBAGceAEEBECeRKrvAs/Kb926ymac\nbP0p4auNl+vJOYVxKKy7E7h0DfMUNtNOhuX4rgzf6zoOGF20beysF2zHfXYcIqG5\n3PJbMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0B\nAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBA4ICAQBXXzX8w+z06JNKLVAa9vyE\njC69c7uvfTPScqLzOCV+S8yZ7Ibpn6gdRcgn5s3F7uerVs9/8mq+rDpMzLTVxLei\nYAW9jDS9VdEgfUp3GzzL1g3zsWNZPpWuAu0Cw1V7KnQ9kiGsJMRKerx8QLrm+aAH\nOiob4XHl2naUx9aILzCLbNgLBdh6Tw2XkGj8NB9O7kNQoINEz6U+cAJL5LWzuoYt\nW1IJkYUEMydvLImFHeFIFtB2wI4mTSuCjtb/pBUeRdvDm5dmY/VPvh+CkvCeXNze\nHPZ8vcQ+ZZNS44O9rMnSUOtRFZb3ow3atXsx53Gy9rp41Bd0OZgSMrnHH74lDQX0\nkkNP+UrRYs66q0gJaSZglzkWfHLtAGfuRh9XyBh4kBgHcjF1Qh6frTpotX9t+0V/\nQZv3KjPVMsGaUN407WHEoAl6qX6TSS/An2EdXgqbhXS5O81gzatWDTcT2D3VJG1N\nHYtkh1J5WmrFTphc7OhxmVk7l3UkWPyS8Oi8be2y8Q4x0wgviZn5eOa/djpHoarW\nLS91KKPZXGyXlj49TlCjbl4RfyKYOd/HqgAYYdtqBe84AyJQRvuD5gWmdBzagncb\nyKjs6tYr74aAGnAqulp+yqvrzb7teUQmCMkROfzFjYZmLByqw6UGRdHgCf8hOzmO\nch4hf9cHRLAUJpqynRmb+g==\n-----END CERTIFICATE-----\n" defaultVCEK := "-----BEGIN CERTIFICATE-----\nMIIFTDCCAvugAwIBAgIBADBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQCAgUA\noRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBATB7MRQwEgYD\nVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDASBgNVBAcMC1NhbnRhIENs\nYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5jZWQgTWljcm8gRGV2aWNl\nczESMBAGA1UEAwwJU0VWLU1pbGFuMB4XDTIyMDYyOTE2MzEzMFoXDTI5MDYyOTE2\nMzEzMFowejEUMBIGA1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVTMRQwEgYD\nVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFkdmFuY2Vk\nIE1pY3JvIERldmljZXMxETAPBgNVBAMMCFNFVi1WQ0VLMHYwEAYHKoZIzj0CAQYF\nK4EEACIDYgAEhPX8Cl9uA7PxqNGzeqamJNYJLx/VFE/s3+8qOWtaztKNcn1PaAI4\nndE+yaVfMHsiA8CLTylumpWXcVBHPYV9kPEVrtozhvrrT5Oii9OpZPYHJ7/WPVmM\nJ3K8/Iz3AshTo4IBFjCCARIwEAYJKwYBBAGceAEBBAMCAQAwFwYJKwYBBAGceAEC\nBAoWCE1pbGFuLUIwMBEGCisGAQQBnHgBAwEEAwIBAjARBgorBgEEAZx4AQMCBAMC\nAQAwEQYKKwYBBAGceAEDBAQDAgEAMBEGCisGAQQBnHgBAwUEAwIBADARBgorBgEE\nAZx4AQMGBAMCAQAwEQYKKwYBBAGceAEDBwQDAgEAMBEGCisGAQQBnHgBAwMEAwIB\nBjARBgorBgEEAZx4AQMIBAMCAV0wTQYJKwYBBAGceAEEBECeRKrvAs/Kb926ymac\nbP0p4auNl+vJOYVxKKy7E7h0DfMUNtNOhuX4rgzf6zoOGF20beysF2zHfXYcIqG5\n3PJbMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0B\nAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBA4ICAQBXXzX8w+z06JNKLVAa9vyE\njC69c7uvfTPScqLzOCV+S8yZ7Ibpn6gdRcgn5s3F7uerVs9/8mq+rDpMzLTVxLei\nYAW9jDS9VdEgfUp3GzzL1g3zsWNZPpWuAu0Cw1V7KnQ9kiGsJMRKerx8QLrm+aAH\nOiob4XHl2naUx9aILzCLbNgLBdh6Tw2XkGj8NB9O7kNQoINEz6U+cAJL5LWzuoYt\nW1IJkYUEMydvLImFHeFIFtB2wI4mTSuCjtb/pBUeRdvDm5dmY/VPvh+CkvCeXNze\nHPZ8vcQ+ZZNS44O9rMnSUOtRFZb3ow3atXsx53Gy9rp41Bd0OZgSMrnHH74lDQX0\nkkNP+UrRYs66q0gJaSZglzkWfHLtAGfuRh9XyBh4kBgHcjF1Qh6frTpotX9t+0V/\nQZv3KjPVMsGaUN407WHEoAl6qX6TSS/An2EdXgqbhXS5O81gzatWDTcT2D3VJG1N\nHYtkh1J5WmrFTphc7OhxmVk7l3UkWPyS8Oi8be2y8Q4x0wgviZn5eOa/djpHoarW\nLS91KKPZXGyXlj49TlCjbl4RfyKYOd/HqgAYYdtqBe84AyJQRvuD5gWmdBzagncb\nyKjs6tYr74aAGnAqulp+yqvrzb7teUQmCMkROfzFjYZmLByqw6UGRdHgCf8hOzmO\nch4hf9cHRLAUJpqynRmb+g==\n-----END CERTIFICATE-----\n"
defaultCertChain := "-----BEGIN CERTIFICATE-----\n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\n-----END CERTIFICATE-----\n-----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" defaultCertChain := "-----BEGIN CERTIFICATE-----\n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\n-----END CERTIFICATE-----\n-----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"
defaultIDKeyDigest := "57e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc1" defaultIDKeyDigestOld, err := hex.DecodeString("57e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc1")
require.NoError(err)
defaultIDKeyDigest := idkeydigest.NewIDKeyDigests([][]byte{defaultIDKeyDigestOld})
testCases := map[string]struct { testCases := map[string]struct {
report string report string
runtimeData string runtimeData string
vcek string vcek string
certChain string certChain string
idkeydigest string idkeydigest idkeydigest.IDKeyDigests
enforceIDKeyDigest bool enforceIDKeyDigest bool
wantErr bool wantErr bool
assertCorrectError func(error) assertCorrectError func(error)
@ -118,7 +121,7 @@ func TestTrustedKeyFromSNP(t *testing.T) {
runtimeData: defaultRuntimeData, runtimeData: defaultRuntimeData,
vcek: defaultVCEK, vcek: defaultVCEK,
certChain: defaultCertChain, certChain: defaultCertChain,
idkeydigest: "67e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc1", idkeydigest: idkeydigest.IDKeyDigests{[]byte{0x00}},
enforceIDKeyDigest: true, enforceIDKeyDigest: true,
wantErr: true, wantErr: true,
assertCorrectError: func(err error) { assertCorrectError: func(err error) {
@ -131,7 +134,7 @@ func TestTrustedKeyFromSNP(t *testing.T) {
runtimeData: defaultRuntimeData, runtimeData: defaultRuntimeData,
vcek: defaultVCEK, vcek: defaultVCEK,
certChain: defaultCertChain, certChain: defaultCertChain,
idkeydigest: "", idkeydigest: idkeydigest.IDKeyDigests{[]byte{0x00}},
}, },
"unsupported microcode version": { "unsupported microcode version": {
report: "02000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065c0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c02000001000000020000000100000048020000", report: "02000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065c0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c02000001000000020000000100000048020000",
@ -198,10 +201,7 @@ func TestTrustedKeyFromSNP(t *testing.T) {
assert.Error(err) assert.Error(err)
} }
idkeydigest, err := hex.DecodeString(tc.idkeydigest) key, err := getTrustedKey(&instanceInfo, tc.idkeydigest, tc.enforceIDKeyDigest, nil)(akPub, statement)
assert.NoError(err)
key, err := getTrustedKey(&instanceInfo, idkeydigest, tc.enforceIDKeyDigest, nil)(akPub, statement)
if tc.wantErr { if tc.wantErr {
tc.assertCorrectError(err) tc.assertCorrectError(err)
} else { } else {

View File

@ -0,0 +1,117 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package idkeydigest
import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"go.uber.org/multierr"
)
// IDKeyDigests is a list of trusted digest values for the ID key.
type IDKeyDigests [][]byte
type encodedIDKeyDigests []string
// encodedDigestLength is the length of a digest in hex encoding.
const encodedDigestLength = 2 * 48
// NewIDKeyDigests creates a new IDKeyDigests from a list of digests.
func NewIDKeyDigests(digests [][]byte) IDKeyDigests {
idKeyDigests := make(IDKeyDigests, len(digests))
copy(idKeyDigests, digests)
return idKeyDigests
}
// DefaultsFor returns the default IDKeyDigests for the given cloud provider.
func DefaultsFor(csp cloudprovider.Provider) IDKeyDigests {
switch csp {
case cloudprovider.Azure:
return IDKeyDigests{
{0x57, 0x48, 0x6a, 0x44, 0x7e, 0xc0, 0xf1, 0x95, 0x80, 0x02, 0xa2, 0x2a, 0x06, 0xb7, 0x67, 0x3b, 0x9f, 0xd2, 0x7d, 0x11, 0xe1, 0xc6, 0x52, 0x74, 0x98, 0x05, 0x60, 0x54, 0xc5, 0xfa, 0x92, 0xd2, 0x3c, 0x50, 0xf9, 0xde, 0x44, 0x07, 0x27, 0x60, 0xfe, 0x2b, 0x6f, 0xb8, 0x97, 0x40, 0xb6, 0x96},
{0x03, 0x56, 0x21, 0x58, 0x82, 0xa8, 0x25, 0x27, 0x9a, 0x85, 0xb3, 0x00, 0xb0, 0xb7, 0x42, 0x93, 0x1d, 0x11, 0x3b, 0xf7, 0xe3, 0x2d, 0xde, 0x2e, 0x50, 0xff, 0xde, 0x7e, 0xc7, 0x43, 0xca, 0x49, 0x1e, 0xcd, 0xd7, 0xf3, 0x36, 0xdc, 0x28, 0xa6, 0xe0, 0xb2, 0xbb, 0x57, 0xaf, 0x7a, 0x44, 0xa3},
}
default:
return nil
}
}
// MarshalYAML implements the yaml.Marshaler interface.
func (d IDKeyDigests) MarshalYAML() (any, error) {
encodedIDKeyDigests := []string{}
for _, digest := range d {
encodedIDKeyDigests = append(encodedIDKeyDigests, hex.EncodeToString(digest))
}
return encodedIDKeyDigests, nil
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (d *IDKeyDigests) UnmarshalYAML(unmarshal func(any) error) error {
var encodedDigests encodedIDKeyDigests
if err := unmarshal(&encodedDigests); err != nil {
// Unmarshalling failed, IDKeyDigests might be a simple string instead of IDKeyDigests struct.
var unmarshalledString string
if legacyErr := unmarshal(&unmarshalledString); legacyErr != nil {
return multierr.Append(
err,
fmt.Errorf("trying legacy format: %w", legacyErr),
)
}
encodedDigests = append(encodedDigests, unmarshalledString)
}
if err := d.unmarshal(encodedDigests); err != nil {
return fmt.Errorf("unmarshalling yaml: %w", err)
}
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (d IDKeyDigests) MarshalJSON() ([]byte, error) {
encodedIDKeyDigests := []string{}
for _, digest := range d {
encodedIDKeyDigests = append(encodedIDKeyDigests, hex.EncodeToString(digest))
}
return json.Marshal(encodedIDKeyDigests)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (d *IDKeyDigests) UnmarshalJSON(b []byte) error {
var encodedDigests encodedIDKeyDigests
if err := json.Unmarshal(b, &encodedDigests); err != nil {
// Unmarshalling failed, IDKeyDigests might be a simple string instead of IDKeyDigests struct.
var unmarshalledString string
if legacyErr := json.Unmarshal(b, &unmarshalledString); legacyErr != nil {
return multierr.Append(
err,
fmt.Errorf("trying legacy format: %w", legacyErr),
)
}
encodedDigests = []string{unmarshalledString}
}
if err := d.unmarshal(encodedDigests); err != nil {
return fmt.Errorf("unmarshalling json: %w", err)
}
return nil
}
// unmarshal is a helper function for unmarshalling encodedIDKeyDigests into IDKeyDigests.
func (d *IDKeyDigests) unmarshal(encodedDigests encodedIDKeyDigests) error {
for _, encodedDigest := range encodedDigests {
if len(encodedDigest) != encodedDigestLength {
return fmt.Errorf("invalid digest length: %d", len(encodedDigest))
}
digest, err := hex.DecodeString(encodedDigest)
if err != nil {
return fmt.Errorf("decoding digest: %w", err)
}
*d = append(*d, digest)
}
return nil
}

View File

@ -0,0 +1,119 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package idkeydigest
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)
func TestMarshal(t *testing.T) {
testCases := map[string]struct {
dgst IDKeyDigests
wantYAML string
wantJSON string
}{
"digest": {
dgst: IDKeyDigests{{0x01, 0x02, 0x03, 0x04}, {0xff, 0xff, 0xff, 0xff}},
wantJSON: `["01020304","ffffffff"]`,
wantYAML: `
- "01020304"
- "ffffffff"`,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
{
// YAML
yaml, err := yaml.Marshal(tc.dgst)
require.NoError(err)
assert.YAMLEq(tc.wantYAML, string(yaml))
}
{
// JSON
json, err := json.Marshal(tc.dgst)
require.NoError(err)
assert.JSONEq(tc.wantJSON, string(json))
}
})
}
}
func TestUnmarshal(t *testing.T) {
testCases := map[string]struct {
yaml string
json string
wantDgst IDKeyDigests
wantErr bool
}{
"digest struct": {
json: `["57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696","0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3"]`,
yaml: `
- "57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696"
- "0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3"`,
wantDgst: IDKeyDigests{
{0x57, 0x48, 0x6a, 0x44, 0x7e, 0xc0, 0xf1, 0x95, 0x80, 0x02, 0xa2, 0x2a, 0x06, 0xb7, 0x67, 0x3b, 0x9f, 0xd2, 0x7d, 0x11, 0xe1, 0xc6, 0x52, 0x74, 0x98, 0x05, 0x60, 0x54, 0xc5, 0xfa, 0x92, 0xd2, 0x3c, 0x50, 0xf9, 0xde, 0x44, 0x07, 0x27, 0x60, 0xfe, 0x2b, 0x6f, 0xb8, 0x97, 0x40, 0xb6, 0x96},
{0x03, 0x56, 0x21, 0x58, 0x82, 0xa8, 0x25, 0x27, 0x9a, 0x85, 0xb3, 0x00, 0xb0, 0xb7, 0x42, 0x93, 0x1d, 0x11, 0x3b, 0xf7, 0xe3, 0x2d, 0xde, 0x2e, 0x50, 0xff, 0xde, 0x7e, 0xc7, 0x43, 0xca, 0x49, 0x1e, 0xcd, 0xd7, 0xf3, 0x36, 0xdc, 0x28, 0xa6, 0xe0, 0xb2, 0xbb, 0x57, 0xaf, 0x7a, 0x44, 0xa3},
},
},
"legacy digest as string": {
json: `"57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696"`,
yaml: `"57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696"`,
wantDgst: IDKeyDigests{{0x57, 0x48, 0x6a, 0x44, 0x7e, 0xc0, 0xf1, 0x95, 0x80, 0x02, 0xa2, 0x2a, 0x06, 0xb7, 0x67, 0x3b, 0x9f, 0xd2, 0x7d, 0x11, 0xe1, 0xc6, 0x52, 0x74, 0x98, 0x05, 0x60, 0x54, 0xc5, 0xfa, 0x92, 0xd2, 0x3c, 0x50, 0xf9, 0xde, 0x44, 0x07, 0x27, 0x60, 0xfe, 0x2b, 0x6f, 0xb8, 0x97, 0x40, 0xb6, 0x96}},
},
"invalid length": {
json: `"010203"`,
yaml: `"010203"`,
wantDgst: IDKeyDigests{{}},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
{
// YAML
var dgst IDKeyDigests
err := yaml.Unmarshal([]byte(tc.yaml), &dgst)
if tc.wantErr {
require.Error(err)
} else {
require.NoError(err)
assert.Equal(tc.wantDgst, dgst)
}
}
{
// JSON
var dgst IDKeyDigests
err := json.Unmarshal([]byte(tc.json), &dgst)
if tc.wantErr {
require.Error(err)
} else {
require.NoError(err)
assert.Equal(tc.wantDgst, dgst)
}
}
})
}
}

View File

@ -1,36 +0,0 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package vmtype
import "strings"
//go:generate stringer -type=VMType
// VMType describes different vm types we support. Introduced for Azure SNP / Trusted Launch attestation.
type VMType uint32
const (
// Unknown is the default value for VMType and should not be used.
Unknown VMType = iota
// AzureCVM is an Azure Confidential Virtual Machine (CVM).
AzureCVM
// AzureTrustedLaunch is an Azure Trusted Launch VM.
AzureTrustedLaunch
)
// FromString returns a VMType from a string.
func FromString(s string) VMType {
s = strings.ToLower(s)
switch s {
case "azurecvm":
return AzureCVM
case "azuretrustedlaunch":
return AzureTrustedLaunch
default:
return Unknown
}
}

View File

@ -1,25 +0,0 @@
// Code generated by "stringer -type=VMType"; DO NOT EDIT.
package vmtype
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Unknown-0]
_ = x[AzureCVM-1]
_ = x[AzureTrustedLaunch-2]
}
const _VMType_name = "UnknownAzureCVMAzureTrustedLaunch"
var _VMType_index = [...]uint8{0, 7, 15, 33}
func (i VMType) String() string {
if i >= VMType(len(_VMType_index)-1) {
return "VMType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _VMType_name[_VMType_index[i]:_VMType_index[i+1]]
}

View File

@ -17,6 +17,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
@ -33,6 +34,10 @@ import (
// types in other packages. // types in other packages.
type Measurements = measurements.M type Measurements = measurements.M
// Digests is a required alias since docgen is not able to work with
// types in other packages.
type Digests = idkeydigest.IDKeyDigests
const ( const (
// Version2 is the second version number for Constellation config file. // Version2 is the second version number for Constellation config file.
Version2 = "v2" Version2 = "v2"
@ -161,8 +166,8 @@ type AzureConfig struct {
// Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob. // Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob.
SecureBoot *bool `yaml:"secureBoot" validate:"required"` SecureBoot *bool `yaml:"secureBoot" validate:"required"`
// description: | // description: |
// Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf // List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf
IDKeyDigest string `yaml:"idKeyDigest" validate:"required_if=EnforceIdKeyDigest true,omitempty,hexadecimal,len=96"` IDKeyDigests Digests `yaml:"idKeyDigests" validate:"required_if=EnforceIdKeyDigest true,omitempty"`
// description: | // description: |
// Enforce the specified idKeyDigest value during remote attestation. // Enforce the specified idKeyDigest value during remote attestation.
EnforceIDKeyDigest *bool `yaml:"enforceIdKeyDigest" validate:"required"` EnforceIDKeyDigest *bool `yaml:"enforceIdKeyDigest" validate:"required"`
@ -255,7 +260,7 @@ func Default() *Config {
InstanceType: "Standard_DC4as_v5", InstanceType: "Standard_DC4as_v5",
StateDiskType: "Premium_LRS", StateDiskType: "Premium_LRS",
DeployCSIDriver: func() *bool { b := true; return &b }(), DeployCSIDriver: func() *bool { b := true; return &b }(),
IDKeyDigest: "57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696", IDKeyDigests: idkeydigest.DefaultsFor(cloudprovider.Azure),
EnforceIDKeyDigest: func() *bool { b := true; return &b }(), EnforceIDKeyDigest: func() *bool { b := true; return &b }(),
ConfidentialVM: func() *bool { b := true; return &b }(), ConfidentialVM: func() *bool { b := true; return &b }(),
SecureBoot: func() *bool { b := false; return &b }(), SecureBoot: func() *bool { b := false; return &b }(),
@ -428,6 +433,14 @@ func (c *Config) EnforcedPCRs() []uint32 {
} }
} }
// IDKeyDigests returns the ID Key Digests for the configured cloud provider.
func (c *Config) IDKeyDigests() idkeydigest.IDKeyDigests {
if c.Provider.Azure != nil {
return c.Provider.Azure.IDKeyDigests
}
return nil
}
// DeployCSIDriver returns whether the CSI driver should be deployed for a given cloud provider. // DeployCSIDriver returns whether the CSI driver should be deployed for a given cloud provider.
func (c *Config) DeployCSIDriver() bool { func (c *Config) DeployCSIDriver() bool {
return c.Provider.Azure != nil && c.Provider.Azure.DeployCSIDriver != nil && *c.Provider.Azure.DeployCSIDriver || return c.Provider.Azure != nil && c.Provider.Azure.DeployCSIDriver != nil && *c.Provider.Azure.DeployCSIDriver ||

View File

@ -238,11 +238,11 @@ func init() {
AzureConfigDoc.Fields[11].Note = "" AzureConfigDoc.Fields[11].Note = ""
AzureConfigDoc.Fields[11].Description = "Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob." AzureConfigDoc.Fields[11].Description = "Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob."
AzureConfigDoc.Fields[11].Comments[encoder.LineComment] = "Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob." AzureConfigDoc.Fields[11].Comments[encoder.LineComment] = "Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob."
AzureConfigDoc.Fields[12].Name = "idKeyDigest" AzureConfigDoc.Fields[12].Name = "idKeyDigests"
AzureConfigDoc.Fields[12].Type = "string" AzureConfigDoc.Fields[12].Type = "Digests"
AzureConfigDoc.Fields[12].Note = "" AzureConfigDoc.Fields[12].Note = ""
AzureConfigDoc.Fields[12].Description = "Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf" AzureConfigDoc.Fields[12].Description = "List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
AzureConfigDoc.Fields[12].Comments[encoder.LineComment] = "Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf" AzureConfigDoc.Fields[12].Comments[encoder.LineComment] = "List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
AzureConfigDoc.Fields[13].Name = "enforceIdKeyDigest" AzureConfigDoc.Fields[13].Name = "enforceIdKeyDigest"
AzureConfigDoc.Fields[13].Type = "bool" AzureConfigDoc.Fields[13].Type = "bool"
AzureConfigDoc.Fields[13].Note = "" AzureConfigDoc.Fields[13].Note = ""

View File

@ -111,7 +111,7 @@ const (
// MeasurementSecretFilename is the filename of the secret used in creation of the clusterID. // MeasurementSecretFilename is the filename of the secret used in creation of the clusterID.
MeasurementSecretFilename = "measurementSecret" MeasurementSecretFilename = "measurementSecret"
// IDKeyDigestFilename is the name of the file holding the currently enforced idkeydigest. // IDKeyDigestFilename is the name of the file holding the currently enforced idkeydigest.
IDKeyDigestFilename = "idkeydigest" IDKeyDigestFilename = "idkeydigests"
// EnforceIDKeyDigestFilename is the name of the file configuring whether idkeydigest is enforced or not. // EnforceIDKeyDigestFilename is the name of the file configuring whether idkeydigest is enforced or not.
EnforceIDKeyDigestFilename = "enforceIdKeyDigest" EnforceIDKeyDigestFilename = "enforceIdKeyDigest"
// AzureCVM is the name of the file indicating whether the cluster is expected to run on CVMs or not. // AzureCVM is the name of the file indicating whether the cluster is expected to run on CVMs or not.

View File

@ -8,7 +8,7 @@ package watcher
import ( import (
"encoding/asn1" "encoding/asn1"
"encoding/hex" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@ -21,6 +21,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp"
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch"
"github.com/edgelesssys/constellation/v2/internal/attestation/gcp" "github.com/edgelesssys/constellation/v2/internal/attestation/gcp"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/qemu" "github.com/edgelesssys/constellation/v2/internal/attestation/qemu"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
@ -45,25 +46,25 @@ func NewValidator(log *logger.Logger, csp string, fileHandler file.Handler, azur
var newValidator newValidatorFunc var newValidator newValidatorFunc
switch cloudprovider.FromString(csp) { switch cloudprovider.FromString(csp) {
case cloudprovider.AWS: case cloudprovider.AWS:
newValidator = func(m measurements.M, _ []byte, _ bool, log *logger.Logger) atls.Validator { newValidator = func(m measurements.M, _ idkeydigest.IDKeyDigests, _ bool, log *logger.Logger) atls.Validator {
return aws.NewValidator(m, log) return aws.NewValidator(m, log)
} }
case cloudprovider.Azure: case cloudprovider.Azure:
if azureCVM { if azureCVM {
newValidator = func(m measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator { newValidator = func(m measurements.M, idkeydigest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator {
return snp.NewValidator(m, idkeydigest, enforceIdKeyDigest, log) return snp.NewValidator(m, idkeydigest, enforceIdKeyDigest, log)
} }
} else { } else {
newValidator = func(m measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator { newValidator = func(m measurements.M, idkeydigest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator {
return trustedlaunch.NewValidator(m, log) return trustedlaunch.NewValidator(m, log)
} }
} }
case cloudprovider.GCP: case cloudprovider.GCP:
newValidator = func(m measurements.M, _ []byte, _ bool, log *logger.Logger) atls.Validator { newValidator = func(m measurements.M, _ idkeydigest.IDKeyDigests, _ bool, log *logger.Logger) atls.Validator {
return gcp.NewValidator(m, log) return gcp.NewValidator(m, log)
} }
case cloudprovider.QEMU: case cloudprovider.QEMU:
newValidator = func(m measurements.M, _ []byte, _ bool, log *logger.Logger) atls.Validator { newValidator = func(m measurements.M, _ idkeydigest.IDKeyDigests, _ bool, log *logger.Logger) atls.Validator {
return qemu.NewValidator(m, log) return qemu.NewValidator(m, log)
} }
default: default:
@ -124,7 +125,7 @@ func (u *Updatable) Update() error {
return err return err
} }
var idkeydigest []byte var digest idkeydigest.IDKeyDigests
var enforceIDKeyDigest bool var enforceIDKeyDigest bool
if u.csp == cloudprovider.Azure && u.azureCVM { if u.csp == cloudprovider.Azure && u.azureCVM {
u.log.Infof("Updating encforceIdKeyDigest value") u.log.Infof("Updating encforceIdKeyDigest value")
@ -143,16 +144,15 @@ func (u *Updatable) Update() error {
if err != nil { if err != nil {
return err return err
} }
idkeydigest, err = hex.DecodeString(string(idkeydigestRaw)) if err = json.Unmarshal(idkeydigestRaw, &digest); err != nil {
if err != nil { return fmt.Errorf("unmarshaling content of IDKeyDigestFilename: %s: %w", idkeydigestRaw, err)
return fmt.Errorf("parsing hexstring: %s: %w", idkeydigestRaw, err)
} }
u.log.Debugf("New idkeydigest: %x", idkeydigest) u.log.Debugf("New idkeydigest: %v", digest)
} }
u.Validator = u.newValidator(measurements, idkeydigest, enforceIDKeyDigest, u.log) u.Validator = u.newValidator(measurements, digest, enforceIDKeyDigest, u.log)
return nil return nil
} }
type newValidatorFunc func(measurements measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator type newValidatorFunc func(measurements measurements.M, idkeydigest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator

View File

@ -20,6 +20,7 @@ import (
"testing" "testing"
"github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/atls"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
@ -125,7 +126,7 @@ func TestUpdate(t *testing.T) {
defer oidLock.Unlock() defer oidLock.Unlock()
oid = newOID oid = newOID
} }
newValidator := func(m measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator { newValidator := func(m measurements.M, digest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
oidLock.Lock() oidLock.Lock()
defer oidLock.Unlock() defer oidLock.Unlock()
return fakeValidator{fakeOID: oid} return fakeValidator{fakeOID: oid}
@ -225,7 +226,7 @@ func TestOIDConcurrency(t *testing.T) {
[]byte{}, []byte{},
)) ))
newValidator := func(m measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator { newValidator := func(m measurements.M, digest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}} return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}}
} }
// create server // create server
@ -261,7 +262,7 @@ func TestUpdateConcurrency(t *testing.T) {
validator := &Updatable{ validator := &Updatable{
log: logger.NewTest(t), log: logger.NewTest(t),
fileHandler: handler, fileHandler: handler,
newValidator: func(m measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator { newValidator: func(m measurements.M, digest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}} return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}}
}, },
} }