attestation: tdx issuer/validator (#1265)

* Add TDX validator

* Add TDX issuer

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-03-08 14:13:57 +01:00 committed by Malte Poll
parent d104af6e51
commit dd2da25ebe
53 changed files with 808 additions and 229 deletions

View File

@ -1889,6 +1889,14 @@ def go_dependencies():
sum = "h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=", sum = "h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=",
version = "v0.0.0-20230303085714-62ede861d33f", 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( go_repository(
name = "com_github_edsrzf_mmap_go", name = "com_github_edsrzf_mmap_go",
@ -6680,6 +6688,15 @@ def go_dependencies():
sum = "h1:gpw/0Ku+6RgF3jsi7fnCLmlcikBHfKBCUcu1qgc16OU=", sum = "h1:gpw/0Ku+6RgF3jsi7fnCLmlcikBHfKBCUcu1qgc16OU=",
version = "v0.20.3", 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( go_repository(
name = "com_github_weppos_publicsuffix_go", name = "com_github_weppos_publicsuffix_go",
build_file_generation = "on", build_file_generation = "on",

View File

@ -23,7 +23,9 @@ go_library(
"//bootstrapper/internal/nodelock", "//bootstrapper/internal/nodelock",
"//internal/atls", "//internal/atls",
"//internal/attestation/choose", "//internal/attestation/choose",
"//internal/attestation/initialize",
"//internal/attestation/simulator", "//internal/attestation/simulator",
"//internal/attestation/tdx",
"//internal/attestation/vtpm", "//internal/attestation/vtpm",
"//internal/cloud/aws", "//internal/cloud/aws",
"//internal/cloud/azure", "//internal/cloud/azure",

View File

@ -23,6 +23,7 @@ import (
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging"
"github.com/edgelesssys/constellation/v2/internal/attestation/choose" "github.com/edgelesssys/constellation/v2/internal/attestation/choose"
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator" "github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
"github.com/edgelesssys/constellation/v2/internal/attestation/tdx"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
awscloud "github.com/edgelesssys/constellation/v2/internal/cloud/aws" awscloud "github.com/edgelesssys/constellation/v2/internal/cloud/aws"
azurecloud "github.com/edgelesssys/constellation/v2/internal/cloud/azure" azurecloud "github.com/edgelesssys/constellation/v2/internal/cloud/azure"
@ -63,7 +64,7 @@ func main() {
var clusterInitJoiner clusterInitJoiner var clusterInitJoiner clusterInitJoiner
var metadataAPI metadataAPI var metadataAPI metadataAPI
var cloudLogger logging.CloudLogger var cloudLogger logging.CloudLogger
var openTPM vtpm.TPMOpenFunc var openDevice vtpm.TPMOpenFunc
var fs afero.Fs var fs afero.Fs
helmClient, err := helm.New(log) helmClient, err := helm.New(log)
@ -97,7 +98,7 @@ func main() {
"aws", k8sapi.NewKubernetesUtil(), &k8sapi.KubdeadmConfiguration{}, kubectl.New(), "aws", k8sapi.NewKubernetesUtil(), &k8sapi.KubdeadmConfiguration{}, kubectl.New(),
metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{}, metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{},
) )
openTPM = vtpm.OpenVTPM openDevice = vtpm.OpenVTPM
fs = afero.NewOsFs() fs = afero.NewOsFs()
case cloudprovider.GCP: case cloudprovider.GCP:
@ -117,7 +118,7 @@ func main() {
"gcp", k8sapi.NewKubernetesUtil(), &k8sapi.KubdeadmConfiguration{}, kubectl.New(), "gcp", k8sapi.NewKubernetesUtil(), &k8sapi.KubdeadmConfiguration{}, kubectl.New(),
metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{}, metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{},
) )
openTPM = vtpm.OpenVTPM openDevice = vtpm.OpenVTPM
fs = afero.NewOsFs() fs = afero.NewOsFs()
log.Infof("Added load balancer IP to routing table") log.Infof("Added load balancer IP to routing table")
@ -136,7 +137,7 @@ func main() {
metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{}, metadata, helmClient, &kubewaiter.CloudKubeAPIWaiter{},
) )
openTPM = vtpm.OpenVTPM openDevice = vtpm.OpenVTPM
fs = afero.NewOsFs() fs = afero.NewOsFs()
case cloudprovider.QEMU: case cloudprovider.QEMU:
@ -148,7 +149,16 @@ func main() {
) )
metadataAPI = metadata 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() fs = afero.NewOsFs()
case cloudprovider.OpenStack: case cloudprovider.OpenStack:
cloudLogger = &logging.NopLogger{} cloudLogger = &logging.NopLogger{}
@ -162,19 +172,18 @@ func main() {
) )
metadataAPI = metadata metadataAPI = metadata
openTPM = vtpm.OpenVTPM
fs = afero.NewOsFs() fs = afero.NewOsFs()
default: default:
clusterInitJoiner = &clusterFake{} clusterInitJoiner = &clusterFake{}
metadataAPI = &providerMetadataFake{} metadataAPI = &providerMetadataFake{}
cloudLogger = &logging.NopLogger{} cloudLogger = &logging.NopLogger{}
var simulatedTPMCloser io.Closer var simulatedTPMCloser io.Closer
openTPM, simulatedTPMCloser = simulator.NewSimulatedTPMOpenFunc() openDevice, simulatedTPMCloser = simulator.NewSimulatedTPMOpenFunc()
defer simulatedTPMCloser.Close() defer simulatedTPMCloser.Close()
fs = afero.NewMemMapFs() fs = afero.NewMemMapFs()
} }
fileHandler := file.NewHandler(fs) fileHandler := file.NewHandler(fs)
run(issuer, openTPM, fileHandler, clusterInitJoiner, metadataAPI, bindIP, bindPort, log, cloudLogger) run(issuer, openDevice, fileHandler, clusterInitJoiner, metadataAPI, bindIP, bindPort, log, cloudLogger)
} }

View File

@ -17,6 +17,7 @@ import (
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging"
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/nodelock" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/nodelock"
"github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/atls"
"github.com/edgelesssys/constellation/v2/internal/attestation/initialize"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/file"
@ -25,7 +26,7 @@ import (
"go.uber.org/zap" "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, kube clusterInitJoiner, metadata metadataAPI,
bindIP, bindPort string, log *logger.Logger, bindIP, bindPort string, log *logger.Logger,
cloudLogger logging.CloudLogger, cloudLogger logging.CloudLogger,
@ -44,7 +45,7 @@ func run(issuer atls.Issuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler,
cloudLogger.Disclose("Disk UUID: " + uuid) cloudLogger.Disclose("Disk UUID: " + uuid)
} }
nodeBootstrapped, err := vtpm.IsNodeBootstrapped(tpm) nodeBootstrapped, err := initialize.IsNodeBootstrapped(openDevice)
if err != nil { if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to check if node was previously bootstrapped") 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 return
} }
nodeLock := nodelock.New(tpm) nodeLock := nodelock.New(openDevice)
initServer, err := initserver.New(context.Background(), nodeLock, kube, issuer, fileHandler, metadata, log) initServer, err := initserver.New(context.Background(), nodeLock, kube, issuer, fileHandler, metadata, log)
if err != nil { if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to create init server") log.With(zap.Error(err)).Fatalf("Failed to create init server")

View File

@ -5,5 +5,8 @@ go_library(
srcs = ["nodelock.go"], srcs = ["nodelock.go"],
importpath = "github.com/edgelesssys/constellation/v2/bootstrapper/internal/nodelock", importpath = "github.com/edgelesssys/constellation/v2/bootstrapper/internal/nodelock",
visibility = ["//bootstrapper:__subpackages__"], visibility = ["//bootstrapper:__subpackages__"],
deps = ["//internal/attestation/vtpm"], deps = [
"//internal/attestation/initialize",
"//internal/attestation/vtpm",
],
) )

View File

@ -10,6 +10,7 @@ package nodelock
import ( import (
"sync" "sync"
"github.com/edgelesssys/constellation/v2/internal/attestation/initialize"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
) )
@ -40,5 +41,5 @@ func (l *Lock) TryLockOnce(clusterID []byte) (bool, error) {
return false, nil return false, nil
} }
return true, vtpm.MarkNodeAsBootstrapped(l.tpm, clusterID) return true, initialize.MarkNodeAsBootstrapped(l.tpm, clusterID)
} }

View File

@ -59,7 +59,7 @@ func updatePCR(m measurements.M, pcrIndex uint32, encoded string) error {
oldExpected := m[pcrIndex].Expected oldExpected := m[pcrIndex].Expected
expectedPcr := sha256.Sum256(append(oldExpected[:], hashedInput[:]...)) expectedPcr := sha256.Sum256(append(oldExpected[:], hashedInput[:]...))
m[pcrIndex] = measurements.Measurement{ m[pcrIndex] = measurements.Measurement{
Expected: expectedPcr, Expected: expectedPcr[:],
ValidationOpt: m[pcrIndex].ValidationOpt, ValidationOpt: m[pcrIndex].ValidationOpt,
} }
return nil return nil

View File

@ -132,7 +132,7 @@ func TestValidatorUpdateInitPCRs(t *testing.T) {
case i == int(measurements.PCRIndexClusterID): case i == int(measurements.PCRIndexClusterID):
pcr, ok := m[uint32(i)] pcr, ok := m[uint32(i)]
assert.True(ok) assert.True(ok)
assert.Equal(pcrZeroUpdatedOne, pcr.Expected) assert.Equal(pcrZeroUpdatedOne[:], pcr.Expected)
case i == int(measurements.PCRIndexOwnerID) && tc.ownerID == "": case i == int(measurements.PCRIndexOwnerID) && tc.ownerID == "":
// should be deleted // should be deleted
@ -142,7 +142,7 @@ func TestValidatorUpdateInitPCRs(t *testing.T) {
case i == int(measurements.PCRIndexOwnerID): case i == int(measurements.PCRIndexOwnerID):
pcr, ok := m[uint32(i)] pcr, ok := m[uint32(i)]
assert.True(ok) assert.True(ok)
assert.Equal(pcrZeroUpdatedOne, pcr.Expected) assert.Equal(pcrZeroUpdatedOne[:], pcr.Expected)
default: default:
if i >= 17 && i <= 22 { if i >= 17 && i <= 22 {

View File

@ -69,6 +69,7 @@ go_library(
"terraform/openstack/modules/loadbalancer/variables.tf", "terraform/openstack/modules/loadbalancer/variables.tf",
"terraform/openstack/outputs.tf", "terraform/openstack/outputs.tf",
"terraform/openstack/variables.tf", "terraform/openstack/variables.tf",
"terraform/qemu/modules/instance_group/tdx_domain.xsl",
], ],
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/terraform", importpath = "github.com/edgelesssys/constellation/v2/cli/internal/terraform",
visibility = ["//cli:__subpackages__"], visibility = ["//cli:__subpackages__"],

View File

@ -82,22 +82,6 @@
<xsl:template match="/domain/vcpu"> <xsl:template match="/domain/vcpu">
<vcpu placement="static"><xsl:apply-templates select="@*|node()"/></vcpu> <vcpu placement="static"><xsl:apply-templates select="@*|node()"/></vcpu>
</xsl:template> </xsl:template>
<xsl:template match="/domain/cpu">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<xsl:element name ="topology">
<xsl:attribute name="sockets">
<xsl:value-of select="'1'"/>
</xsl:attribute>
<xsl:attribute name="cores">
<xsl:value-of select="'1'"/>
</xsl:attribute>
<xsl:attribute name="threads">
<xsl:value-of select="'1'"/>
</xsl:attribute>
</xsl:element>
</xsl:copy>
</xsl:template>
<xsl:template match="/domain/devices/console"> <xsl:template match="/domain/devices/console">
<console type="pty"> <console type="pty">
<target type="virtio" port="1" /> <target type="virtio" port="1" />

View File

@ -12,6 +12,7 @@ go_library(
deps = [ deps = [
"//disk-mapper/internal/systemd", "//disk-mapper/internal/systemd",
"//internal/attestation", "//internal/attestation",
"//internal/attestation/initialize",
"//internal/attestation/vtpm", "//internal/attestation/vtpm",
"//internal/cloud/metadata", "//internal/cloud/metadata",
"//internal/constants", "//internal/constants",

View File

@ -26,6 +26,7 @@ import (
"github.com/edgelesssys/constellation/v2/disk-mapper/internal/systemd" "github.com/edgelesssys/constellation/v2/disk-mapper/internal/systemd"
"github.com/edgelesssys/constellation/v2/internal/attestation" "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/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/crypto" "github.com/edgelesssys/constellation/v2/internal/crypto"
@ -109,7 +110,7 @@ func (s *Manager) PrepareExistingDisk(recover RecoveryDoer) error {
} }
// taint the node as initialized // 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 return err
} }

2
go.mod
View File

@ -70,6 +70,7 @@ require (
github.com/docker/docker v23.0.3+incompatible 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/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-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/fsnotify/fsnotify v1.6.0
github.com/go-playground/locales v0.14.1 github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.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/theupdateframework/go-tuf v0.5.2 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/transparency-dev/merkle v0.0.1 // 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/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect

4
go.sum
View File

@ -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/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 h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc= 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/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= 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 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 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/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/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.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=

View File

@ -125,6 +125,7 @@ require (
github.com/docker/go-units v0.5.0 // indirect 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/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-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/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // 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/theupdateframework/go-tuf v0.5.2 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/transparency-dev/merkle v0.0.1 // 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/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect

View File

@ -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/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 h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc= 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/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= 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 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 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/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/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.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=

View File

@ -29,6 +29,9 @@ Attestation code for new platforms needs to implement these two interfaces.
package attestation package attestation
import ( import (
"bytes"
"crypto/sha256"
"github.com/edgelesssys/constellation/v2/internal/crypto" "github.com/edgelesssys/constellation/v2/internal/crypto"
) )
@ -40,7 +43,47 @@ const (
MeasurementSecretContext = "measurementSecret" 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. // DeriveClusterID derives the cluster ID from a salt and secret value.
func DeriveClusterID(secret, salt []byte) ([]byte, error) { func DeriveClusterID(secret, salt []byte) ([]byte, error) {
return crypto.DeriveKey(secret, salt, []byte(crypto.DEKPrefix+clusterIDContext), crypto.DerivedKeyLengthDefault) 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)
}

View File

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package attestation package attestation
import ( import (
"bytes"
"testing" "testing"
"github.com/edgelesssys/constellation/v2/internal/crypto/testvector" "github.com/edgelesssys/constellation/v2/internal/crypto/testvector"
@ -31,3 +32,48 @@ func TestDeriveClusterID(t *testing.T) {
require.NoError(err) require.NoError(err)
assert.NotEqual(clusterID, clusterIDdiff) 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)
})
}
}

View File

@ -11,6 +11,7 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/aws", importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/aws",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = [ deps = [
"//internal/attestation",
"//internal/attestation/vtpm", "//internal/attestation/vtpm",
"//internal/config", "//internal/config",
"//internal/variant", "//internal/variant",

View File

@ -14,6 +14,7 @@ import (
"log" "log"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "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/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
@ -28,7 +29,7 @@ type Issuer struct {
} }
// NewIssuer creates a new OpenVTPM based issuer for AWS. // NewIssuer creates a new OpenVTPM based issuer for AWS.
func NewIssuer(log vtpm.AttestationLogger) *Issuer { func NewIssuer(log attestation.Logger) *Issuer {
return &Issuer{ return &Issuer{
Issuer: vtpm.NewIssuer( Issuer: vtpm.NewIssuer(
vtpm.OpenVTPM, vtpm.OpenVTPM,

View File

@ -15,6 +15,7 @@ import (
awsConfig "github.com/aws/aws-sdk-go-v2/config" 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/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/ec2" "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/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
@ -30,7 +31,7 @@ type Validator struct {
} }
// NewValidator create a new Validator structure and returns it. // 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{}
v.Validator = vtpm.NewValidator( v.Validator = vtpm.NewValidator(
cfg.Measurements, cfg.Measurements,

View File

@ -14,6 +14,7 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp", importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = [ deps = [
"//internal/attestation",
"//internal/attestation/idkeydigest", "//internal/attestation/idkeydigest",
"//internal/attestation/vtpm", "//internal/attestation/vtpm",
"//internal/cloud/azure", "//internal/cloud/azure",

View File

@ -12,6 +12,7 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
"github.com/edgelesssys/go-azguestattestation/maa" "github.com/edgelesssys/go-azguestattestation/maa"
@ -30,7 +31,7 @@ type Issuer struct {
} }
// NewIssuer initializes a new Azure Issuer. // NewIssuer initializes a new Azure Issuer.
func NewIssuer(log vtpm.AttestationLogger) *Issuer { func NewIssuer(log attestation.Logger) *Issuer {
i := &Issuer{ i := &Issuer{
imds: newIMDSClient(), imds: newIMDSClient(),
maa: newMAAClient(), maa: newMAAClient(),

View File

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
@ -38,11 +39,11 @@ type Validator struct {
config *config.AzureSEVSNP config *config.AzureSEVSNP
log vtpm.AttestationLogger log attestation.Logger
} }
// NewValidator initializes a new Azure validator with the provided PCR values. // 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 { if log == nil {
log = nopAttestationLogger{} log = nopAttestationLogger{}
} }

View File

@ -11,6 +11,7 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch", importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = [ deps = [
"//internal/attestation",
"//internal/attestation/vtpm", "//internal/attestation/vtpm",
"//internal/config", "//internal/config",
"//internal/crypto", "//internal/crypto",

View File

@ -15,6 +15,7 @@ import (
"io" "io"
"net/http" "net/http"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
tpmclient "github.com/google/go-tpm-tools/client" tpmclient "github.com/google/go-tpm-tools/client"
@ -34,7 +35,7 @@ type Issuer struct {
} }
// NewIssuer initializes a new Azure Issuer. // NewIssuer initializes a new Azure Issuer.
func NewIssuer(log vtpm.AttestationLogger) *Issuer { func NewIssuer(log attestation.Logger) *Issuer {
i := &Issuer{ i := &Issuer{
hClient: &http.Client{}, hClient: &http.Client{},
} }

View File

@ -15,6 +15,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
certutil "github.com/edgelesssys/constellation/v2/internal/crypto" 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. // 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 := x509.NewCertPool()
rootPool.AddCert(ameRoot) rootPool.AddCert(ameRoot)
v := &Validator{roots: rootPool} v := &Validator{roots: rootPool}

View File

@ -8,12 +8,13 @@ go_library(
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = [ deps = [
"//internal/atls", "//internal/atls",
"//internal/attestation",
"//internal/attestation/aws", "//internal/attestation/aws",
"//internal/attestation/azure/snp", "//internal/attestation/azure/snp",
"//internal/attestation/azure/trustedlaunch", "//internal/attestation/azure/trustedlaunch",
"//internal/attestation/gcp", "//internal/attestation/gcp",
"//internal/attestation/qemu", "//internal/attestation/qemu",
"//internal/attestation/vtpm", "//internal/attestation/tdx",
"//internal/config", "//internal/config",
"//internal/variant", "//internal/variant",
], ],

View File

@ -10,18 +10,19 @@ import (
"fmt" "fmt"
"github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/atls"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/aws" "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/snp"
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch"
"github.com/edgelesssys/constellation/v2/internal/attestation/gcp" "github.com/edgelesssys/constellation/v2/internal/attestation/gcp"
"github.com/edgelesssys/constellation/v2/internal/attestation/qemu" "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/config"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
) )
// Issuer returns the issuer for the given 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 { switch attestationVariant {
case variant.AWSNitroTPM{}: case variant.AWSNitroTPM{}:
return aws.NewIssuer(log), nil return aws.NewIssuer(log), nil
@ -33,6 +34,8 @@ func Issuer(attestationVariant variant.Variant, log vtpm.AttestationLogger) (atl
return gcp.NewIssuer(log), nil return gcp.NewIssuer(log), nil
case variant.QEMUVTPM{}: case variant.QEMUVTPM{}:
return qemu.NewIssuer(log), nil return qemu.NewIssuer(log), nil
case variant.QEMUTDX{}:
return tdx.NewIssuer(log), nil
case variant.Dummy{}: case variant.Dummy{}:
return atls.NewFakeIssuer(variant.Dummy{}), nil return atls.NewFakeIssuer(variant.Dummy{}), nil
default: default:
@ -41,7 +44,7 @@ func Issuer(attestationVariant variant.Variant, log vtpm.AttestationLogger) (atl
} }
// Validator returns the validator for the given variant. // 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) { switch cfg := cfg.(type) {
case *config.AWSNitroTPM: case *config.AWSNitroTPM:
return aws.NewValidator(cfg, log), nil 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 return gcp.NewValidator(cfg, log), nil
case *config.QEMUVTPM: case *config.QEMUVTPM:
return qemu.NewValidator(cfg, log), nil return qemu.NewValidator(cfg, log), nil
case *config.QEMUTDX:
return tdx.NewValidator(cfg, log), nil
case *config.DummyCfg: case *config.DummyCfg:
return atls.NewFakeValidator(variant.Dummy{}), nil return atls.NewFakeValidator(variant.Dummy{}), nil
default: default:

View File

@ -11,6 +11,7 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/gcp", importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/gcp",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = [ deps = [
"//internal/attestation",
"//internal/attestation/vtpm", "//internal/attestation/vtpm",
"//internal/config", "//internal/config",
"//internal/variant", "//internal/variant",

View File

@ -13,6 +13,7 @@ import (
"io" "io"
"cloud.google.com/go/compute/metadata" "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/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
tpmclient "github.com/google/go-tpm-tools/client" tpmclient "github.com/google/go-tpm-tools/client"
@ -26,7 +27,7 @@ type Issuer struct {
} }
// NewIssuer initializes a new GCP Issuer. // NewIssuer initializes a new GCP Issuer.
func NewIssuer(log vtpm.AttestationLogger) *Issuer { func NewIssuer(log attestation.Logger) *Issuer {
return &Issuer{ return &Issuer{
Issuer: vtpm.NewIssuer( Issuer: vtpm.NewIssuer(
vtpm.OpenVTPM, vtpm.OpenVTPM,

View File

@ -16,6 +16,7 @@ import (
compute "cloud.google.com/go/compute/apiv1" compute "cloud.google.com/go/compute/apiv1"
"cloud.google.com/go/compute/apiv1/computepb" "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/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/variant" "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. // 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{ v := &Validator{
restClient: newInstanceClient, restClient: newInstanceClient,
} }

View File

@ -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",
],
)

View File

@ -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)))
}

View File

@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only SPDX-License-Identifier: AGPL-3.0-only
*/ */
package vtpm package initialize
import ( import (
"errors" "errors"

View File

@ -172,8 +172,8 @@ func verifyWithRekor(ctx context.Context, verifier rekorVerifier, hash string) e
// byteArrayCompositeLit returns a *ast.CompositeLit representing a byte array literal. // byteArrayCompositeLit returns a *ast.CompositeLit representing a byte array literal.
// The returned literal is of the form: // The returned literal is of the form:
// [32]byte{ 0x01, 0x02, 0x03, ... }. // []byte{ 0x01, 0x02, 0x03, ... }.
func byteArrayCompositeLit(hex [32]byte) *ast.CompositeLit { func byteArrayCompositeLit(hex []byte) *ast.CompositeLit {
var elts []ast.Expr var elts []ast.Expr
// create list of byte literals // create list of byte literals
for _, b := range hex { for _, b := range hex {
@ -183,10 +183,9 @@ func byteArrayCompositeLit(hex [32]byte) *ast.CompositeLit {
}) })
} }
// create the composite literal // 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{ return &ast.CompositeLit{
Type: &ast.ArrayType{ Type: &ast.ArrayType{
Len: ast.NewIdent("32"),
Elt: ast.NewIdent("byte"), Elt: ast.NewIdent("byte"),
}, },
Elts: elts, Elts: elts,
@ -197,7 +196,7 @@ func byteArrayCompositeLit(hex [32]byte) *ast.CompositeLit {
// The returned expression is of the form: // The returned expression is of the form:
// //
// 0: { // 0: {
// Expected: [32]byte{ 0x01, 0x02, 0x03, ... }, // Expected: []byte{ 0x01, 0x02, 0x03, ... },
// WarnOnly: false, // WarnOnly: false,
// }, // },
func measurementsEntryKeyValueExpr(pcr uint32, measuremnt measurements.Measurement) *ast.KeyValueExpr { func measurementsEntryKeyValueExpr(pcr uint32, measuremnt measurements.Measurement) *ast.KeyValueExpr {
@ -235,11 +234,11 @@ func measurementsEntryKeyValueExpr(pcr uint32, measuremnt measurements.Measureme
// //
// M{ // M{
// 0: { // 0: {
// Expected: [32]byte{ 0x01, 0x02, 0x03, ... }, // Expected: []byte{ 0x01, 0x02, 0x03, ... },
// WarnOnly: false, // WarnOnly: false,
// }, // },
// 1: { // 1: {
// Expected: [32]byte{ 0x01, 0x02, 0x03, ... }, // Expected: []byte{ 0x01, 0x02, 0x03, ... },
// WarnOnly: false, // WarnOnly: false,
// }, // },
// ... // ...

View File

@ -43,6 +43,12 @@ const (
// The value used to extend is derived from Constellation's master key. // The value used to extend is derived from Constellation's master key.
// TODO: move to stable, non-debug PCR before use. // TODO: move to stable, non-debug PCR before use.
PCRIndexOwnerID = tpmutil.Handle(16) 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. // 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 { for k, v := range *m {
otherExpected := other[k].Expected otherExpected := other[k].Expected
if !bytes.Equal(v.Expected[:], otherExpected[:]) { if !bytes.Equal(v.Expected, otherExpected) {
return false return false
} }
if v.ValidationOpt != other[k].ValidationOpt { if v.ValidationOpt != other[k].ValidationOpt {
@ -177,10 +183,45 @@ func (m *M) SetEnforced(enforced []uint32) error {
return nil 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. // Measurement wraps expected PCR value and whether it is enforced.
type Measurement struct { type Measurement struct {
// Expected measurement value. // 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 indicates how measurement mismatches should be handled.
ValidationOpt MeasurementValidationOption `json:"warnOnly" yaml:"warnOnly"` 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)) return fmt.Errorf("invalid measurement: invalid length: %d", len(expected))
} }
m.Expected = *(*[32]byte)(expected) m.Expected = expected
m.ValidationOpt = eM.WarnOnly m.ValidationOpt = eM.WarnOnly
return nil 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. // WithAllBytes returns a measurement value where all 32 bytes are set to b.
func WithAllBytes(b byte, validationOpt MeasurementValidationOption) Measurement { func WithAllBytes(b byte, validationOpt MeasurementValidationOption) Measurement {
return Measurement{ return Measurement{
Expected: *(*[32]byte)(bytes.Repeat([]byte{b}, 32)), Expected: bytes.Repeat([]byte{b}, 32),
ValidationOpt: validationOpt, ValidationOpt: validationOpt,
} }
} }
@ -290,7 +331,7 @@ func WithAllBytes(b byte, validationOpt MeasurementValidationOption) Measurement
// PlaceHolderMeasurement returns a measurement with placeholder values for Expected. // PlaceHolderMeasurement returns a measurement with placeholder values for Expected.
func PlaceHolderMeasurement() Measurement { func PlaceHolderMeasurement() Measurement {
return Measurement{ return Measurement{
Expected: *(*[32]byte)(bytes.Repeat([]byte{0x12, 0x34}, 16)), Expected: bytes.Repeat([]byte{0x12, 0x34}, 16),
ValidationOpt: Enforce, ValidationOpt: Enforce,
} }
} }
@ -316,6 +357,18 @@ func getFromURL(ctx context.Context, client *http.Client, sourceURL *url.URL) ([
return content, nil 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 { type encodedMeasurement struct {
Expected string `json:"expected" yaml:"expected"` Expected string `json:"expected" yaml:"expected"`
WarnOnly MeasurementValidationOption `json:"warnOnly" yaml:"warnOnly"` WarnOnly MeasurementValidationOption `json:"warnOnly" yaml:"warnOnly"`

View File

@ -16,19 +16,19 @@ import "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
//go:generate measurement-generator //go:generate measurement-generator
// DefaultsFor provides the default measurements for given cloud provider. // DefaultsFor provides the default measurements for given cloud provider.
func DefaultsFor(provider cloudprovider.Provider) M { func DefaultsFor(attestationVariant variant.Variant) M {
switch provider { switch provider {
case cloudprovider.AWS: 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: 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: 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: 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: default:
return nil return nil

View File

@ -9,6 +9,7 @@ package measurements
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
@ -30,14 +31,14 @@ func TestMarshal(t *testing.T) {
}{ }{
"measurement": { "measurement": {
m: 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", wantYAML: "expected: \"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35\"\nwarnOnly: false",
wantJSON: `{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35","warnOnly":false}`, wantJSON: `{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35","warnOnly":false}`,
}, },
"warn only": { "warn only": {
m: Measurement{ 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, ValidationOpt: WarnOnly,
}, },
wantYAML: "expected: \"0102030400000000000000000000000000000000000000000000000000000000\"\nwarnOnly: true", wantYAML: "expected: \"0102030400000000000000000000000000000000000000000000000000000000\"\nwarnOnly: true",
@ -81,10 +82,10 @@ func TestUnmarshal(t *testing.T) {
inputJSON: `{"2":{"expected":"/V3p3zUOO8RBCsBrv+XM3rk/U7nvUSOfdSzmnbxgDzU="},"3":{"expected":"1aRJbSHeyaUljdsZxv61O7TTwEY/5gfySI3fTxAG754="}}`, inputJSON: `{"2":{"expected":"/V3p3zUOO8RBCsBrv+XM3rk/U7nvUSOfdSzmnbxgDzU="},"3":{"expected":"1aRJbSHeyaUljdsZxv61O7TTwEY/5gfySI3fTxAG754="}}`,
wantMeasurements: M{ wantMeasurements: M{
2: { 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: { 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"}}`, inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9e"}}`,
wantMeasurements: M{ wantMeasurements: M{
2: { 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: { 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="}}`, inputJSON: `{"2":{"expected":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},"3":{"expected":"AQIDBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}`,
wantMeasurements: M{ wantMeasurements: M{
2: { 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: { 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="}`, inputJSON: `{"2":"/V3p3zUOO8RBCsBrv+XM3rk/U7nvUSOfdSzmnbxgDzU=","3":"1aRJbSHeyaUljdsZxv61O7TTwEY/5gfySI3fTxAG754="}`,
wantMeasurements: M{ wantMeasurements: M{
2: { 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: { 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"}}`, inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463f"}}`,
wantErr: true, 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": { "invalid length base64": {
inputYAML: "2:\n expected: \"AA==\"\n3:\n expected: \"AQIDBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\"", inputYAML: "2:\n expected: \"AA==\"\n3:\n expected: \"AQIDBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\"",
inputJSON: `{"2":{"expected":"AA=="},"3":{"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) err := json.Unmarshal([]byte(tc.inputJSON), &m)
if tc.wantErr { if tc.wantErr {
fmt.Println(err)
assert.Error(err, "json.Unmarshal should have failed") assert.Error(err, "json.Unmarshal should have failed")
} else { } else {
require.NoError(err, "json.Unmarshal failed") require.NoError(err, "json.Unmarshal failed")
@ -200,10 +219,10 @@ func TestEncodeM(t *testing.T) {
}, },
"output is sorted": { "output is sorted": {
m: M{ m: M{
3: {}, 3: WithAllBytes(0, false),
1: {}, 1: WithAllBytes(0, false),
11: {}, 11: WithAllBytes(0, false),
2: {}, 2: WithAllBytes(0, false),
}, },
want: `1: want: `1:
expected: "0000000000000000000000000000000000000000000000000000000000000000" expected: "0000000000000000000000000000000000000000000000000000000000000000"
@ -605,7 +624,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0x00, b: 0x00,
warnOnly: true, warnOnly: true,
wantMeasurement: Measurement{ 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, ValidationOpt: WarnOnly,
}, },
}, },
@ -613,7 +632,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0x00, b: 0x00,
warnOnly: false, warnOnly: false,
wantMeasurement: Measurement{ 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, ValidationOpt: Enforce,
}, },
}, },
@ -621,7 +640,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0x01, b: 0x01,
warnOnly: true, warnOnly: true,
wantMeasurement: Measurement{ 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, ValidationOpt: WarnOnly,
}, },
}, },
@ -629,7 +648,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0x01, b: 0x01,
warnOnly: false, warnOnly: false,
wantMeasurement: Measurement{ 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, ValidationOpt: Enforce,
}, },
}, },
@ -637,7 +656,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0xFF, b: 0xFF,
warnOnly: true, warnOnly: true,
wantMeasurement: Measurement{ 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, ValidationOpt: WarnOnly,
}, },
}, },
@ -645,7 +664,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0xFF, b: 0xFF,
warnOnly: false, warnOnly: false,
wantMeasurement: Measurement{ 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, ValidationOpt: Enforce,
}, },
}, },

View File

@ -10,6 +10,7 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/qemu", importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/qemu",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = [ deps = [
"//internal/attestation",
"//internal/attestation/vtpm", "//internal/attestation/vtpm",
"//internal/config", "//internal/config",
"//internal/variant", "//internal/variant",

View File

@ -10,6 +10,7 @@ import (
"context" "context"
"io" "io"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/variant"
tpmclient "github.com/google/go-tpm-tools/client" tpmclient "github.com/google/go-tpm-tools/client"
@ -22,7 +23,7 @@ type Issuer struct {
} }
// NewIssuer initializes a new QEMU Issuer. // NewIssuer initializes a new QEMU Issuer.
func NewIssuer(log vtpm.AttestationLogger) *Issuer { func NewIssuer(log attestation.Logger) *Issuer {
return &Issuer{ return &Issuer{
Issuer: vtpm.NewIssuer( Issuer: vtpm.NewIssuer(
vtpm.OpenVTPM, vtpm.OpenVTPM,

View File

@ -10,6 +10,7 @@ import (
"context" "context"
"crypto" "crypto"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/variant" "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. // 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{ return &Validator{
Validator: vtpm.NewValidator( Validator: vtpm.NewValidator(
cfg.Measurements, cfg.Measurements,

View File

@ -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",
],
)

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -5,12 +5,12 @@ go_library(
name = "vtpm", name = "vtpm",
srcs = [ srcs = [
"attestation.go", "attestation.go",
"initialize.go",
"vtpm.go", "vtpm.go",
], ],
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm", importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = [ deps = [
"//internal/attestation",
"//internal/attestation/measurements", "//internal/attestation/measurements",
"@com_github_google_go_tpm//tpm2", "@com_github_google_go_tpm//tpm2",
"@com_github_google_go_tpm_tools//client", "@com_github_google_go_tpm_tools//client",
@ -24,7 +24,6 @@ go_test(
name = "vtpm_test", name = "vtpm_test",
srcs = [ srcs = [
"attestation_test.go", "attestation_test.go",
"initialize_test.go",
"vtpm_test.go", "vtpm_test.go",
], ],
embed = [":vtpm"], embed = [":vtpm"],
@ -34,6 +33,7 @@ go_test(
"//conditions:default": ["disable_tpm_simulator"], "//conditions:default": ["disable_tpm_simulator"],
}), }),
deps = [ deps = [
"//internal/attestation/initialize",
"//internal/attestation/measurements", "//internal/attestation/measurements",
"//internal/attestation/simulator", "//internal/attestation/simulator",
"//internal/logger", "//internal/logger",

View File

@ -10,7 +10,6 @@ import (
"bytes" "bytes"
"context" "context"
"crypto" "crypto"
"crypto/sha256"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -21,6 +20,7 @@ import (
tpmServer "github.com/google/go-tpm-tools/server" tpmServer "github.com/google/go-tpm-tools/server"
"github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
) )
@ -66,12 +66,6 @@ type (
ValidateCVM func(attestation AttestationDocument, state *attest.MachineState) error 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. // AttestationDocument contains the TPM attestation with signed user data.
type AttestationDocument struct { type AttestationDocument struct {
// Attestation contains the TPM event log, PCR values and quotes, and public key of the key used to sign the attestation. // 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 openTPM TPMOpenFunc
getAttestationKey GetTPMAttestationKey getAttestationKey GetTPMAttestationKey
getInstanceInfo GetInstanceInfo getInstanceInfo GetInstanceInfo
log AttestationLogger log attestation.Logger
} }
// NewIssuer returns a new Issuer. // NewIssuer returns a new Issuer.
func NewIssuer( func NewIssuer(
openTPM TPMOpenFunc, getAttestationKey GetTPMAttestationKey, openTPM TPMOpenFunc, getAttestationKey GetTPMAttestationKey,
getInstanceInfo GetInstanceInfo, log AttestationLogger, getInstanceInfo GetInstanceInfo, log attestation.Logger,
) *Issuer { ) *Issuer {
if log == nil { if log == nil {
log = &nopAttestationLogger{} log = &attestation.NOPLogger{}
} }
return &Issuer{ return &Issuer{
openTPM: openTPM, openTPM: openTPM,
@ -129,7 +123,7 @@ func (i *Issuer) Issue(ctx context.Context, userData []byte, nonce []byte) (res
defer aK.Close() defer aK.Close()
// Create an attestation using the loaded key // Create an attestation using the loaded key
extraData := makeExtraData(userData, nonce) extraData := attestation.MakeExtraData(userData, nonce)
tpmAttestation, err := aK.Attest(tpmClient.AttestOpts{Nonce: extraData}) tpmAttestation, err := aK.Attest(tpmClient.AttestOpts{Nonce: extraData})
if err != nil { if err != nil {
return nil, fmt.Errorf("creating attestation: %w", err) return nil, fmt.Errorf("creating attestation: %w", err)
@ -162,15 +156,15 @@ type Validator struct {
getTrustedKey GetTPMTrustedAttestationPublicKey getTrustedKey GetTPMTrustedAttestationPublicKey
validateCVM ValidateCVM validateCVM ValidateCVM
log AttestationLogger log attestation.Logger
} }
// NewValidator returns a new Validator. // NewValidator returns a new Validator.
func NewValidator(expected measurements.M, getTrustedKey GetTPMTrustedAttestationPublicKey, func NewValidator(expected measurements.M, getTrustedKey GetTPMTrustedAttestationPublicKey,
validateCVM ValidateCVM, log AttestationLogger, validateCVM ValidateCVM, log attestation.Logger,
) *Validator { ) *Validator {
if log == nil { if log == nil {
log = &nopAttestationLogger{} log = &attestation.NOPLogger{}
} }
return &Validator{ return &Validator{
expected: expected, 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) 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 // Verify and retrieve the trusted attestation public key using the provided instance info
aKP, err := v.getTrustedKey(ctx, attDoc, extraData) 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)) return nil, fmt.Errorf("invalid measurement: invalid length: %d", len(pcr))
} }
m[i] = measurements.Measurement{ m[i] = measurements.Measurement{
Expected: *(*[32]byte)(pcr), Expected: pcr,
} }
} }
return m, nil 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{}) {}

View File

@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "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/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator" "github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
tpmsim "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) require.Equal(challenge, out)
// validation must fail after bootstrapping (change of enforced PCR) // 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) attDocBootstrappedRaw, err := issuer.Issue(ctx, challenge, nonce)
require.NoError(err) require.NoError(err)
_, err = validator.Validate(ctx, attDocBootstrappedRaw, nonce) _, err = validator.Validate(ctx, attDocBootstrappedRaw, nonce)
@ -121,19 +122,19 @@ func TestValidate(t *testing.T) {
0: measurements.WithAllBytes(0x00, measurements.WarnOnly), 0: measurements.WithAllBytes(0x00, measurements.WarnOnly),
1: measurements.WithAllBytes(0x00, measurements.WarnOnly), 1: measurements.WithAllBytes(0x00, measurements.WarnOnly),
2: measurements.Measurement{ 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, ValidationOpt: measurements.WarnOnly,
}, },
3: measurements.Measurement{ 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, ValidationOpt: measurements.WarnOnly,
}, },
4: measurements.Measurement{ 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, ValidationOpt: measurements.WarnOnly,
}, },
5: measurements.Measurement{ 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, ValidationOpt: measurements.WarnOnly,
}, },
} }
@ -202,7 +203,7 @@ func TestValidate(t *testing.T) {
validator: NewValidator( validator: NewValidator(
measurements.M{ measurements.M{
0: measurements.Measurement{ 0: measurements.Measurement{
Expected: [32]byte{0xFF}, Expected: []byte{0xFF},
ValidationOpt: measurements.Enforce, ValidationOpt: measurements.Enforce,
}, },
}, },

View File

@ -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
}

View File

@ -9,16 +9,9 @@ package vtpm
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert"
"go.uber.org/goleak" "go.uber.org/goleak"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
goleak.VerifyTestMain(m) goleak.VerifyTestMain(m)
} }
func TestNOPTPM(t *testing.T) {
assert := assert.New(t)
assert.NoError(MarkNodeAsBootstrapped(OpenNOPTPM, []byte{0x0, 0x1, 0x2, 0x3}))
}

View File

@ -885,6 +885,37 @@ func (c QEMUVTPM) EqualTo(other AttestationCfg) (bool, error) {
return c.Measurements.EqualTo(otherCfg.Measurements), nil 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 { func toPtr[T any](v T) *T {
return &v return &v
} }

View File

@ -43,6 +43,7 @@ const (
azureSEVSNP = "azure-sev-snp" azureSEVSNP = "azure-sev-snp"
azureTrustedLaunch = "azure-trustedlaunch" azureTrustedLaunch = "azure-trustedlaunch"
qemuVTPM = "qemu-vtpm" qemuVTPM = "qemu-vtpm"
qemuTDX = "qemu-tdx"
) )
// Getter returns an ASN.1 Object Identifier. // Getter returns an ASN.1 Object Identifier.
@ -72,6 +73,8 @@ func FromString(oid string) (Variant, error) {
return AzureTrustedLaunch{}, nil return AzureTrustedLaunch{}, nil
case qemuVTPM: case qemuVTPM:
return QEMUVTPM{}, nil return QEMUVTPM{}, nil
case qemuTDX:
return QEMUTDX{}, nil
} }
return nil, fmt.Errorf("unknown OID: %q", oid) return nil, fmt.Errorf("unknown OID: %q", oid)
} }
@ -183,3 +186,22 @@ func (QEMUVTPM) String() string {
func (QEMUVTPM) Equal(other Getter) bool { func (QEMUVTPM) Equal(other Getter) bool {
return other.OID().Equal(QEMUVTPM{}.OID()) 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())
}