From dd2da25ebe3df7ca3c4f61a0a05bf5266ce61b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wei=C3=9Fe?= <66256922+daniel-weisse@users.noreply.github.com> Date: Wed, 8 Mar 2023 14:13:57 +0100 Subject: [PATCH] attestation: tdx issuer/validator (#1265) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add TDX validator * Add TDX issuer --------- Signed-off-by: Daniel Weiße --- bazel/toolchains/go_module_deps.bzl | 17 +++ bootstrapper/cmd/bootstrapper/BUILD.bazel | 2 + bootstrapper/cmd/bootstrapper/main.go | 25 ++-- bootstrapper/cmd/bootstrapper/run.go | 7 +- bootstrapper/internal/nodelock/BUILD.bazel | 5 +- bootstrapper/internal/nodelock/nodelock.go | 3 +- cli/internal/cloudcmd/validators.go | 2 +- cli/internal/cloudcmd/validators_test.go | 4 +- cli/internal/terraform/BUILD.bazel | 1 + .../modules/instance_group/tdx_domain.xsl | 16 --- disk-mapper/internal/setup/BUILD.bazel | 1 + disk-mapper/internal/setup/setup.go | 3 +- go.mod | 2 + go.sum | 4 + hack/go.mod | 2 + hack/go.sum | 4 + internal/attestation/attestation.go | 43 ++++++ internal/attestation/attestation_test.go | 46 +++++++ internal/attestation/aws/BUILD.bazel | 1 + internal/attestation/aws/issuer.go | 3 +- internal/attestation/aws/validator.go | 3 +- internal/attestation/azure/snp/BUILD.bazel | 1 + internal/attestation/azure/snp/issuer.go | 3 +- internal/attestation/azure/snp/validator.go | 5 +- .../azure/trustedlaunch/BUILD.bazel | 1 + .../attestation/azure/trustedlaunch/issuer.go | 3 +- .../azure/trustedlaunch/validator.go | 3 +- internal/attestation/choose/BUILD.bazel | 3 +- internal/attestation/choose/choose.go | 11 +- internal/attestation/gcp/BUILD.bazel | 1 + internal/attestation/gcp/issuer.go | 3 +- internal/attestation/gcp/validator.go | 3 +- internal/attestation/initialize/BUILD.bazel | 29 ++++ internal/attestation/initialize/initialize.go | 130 ++++++++++++++++++ .../{vtpm => initialize}/initialize_test.go | 2 +- .../measurement-generator/generate.go | 13 +- .../attestation/measurements/measurements.go | 65 ++++++++- .../measurements/measurements_enterprise.go | 10 +- .../measurements/measurements_test.go | 59 +++++--- internal/attestation/qemu/BUILD.bazel | 1 + internal/attestation/qemu/issuer.go | 3 +- internal/attestation/qemu/validator.go | 3 +- internal/attestation/tdx/BUILD.bazel | 21 +++ internal/attestation/tdx/issuer.go | 67 +++++++++ internal/attestation/tdx/tdx.go | 95 +++++++++++++ internal/attestation/tdx/validator.go | 94 +++++++++++++ internal/attestation/vtpm/BUILD.bazel | 4 +- internal/attestation/vtpm/attestation.go | 42 ++---- internal/attestation/vtpm/attestation_test.go | 13 +- internal/attestation/vtpm/initialize.go | 95 ------------- internal/attestation/vtpm/vtpm_test.go | 7 - internal/config/config.go | 31 +++++ internal/variant/variant.go | 22 +++ 53 files changed, 808 insertions(+), 229 deletions(-) create mode 100644 internal/attestation/initialize/BUILD.bazel create mode 100644 internal/attestation/initialize/initialize.go rename internal/attestation/{vtpm => initialize}/initialize_test.go (99%) create mode 100644 internal/attestation/tdx/BUILD.bazel create mode 100644 internal/attestation/tdx/issuer.go create mode 100644 internal/attestation/tdx/tdx.go create mode 100644 internal/attestation/tdx/validator.go delete mode 100644 internal/attestation/vtpm/initialize.go diff --git a/bazel/toolchains/go_module_deps.bzl b/bazel/toolchains/go_module_deps.bzl index 4ed214d9c..b2e1ff9fb 100644 --- a/bazel/toolchains/go_module_deps.bzl +++ b/bazel/toolchains/go_module_deps.bzl @@ -1889,6 +1889,14 @@ def go_dependencies(): sum = "h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=", version = "v0.0.0-20230303085714-62ede861d33f", ) + go_repository( + name = "com_github_edgelesssys_go_tdx_qpl", + build_file_generation = "on", + build_file_proto_mode = "disable_global", + importpath = "github.com/edgelesssys/go-tdx-qpl", + sum = "h1:uQMmc/B1RGE2VeSsh/NqjRgEheqp1cjy8ELIDTFpaUw=", + version = "v0.0.0-20230307140231-bb361f158928", + ) go_repository( name = "com_github_edsrzf_mmap_go", @@ -6680,6 +6688,15 @@ def go_dependencies(): sum = "h1:gpw/0Ku+6RgF3jsi7fnCLmlcikBHfKBCUcu1qgc16OU=", version = "v0.20.3", ) + go_repository( + name = "com_github_vtolstov_go_ioctl", + build_file_generation = "on", + build_file_proto_mode = "disable_global", + importpath = "github.com/vtolstov/go-ioctl", + sum = "h1:X6ps8XHfpQjw8dUStzlMi2ybiKQ2Fmdw7UM+TinwvyM=", + version = "v0.0.0-20151206205506-6be9cced4810", + ) + go_repository( name = "com_github_weppos_publicsuffix_go", build_file_generation = "on", diff --git a/bootstrapper/cmd/bootstrapper/BUILD.bazel b/bootstrapper/cmd/bootstrapper/BUILD.bazel index d0a311163..19dbc3285 100644 --- a/bootstrapper/cmd/bootstrapper/BUILD.bazel +++ b/bootstrapper/cmd/bootstrapper/BUILD.bazel @@ -23,7 +23,9 @@ go_library( "//bootstrapper/internal/nodelock", "//internal/atls", "//internal/attestation/choose", + "//internal/attestation/initialize", "//internal/attestation/simulator", + "//internal/attestation/tdx", "//internal/attestation/vtpm", "//internal/cloud/aws", "//internal/cloud/azure", diff --git a/bootstrapper/cmd/bootstrapper/main.go b/bootstrapper/cmd/bootstrapper/main.go index 9f529ac78..986cc9e47 100644 --- a/bootstrapper/cmd/bootstrapper/main.go +++ b/bootstrapper/cmd/bootstrapper/main.go @@ -23,6 +23,7 @@ import ( "github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging" "github.com/edgelesssys/constellation/v2/internal/attestation/choose" "github.com/edgelesssys/constellation/v2/internal/attestation/simulator" + "github.com/edgelesssys/constellation/v2/internal/attestation/tdx" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" awscloud "github.com/edgelesssys/constellation/v2/internal/cloud/aws" azurecloud "github.com/edgelesssys/constellation/v2/internal/cloud/azure" @@ -63,7 +64,7 @@ func main() { var clusterInitJoiner clusterInitJoiner var metadataAPI metadataAPI var cloudLogger logging.CloudLogger - var openTPM vtpm.TPMOpenFunc + var openDevice vtpm.TPMOpenFunc var fs afero.Fs helmClient, err := helm.New(log) @@ -97,7 +98,7 @@ func main() { "aws", k8sapi.NewKubernetesUtil(), &k8sapi.KubdeadmConfiguration{}, kubectl.New(), metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{}, ) - openTPM = vtpm.OpenVTPM + openDevice = vtpm.OpenVTPM fs = afero.NewOsFs() case cloudprovider.GCP: @@ -117,7 +118,7 @@ func main() { "gcp", k8sapi.NewKubernetesUtil(), &k8sapi.KubdeadmConfiguration{}, kubectl.New(), metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{}, ) - openTPM = vtpm.OpenVTPM + openDevice = vtpm.OpenVTPM fs = afero.NewOsFs() log.Infof("Added load balancer IP to routing table") @@ -136,7 +137,7 @@ func main() { metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{}, ) - openTPM = vtpm.OpenVTPM + openDevice = vtpm.OpenVTPM fs = afero.NewOsFs() case cloudprovider.QEMU: @@ -148,7 +149,16 @@ func main() { ) metadataAPI = metadata - openTPM = vtpm.OpenVTPM + switch attestVariant { + case variant.QEMUVTPM{}: + openDevice = vtpm.OpenVTPM + case variant.QEMUTDX{}: + openDevice = func() (io.ReadWriteCloser, error) { + return tdx.Open() + } + default: + log.Fatalf("Unsupported attestation variant: %s", attestVariant) + } fs = afero.NewOsFs() case cloudprovider.OpenStack: cloudLogger = &logging.NopLogger{} @@ -162,19 +172,18 @@ func main() { ) metadataAPI = metadata - openTPM = vtpm.OpenVTPM fs = afero.NewOsFs() default: clusterInitJoiner = &clusterFake{} metadataAPI = &providerMetadataFake{} cloudLogger = &logging.NopLogger{} var simulatedTPMCloser io.Closer - openTPM, simulatedTPMCloser = simulator.NewSimulatedTPMOpenFunc() + openDevice, simulatedTPMCloser = simulator.NewSimulatedTPMOpenFunc() defer simulatedTPMCloser.Close() fs = afero.NewMemMapFs() } fileHandler := file.NewHandler(fs) - run(issuer, openTPM, fileHandler, clusterInitJoiner, metadataAPI, bindIP, bindPort, log, cloudLogger) + run(issuer, openDevice, fileHandler, clusterInitJoiner, metadataAPI, bindIP, bindPort, log, cloudLogger) } diff --git a/bootstrapper/cmd/bootstrapper/run.go b/bootstrapper/cmd/bootstrapper/run.go index 0a6639b2e..f9b133e09 100644 --- a/bootstrapper/cmd/bootstrapper/run.go +++ b/bootstrapper/cmd/bootstrapper/run.go @@ -17,6 +17,7 @@ import ( "github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/nodelock" "github.com/edgelesssys/constellation/v2/internal/atls" + "github.com/edgelesssys/constellation/v2/internal/attestation/initialize" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/file" @@ -25,7 +26,7 @@ import ( "go.uber.org/zap" ) -func run(issuer atls.Issuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler, +func run(issuer atls.Issuer, openDevice vtpm.TPMOpenFunc, fileHandler file.Handler, kube clusterInitJoiner, metadata metadataAPI, bindIP, bindPort string, log *logger.Logger, cloudLogger logging.CloudLogger, @@ -44,7 +45,7 @@ func run(issuer atls.Issuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler, cloudLogger.Disclose("Disk UUID: " + uuid) } - nodeBootstrapped, err := vtpm.IsNodeBootstrapped(tpm) + nodeBootstrapped, err := initialize.IsNodeBootstrapped(openDevice) if err != nil { log.With(zap.Error(err)).Fatalf("Failed to check if node was previously bootstrapped") } @@ -56,7 +57,7 @@ func run(issuer atls.Issuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler, return } - nodeLock := nodelock.New(tpm) + nodeLock := nodelock.New(openDevice) initServer, err := initserver.New(context.Background(), nodeLock, kube, issuer, fileHandler, metadata, log) if err != nil { log.With(zap.Error(err)).Fatalf("Failed to create init server") diff --git a/bootstrapper/internal/nodelock/BUILD.bazel b/bootstrapper/internal/nodelock/BUILD.bazel index a49d8bc6d..3efb9c78f 100644 --- a/bootstrapper/internal/nodelock/BUILD.bazel +++ b/bootstrapper/internal/nodelock/BUILD.bazel @@ -5,5 +5,8 @@ go_library( srcs = ["nodelock.go"], importpath = "github.com/edgelesssys/constellation/v2/bootstrapper/internal/nodelock", visibility = ["//bootstrapper:__subpackages__"], - deps = ["//internal/attestation/vtpm"], + deps = [ + "//internal/attestation/initialize", + "//internal/attestation/vtpm", + ], ) diff --git a/bootstrapper/internal/nodelock/nodelock.go b/bootstrapper/internal/nodelock/nodelock.go index cf4805d6d..0e4053eda 100644 --- a/bootstrapper/internal/nodelock/nodelock.go +++ b/bootstrapper/internal/nodelock/nodelock.go @@ -10,6 +10,7 @@ package nodelock import ( "sync" + "github.com/edgelesssys/constellation/v2/internal/attestation/initialize" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" ) @@ -40,5 +41,5 @@ func (l *Lock) TryLockOnce(clusterID []byte) (bool, error) { return false, nil } - return true, vtpm.MarkNodeAsBootstrapped(l.tpm, clusterID) + return true, initialize.MarkNodeAsBootstrapped(l.tpm, clusterID) } diff --git a/cli/internal/cloudcmd/validators.go b/cli/internal/cloudcmd/validators.go index 7653ec3e3..caa410762 100644 --- a/cli/internal/cloudcmd/validators.go +++ b/cli/internal/cloudcmd/validators.go @@ -59,7 +59,7 @@ func updatePCR(m measurements.M, pcrIndex uint32, encoded string) error { oldExpected := m[pcrIndex].Expected expectedPcr := sha256.Sum256(append(oldExpected[:], hashedInput[:]...)) m[pcrIndex] = measurements.Measurement{ - Expected: expectedPcr, + Expected: expectedPcr[:], ValidationOpt: m[pcrIndex].ValidationOpt, } return nil diff --git a/cli/internal/cloudcmd/validators_test.go b/cli/internal/cloudcmd/validators_test.go index b0da5782d..f0003f998 100644 --- a/cli/internal/cloudcmd/validators_test.go +++ b/cli/internal/cloudcmd/validators_test.go @@ -132,7 +132,7 @@ func TestValidatorUpdateInitPCRs(t *testing.T) { case i == int(measurements.PCRIndexClusterID): pcr, ok := m[uint32(i)] assert.True(ok) - assert.Equal(pcrZeroUpdatedOne, pcr.Expected) + assert.Equal(pcrZeroUpdatedOne[:], pcr.Expected) case i == int(measurements.PCRIndexOwnerID) && tc.ownerID == "": // should be deleted @@ -142,7 +142,7 @@ func TestValidatorUpdateInitPCRs(t *testing.T) { case i == int(measurements.PCRIndexOwnerID): pcr, ok := m[uint32(i)] assert.True(ok) - assert.Equal(pcrZeroUpdatedOne, pcr.Expected) + assert.Equal(pcrZeroUpdatedOne[:], pcr.Expected) default: if i >= 17 && i <= 22 { diff --git a/cli/internal/terraform/BUILD.bazel b/cli/internal/terraform/BUILD.bazel index 9d1a9ad59..aa1fd8bdf 100644 --- a/cli/internal/terraform/BUILD.bazel +++ b/cli/internal/terraform/BUILD.bazel @@ -69,6 +69,7 @@ go_library( "terraform/openstack/modules/loadbalancer/variables.tf", "terraform/openstack/outputs.tf", "terraform/openstack/variables.tf", + "terraform/qemu/modules/instance_group/tdx_domain.xsl", ], importpath = "github.com/edgelesssys/constellation/v2/cli/internal/terraform", visibility = ["//cli:__subpackages__"], diff --git a/cli/internal/terraform/terraform/qemu/modules/instance_group/tdx_domain.xsl b/cli/internal/terraform/terraform/qemu/modules/instance_group/tdx_domain.xsl index 76fb167c9..69257a7b7 100644 --- a/cli/internal/terraform/terraform/qemu/modules/instance_group/tdx_domain.xsl +++ b/cli/internal/terraform/terraform/qemu/modules/instance_group/tdx_domain.xsl @@ -82,22 +82,6 @@ - - - - - - - - - - - - - - - - diff --git a/disk-mapper/internal/setup/BUILD.bazel b/disk-mapper/internal/setup/BUILD.bazel index 10539b9ab..3b5d51edc 100644 --- a/disk-mapper/internal/setup/BUILD.bazel +++ b/disk-mapper/internal/setup/BUILD.bazel @@ -12,6 +12,7 @@ go_library( deps = [ "//disk-mapper/internal/systemd", "//internal/attestation", + "//internal/attestation/initialize", "//internal/attestation/vtpm", "//internal/cloud/metadata", "//internal/constants", diff --git a/disk-mapper/internal/setup/setup.go b/disk-mapper/internal/setup/setup.go index 699041fed..06cd97997 100644 --- a/disk-mapper/internal/setup/setup.go +++ b/disk-mapper/internal/setup/setup.go @@ -26,6 +26,7 @@ import ( "github.com/edgelesssys/constellation/v2/disk-mapper/internal/systemd" "github.com/edgelesssys/constellation/v2/internal/attestation" + "github.com/edgelesssys/constellation/v2/internal/attestation/initialize" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/crypto" @@ -109,7 +110,7 @@ func (s *Manager) PrepareExistingDisk(recover RecoveryDoer) error { } // taint the node as initialized - if err := vtpm.MarkNodeAsBootstrapped(s.openTPM, clusterID); err != nil { + if err := initialize.MarkNodeAsBootstrapped(s.openTPM, clusterID); err != nil { return err } diff --git a/go.mod b/go.mod index 640acb4c2..eb41e4b5b 100644 --- a/go.mod +++ b/go.mod @@ -70,6 +70,7 @@ require ( github.com/docker/docker v23.0.3+incompatible github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0 github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f + github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928 github.com/fsnotify/fsnotify v1.6.0 github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 @@ -301,6 +302,7 @@ require ( github.com/theupdateframework/go-tuf v0.5.2 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/transparency-dev/merkle v0.0.1 // indirect + github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect diff --git a/go.sum b/go.sum index 992b83766..41254dc07 100644 --- a/go.sum +++ b/go.sum @@ -422,6 +422,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU= github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc= +github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928 h1:uQMmc/B1RGE2VeSsh/NqjRgEheqp1cjy8ELIDTFpaUw= +github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928/go.mod h1:IC72qyykUIWl0ZmSk53L4xbLCFDBEGZVaujUmPQOEyw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= @@ -1362,6 +1364,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9 github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810 h1:X6ps8XHfpQjw8dUStzlMi2ybiKQ2Fmdw7UM+TinwvyM= +github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810/go.mod h1:dF0BBJ2YrV1+2eAIyEI+KeSidgA6HqoIP1u5XTlMq/o= github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= diff --git a/hack/go.mod b/hack/go.mod index 2fc716c49..bc04d269c 100644 --- a/hack/go.mod +++ b/hack/go.mod @@ -125,6 +125,7 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0 // indirect github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f // indirect + github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928 // indirect github.com/emicklei/go-restful/v3 v3.10.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect @@ -259,6 +260,7 @@ require ( github.com/theupdateframework/go-tuf v0.5.2 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/transparency-dev/merkle v0.0.1 // indirect + github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect diff --git a/hack/go.sum b/hack/go.sum index b5cc49a15..a69c8a300 100644 --- a/hack/go.sum +++ b/hack/go.sum @@ -394,6 +394,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU= github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc= +github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928 h1:uQMmc/B1RGE2VeSsh/NqjRgEheqp1cjy8ELIDTFpaUw= +github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928/go.mod h1:IC72qyykUIWl0ZmSk53L4xbLCFDBEGZVaujUmPQOEyw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= @@ -1351,6 +1353,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9 github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810 h1:X6ps8XHfpQjw8dUStzlMi2ybiKQ2Fmdw7UM+TinwvyM= +github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810/go.mod h1:dF0BBJ2YrV1+2eAIyEI+KeSidgA6HqoIP1u5XTlMq/o= github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= diff --git a/internal/attestation/attestation.go b/internal/attestation/attestation.go index 52475623d..f09988dce 100644 --- a/internal/attestation/attestation.go +++ b/internal/attestation/attestation.go @@ -29,6 +29,9 @@ Attestation code for new platforms needs to implement these two interfaces. package attestation import ( + "bytes" + "crypto/sha256" + "github.com/edgelesssys/constellation/v2/internal/crypto" ) @@ -40,7 +43,47 @@ const ( MeasurementSecretContext = "measurementSecret" ) +// Logger is a logger used to print warnings and infos during attestation validation. +type Logger interface { + Infof(format string, args ...any) + Warnf(format string, args ...any) +} + +// NOPLogger is a no-op implementation of [Logger]. +type NOPLogger struct{} + +// Infof is a no-op. +func (NOPLogger) Infof(string, ...interface{}) {} + +// Warnf is a no-op. +func (NOPLogger) Warnf(string, ...interface{}) {} + // DeriveClusterID derives the cluster ID from a salt and secret value. func DeriveClusterID(secret, salt []byte) ([]byte, error) { return crypto.DeriveKey(secret, salt, []byte(crypto.DEKPrefix+clusterIDContext), crypto.DerivedKeyLengthDefault) } + +// MakeExtraData binds userData to a random nonce used in attestation. +func MakeExtraData(userData []byte, nonce []byte) []byte { + data := append([]byte{}, userData...) + data = append(data, nonce...) + digest := sha256.Sum256(data) + return digest[:] +} + +// CompareExtraData compares the extra data of a quote with the expected extra data. +// Returns true if the data from the quote matches the expected data. +// If the slices are not of equal length, the shorter slice is padded with zeros. +func CompareExtraData(quoteData, expectedData []byte) bool { + if len(quoteData) != len(expectedData) { + // If the lengths are not equal, pad the shorter slice with zeros. + diff := len(quoteData) - len(expectedData) + if diff < 0 { + diff = -diff + quoteData = append(quoteData, bytes.Repeat([]byte{0x00}, diff)...) + } else { + expectedData = append(expectedData, bytes.Repeat([]byte{0x00}, diff)...) + } + } + return bytes.Equal(quoteData, expectedData) +} diff --git a/internal/attestation/attestation_test.go b/internal/attestation/attestation_test.go index 1092bd5ed..3615859c0 100644 --- a/internal/attestation/attestation_test.go +++ b/internal/attestation/attestation_test.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only package attestation import ( + "bytes" "testing" "github.com/edgelesssys/constellation/v2/internal/crypto/testvector" @@ -31,3 +32,48 @@ func TestDeriveClusterID(t *testing.T) { require.NoError(err) assert.NotEqual(clusterID, clusterIDdiff) } + +func TestCompareExtraData(t *testing.T) { + testCases := map[string]struct { + ExtraData1 []byte + ExtraData2 []byte + Expected bool + }{ + "equal": { + ExtraData1: bytes.Repeat([]byte{0xAB}, 32), + ExtraData2: bytes.Repeat([]byte{0xAB}, 32), + Expected: true, + }, + "unequal": { + ExtraData1: bytes.Repeat([]byte{0xAB}, 32), + ExtraData2: bytes.Repeat([]byte{0xCD}, 32), + Expected: false, + }, + "unequal length": { + ExtraData1: bytes.Repeat([]byte{0xAB}, 32), + ExtraData2: bytes.Repeat([]byte{0xAB}, 64), + Expected: false, + }, + "unequal length, padded with 0": { + ExtraData1: []byte{0xAB, 0xAB, 0xAB, 0xAB}, + ExtraData2: []byte{0xAB, 0xAB, 0xAB, 0xAB, 0x00, 0x00, 0x00, 0x00}, + Expected: true, + }, + "unequal length, prefixed with 0": { + ExtraData1: []byte{0x00, 0x00, 0x00, 0x00, 0xAB, 0xAB, 0xAB, 0xAB}, + ExtraData2: []byte{0xAB, 0xAB, 0xAB, 0xAB}, + Expected: false, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + + actual := CompareExtraData(tc.ExtraData1, tc.ExtraData2) + assert.Equal(tc.Expected, actual) + actual = CompareExtraData(tc.ExtraData2, tc.ExtraData1) + assert.Equal(tc.Expected, actual) + }) + } +} diff --git a/internal/attestation/aws/BUILD.bazel b/internal/attestation/aws/BUILD.bazel index b22062ed8..730e93615 100644 --- a/internal/attestation/aws/BUILD.bazel +++ b/internal/attestation/aws/BUILD.bazel @@ -11,6 +11,7 @@ go_library( importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/aws", visibility = ["//:__subpackages__"], deps = [ + "//internal/attestation", "//internal/attestation/vtpm", "//internal/config", "//internal/variant", diff --git a/internal/attestation/aws/issuer.go b/internal/attestation/aws/issuer.go index 4a4ff3fe6..ae8f51200 100644 --- a/internal/attestation/aws/issuer.go +++ b/internal/attestation/aws/issuer.go @@ -14,6 +14,7 @@ import ( "log" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/variant" @@ -28,7 +29,7 @@ type Issuer struct { } // NewIssuer creates a new OpenVTPM based issuer for AWS. -func NewIssuer(log vtpm.AttestationLogger) *Issuer { +func NewIssuer(log attestation.Logger) *Issuer { return &Issuer{ Issuer: vtpm.NewIssuer( vtpm.OpenVTPM, diff --git a/internal/attestation/aws/validator.go b/internal/attestation/aws/validator.go index ee948a4fe..28c162820 100644 --- a/internal/attestation/aws/validator.go +++ b/internal/attestation/aws/validator.go @@ -15,6 +15,7 @@ import ( 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/vtpm" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/variant" @@ -30,7 +31,7 @@ type Validator struct { } // NewValidator create a new Validator structure and returns it. -func NewValidator(cfg *config.AWSNitroTPM, log vtpm.AttestationLogger) *Validator { +func NewValidator(cfg *config.AWSNitroTPM, log attestation.Logger) *Validator { v := &Validator{} v.Validator = vtpm.NewValidator( cfg.Measurements, diff --git a/internal/attestation/azure/snp/BUILD.bazel b/internal/attestation/azure/snp/BUILD.bazel index baf2bc164..90769735e 100644 --- a/internal/attestation/azure/snp/BUILD.bazel +++ b/internal/attestation/azure/snp/BUILD.bazel @@ -14,6 +14,7 @@ go_library( importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp", visibility = ["//:__subpackages__"], deps = [ + "//internal/attestation", "//internal/attestation/idkeydigest", "//internal/attestation/vtpm", "//internal/cloud/azure", diff --git a/internal/attestation/azure/snp/issuer.go b/internal/attestation/azure/snp/issuer.go index 93586190e..38b4855be 100644 --- a/internal/attestation/azure/snp/issuer.go +++ b/internal/attestation/azure/snp/issuer.go @@ -12,6 +12,7 @@ import ( "fmt" "io" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/go-azguestattestation/maa" @@ -30,7 +31,7 @@ type Issuer struct { } // NewIssuer initializes a new Azure Issuer. -func NewIssuer(log vtpm.AttestationLogger) *Issuer { +func NewIssuer(log attestation.Logger) *Issuer { i := &Issuer{ imds: newIMDSClient(), maa: newMAAClient(), diff --git a/internal/attestation/azure/snp/validator.go b/internal/attestation/azure/snp/validator.go index 772c2cf54..f1f85fbfe 100644 --- a/internal/attestation/azure/snp/validator.go +++ b/internal/attestation/azure/snp/validator.go @@ -20,6 +20,7 @@ import ( "fmt" "math/big" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/config" @@ -38,11 +39,11 @@ type Validator struct { config *config.AzureSEVSNP - log vtpm.AttestationLogger + log attestation.Logger } // NewValidator initializes a new Azure validator with the provided PCR values. -func NewValidator(cfg *config.AzureSEVSNP, log vtpm.AttestationLogger) *Validator { +func NewValidator(cfg *config.AzureSEVSNP, log attestation.Logger) *Validator { if log == nil { log = nopAttestationLogger{} } diff --git a/internal/attestation/azure/trustedlaunch/BUILD.bazel b/internal/attestation/azure/trustedlaunch/BUILD.bazel index f56d5ca0a..ae4621941 100644 --- a/internal/attestation/azure/trustedlaunch/BUILD.bazel +++ b/internal/attestation/azure/trustedlaunch/BUILD.bazel @@ -11,6 +11,7 @@ go_library( importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch", visibility = ["//:__subpackages__"], deps = [ + "//internal/attestation", "//internal/attestation/vtpm", "//internal/config", "//internal/crypto", diff --git a/internal/attestation/azure/trustedlaunch/issuer.go b/internal/attestation/azure/trustedlaunch/issuer.go index 830426ccf..d848c3451 100644 --- a/internal/attestation/azure/trustedlaunch/issuer.go +++ b/internal/attestation/azure/trustedlaunch/issuer.go @@ -15,6 +15,7 @@ import ( "io" "net/http" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/variant" tpmclient "github.com/google/go-tpm-tools/client" @@ -34,7 +35,7 @@ type Issuer struct { } // NewIssuer initializes a new Azure Issuer. -func NewIssuer(log vtpm.AttestationLogger) *Issuer { +func NewIssuer(log attestation.Logger) *Issuer { i := &Issuer{ hClient: &http.Client{}, } diff --git a/internal/attestation/azure/trustedlaunch/validator.go b/internal/attestation/azure/trustedlaunch/validator.go index b7169a857..4ccc9b6d5 100644 --- a/internal/attestation/azure/trustedlaunch/validator.go +++ b/internal/attestation/azure/trustedlaunch/validator.go @@ -15,6 +15,7 @@ import ( "errors" "fmt" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/config" certutil "github.com/edgelesssys/constellation/v2/internal/crypto" @@ -35,7 +36,7 @@ type Validator struct { } // NewValidator initializes a new Azure validator with the provided PCR values. -func NewValidator(cfg *config.AzureTrustedLaunch, log vtpm.AttestationLogger) *Validator { +func NewValidator(cfg *config.AzureTrustedLaunch, log attestation.Logger) *Validator { rootPool := x509.NewCertPool() rootPool.AddCert(ameRoot) v := &Validator{roots: rootPool} diff --git a/internal/attestation/choose/BUILD.bazel b/internal/attestation/choose/BUILD.bazel index 1049e5ac7..705c52bc4 100644 --- a/internal/attestation/choose/BUILD.bazel +++ b/internal/attestation/choose/BUILD.bazel @@ -8,12 +8,13 @@ go_library( visibility = ["//:__subpackages__"], deps = [ "//internal/atls", + "//internal/attestation", "//internal/attestation/aws", "//internal/attestation/azure/snp", "//internal/attestation/azure/trustedlaunch", "//internal/attestation/gcp", "//internal/attestation/qemu", - "//internal/attestation/vtpm", + "//internal/attestation/tdx", "//internal/config", "//internal/variant", ], diff --git a/internal/attestation/choose/choose.go b/internal/attestation/choose/choose.go index e02d12dde..0a3419ee1 100644 --- a/internal/attestation/choose/choose.go +++ b/internal/attestation/choose/choose.go @@ -10,18 +10,19 @@ import ( "fmt" "github.com/edgelesssys/constellation/v2/internal/atls" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/aws" "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/gcp" "github.com/edgelesssys/constellation/v2/internal/attestation/qemu" - "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" + "github.com/edgelesssys/constellation/v2/internal/attestation/tdx" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/variant" ) // Issuer returns the issuer for the given variant. -func Issuer(attestationVariant variant.Variant, log vtpm.AttestationLogger) (atls.Issuer, error) { +func Issuer(attestationVariant variant.Variant, log attestation.Logger) (atls.Issuer, error) { switch attestationVariant { case variant.AWSNitroTPM{}: return aws.NewIssuer(log), nil @@ -33,6 +34,8 @@ func Issuer(attestationVariant variant.Variant, log vtpm.AttestationLogger) (atl return gcp.NewIssuer(log), nil case variant.QEMUVTPM{}: return qemu.NewIssuer(log), nil + case variant.QEMUTDX{}: + return tdx.NewIssuer(log), nil case variant.Dummy{}: return atls.NewFakeIssuer(variant.Dummy{}), nil default: @@ -41,7 +44,7 @@ func Issuer(attestationVariant variant.Variant, log vtpm.AttestationLogger) (atl } // Validator returns the validator for the given variant. -func Validator(cfg config.AttestationCfg, log vtpm.AttestationLogger) (atls.Validator, error) { +func Validator(cfg config.AttestationCfg, log attestation.Logger) (atls.Validator, error) { switch cfg := cfg.(type) { case *config.AWSNitroTPM: return aws.NewValidator(cfg, log), nil @@ -53,6 +56,8 @@ func Validator(cfg config.AttestationCfg, log vtpm.AttestationLogger) (atls.Vali return gcp.NewValidator(cfg, log), nil case *config.QEMUVTPM: return qemu.NewValidator(cfg, log), nil + case *config.QEMUTDX: + return tdx.NewValidator(cfg, log), nil case *config.DummyCfg: return atls.NewFakeValidator(variant.Dummy{}), nil default: diff --git a/internal/attestation/gcp/BUILD.bazel b/internal/attestation/gcp/BUILD.bazel index 0b4b455b5..299804ddf 100644 --- a/internal/attestation/gcp/BUILD.bazel +++ b/internal/attestation/gcp/BUILD.bazel @@ -11,6 +11,7 @@ go_library( importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/gcp", visibility = ["//:__subpackages__"], deps = [ + "//internal/attestation", "//internal/attestation/vtpm", "//internal/config", "//internal/variant", diff --git a/internal/attestation/gcp/issuer.go b/internal/attestation/gcp/issuer.go index ccd17ac18..4ea1a66d6 100644 --- a/internal/attestation/gcp/issuer.go +++ b/internal/attestation/gcp/issuer.go @@ -13,6 +13,7 @@ import ( "io" "cloud.google.com/go/compute/metadata" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/variant" tpmclient "github.com/google/go-tpm-tools/client" @@ -26,7 +27,7 @@ type Issuer struct { } // NewIssuer initializes a new GCP Issuer. -func NewIssuer(log vtpm.AttestationLogger) *Issuer { +func NewIssuer(log attestation.Logger) *Issuer { return &Issuer{ Issuer: vtpm.NewIssuer( vtpm.OpenVTPM, diff --git a/internal/attestation/gcp/validator.go b/internal/attestation/gcp/validator.go index b7a83169d..43b8d428c 100644 --- a/internal/attestation/gcp/validator.go +++ b/internal/attestation/gcp/validator.go @@ -16,6 +16,7 @@ import ( compute "cloud.google.com/go/compute/apiv1" "cloud.google.com/go/compute/apiv1/computepb" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/variant" @@ -35,7 +36,7 @@ type Validator struct { } // NewValidator initializes a new GCP validator with the provided PCR values. -func NewValidator(cfg *config.GCPSEVES, log vtpm.AttestationLogger) *Validator { +func NewValidator(cfg *config.GCPSEVES, log attestation.Logger) *Validator { v := &Validator{ restClient: newInstanceClient, } diff --git a/internal/attestation/initialize/BUILD.bazel b/internal/attestation/initialize/BUILD.bazel new file mode 100644 index 000000000..2b05cc694 --- /dev/null +++ b/internal/attestation/initialize/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("//bazel/go:go_test.bzl", "go_test") + +go_library( + name = "initialize", + srcs = ["initialize.go"], + importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/initialize", + visibility = ["//:__subpackages__"], + deps = [ + "//internal/attestation/measurements", + "//internal/attestation/tdx", + "@com_github_edgelesssys_go_tdx_qpl//tdx", + "@com_github_google_go_tpm//tpm2", + ], +) + +go_test( + name = "initialize_test", + srcs = ["initialize_test.go"], + embed = [":initialize"], + deps = [ + "//internal/attestation/measurements", + "//internal/attestation/simulator", + "@com_github_google_go_tpm//tpm2", + "@com_github_google_go_tpm_tools//client", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + ], +) diff --git a/internal/attestation/initialize/initialize.go b/internal/attestation/initialize/initialize.go new file mode 100644 index 000000000..49b7d3a8c --- /dev/null +++ b/internal/attestation/initialize/initialize.go @@ -0,0 +1,130 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +// Package initialize implements functions to mark a node as initialized in the context of cluster attestation. +// This is done by measuring the cluster ID using the available CC technology. +package initialize + +import ( + "bytes" + "errors" + "io" + + "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" + "github.com/edgelesssys/constellation/v2/internal/attestation/tdx" + tdxapi "github.com/edgelesssys/go-tdx-qpl/tdx" + "github.com/google/go-tpm/tpm2" +) + +// MarkNodeAsBootstrapped marks a node as initialized by extending PCRs. +// clusterID is used to uniquely identify this running instance of Constellation. +func MarkNodeAsBootstrapped(openDevice func() (io.ReadWriteCloser, error), clusterID []byte) error { + device, err := openDevice() + if err != nil { + return err + } + defer device.Close() + + // The TDX device is of type *os.File, while the TPM device may be + // *os.File or an emulated device over a unix socket. + // Therefore, we can't simply use a type switch here, + // since the TPM may implement the same methods as the TDX device + if handle, ok := tdx.IsTDXDevice(device); ok { + return tdxMarkNodeAsBootstrapped(handle, clusterID) + } + return tpmMarkNodeAsBootstrapped(device, clusterID) +} + +// IsNodeBootstrapped checks if a node is already bootstrapped by reading PCRs. +func IsNodeBootstrapped(openDevice func() (io.ReadWriteCloser, error)) (bool, error) { + device, err := openDevice() + if err != nil { + return false, err + } + defer device.Close() + + // The TDX device is of type *os.File, while the TPM device may be + // *os.File or an emulated device over a unix socket. + // Therefore, we can't simply use a type switch here, + // since the TPM may implement the same methods as the TDX device + if handle, ok := tdx.IsTDXDevice(device); ok { + return tdxIsNodeBootstrapped(handle) + } + return tpmIsNodeBootstrapped(device) +} + +func tdxIsNodeBootstrapped(handle tdx.Device) (bool, error) { + tdMeasure, err := tdxapi.ReadMeasurements(handle) + if err != nil { + return false, err + } + return measurementInitialized(tdMeasure[measurements.TDXIndexClusterID][:]), nil +} + +func tpmIsNodeBootstrapped(tpm io.ReadWriteCloser) (bool, error) { + idxClusterID := int(measurements.PCRIndexClusterID) + pcrs, err := tpm2.ReadPCRs(tpm, tpm2.PCRSelection{ + Hash: tpm2.AlgSHA256, + PCRs: []int{idxClusterID}, + }) + if err != nil { + return false, err + } + if len(pcrs[idxClusterID]) == 0 { + return false, errors.New("cluster ID PCR does not exist") + } + return measurementInitialized(pcrs[idxClusterID]), nil + + /* Old code that will be reenabled in the future + idxOwner := int(PCRIndexOwnerID) + idxCluster := int(PCRIndexClusterID) + selection := tpm2.PCRSelection{ + Hash: tpm2.AlgSHA256, + PCRs: []int{idxOwner, idxCluster}, + } + + pcrs, err := tpm2.ReadPCRs(tpm, selection) + if err != nil { + return false, err + } + + if len(pcrs[idxOwner]) == 0 { + return false, errors.New("owner ID PCR does not exist") + } + if len(pcrs[idxCluster]) == 0 { + return false, errors.New("cluster ID PCR does not exist") + } + + ownerInitialized := pcrInitialized(pcrs[idxOwner]) + clusterInitialized := pcrInitialized(pcrs[idxCluster]) + + if ownerInitialized == clusterInitialized { + return ownerInitialized && clusterInitialized, nil + } + ownerState := "not initialized" + if ownerInitialized { + ownerState = "initialized" + } + clusterState := "not initialized" + if clusterInitialized { + clusterState = "initialized" + } + return false, fmt.Errorf("PCRs %v and %v are not consistent: PCR[%v]=%v (%v), PCR[%v]=%v (%v)", idxOwner, idxCluster, idxOwner, pcrs[idxOwner], ownerState, idxCluster, pcrs[idxCluster], clusterState) + */ +} + +func tdxMarkNodeAsBootstrapped(handle tdx.Device, clusterID []byte) error { + return tdxapi.ExtendRTMR(handle, clusterID, measurements.RTMRIndexClusterID) +} + +func tpmMarkNodeAsBootstrapped(tpm io.ReadWriteCloser, clusterID []byte) error { + return tpm2.PCREvent(tpm, measurements.PCRIndexClusterID, clusterID) +} + +// measurementInitialized checks if a PCR value is set to a non-zero value. +func measurementInitialized(measurement []byte) bool { + return !bytes.Equal(measurement, bytes.Repeat([]byte{0x00}, len(measurement))) +} diff --git a/internal/attestation/vtpm/initialize_test.go b/internal/attestation/initialize/initialize_test.go similarity index 99% rename from internal/attestation/vtpm/initialize_test.go rename to internal/attestation/initialize/initialize_test.go index b9b2a8496..ce1508968 100644 --- a/internal/attestation/vtpm/initialize_test.go +++ b/internal/attestation/initialize/initialize_test.go @@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package vtpm +package initialize import ( "errors" diff --git a/internal/attestation/measurements/measurement-generator/generate.go b/internal/attestation/measurements/measurement-generator/generate.go index 7a06cb900..fbafbb4fc 100644 --- a/internal/attestation/measurements/measurement-generator/generate.go +++ b/internal/attestation/measurements/measurement-generator/generate.go @@ -172,8 +172,8 @@ func verifyWithRekor(ctx context.Context, verifier rekorVerifier, hash string) e // byteArrayCompositeLit returns a *ast.CompositeLit representing a byte array literal. // The returned literal is of the form: -// [32]byte{ 0x01, 0x02, 0x03, ... }. -func byteArrayCompositeLit(hex [32]byte) *ast.CompositeLit { +// []byte{ 0x01, 0x02, 0x03, ... }. +func byteArrayCompositeLit(hex []byte) *ast.CompositeLit { var elts []ast.Expr // create list of byte literals for _, b := range hex { @@ -183,10 +183,9 @@ func byteArrayCompositeLit(hex [32]byte) *ast.CompositeLit { }) } // create the composite literal - // containing the byte literals as part of an [32]byte array + // containing the byte literals as part of an []byte slice return &ast.CompositeLit{ Type: &ast.ArrayType{ - Len: ast.NewIdent("32"), Elt: ast.NewIdent("byte"), }, Elts: elts, @@ -197,7 +196,7 @@ func byteArrayCompositeLit(hex [32]byte) *ast.CompositeLit { // The returned expression is of the form: // // 0: { -// Expected: [32]byte{ 0x01, 0x02, 0x03, ... }, +// Expected: []byte{ 0x01, 0x02, 0x03, ... }, // WarnOnly: false, // }, func measurementsEntryKeyValueExpr(pcr uint32, measuremnt measurements.Measurement) *ast.KeyValueExpr { @@ -235,11 +234,11 @@ func measurementsEntryKeyValueExpr(pcr uint32, measuremnt measurements.Measureme // // M{ // 0: { -// Expected: [32]byte{ 0x01, 0x02, 0x03, ... }, +// Expected: []byte{ 0x01, 0x02, 0x03, ... }, // WarnOnly: false, // }, // 1: { -// Expected: [32]byte{ 0x01, 0x02, 0x03, ... }, +// Expected: []byte{ 0x01, 0x02, 0x03, ... }, // WarnOnly: false, // }, // ... diff --git a/internal/attestation/measurements/measurements.go b/internal/attestation/measurements/measurements.go index 26e057f9f..79a90f310 100644 --- a/internal/attestation/measurements/measurements.go +++ b/internal/attestation/measurements/measurements.go @@ -43,6 +43,12 @@ const ( // The value used to extend is derived from Constellation's master key. // TODO: move to stable, non-debug PCR before use. PCRIndexOwnerID = tpmutil.Handle(16) + + // TDXIndexClusterID is the measurement used to mark the node as initialized. + // The value is the index of the RTMR + 1, since index 0 of the TDX measurements is reserved for MRTD. + TDXIndexClusterID = RTMRIndexClusterID + 1 + // RTMRIndexClusterID is the RTMR we extend to mark the node as initialized. + RTMRIndexClusterID = 2 ) // M are Platform Configuration Register (PCR) values that make up the Measurements. @@ -128,7 +134,7 @@ func (m *M) EqualTo(other M) bool { } for k, v := range *m { otherExpected := other[k].Expected - if !bytes.Equal(v.Expected[:], otherExpected[:]) { + if !bytes.Equal(v.Expected, otherExpected) { return false } if v.ValidationOpt != other[k].ValidationOpt { @@ -177,10 +183,45 @@ func (m *M) SetEnforced(enforced []uint32) error { return nil } +// UnmarshalJSON unmarshals measurements from json. +// This function enforces all measurements to be of equal length. +func (m *M) UnmarshalJSON(b []byte) error { + newM := make(map[uint32]Measurement) + if err := json.Unmarshal(b, &newM); err != nil { + return err + } + + // check if all measurements are of equal length + if err := checkLength(newM); err != nil { + return err + } + + *m = newM + return nil +} + +// UnmarshalYAML unmarshals measurements from yaml. +// This function enforces all measurements to be of equal length. +func (m *M) UnmarshalYAML(unmarshal func(any) error) error { + newM := make(map[uint32]Measurement) + if err := unmarshal(&newM); err != nil { + return err + } + + // check if all measurements are of equal length + if err := checkLength(newM); err != nil { + return err + } + + *m = newM + return nil +} + // Measurement wraps expected PCR value and whether it is enforced. type Measurement struct { // Expected measurement value. - Expected [32]byte `json:"expected" yaml:"expected"` + // 32 bytes for vTPM attestation, 48 for TDX. + Expected []byte `json:"expected" yaml:"expected"` // ValidationOpt indicates how measurement mismatches should be handled. ValidationOpt MeasurementValidationOption `json:"warnOnly" yaml:"warnOnly"` } @@ -269,11 +310,11 @@ func (m *Measurement) unmarshal(eM encodedMeasurement) error { } } - if len(expected) != 32 { + if len(expected) != 32 && len(expected) != 48 { return fmt.Errorf("invalid measurement: invalid length: %d", len(expected)) } - m.Expected = *(*[32]byte)(expected) + m.Expected = expected m.ValidationOpt = eM.WarnOnly return nil @@ -282,7 +323,7 @@ func (m *Measurement) unmarshal(eM encodedMeasurement) error { // WithAllBytes returns a measurement value where all 32 bytes are set to b. func WithAllBytes(b byte, validationOpt MeasurementValidationOption) Measurement { return Measurement{ - Expected: *(*[32]byte)(bytes.Repeat([]byte{b}, 32)), + Expected: bytes.Repeat([]byte{b}, 32), ValidationOpt: validationOpt, } } @@ -290,7 +331,7 @@ func WithAllBytes(b byte, validationOpt MeasurementValidationOption) Measurement // PlaceHolderMeasurement returns a measurement with placeholder values for Expected. func PlaceHolderMeasurement() Measurement { return Measurement{ - Expected: *(*[32]byte)(bytes.Repeat([]byte{0x12, 0x34}, 16)), + Expected: bytes.Repeat([]byte{0x12, 0x34}, 16), ValidationOpt: Enforce, } } @@ -316,6 +357,18 @@ func getFromURL(ctx context.Context, client *http.Client, sourceURL *url.URL) ([ return content, nil } +func checkLength(m map[uint32]Measurement) error { + var length int + for idx, measurement := range m { + if length == 0 { + length = len(measurement.Expected) + } else if len(measurement.Expected) != length { + return fmt.Errorf("inconsistent measurement length: index %d: expected %d, got %d", idx, length, len(measurement.Expected)) + } + } + return nil +} + type encodedMeasurement struct { Expected string `json:"expected" yaml:"expected"` WarnOnly MeasurementValidationOption `json:"warnOnly" yaml:"warnOnly"` diff --git a/internal/attestation/measurements/measurements_enterprise.go b/internal/attestation/measurements/measurements_enterprise.go index 7f97de9d7..8237ad7af 100644 --- a/internal/attestation/measurements/measurements_enterprise.go +++ b/internal/attestation/measurements/measurements_enterprise.go @@ -16,19 +16,19 @@ import "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" //go:generate measurement-generator // DefaultsFor provides the default measurements for given cloud provider. -func DefaultsFor(provider cloudprovider.Provider) M { +func DefaultsFor(attestationVariant variant.Variant) M { switch provider { case cloudprovider.AWS: - return M{0: {Expected: [32]byte{0x73, 0x7f, 0x76, 0x7a, 0x12, 0xf5, 0x4e, 0x70, 0xee, 0xcb, 0xc8, 0x68, 0x40, 0x11, 0x32, 0x3a, 0xe2, 0xfe, 0x2d, 0xd9, 0xf9, 0x07, 0x85, 0x57, 0x79, 0x69, 0xd7, 0xa2, 0x01, 0x3e, 0x8c, 0x12}, ValidationOpt: WarnOnly}, 2: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: [32]byte{0x42, 0x3e, 0x51, 0x3f, 0x60, 0x8d, 0x3e, 0x0e, 0x7b, 0xa4, 0xc4, 0xa4, 0x97, 0x50, 0xb4, 0x21, 0xed, 0x49, 0xde, 0xa6, 0x93, 0xb4, 0xdd, 0x60, 0xaa, 0x39, 0x0f, 0x12, 0x26, 0x34, 0x28, 0x38}, ValidationOpt: Enforce}, 6: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 7: {Expected: [32]byte{0x12, 0x0e, 0x49, 0x8d, 0xb2, 0xa2, 0x24, 0xbd, 0x51, 0x2b, 0x6e, 0xfc, 0x9b, 0x02, 0x34, 0xf8, 0x43, 0xe1, 0x0b, 0xf0, 0x61, 0xeb, 0x7a, 0x76, 0xec, 0xca, 0x55, 0x09, 0xa2, 0x23, 0x89, 0x01}, ValidationOpt: WarnOnly}, 8: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: [32]byte{0xe1, 0x13, 0x34, 0x51, 0x4d, 0xf4, 0x9a, 0xf5, 0x6a, 0x2b, 0x13, 0x45, 0x3b, 0x3e, 0xbd, 0xc8, 0x96, 0xb0, 0xac, 0x1f, 0x36, 0x4c, 0x5c, 0x68, 0x34, 0xd4, 0x4a, 0x43, 0x6b, 0x80, 0x54, 0xe5}, ValidationOpt: Enforce}, 11: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: [32]byte{0x2a, 0xc0, 0xcd, 0x92, 0x5e, 0x18, 0x07, 0xfd, 0xe4, 0xf1, 0x48, 0x68, 0xa9, 0xdb, 0x58, 0x3e, 0x88, 0x24, 0x45, 0x50, 0x66, 0x5a, 0x2c, 0x5f, 0xc3, 0x7b, 0xac, 0xc5, 0x3d, 0x69, 0xb6, 0xa5}, ValidationOpt: Enforce}, 13: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: [32]byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}} + return M{0: {Expected: []byte{0x73, 0x7f, 0x76, 0x7a, 0x12, 0xf5, 0x4e, 0x70, 0xee, 0xcb, 0xc8, 0x68, 0x40, 0x11, 0x32, 0x3a, 0xe2, 0xfe, 0x2d, 0xd9, 0xf9, 0x07, 0x85, 0x57, 0x79, 0x69, 0xd7, 0xa2, 0x01, 0x3e, 0x8c, 0x12}, ValidationOpt: WarnOnly}, 2: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: []byte{0x42, 0x3e, 0x51, 0x3f, 0x60, 0x8d, 0x3e, 0x0e, 0x7b, 0xa4, 0xc4, 0xa4, 0x97, 0x50, 0xb4, 0x21, 0xed, 0x49, 0xde, 0xa6, 0x93, 0xb4, 0xdd, 0x60, 0xaa, 0x39, 0x0f, 0x12, 0x26, 0x34, 0x28, 0x38}, ValidationOpt: Enforce}, 6: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 7: {Expected: []byte{0x12, 0x0e, 0x49, 0x8d, 0xb2, 0xa2, 0x24, 0xbd, 0x51, 0x2b, 0x6e, 0xfc, 0x9b, 0x02, 0x34, 0xf8, 0x43, 0xe1, 0x0b, 0xf0, 0x61, 0xeb, 0x7a, 0x76, 0xec, 0xca, 0x55, 0x09, 0xa2, 0x23, 0x89, 0x01}, ValidationOpt: WarnOnly}, 8: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: []byte{0xe1, 0x13, 0x34, 0x51, 0x4d, 0xf4, 0x9a, 0xf5, 0x6a, 0x2b, 0x13, 0x45, 0x3b, 0x3e, 0xbd, 0xc8, 0x96, 0xb0, 0xac, 0x1f, 0x36, 0x4c, 0x5c, 0x68, 0x34, 0xd4, 0x4a, 0x43, 0x6b, 0x80, 0x54, 0xe5}, ValidationOpt: Enforce}, 11: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: []byte{0x2a, 0xc0, 0xcd, 0x92, 0x5e, 0x18, 0x07, 0xfd, 0xe4, 0xf1, 0x48, 0x68, 0xa9, 0xdb, 0x58, 0x3e, 0x88, 0x24, 0x45, 0x50, 0x66, 0x5a, 0x2c, 0x5f, 0xc3, 0x7b, 0xac, 0xc5, 0x3d, 0x69, 0xb6, 0xa5}, ValidationOpt: Enforce}, 13: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: []byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}} case cloudprovider.Azure: - return M{1: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 2: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: [32]byte{0x97, 0x29, 0x84, 0xaf, 0x96, 0xfa, 0xc7, 0x57, 0xfd, 0x3b, 0x5a, 0xb1, 0x88, 0x0c, 0xd2, 0x4d, 0x48, 0x23, 0x65, 0xe6, 0x32, 0x2c, 0x79, 0x22, 0x14, 0xb8, 0x20, 0x9d, 0xb6, 0x80, 0x15, 0x9a}, ValidationOpt: Enforce}, 7: {Expected: [32]byte{0x34, 0x65, 0x47, 0xa8, 0xce, 0x59, 0x57, 0xaf, 0x27, 0xe5, 0x52, 0x42, 0x7d, 0x6b, 0x9e, 0x6d, 0x9c, 0xb5, 0x02, 0xf0, 0x15, 0x6e, 0x91, 0x55, 0x38, 0x04, 0x51, 0xee, 0xa1, 0xb3, 0xf0, 0xed}, ValidationOpt: WarnOnly}, 8: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: [32]byte{0x64, 0x68, 0x17, 0x07, 0x27, 0x97, 0xdd, 0xc1, 0xd4, 0xf2, 0xcf, 0x76, 0x46, 0xfe, 0x59, 0xf1, 0x7c, 0x13, 0xc4, 0x1a, 0x7b, 0x59, 0x76, 0xd6, 0xa5, 0x40, 0xda, 0xe1, 0x1d, 0x71, 0x3d, 0x8c}, ValidationOpt: Enforce}, 11: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: [32]byte{0x36, 0xb5, 0xf8, 0x15, 0x3a, 0x8a, 0x31, 0x5b, 0xac, 0xd8, 0x75, 0x97, 0x8e, 0x92, 0x1e, 0xf7, 0xa1, 0xfc, 0x5c, 0x89, 0x25, 0x59, 0x03, 0x17, 0xbc, 0x1b, 0x53, 0xf2, 0x13, 0xb6, 0x75, 0x62}, ValidationOpt: Enforce}, 13: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: [32]byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}} + return M{1: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 2: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: []byte{0x97, 0x29, 0x84, 0xaf, 0x96, 0xfa, 0xc7, 0x57, 0xfd, 0x3b, 0x5a, 0xb1, 0x88, 0x0c, 0xd2, 0x4d, 0x48, 0x23, 0x65, 0xe6, 0x32, 0x2c, 0x79, 0x22, 0x14, 0xb8, 0x20, 0x9d, 0xb6, 0x80, 0x15, 0x9a}, ValidationOpt: Enforce}, 7: {Expected: []byte{0x34, 0x65, 0x47, 0xa8, 0xce, 0x59, 0x57, 0xaf, 0x27, 0xe5, 0x52, 0x42, 0x7d, 0x6b, 0x9e, 0x6d, 0x9c, 0xb5, 0x02, 0xf0, 0x15, 0x6e, 0x91, 0x55, 0x38, 0x04, 0x51, 0xee, 0xa1, 0xb3, 0xf0, 0xed}, ValidationOpt: WarnOnly}, 8: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: []byte{0x64, 0x68, 0x17, 0x07, 0x27, 0x97, 0xdd, 0xc1, 0xd4, 0xf2, 0xcf, 0x76, 0x46, 0xfe, 0x59, 0xf1, 0x7c, 0x13, 0xc4, 0x1a, 0x7b, 0x59, 0x76, 0xd6, 0xa5, 0x40, 0xda, 0xe1, 0x1d, 0x71, 0x3d, 0x8c}, ValidationOpt: Enforce}, 11: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: []byte{0x36, 0xb5, 0xf8, 0x15, 0x3a, 0x8a, 0x31, 0x5b, 0xac, 0xd8, 0x75, 0x97, 0x8e, 0x92, 0x1e, 0xf7, 0xa1, 0xfc, 0x5c, 0x89, 0x25, 0x59, 0x03, 0x17, 0xbc, 0x1b, 0x53, 0xf2, 0x13, 0xb6, 0x75, 0x62}, ValidationOpt: Enforce}, 13: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: []byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}} case cloudprovider.GCP: - return M{1: {Expected: [32]byte{0x74, 0x5f, 0x2f, 0xb4, 0x23, 0x5e, 0x46, 0x47, 0xaa, 0x0a, 0xd5, 0xac, 0xe7, 0x81, 0xcd, 0x92, 0x9e, 0xb6, 0x8c, 0x28, 0x87, 0x0e, 0x7d, 0xd5, 0xd1, 0xa1, 0x53, 0x58, 0x54, 0x32, 0x5e, 0x56}, ValidationOpt: WarnOnly}, 2: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: [32]byte{0xb0, 0x9c, 0x1d, 0xff, 0x20, 0xbd, 0x38, 0x9e, 0xc9, 0xd0, 0x56, 0x53, 0x19, 0xa6, 0x55, 0x54, 0x08, 0x42, 0x6e, 0xe1, 0x72, 0x7b, 0x29, 0xbb, 0xb7, 0x16, 0xeb, 0xe9, 0x72, 0x9f, 0x81, 0xba}, ValidationOpt: Enforce}, 6: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 7: {Expected: [32]byte{0xb1, 0xe9, 0xb3, 0x05, 0x32, 0x5c, 0x51, 0xb9, 0x3d, 0xa5, 0x8c, 0xbf, 0x7f, 0x92, 0x51, 0x2d, 0x8e, 0xeb, 0xfa, 0x01, 0x14, 0x3e, 0x4d, 0x88, 0x44, 0xe4, 0x0e, 0x06, 0x2e, 0x9b, 0x6c, 0xd5}, ValidationOpt: WarnOnly}, 8: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: [32]byte{0x64, 0x68, 0x17, 0x07, 0x27, 0x97, 0xdd, 0xc1, 0xd4, 0xf2, 0xcf, 0x76, 0x46, 0xfe, 0x59, 0xf1, 0x7c, 0x13, 0xc4, 0x1a, 0x7b, 0x59, 0x76, 0xd6, 0xa5, 0x40, 0xda, 0xe1, 0x1d, 0x71, 0x3d, 0x8c}, ValidationOpt: Enforce}, 11: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: [32]byte{0xd7, 0x65, 0x82, 0xf1, 0x70, 0xcb, 0x84, 0x81, 0xc2, 0x55, 0x5c, 0x76, 0xae, 0x30, 0x39, 0xe9, 0xe1, 0x69, 0xfe, 0xcb, 0x05, 0x91, 0x2a, 0xa3, 0xb3, 0xcd, 0x7d, 0xc3, 0xec, 0xed, 0x0d, 0xa5}, ValidationOpt: Enforce}, 13: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: [32]byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}} + return M{1: {Expected: []byte{0x74, 0x5f, 0x2f, 0xb4, 0x23, 0x5e, 0x46, 0x47, 0xaa, 0x0a, 0xd5, 0xac, 0xe7, 0x81, 0xcd, 0x92, 0x9e, 0xb6, 0x8c, 0x28, 0x87, 0x0e, 0x7d, 0xd5, 0xd1, 0xa1, 0x53, 0x58, 0x54, 0x32, 0x5e, 0x56}, ValidationOpt: WarnOnly}, 2: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: []byte{0xb0, 0x9c, 0x1d, 0xff, 0x20, 0xbd, 0x38, 0x9e, 0xc9, 0xd0, 0x56, 0x53, 0x19, 0xa6, 0x55, 0x54, 0x08, 0x42, 0x6e, 0xe1, 0x72, 0x7b, 0x29, 0xbb, 0xb7, 0x16, 0xeb, 0xe9, 0x72, 0x9f, 0x81, 0xba}, ValidationOpt: Enforce}, 6: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 7: {Expected: []byte{0xb1, 0xe9, 0xb3, 0x05, 0x32, 0x5c, 0x51, 0xb9, 0x3d, 0xa5, 0x8c, 0xbf, 0x7f, 0x92, 0x51, 0x2d, 0x8e, 0xeb, 0xfa, 0x01, 0x14, 0x3e, 0x4d, 0x88, 0x44, 0xe4, 0x0e, 0x06, 0x2e, 0x9b, 0x6c, 0xd5}, ValidationOpt: WarnOnly}, 8: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: []byte{0x64, 0x68, 0x17, 0x07, 0x27, 0x97, 0xdd, 0xc1, 0xd4, 0xf2, 0xcf, 0x76, 0x46, 0xfe, 0x59, 0xf1, 0x7c, 0x13, 0xc4, 0x1a, 0x7b, 0x59, 0x76, 0xd6, 0xa5, 0x40, 0xda, 0xe1, 0x1d, 0x71, 0x3d, 0x8c}, ValidationOpt: Enforce}, 11: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: []byte{0xd7, 0x65, 0x82, 0xf1, 0x70, 0xcb, 0x84, 0x81, 0xc2, 0x55, 0x5c, 0x76, 0xae, 0x30, 0x39, 0xe9, 0xe1, 0x69, 0xfe, 0xcb, 0x05, 0x91, 0x2a, 0xa3, 0xb3, 0xcd, 0x7d, 0xc3, 0xec, 0xed, 0x0d, 0xa5}, ValidationOpt: Enforce}, 13: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: []byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}} case cloudprovider.QEMU: - return M{4: {Expected: [32]byte{0xbd, 0x7c, 0x7a, 0x6f, 0x43, 0xd9, 0xa7, 0x16, 0x4a, 0xf5, 0x17, 0xb1, 0xe5, 0x43, 0x2c, 0x14, 0x31, 0xe2, 0xd5, 0xd1, 0x01, 0xe9, 0x5e, 0x7f, 0x52, 0x73, 0x76, 0xda, 0xc2, 0x0b, 0xe8, 0x70}, ValidationOpt: Enforce}, 8: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: [32]byte{0xe1, 0x13, 0x34, 0x51, 0x4d, 0xf4, 0x9a, 0xf5, 0x6a, 0x2b, 0x13, 0x45, 0x3b, 0x3e, 0xbd, 0xc8, 0x96, 0xb0, 0xac, 0x1f, 0x36, 0x4c, 0x5c, 0x68, 0x34, 0xd4, 0x4a, 0x43, 0x6b, 0x80, 0x54, 0xe5}, ValidationOpt: Enforce}, 11: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: [32]byte{0x2c, 0x45, 0x39, 0xc4, 0x40, 0x95, 0xa3, 0x2a, 0x4e, 0xb2, 0xde, 0x12, 0xb2, 0x04, 0x2a, 0x34, 0xaa, 0x13, 0xaf, 0x5f, 0x30, 0x79, 0xef, 0x95, 0x2a, 0x0f, 0xca, 0xaa, 0x07, 0x6a, 0x27, 0x2d}, ValidationOpt: Enforce}, 13: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 15: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}} + return M{4: {Expected: []byte{0xbd, 0x7c, 0x7a, 0x6f, 0x43, 0xd9, 0xa7, 0x16, 0x4a, 0xf5, 0x17, 0xb1, 0xe5, 0x43, 0x2c, 0x14, 0x31, 0xe2, 0xd5, 0xd1, 0x01, 0xe9, 0x5e, 0x7f, 0x52, 0x73, 0x76, 0xda, 0xc2, 0x0b, 0xe8, 0x70}, ValidationOpt: Enforce}, 8: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: []byte{0xe1, 0x13, 0x34, 0x51, 0x4d, 0xf4, 0x9a, 0xf5, 0x6a, 0x2b, 0x13, 0x45, 0x3b, 0x3e, 0xbd, 0xc8, 0x96, 0xb0, 0xac, 0x1f, 0x36, 0x4c, 0x5c, 0x68, 0x34, 0xd4, 0x4a, 0x43, 0x6b, 0x80, 0x54, 0xe5}, ValidationOpt: Enforce}, 11: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: []byte{0x2c, 0x45, 0x39, 0xc4, 0x40, 0x95, 0xa3, 0x2a, 0x4e, 0xb2, 0xde, 0x12, 0xb2, 0x04, 0x2a, 0x34, 0xaa, 0x13, 0xaf, 0x5f, 0x30, 0x79, 0xef, 0x95, 0x2a, 0x0f, 0xca, 0xaa, 0x07, 0x6a, 0x27, 0x2d}, ValidationOpt: Enforce}, 13: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 15: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}} default: return nil diff --git a/internal/attestation/measurements/measurements_test.go b/internal/attestation/measurements/measurements_test.go index bad67de38..ea605f8f0 100644 --- a/internal/attestation/measurements/measurements_test.go +++ b/internal/attestation/measurements/measurements_test.go @@ -9,6 +9,7 @@ package measurements import ( "context" "encoding/json" + "fmt" "io" "net/http" "net/url" @@ -30,14 +31,14 @@ func TestMarshal(t *testing.T) { }{ "measurement": { m: Measurement{ - Expected: [32]byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53}, + Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53}, }, wantYAML: "expected: \"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35\"\nwarnOnly: false", wantJSON: `{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35","warnOnly":false}`, }, "warn only": { m: Measurement{ - Expected: [32]byte{1, 2, 3, 4}, // implicitly padded with 0s + Expected: []byte{1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ValidationOpt: WarnOnly, }, wantYAML: "expected: \"0102030400000000000000000000000000000000000000000000000000000000\"\nwarnOnly: true", @@ -81,10 +82,10 @@ func TestUnmarshal(t *testing.T) { inputJSON: `{"2":{"expected":"/V3p3zUOO8RBCsBrv+XM3rk/U7nvUSOfdSzmnbxgDzU="},"3":{"expected":"1aRJbSHeyaUljdsZxv61O7TTwEY/5gfySI3fTxAG754="}}`, wantMeasurements: M{ 2: { - Expected: [32]byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53}, + Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53}, }, 3: { - Expected: [32]byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158}, + Expected: []byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158}, }, }, }, @@ -93,10 +94,22 @@ func TestUnmarshal(t *testing.T) { inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9e"}}`, wantMeasurements: M{ 2: { - Expected: [32]byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53}, + Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53}, }, 3: { - Expected: [32]byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158}, + Expected: []byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158}, + }, + }, + }, + "valid measurements hex 48 bytes": { + inputYAML: "2:\n expected: \"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35fd5de9df350e3bc4410ac06bbfe5ccde\"\n3:\n expected: \"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9efd5de9df350e3bc4410ac06bbfe5ccde\"", + inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35fd5de9df350e3bc4410ac06bbfe5ccde"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9efd5de9df350e3bc4410ac06bbfe5ccde"}}`, + wantMeasurements: M{ + 2: { + Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53, 253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222}, + }, + 3: { + Expected: []byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158, 253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222}, }, }, }, @@ -105,10 +118,10 @@ func TestUnmarshal(t *testing.T) { inputJSON: `{"2":{"expected":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},"3":{"expected":"AQIDBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}`, wantMeasurements: M{ 2: { - Expected: [32]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Expected: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, 3: { - Expected: [32]byte{1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Expected: []byte{1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, }, }, @@ -122,10 +135,10 @@ func TestUnmarshal(t *testing.T) { inputJSON: `{"2":"/V3p3zUOO8RBCsBrv+XM3rk/U7nvUSOfdSzmnbxgDzU=","3":"1aRJbSHeyaUljdsZxv61O7TTwEY/5gfySI3fTxAG754="}`, wantMeasurements: M{ 2: { - Expected: [32]byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53}, + Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53}, }, 3: { - Expected: [32]byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158}, + Expected: []byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158}, }, }, }, @@ -134,6 +147,11 @@ func TestUnmarshal(t *testing.T) { inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463f"}}`, wantErr: true, }, + "mixed length hex": { + inputYAML: "2:\n expected: \"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35fd5de9df350e3bc4410ac06bbfe5ccde\"\n3:\n expected: \"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9e\"", + inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35fd5de9df350e3bc4410ac06bbfe5ccde"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9e"}}`, + wantErr: true, + }, "invalid length base64": { inputYAML: "2:\n expected: \"AA==\"\n3:\n expected: \"AQIDBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\"", inputJSON: `{"2":{"expected":"AA=="},"3":{"expected":"AQIDBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="}}`, @@ -170,6 +188,7 @@ func TestUnmarshal(t *testing.T) { err := json.Unmarshal([]byte(tc.inputJSON), &m) if tc.wantErr { + fmt.Println(err) assert.Error(err, "json.Unmarshal should have failed") } else { require.NoError(err, "json.Unmarshal failed") @@ -200,10 +219,10 @@ func TestEncodeM(t *testing.T) { }, "output is sorted": { m: M{ - 3: {}, - 1: {}, - 11: {}, - 2: {}, + 3: WithAllBytes(0, false), + 1: WithAllBytes(0, false), + 11: WithAllBytes(0, false), + 2: WithAllBytes(0, false), }, want: `1: expected: "0000000000000000000000000000000000000000000000000000000000000000" @@ -605,7 +624,7 @@ func TestWithAllBytes(t *testing.T) { b: 0x00, warnOnly: true, wantMeasurement: Measurement{ - Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: WarnOnly, }, }, @@ -613,7 +632,7 @@ func TestWithAllBytes(t *testing.T) { b: 0x00, warnOnly: false, wantMeasurement: Measurement{ - Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce, }, }, @@ -621,7 +640,7 @@ func TestWithAllBytes(t *testing.T) { b: 0x01, warnOnly: true, wantMeasurement: Measurement{ - Expected: [32]byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + Expected: []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, ValidationOpt: WarnOnly, }, }, @@ -629,7 +648,7 @@ func TestWithAllBytes(t *testing.T) { b: 0x01, warnOnly: false, wantMeasurement: Measurement{ - Expected: [32]byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + Expected: []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, ValidationOpt: Enforce, }, }, @@ -637,7 +656,7 @@ func TestWithAllBytes(t *testing.T) { b: 0xFF, warnOnly: true, wantMeasurement: Measurement{ - Expected: [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + Expected: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, ValidationOpt: WarnOnly, }, }, @@ -645,7 +664,7 @@ func TestWithAllBytes(t *testing.T) { b: 0xFF, warnOnly: false, wantMeasurement: Measurement{ - Expected: [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + Expected: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, ValidationOpt: Enforce, }, }, diff --git a/internal/attestation/qemu/BUILD.bazel b/internal/attestation/qemu/BUILD.bazel index 5b5ba26a6..1d57eebb8 100644 --- a/internal/attestation/qemu/BUILD.bazel +++ b/internal/attestation/qemu/BUILD.bazel @@ -10,6 +10,7 @@ go_library( importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/qemu", visibility = ["//:__subpackages__"], deps = [ + "//internal/attestation", "//internal/attestation/vtpm", "//internal/config", "//internal/variant", diff --git a/internal/attestation/qemu/issuer.go b/internal/attestation/qemu/issuer.go index 073f29153..915ce97a7 100644 --- a/internal/attestation/qemu/issuer.go +++ b/internal/attestation/qemu/issuer.go @@ -10,6 +10,7 @@ import ( "context" "io" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/variant" tpmclient "github.com/google/go-tpm-tools/client" @@ -22,7 +23,7 @@ type Issuer struct { } // NewIssuer initializes a new QEMU Issuer. -func NewIssuer(log vtpm.AttestationLogger) *Issuer { +func NewIssuer(log attestation.Logger) *Issuer { return &Issuer{ Issuer: vtpm.NewIssuer( vtpm.OpenVTPM, diff --git a/internal/attestation/qemu/validator.go b/internal/attestation/qemu/validator.go index 48b267491..2fc599c78 100644 --- a/internal/attestation/qemu/validator.go +++ b/internal/attestation/qemu/validator.go @@ -10,6 +10,7 @@ import ( "context" "crypto" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/variant" @@ -24,7 +25,7 @@ type Validator struct { } // NewValidator initializes a new QEMU validator with the provided PCR values. -func NewValidator(cfg *config.QEMUVTPM, log vtpm.AttestationLogger) *Validator { +func NewValidator(cfg *config.QEMUVTPM, log attestation.Logger) *Validator { return &Validator{ Validator: vtpm.NewValidator( cfg.Measurements, diff --git a/internal/attestation/tdx/BUILD.bazel b/internal/attestation/tdx/BUILD.bazel new file mode 100644 index 000000000..629518d3c --- /dev/null +++ b/internal/attestation/tdx/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tdx", + srcs = [ + "issuer.go", + "tdx.go", + "validator.go", + ], + importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/tdx", + visibility = ["//:__subpackages__"], + deps = [ + "//internal/attestation", + "//internal/attestation/measurements", + "//internal/config", + "//internal/variant", + "@com_github_edgelesssys_go_tdx_qpl//tdx", + "@com_github_edgelesssys_go_tdx_qpl//verification", + "@com_github_edgelesssys_go_tdx_qpl//verification/types", + ], +) diff --git a/internal/attestation/tdx/issuer.go b/internal/attestation/tdx/issuer.go new file mode 100644 index 000000000..9ccc56c1a --- /dev/null +++ b/internal/attestation/tdx/issuer.go @@ -0,0 +1,67 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package tdx + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/edgelesssys/constellation/v2/internal/attestation" + "github.com/edgelesssys/constellation/v2/internal/variant" + "github.com/edgelesssys/go-tdx-qpl/tdx" +) + +// Issuer is the TDX attestation issuer. +type Issuer struct { + variant.QEMUTDX + + open OpenFunc + log attestation.Logger +} + +// NewIssuer initializes a new TDX Issuer. +func NewIssuer(log attestation.Logger) *Issuer { + if log == nil { + log = attestation.NOPLogger{} + } + return &Issuer{ + open: Open, + log: log, + } +} + +// Issue issues a TDX attestation document. +func (i *Issuer) Issue(_ context.Context, userData []byte, nonce []byte) (attDoc []byte, err error) { + i.log.Infof("Issuing attestation statement") + defer func() { + if err != nil { + i.log.Warnf("Failed to issue attestation document: %s", err) + } + }() + + handle, err := i.open() + if err != nil { + return nil, fmt.Errorf("opening TDX device: %w", err) + } + defer handle.Close() + + quote, err := tdx.GenerateQuote(handle, attestation.MakeExtraData(userData, nonce)) + if err != nil { + return nil, fmt.Errorf("generating quote: %w", err) + } + + rawAttDoc, err := json.Marshal(tdxAttestationDocument{ + RawQuote: quote, + UserData: userData, + }) + if err != nil { + return nil, fmt.Errorf("marshaling attestation document: %w", err) + } + + return rawAttDoc, nil +} diff --git a/internal/attestation/tdx/tdx.go b/internal/attestation/tdx/tdx.go new file mode 100644 index 000000000..ea0cb67c4 --- /dev/null +++ b/internal/attestation/tdx/tdx.go @@ -0,0 +1,95 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +// Package TDX implements attestation for Intel TDX. +package tdx + +import ( + "fmt" + "io" + "os" + + "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" + "github.com/edgelesssys/go-tdx-qpl/tdx" +) + +type tdxAttestationDocument struct { + // RawQuote is the raw TDX quote. + RawQuote []byte + // UserData is the user data that was passed to the enclave and was included in the quote. + UserData []byte +} + +// Device is an interface for a TDX device. +type Device interface { + io.ReadWriteCloser + Fd() uintptr +} + +// OpenFunc is a function that opens the TDX device. +type OpenFunc func() (Device, error) + +// GetSelectedMeasurements returns the selected measurements from the RTMRs. +func GetSelectedMeasurements(open OpenFunc, selection []int) (measurements.M, error) { + for _, idx := range selection { + if idx < 0 || idx >= 5 { + return nil, fmt.Errorf("invalid measurement index %d", idx) + } + } + + handle, err := open() + if err != nil { + return nil, err + } + defer handle.Close() + + tdxMeasurements, err := tdx.ReadMeasurements(handle) + if err != nil { + return nil, err + } + + m := make(measurements.M) + for _, idx := range selection { + m[uint32(idx)] = measurements.Measurement{ + Expected: tdxMeasurements[idx][:], + } + } + + return m, nil +} + +// Available returns true if the TDX device is available and can be opened. +func Available() bool { + handle, err := Open() + if err != nil { + return false + } + defer handle.Close() + return true +} + +// Open opens the TDX guest device. +func Open() (Device, error) { + handle, err := os.Open(tdx.GuestDevice) + if err != nil { + return nil, err + } + + return handle, nil +} + +// IsTDXDevice checks if the given device is a TDX guest device. +func IsTDXDevice(device io.ReadWriteCloser) (Device, bool) { + handle, ok := device.(Device) + if !ok { + return nil, false + } + f, ok := device.(*os.File) + if !ok { + return nil, false + } + return handle, f.Name() == tdx.GuestDevice +} diff --git a/internal/attestation/tdx/validator.go b/internal/attestation/tdx/validator.go new file mode 100644 index 000000000..8385e24e8 --- /dev/null +++ b/internal/attestation/tdx/validator.go @@ -0,0 +1,94 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package tdx + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + + "github.com/edgelesssys/constellation/v2/internal/attestation" + "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" + "github.com/edgelesssys/constellation/v2/internal/config" + "github.com/edgelesssys/constellation/v2/internal/variant" + "github.com/edgelesssys/go-tdx-qpl/verification" + "github.com/edgelesssys/go-tdx-qpl/verification/types" +) + +type tdxVerifier interface { + Verify(ctx context.Context, quote []byte) (types.SGXQuote4, error) +} + +// Validator is the TDX attestation validator. +type Validator struct { + variant.QEMUTDX + + tdx tdxVerifier + expected measurements.M + + log attestation.Logger +} + +// NewValidator initializes a new TDX Validator. +func NewValidator(cfg *config.QEMUTDX, log attestation.Logger) *Validator { + if log == nil { + log = attestation.NOPLogger{} + } + + return &Validator{ + tdx: verification.New(), + expected: cfg.Measurements, + log: log, + } +} + +// Validate validates the given attestation document using TDX attestation. +func (v *Validator) Validate(ctx context.Context, attDocRaw []byte, nonce []byte) (userData []byte, err error) { + v.log.Infof("Validating attestation document") + defer func() { + if err != nil { + v.log.Warnf("Failed to validate attestation document: %s", err) + } + }() + + var attDoc tdxAttestationDocument + if err := json.Unmarshal(attDocRaw, &attDoc); err != nil { + return nil, fmt.Errorf("unmarshaling attestation document: %w", err) + } + + // Verify the quote. + quote, err := v.tdx.Verify(ctx, attDoc.RawQuote) + if err != nil { + return nil, fmt.Errorf("verifying TDX quote: %w", err) + } + + // Report data + extraData := attestation.MakeExtraData(attDoc.UserData, nonce) + if !attestation.CompareExtraData(quote.Body.ReportData[:], extraData) { + return nil, fmt.Errorf("report data in TDX quote does not match provided nonce") + } + + // Convert RTMRs and MRTD to map. + tdMeasure := make(map[uint32][]byte, 5) + tdMeasure[0] = quote.Body.MRTD[:] + for idx := 0; idx < len(quote.Body.RTMR); idx++ { + tdMeasure[uint32(idx+1)] = quote.Body.RTMR[idx][:] + } + + // Verify the quote against the expected measurements. + for idx, ex := range v.expected { + if !bytes.Equal(ex.Expected, tdMeasure[idx]) { + if !ex.ValidationOpt { + return nil, fmt.Errorf("untrusted TD measurement value at index %d", idx) + } + v.log.Warnf("Encountered untrusted TD measurement value at index %d", idx) + } + } + + return attDoc.UserData, nil +} diff --git a/internal/attestation/vtpm/BUILD.bazel b/internal/attestation/vtpm/BUILD.bazel index bd27e2c2e..cd27aeb56 100644 --- a/internal/attestation/vtpm/BUILD.bazel +++ b/internal/attestation/vtpm/BUILD.bazel @@ -5,12 +5,12 @@ go_library( name = "vtpm", srcs = [ "attestation.go", - "initialize.go", "vtpm.go", ], importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm", visibility = ["//:__subpackages__"], deps = [ + "//internal/attestation", "//internal/attestation/measurements", "@com_github_google_go_tpm//tpm2", "@com_github_google_go_tpm_tools//client", @@ -24,7 +24,6 @@ go_test( name = "vtpm_test", srcs = [ "attestation_test.go", - "initialize_test.go", "vtpm_test.go", ], embed = [":vtpm"], @@ -34,6 +33,7 @@ go_test( "//conditions:default": ["disable_tpm_simulator"], }), deps = [ + "//internal/attestation/initialize", "//internal/attestation/measurements", "//internal/attestation/simulator", "//internal/logger", diff --git a/internal/attestation/vtpm/attestation.go b/internal/attestation/vtpm/attestation.go index 7a7cd72ef..ef4c35092 100644 --- a/internal/attestation/vtpm/attestation.go +++ b/internal/attestation/vtpm/attestation.go @@ -10,7 +10,6 @@ import ( "bytes" "context" "crypto" - "crypto/sha256" "encoding/json" "fmt" "io" @@ -21,6 +20,7 @@ import ( tpmServer "github.com/google/go-tpm-tools/server" "github.com/google/go-tpm/tpm2" + "github.com/edgelesssys/constellation/v2/internal/attestation" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" ) @@ -66,12 +66,6 @@ type ( ValidateCVM func(attestation AttestationDocument, state *attest.MachineState) error ) -// AttestationLogger is a logger used to print warnings and infos during attestation validation. -type AttestationLogger interface { - Infof(format string, args ...any) - Warnf(format string, args ...any) -} - // AttestationDocument contains the TPM attestation with signed user data. type AttestationDocument struct { // Attestation contains the TPM event log, PCR values and quotes, and public key of the key used to sign the attestation. @@ -87,16 +81,16 @@ type Issuer struct { openTPM TPMOpenFunc getAttestationKey GetTPMAttestationKey getInstanceInfo GetInstanceInfo - log AttestationLogger + log attestation.Logger } // NewIssuer returns a new Issuer. func NewIssuer( openTPM TPMOpenFunc, getAttestationKey GetTPMAttestationKey, - getInstanceInfo GetInstanceInfo, log AttestationLogger, + getInstanceInfo GetInstanceInfo, log attestation.Logger, ) *Issuer { if log == nil { - log = &nopAttestationLogger{} + log = &attestation.NOPLogger{} } return &Issuer{ openTPM: openTPM, @@ -129,7 +123,7 @@ func (i *Issuer) Issue(ctx context.Context, userData []byte, nonce []byte) (res defer aK.Close() // Create an attestation using the loaded key - extraData := makeExtraData(userData, nonce) + extraData := attestation.MakeExtraData(userData, nonce) tpmAttestation, err := aK.Attest(tpmClient.AttestOpts{Nonce: extraData}) if err != nil { return nil, fmt.Errorf("creating attestation: %w", err) @@ -162,15 +156,15 @@ type Validator struct { getTrustedKey GetTPMTrustedAttestationPublicKey validateCVM ValidateCVM - log AttestationLogger + log attestation.Logger } // NewValidator returns a new Validator. func NewValidator(expected measurements.M, getTrustedKey GetTPMTrustedAttestationPublicKey, - validateCVM ValidateCVM, log AttestationLogger, + validateCVM ValidateCVM, log attestation.Logger, ) *Validator { if log == nil { - log = &nopAttestationLogger{} + log = &attestation.NOPLogger{} } return &Validator{ expected: expected, @@ -194,7 +188,7 @@ func (v *Validator) Validate(ctx context.Context, attDocRaw []byte, nonce []byte return nil, fmt.Errorf("unmarshaling TPM attestation document: %w", err) } - extraData := makeExtraData(attDoc.UserData, nonce) + extraData := attestation.MakeExtraData(attDoc.UserData, nonce) // Verify and retrieve the trusted attestation public key using the provided instance info aKP, err := v.getTrustedKey(ctx, attDoc, extraData) @@ -276,25 +270,9 @@ func GetSelectedMeasurements(open TPMOpenFunc, selection tpm2.PCRSelection) (mea return nil, fmt.Errorf("invalid measurement: invalid length: %d", len(pcr)) } m[i] = measurements.Measurement{ - Expected: *(*[32]byte)(pcr), + Expected: pcr, } } return m, nil } - -func makeExtraData(userData []byte, nonce []byte) []byte { - data := append([]byte{}, userData...) - data = append(data, nonce...) - digest := sha256.Sum256(data) - return digest[:] -} - -// nopAttestationLogger is a no-op implementation of AttestationLogger. -type nopAttestationLogger struct{} - -// Infof is a no-op. -func (nopAttestationLogger) Infof(string, ...interface{}) {} - -// Warnf is a no-op. -func (nopAttestationLogger) Warnf(string, ...interface{}) {} diff --git a/internal/attestation/vtpm/attestation_test.go b/internal/attestation/vtpm/attestation_test.go index db5238880..74d6a0668 100644 --- a/internal/attestation/vtpm/attestation_test.go +++ b/internal/attestation/vtpm/attestation_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/edgelesssys/constellation/v2/internal/attestation/initialize" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/simulator" tpmsim "github.com/edgelesssys/constellation/v2/internal/attestation/simulator" @@ -100,7 +101,7 @@ func TestValidate(t *testing.T) { require.Equal(challenge, out) // validation must fail after bootstrapping (change of enforced PCR) - require.NoError(MarkNodeAsBootstrapped(tpmOpen, []byte{2})) + require.NoError(initialize.MarkNodeAsBootstrapped(tpmOpen, []byte{2})) attDocBootstrappedRaw, err := issuer.Issue(ctx, challenge, nonce) require.NoError(err) _, err = validator.Validate(ctx, attDocBootstrappedRaw, nonce) @@ -121,19 +122,19 @@ func TestValidate(t *testing.T) { 0: measurements.WithAllBytes(0x00, measurements.WarnOnly), 1: measurements.WithAllBytes(0x00, measurements.WarnOnly), 2: measurements.Measurement{ - Expected: [32]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}, + Expected: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20}, ValidationOpt: measurements.WarnOnly, }, 3: measurements.Measurement{ - Expected: [32]byte{0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40}, + Expected: []byte{0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40}, ValidationOpt: measurements.WarnOnly, }, 4: measurements.Measurement{ - Expected: [32]byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60}, + Expected: []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60}, ValidationOpt: measurements.WarnOnly, }, 5: measurements.Measurement{ - Expected: [32]byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80}, + Expected: []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80}, ValidationOpt: measurements.WarnOnly, }, } @@ -202,7 +203,7 @@ func TestValidate(t *testing.T) { validator: NewValidator( measurements.M{ 0: measurements.Measurement{ - Expected: [32]byte{0xFF}, + Expected: []byte{0xFF}, ValidationOpt: measurements.Enforce, }, }, diff --git a/internal/attestation/vtpm/initialize.go b/internal/attestation/vtpm/initialize.go deleted file mode 100644 index 427f23647..000000000 --- a/internal/attestation/vtpm/initialize.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package vtpm - -import ( - "errors" - - "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" - "github.com/google/go-tpm/tpm2" -) - -// MarkNodeAsBootstrapped marks a node as initialized by extending PCRs. -func MarkNodeAsBootstrapped(openTPM TPMOpenFunc, clusterID []byte) error { - tpm, err := openTPM() - if err != nil { - return err - } - defer tpm.Close() - - // clusterID is used to uniquely identify this running instance of Constellation - return tpm2.PCREvent(tpm, measurements.PCRIndexClusterID, clusterID) -} - -// IsNodeBootstrapped checks if a node is already bootstrapped by reading PCRs. -func IsNodeBootstrapped(openTPM TPMOpenFunc) (bool, error) { - tpm, err := openTPM() - if err != nil { - return false, err - } - defer tpm.Close() - - idxClusterID := int(measurements.PCRIndexClusterID) - pcrs, err := tpm2.ReadPCRs(tpm, tpm2.PCRSelection{ - Hash: tpm2.AlgSHA256, - PCRs: []int{idxClusterID}, - }) - if err != nil { - return false, err - } - if len(pcrs[idxClusterID]) == 0 { - return false, errors.New("cluster ID PCR does not exist") - } - return pcrInitialized(pcrs[idxClusterID]), nil - - /* Old code that will be reenabled in the future - idxOwner := int(PCRIndexOwnerID) - idxCluster := int(PCRIndexClusterID) - selection := tpm2.PCRSelection{ - Hash: tpm2.AlgSHA256, - PCRs: []int{idxOwner, idxCluster}, - } - - pcrs, err := tpm2.ReadPCRs(tpm, selection) - if err != nil { - return false, err - } - - if len(pcrs[idxOwner]) == 0 { - return false, errors.New("owner ID PCR does not exist") - } - if len(pcrs[idxCluster]) == 0 { - return false, errors.New("cluster ID PCR does not exist") - } - - ownerInitialized := pcrInitialized(pcrs[idxOwner]) - clusterInitialized := pcrInitialized(pcrs[idxCluster]) - - if ownerInitialized == clusterInitialized { - return ownerInitialized && clusterInitialized, nil - } - ownerState := "not initialized" - if ownerInitialized { - ownerState = "initialized" - } - clusterState := "not initialized" - if clusterInitialized { - clusterState = "initialized" - } - return false, fmt.Errorf("PCRs %v and %v are not consistent: PCR[%v]=%v (%v), PCR[%v]=%v (%v)", idxOwner, idxCluster, idxOwner, pcrs[idxOwner], ownerState, idxCluster, pcrs[idxCluster], clusterState) - */ -} - -// pcrInitialized checks if a PCR value is set to a non-zero value. -func pcrInitialized(pcr []byte) bool { - for _, b := range pcr { - if b != 0 { - return true - } - } - return false -} diff --git a/internal/attestation/vtpm/vtpm_test.go b/internal/attestation/vtpm/vtpm_test.go index 331de7eab..18ded35ae 100644 --- a/internal/attestation/vtpm/vtpm_test.go +++ b/internal/attestation/vtpm/vtpm_test.go @@ -9,16 +9,9 @@ package vtpm import ( "testing" - "github.com/stretchr/testify/assert" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } - -func TestNOPTPM(t *testing.T) { - assert := assert.New(t) - - assert.NoError(MarkNodeAsBootstrapped(OpenNOPTPM, []byte{0x0, 0x1, 0x2, 0x3})) -} diff --git a/internal/config/config.go b/internal/config/config.go index e1875bd6b..21861e64e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -885,6 +885,37 @@ func (c QEMUVTPM) EqualTo(other AttestationCfg) (bool, error) { return c.Measurements.EqualTo(otherCfg.Measurements), nil } +// QEMUTDX is the configuration for QEMU TDX attestation. +type QEMUTDX struct { + // description: | + // Expected TDX measurements. + Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"` +} + +// GetVariant returns qemu-tdx as the variant. +func (QEMUTDX) GetVariant() variant.Variant { + return variant.QEMUTDX{} +} + +// GetMeasurements returns the measurements used for attestation. +func (c QEMUTDX) GetMeasurements() measurements.M { + return c.Measurements +} + +// SetMeasurements updates a config's measurements using the given measurements. +func (c *QEMUTDX) SetMeasurements(m measurements.M) { + c.Measurements = m +} + +// EqualTo returns true if the config is equal to the given config. +func (c QEMUTDX) EqualTo(other AttestationCfg) (bool, error) { + otherCfg, ok := other.(*QEMUTDX) + if !ok { + return false, fmt.Errorf("cannot compare %T with %T", c, other) + } + return c.Measurements.EqualTo(otherCfg.Measurements), nil +} + func toPtr[T any](v T) *T { return &v } diff --git a/internal/variant/variant.go b/internal/variant/variant.go index 0a6f785f9..8e8ea3d9c 100644 --- a/internal/variant/variant.go +++ b/internal/variant/variant.go @@ -43,6 +43,7 @@ const ( azureSEVSNP = "azure-sev-snp" azureTrustedLaunch = "azure-trustedlaunch" qemuVTPM = "qemu-vtpm" + qemuTDX = "qemu-tdx" ) // Getter returns an ASN.1 Object Identifier. @@ -72,6 +73,8 @@ func FromString(oid string) (Variant, error) { return AzureTrustedLaunch{}, nil case qemuVTPM: return QEMUVTPM{}, nil + case qemuTDX: + return QEMUTDX{}, nil } return nil, fmt.Errorf("unknown OID: %q", oid) } @@ -183,3 +186,22 @@ func (QEMUVTPM) String() string { func (QEMUVTPM) Equal(other Getter) bool { return other.OID().Equal(QEMUVTPM{}.OID()) } + +// QEMUTDX holds the QEMU TDX OID. +// Placeholder for dev-cloud integration. +type QEMUTDX struct{} + +// OID returns the struct's object identifier. +// Placeholder for dev-cloud integration. +func (QEMUTDX) OID() asn1.ObjectIdentifier { + return asn1.ObjectIdentifier{1, 3, 9900, 5, 99} +} + +func (QEMUTDX) String() string { + return qemuTDX +} + +// Equal returns true if the other variant is also QEMUTDX. +func (QEMUTDX) Equal(other Getter) bool { + return other.OID().Equal(QEMUTDX{}.OID()) +}