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())
+}