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=",
version = "v0.0.0-20230303085714-62ede861d33f",
)
go_repository(
name = "com_github_edgelesssys_go_tdx_qpl",
build_file_generation = "on",
build_file_proto_mode = "disable_global",
importpath = "github.com/edgelesssys/go-tdx-qpl",
sum = "h1:uQMmc/B1RGE2VeSsh/NqjRgEheqp1cjy8ELIDTFpaUw=",
version = "v0.0.0-20230307140231-bb361f158928",
)
go_repository(
name = "com_github_edsrzf_mmap_go",
@ -6680,6 +6688,15 @@ def go_dependencies():
sum = "h1:gpw/0Ku+6RgF3jsi7fnCLmlcikBHfKBCUcu1qgc16OU=",
version = "v0.20.3",
)
go_repository(
name = "com_github_vtolstov_go_ioctl",
build_file_generation = "on",
build_file_proto_mode = "disable_global",
importpath = "github.com/vtolstov/go-ioctl",
sum = "h1:X6ps8XHfpQjw8dUStzlMi2ybiKQ2Fmdw7UM+TinwvyM=",
version = "v0.0.0-20151206205506-6be9cced4810",
)
go_repository(
name = "com_github_weppos_publicsuffix_go",
build_file_generation = "on",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -82,22 +82,6 @@
<xsl:template match="/domain/vcpu">
<vcpu placement="static"><xsl:apply-templates select="@*|node()"/></vcpu>
</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">
<console type="pty">
<target type="virtio" port="1" />

View File

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

View File

@ -26,6 +26,7 @@ import (
"github.com/edgelesssys/constellation/v2/disk-mapper/internal/systemd"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/initialize"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/crypto"
@ -109,7 +110,7 @@ func (s *Manager) PrepareExistingDisk(recover RecoveryDoer) error {
}
// taint the node as initialized
if err := vtpm.MarkNodeAsBootstrapped(s.openTPM, clusterID); err != nil {
if err := initialize.MarkNodeAsBootstrapped(s.openTPM, clusterID); err != nil {
return err
}

2
go.mod
View File

@ -70,6 +70,7 @@ require (
github.com/docker/docker v23.0.3+incompatible
github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928
github.com/fsnotify/fsnotify v1.6.0
github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.1
@ -301,6 +302,7 @@ require (
github.com/theupdateframework/go-tuf v0.5.2 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/transparency-dev/merkle v0.0.1 // indirect
github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect

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/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc=
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928 h1:uQMmc/B1RGE2VeSsh/NqjRgEheqp1cjy8ELIDTFpaUw=
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928/go.mod h1:IC72qyykUIWl0ZmSk53L4xbLCFDBEGZVaujUmPQOEyw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
@ -1362,6 +1364,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810 h1:X6ps8XHfpQjw8dUStzlMi2ybiKQ2Fmdw7UM+TinwvyM=
github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810/go.mod h1:dF0BBJ2YrV1+2eAIyEI+KeSidgA6HqoIP1u5XTlMq/o=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=

View File

@ -125,6 +125,7 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0 // indirect
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f // indirect
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
@ -259,6 +260,7 @@ require (
github.com/theupdateframework/go-tuf v0.5.2 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/transparency-dev/merkle v0.0.1 // indirect
github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect

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/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc=
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928 h1:uQMmc/B1RGE2VeSsh/NqjRgEheqp1cjy8ELIDTFpaUw=
github.com/edgelesssys/go-tdx-qpl v0.0.0-20230307140231-bb361f158928/go.mod h1:IC72qyykUIWl0ZmSk53L4xbLCFDBEGZVaujUmPQOEyw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ=
@ -1351,6 +1353,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810 h1:X6ps8XHfpQjw8dUStzlMi2ybiKQ2Fmdw7UM+TinwvyM=
github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810/go.mod h1:dF0BBJ2YrV1+2eAIyEI+KeSidgA6HqoIP1u5XTlMq/o=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=

View File

@ -29,6 +29,9 @@ Attestation code for new platforms needs to implement these two interfaces.
package attestation
import (
"bytes"
"crypto/sha256"
"github.com/edgelesssys/constellation/v2/internal/crypto"
)
@ -40,7 +43,47 @@ const (
MeasurementSecretContext = "measurementSecret"
)
// Logger is a logger used to print warnings and infos during attestation validation.
type Logger interface {
Infof(format string, args ...any)
Warnf(format string, args ...any)
}
// NOPLogger is a no-op implementation of [Logger].
type NOPLogger struct{}
// Infof is a no-op.
func (NOPLogger) Infof(string, ...interface{}) {}
// Warnf is a no-op.
func (NOPLogger) Warnf(string, ...interface{}) {}
// DeriveClusterID derives the cluster ID from a salt and secret value.
func DeriveClusterID(secret, salt []byte) ([]byte, error) {
return crypto.DeriveKey(secret, salt, []byte(crypto.DEKPrefix+clusterIDContext), crypto.DerivedKeyLengthDefault)
}
// MakeExtraData binds userData to a random nonce used in attestation.
func MakeExtraData(userData []byte, nonce []byte) []byte {
data := append([]byte{}, userData...)
data = append(data, nonce...)
digest := sha256.Sum256(data)
return digest[:]
}
// CompareExtraData compares the extra data of a quote with the expected extra data.
// Returns true if the data from the quote matches the expected data.
// If the slices are not of equal length, the shorter slice is padded with zeros.
func CompareExtraData(quoteData, expectedData []byte) bool {
if len(quoteData) != len(expectedData) {
// If the lengths are not equal, pad the shorter slice with zeros.
diff := len(quoteData) - len(expectedData)
if diff < 0 {
diff = -diff
quoteData = append(quoteData, bytes.Repeat([]byte{0x00}, diff)...)
} else {
expectedData = append(expectedData, bytes.Repeat([]byte{0x00}, diff)...)
}
}
return bytes.Equal(quoteData, expectedData)
}

View File

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package attestation
import (
"bytes"
"testing"
"github.com/edgelesssys/constellation/v2/internal/crypto/testvector"
@ -31,3 +32,48 @@ func TestDeriveClusterID(t *testing.T) {
require.NoError(err)
assert.NotEqual(clusterID, clusterIDdiff)
}
func TestCompareExtraData(t *testing.T) {
testCases := map[string]struct {
ExtraData1 []byte
ExtraData2 []byte
Expected bool
}{
"equal": {
ExtraData1: bytes.Repeat([]byte{0xAB}, 32),
ExtraData2: bytes.Repeat([]byte{0xAB}, 32),
Expected: true,
},
"unequal": {
ExtraData1: bytes.Repeat([]byte{0xAB}, 32),
ExtraData2: bytes.Repeat([]byte{0xCD}, 32),
Expected: false,
},
"unequal length": {
ExtraData1: bytes.Repeat([]byte{0xAB}, 32),
ExtraData2: bytes.Repeat([]byte{0xAB}, 64),
Expected: false,
},
"unequal length, padded with 0": {
ExtraData1: []byte{0xAB, 0xAB, 0xAB, 0xAB},
ExtraData2: []byte{0xAB, 0xAB, 0xAB, 0xAB, 0x00, 0x00, 0x00, 0x00},
Expected: true,
},
"unequal length, prefixed with 0": {
ExtraData1: []byte{0x00, 0x00, 0x00, 0x00, 0xAB, 0xAB, 0xAB, 0xAB},
ExtraData2: []byte{0xAB, 0xAB, 0xAB, 0xAB},
Expected: false,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
actual := CompareExtraData(tc.ExtraData1, tc.ExtraData2)
assert.Equal(tc.Expected, actual)
actual = CompareExtraData(tc.ExtraData2, tc.ExtraData1)
assert.Equal(tc.Expected, actual)
})
}
}

View File

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

View File

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

View File

@ -15,6 +15,7 @@ import (
awsConfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/variant"
@ -30,7 +31,7 @@ type Validator struct {
}
// NewValidator create a new Validator structure and returns it.
func NewValidator(cfg *config.AWSNitroTPM, log vtpm.AttestationLogger) *Validator {
func NewValidator(cfg *config.AWSNitroTPM, log attestation.Logger) *Validator {
v := &Validator{}
v.Validator = vtpm.NewValidator(
cfg.Measurements,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,6 +15,7 @@ import (
"errors"
"fmt"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/config"
certutil "github.com/edgelesssys/constellation/v2/internal/crypto"
@ -35,7 +36,7 @@ type Validator struct {
}
// NewValidator initializes a new Azure validator with the provided PCR values.
func NewValidator(cfg *config.AzureTrustedLaunch, log vtpm.AttestationLogger) *Validator {
func NewValidator(cfg *config.AzureTrustedLaunch, log attestation.Logger) *Validator {
rootPool := x509.NewCertPool()
rootPool.AddCert(ameRoot)
v := &Validator{roots: rootPool}

View File

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

View File

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

View File

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

View File

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

View File

@ -16,6 +16,7 @@ import (
compute "cloud.google.com/go/compute/apiv1"
"cloud.google.com/go/compute/apiv1/computepb"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/variant"
@ -35,7 +36,7 @@ type Validator struct {
}
// NewValidator initializes a new GCP validator with the provided PCR values.
func NewValidator(cfg *config.GCPSEVES, log vtpm.AttestationLogger) *Validator {
func NewValidator(cfg *config.GCPSEVES, log attestation.Logger) *Validator {
v := &Validator{
restClient: newInstanceClient,
}

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
*/
package vtpm
package initialize
import (
"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.
// The returned literal is of the form:
// [32]byte{ 0x01, 0x02, 0x03, ... }.
func byteArrayCompositeLit(hex [32]byte) *ast.CompositeLit {
// []byte{ 0x01, 0x02, 0x03, ... }.
func byteArrayCompositeLit(hex []byte) *ast.CompositeLit {
var elts []ast.Expr
// create list of byte literals
for _, b := range hex {
@ -183,10 +183,9 @@ func byteArrayCompositeLit(hex [32]byte) *ast.CompositeLit {
})
}
// create the composite literal
// containing the byte literals as part of an [32]byte array
// containing the byte literals as part of an []byte slice
return &ast.CompositeLit{
Type: &ast.ArrayType{
Len: ast.NewIdent("32"),
Elt: ast.NewIdent("byte"),
},
Elts: elts,
@ -197,7 +196,7 @@ func byteArrayCompositeLit(hex [32]byte) *ast.CompositeLit {
// The returned expression is of the form:
//
// 0: {
// Expected: [32]byte{ 0x01, 0x02, 0x03, ... },
// Expected: []byte{ 0x01, 0x02, 0x03, ... },
// WarnOnly: false,
// },
func measurementsEntryKeyValueExpr(pcr uint32, measuremnt measurements.Measurement) *ast.KeyValueExpr {
@ -235,11 +234,11 @@ func measurementsEntryKeyValueExpr(pcr uint32, measuremnt measurements.Measureme
//
// M{
// 0: {
// Expected: [32]byte{ 0x01, 0x02, 0x03, ... },
// Expected: []byte{ 0x01, 0x02, 0x03, ... },
// WarnOnly: false,
// },
// 1: {
// Expected: [32]byte{ 0x01, 0x02, 0x03, ... },
// Expected: []byte{ 0x01, 0x02, 0x03, ... },
// WarnOnly: false,
// },
// ...

View File

@ -43,6 +43,12 @@ const (
// The value used to extend is derived from Constellation's master key.
// TODO: move to stable, non-debug PCR before use.
PCRIndexOwnerID = tpmutil.Handle(16)
// TDXIndexClusterID is the measurement used to mark the node as initialized.
// The value is the index of the RTMR + 1, since index 0 of the TDX measurements is reserved for MRTD.
TDXIndexClusterID = RTMRIndexClusterID + 1
// RTMRIndexClusterID is the RTMR we extend to mark the node as initialized.
RTMRIndexClusterID = 2
)
// M are Platform Configuration Register (PCR) values that make up the Measurements.
@ -128,7 +134,7 @@ func (m *M) EqualTo(other M) bool {
}
for k, v := range *m {
otherExpected := other[k].Expected
if !bytes.Equal(v.Expected[:], otherExpected[:]) {
if !bytes.Equal(v.Expected, otherExpected) {
return false
}
if v.ValidationOpt != other[k].ValidationOpt {
@ -177,10 +183,45 @@ func (m *M) SetEnforced(enforced []uint32) error {
return nil
}
// UnmarshalJSON unmarshals measurements from json.
// This function enforces all measurements to be of equal length.
func (m *M) UnmarshalJSON(b []byte) error {
newM := make(map[uint32]Measurement)
if err := json.Unmarshal(b, &newM); err != nil {
return err
}
// check if all measurements are of equal length
if err := checkLength(newM); err != nil {
return err
}
*m = newM
return nil
}
// UnmarshalYAML unmarshals measurements from yaml.
// This function enforces all measurements to be of equal length.
func (m *M) UnmarshalYAML(unmarshal func(any) error) error {
newM := make(map[uint32]Measurement)
if err := unmarshal(&newM); err != nil {
return err
}
// check if all measurements are of equal length
if err := checkLength(newM); err != nil {
return err
}
*m = newM
return nil
}
// Measurement wraps expected PCR value and whether it is enforced.
type Measurement struct {
// Expected measurement value.
Expected [32]byte `json:"expected" yaml:"expected"`
// 32 bytes for vTPM attestation, 48 for TDX.
Expected []byte `json:"expected" yaml:"expected"`
// ValidationOpt indicates how measurement mismatches should be handled.
ValidationOpt MeasurementValidationOption `json:"warnOnly" yaml:"warnOnly"`
}
@ -269,11 +310,11 @@ func (m *Measurement) unmarshal(eM encodedMeasurement) error {
}
}
if len(expected) != 32 {
if len(expected) != 32 && len(expected) != 48 {
return fmt.Errorf("invalid measurement: invalid length: %d", len(expected))
}
m.Expected = *(*[32]byte)(expected)
m.Expected = expected
m.ValidationOpt = eM.WarnOnly
return nil
@ -282,7 +323,7 @@ func (m *Measurement) unmarshal(eM encodedMeasurement) error {
// WithAllBytes returns a measurement value where all 32 bytes are set to b.
func WithAllBytes(b byte, validationOpt MeasurementValidationOption) Measurement {
return Measurement{
Expected: *(*[32]byte)(bytes.Repeat([]byte{b}, 32)),
Expected: bytes.Repeat([]byte{b}, 32),
ValidationOpt: validationOpt,
}
}
@ -290,7 +331,7 @@ func WithAllBytes(b byte, validationOpt MeasurementValidationOption) Measurement
// PlaceHolderMeasurement returns a measurement with placeholder values for Expected.
func PlaceHolderMeasurement() Measurement {
return Measurement{
Expected: *(*[32]byte)(bytes.Repeat([]byte{0x12, 0x34}, 16)),
Expected: bytes.Repeat([]byte{0x12, 0x34}, 16),
ValidationOpt: Enforce,
}
}
@ -316,6 +357,18 @@ func getFromURL(ctx context.Context, client *http.Client, sourceURL *url.URL) ([
return content, nil
}
func checkLength(m map[uint32]Measurement) error {
var length int
for idx, measurement := range m {
if length == 0 {
length = len(measurement.Expected)
} else if len(measurement.Expected) != length {
return fmt.Errorf("inconsistent measurement length: index %d: expected %d, got %d", idx, length, len(measurement.Expected))
}
}
return nil
}
type encodedMeasurement struct {
Expected string `json:"expected" yaml:"expected"`
WarnOnly MeasurementValidationOption `json:"warnOnly" yaml:"warnOnly"`

View File

@ -16,19 +16,19 @@ import "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
//go:generate measurement-generator
// DefaultsFor provides the default measurements for given cloud provider.
func DefaultsFor(provider cloudprovider.Provider) M {
func DefaultsFor(attestationVariant variant.Variant) M {
switch provider {
case cloudprovider.AWS:
return M{0: {Expected: [32]byte{0x73, 0x7f, 0x76, 0x7a, 0x12, 0xf5, 0x4e, 0x70, 0xee, 0xcb, 0xc8, 0x68, 0x40, 0x11, 0x32, 0x3a, 0xe2, 0xfe, 0x2d, 0xd9, 0xf9, 0x07, 0x85, 0x57, 0x79, 0x69, 0xd7, 0xa2, 0x01, 0x3e, 0x8c, 0x12}, ValidationOpt: WarnOnly}, 2: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: [32]byte{0x42, 0x3e, 0x51, 0x3f, 0x60, 0x8d, 0x3e, 0x0e, 0x7b, 0xa4, 0xc4, 0xa4, 0x97, 0x50, 0xb4, 0x21, 0xed, 0x49, 0xde, 0xa6, 0x93, 0xb4, 0xdd, 0x60, 0xaa, 0x39, 0x0f, 0x12, 0x26, 0x34, 0x28, 0x38}, ValidationOpt: Enforce}, 6: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 7: {Expected: [32]byte{0x12, 0x0e, 0x49, 0x8d, 0xb2, 0xa2, 0x24, 0xbd, 0x51, 0x2b, 0x6e, 0xfc, 0x9b, 0x02, 0x34, 0xf8, 0x43, 0xe1, 0x0b, 0xf0, 0x61, 0xeb, 0x7a, 0x76, 0xec, 0xca, 0x55, 0x09, 0xa2, 0x23, 0x89, 0x01}, ValidationOpt: WarnOnly}, 8: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: [32]byte{0xe1, 0x13, 0x34, 0x51, 0x4d, 0xf4, 0x9a, 0xf5, 0x6a, 0x2b, 0x13, 0x45, 0x3b, 0x3e, 0xbd, 0xc8, 0x96, 0xb0, 0xac, 0x1f, 0x36, 0x4c, 0x5c, 0x68, 0x34, 0xd4, 0x4a, 0x43, 0x6b, 0x80, 0x54, 0xe5}, ValidationOpt: Enforce}, 11: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: [32]byte{0x2a, 0xc0, 0xcd, 0x92, 0x5e, 0x18, 0x07, 0xfd, 0xe4, 0xf1, 0x48, 0x68, 0xa9, 0xdb, 0x58, 0x3e, 0x88, 0x24, 0x45, 0x50, 0x66, 0x5a, 0x2c, 0x5f, 0xc3, 0x7b, 0xac, 0xc5, 0x3d, 0x69, 0xb6, 0xa5}, ValidationOpt: Enforce}, 13: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: [32]byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}}
return M{0: {Expected: []byte{0x73, 0x7f, 0x76, 0x7a, 0x12, 0xf5, 0x4e, 0x70, 0xee, 0xcb, 0xc8, 0x68, 0x40, 0x11, 0x32, 0x3a, 0xe2, 0xfe, 0x2d, 0xd9, 0xf9, 0x07, 0x85, 0x57, 0x79, 0x69, 0xd7, 0xa2, 0x01, 0x3e, 0x8c, 0x12}, ValidationOpt: WarnOnly}, 2: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: []byte{0x42, 0x3e, 0x51, 0x3f, 0x60, 0x8d, 0x3e, 0x0e, 0x7b, 0xa4, 0xc4, 0xa4, 0x97, 0x50, 0xb4, 0x21, 0xed, 0x49, 0xde, 0xa6, 0x93, 0xb4, 0xdd, 0x60, 0xaa, 0x39, 0x0f, 0x12, 0x26, 0x34, 0x28, 0x38}, ValidationOpt: Enforce}, 6: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 7: {Expected: []byte{0x12, 0x0e, 0x49, 0x8d, 0xb2, 0xa2, 0x24, 0xbd, 0x51, 0x2b, 0x6e, 0xfc, 0x9b, 0x02, 0x34, 0xf8, 0x43, 0xe1, 0x0b, 0xf0, 0x61, 0xeb, 0x7a, 0x76, 0xec, 0xca, 0x55, 0x09, 0xa2, 0x23, 0x89, 0x01}, ValidationOpt: WarnOnly}, 8: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: []byte{0xe1, 0x13, 0x34, 0x51, 0x4d, 0xf4, 0x9a, 0xf5, 0x6a, 0x2b, 0x13, 0x45, 0x3b, 0x3e, 0xbd, 0xc8, 0x96, 0xb0, 0xac, 0x1f, 0x36, 0x4c, 0x5c, 0x68, 0x34, 0xd4, 0x4a, 0x43, 0x6b, 0x80, 0x54, 0xe5}, ValidationOpt: Enforce}, 11: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: []byte{0x2a, 0xc0, 0xcd, 0x92, 0x5e, 0x18, 0x07, 0xfd, 0xe4, 0xf1, 0x48, 0x68, 0xa9, 0xdb, 0x58, 0x3e, 0x88, 0x24, 0x45, 0x50, 0x66, 0x5a, 0x2c, 0x5f, 0xc3, 0x7b, 0xac, 0xc5, 0x3d, 0x69, 0xb6, 0xa5}, ValidationOpt: Enforce}, 13: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: []byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}}
case cloudprovider.Azure:
return M{1: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 2: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: [32]byte{0x97, 0x29, 0x84, 0xaf, 0x96, 0xfa, 0xc7, 0x57, 0xfd, 0x3b, 0x5a, 0xb1, 0x88, 0x0c, 0xd2, 0x4d, 0x48, 0x23, 0x65, 0xe6, 0x32, 0x2c, 0x79, 0x22, 0x14, 0xb8, 0x20, 0x9d, 0xb6, 0x80, 0x15, 0x9a}, ValidationOpt: Enforce}, 7: {Expected: [32]byte{0x34, 0x65, 0x47, 0xa8, 0xce, 0x59, 0x57, 0xaf, 0x27, 0xe5, 0x52, 0x42, 0x7d, 0x6b, 0x9e, 0x6d, 0x9c, 0xb5, 0x02, 0xf0, 0x15, 0x6e, 0x91, 0x55, 0x38, 0x04, 0x51, 0xee, 0xa1, 0xb3, 0xf0, 0xed}, ValidationOpt: WarnOnly}, 8: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: [32]byte{0x64, 0x68, 0x17, 0x07, 0x27, 0x97, 0xdd, 0xc1, 0xd4, 0xf2, 0xcf, 0x76, 0x46, 0xfe, 0x59, 0xf1, 0x7c, 0x13, 0xc4, 0x1a, 0x7b, 0x59, 0x76, 0xd6, 0xa5, 0x40, 0xda, 0xe1, 0x1d, 0x71, 0x3d, 0x8c}, ValidationOpt: Enforce}, 11: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: [32]byte{0x36, 0xb5, 0xf8, 0x15, 0x3a, 0x8a, 0x31, 0x5b, 0xac, 0xd8, 0x75, 0x97, 0x8e, 0x92, 0x1e, 0xf7, 0xa1, 0xfc, 0x5c, 0x89, 0x25, 0x59, 0x03, 0x17, 0xbc, 0x1b, 0x53, 0xf2, 0x13, 0xb6, 0x75, 0x62}, ValidationOpt: Enforce}, 13: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: [32]byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}}
return M{1: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 2: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: []byte{0x97, 0x29, 0x84, 0xaf, 0x96, 0xfa, 0xc7, 0x57, 0xfd, 0x3b, 0x5a, 0xb1, 0x88, 0x0c, 0xd2, 0x4d, 0x48, 0x23, 0x65, 0xe6, 0x32, 0x2c, 0x79, 0x22, 0x14, 0xb8, 0x20, 0x9d, 0xb6, 0x80, 0x15, 0x9a}, ValidationOpt: Enforce}, 7: {Expected: []byte{0x34, 0x65, 0x47, 0xa8, 0xce, 0x59, 0x57, 0xaf, 0x27, 0xe5, 0x52, 0x42, 0x7d, 0x6b, 0x9e, 0x6d, 0x9c, 0xb5, 0x02, 0xf0, 0x15, 0x6e, 0x91, 0x55, 0x38, 0x04, 0x51, 0xee, 0xa1, 0xb3, 0xf0, 0xed}, ValidationOpt: WarnOnly}, 8: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: []byte{0x64, 0x68, 0x17, 0x07, 0x27, 0x97, 0xdd, 0xc1, 0xd4, 0xf2, 0xcf, 0x76, 0x46, 0xfe, 0x59, 0xf1, 0x7c, 0x13, 0xc4, 0x1a, 0x7b, 0x59, 0x76, 0xd6, 0xa5, 0x40, 0xda, 0xe1, 0x1d, 0x71, 0x3d, 0x8c}, ValidationOpt: Enforce}, 11: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: []byte{0x36, 0xb5, 0xf8, 0x15, 0x3a, 0x8a, 0x31, 0x5b, 0xac, 0xd8, 0x75, 0x97, 0x8e, 0x92, 0x1e, 0xf7, 0xa1, 0xfc, 0x5c, 0x89, 0x25, 0x59, 0x03, 0x17, 0xbc, 0x1b, 0x53, 0xf2, 0x13, 0xb6, 0x75, 0x62}, ValidationOpt: Enforce}, 13: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: []byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}}
case cloudprovider.GCP:
return M{1: {Expected: [32]byte{0x74, 0x5f, 0x2f, 0xb4, 0x23, 0x5e, 0x46, 0x47, 0xaa, 0x0a, 0xd5, 0xac, 0xe7, 0x81, 0xcd, 0x92, 0x9e, 0xb6, 0x8c, 0x28, 0x87, 0x0e, 0x7d, 0xd5, 0xd1, 0xa1, 0x53, 0x58, 0x54, 0x32, 0x5e, 0x56}, ValidationOpt: WarnOnly}, 2: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: [32]byte{0xb0, 0x9c, 0x1d, 0xff, 0x20, 0xbd, 0x38, 0x9e, 0xc9, 0xd0, 0x56, 0x53, 0x19, 0xa6, 0x55, 0x54, 0x08, 0x42, 0x6e, 0xe1, 0x72, 0x7b, 0x29, 0xbb, 0xb7, 0x16, 0xeb, 0xe9, 0x72, 0x9f, 0x81, 0xba}, ValidationOpt: Enforce}, 6: {Expected: [32]byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 7: {Expected: [32]byte{0xb1, 0xe9, 0xb3, 0x05, 0x32, 0x5c, 0x51, 0xb9, 0x3d, 0xa5, 0x8c, 0xbf, 0x7f, 0x92, 0x51, 0x2d, 0x8e, 0xeb, 0xfa, 0x01, 0x14, 0x3e, 0x4d, 0x88, 0x44, 0xe4, 0x0e, 0x06, 0x2e, 0x9b, 0x6c, 0xd5}, ValidationOpt: WarnOnly}, 8: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: [32]byte{0x64, 0x68, 0x17, 0x07, 0x27, 0x97, 0xdd, 0xc1, 0xd4, 0xf2, 0xcf, 0x76, 0x46, 0xfe, 0x59, 0xf1, 0x7c, 0x13, 0xc4, 0x1a, 0x7b, 0x59, 0x76, 0xd6, 0xa5, 0x40, 0xda, 0xe1, 0x1d, 0x71, 0x3d, 0x8c}, ValidationOpt: Enforce}, 11: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: [32]byte{0xd7, 0x65, 0x82, 0xf1, 0x70, 0xcb, 0x84, 0x81, 0xc2, 0x55, 0x5c, 0x76, 0xae, 0x30, 0x39, 0xe9, 0xe1, 0x69, 0xfe, 0xcb, 0x05, 0x91, 0x2a, 0xa3, 0xb3, 0xcd, 0x7d, 0xc3, 0xec, 0xed, 0x0d, 0xa5}, ValidationOpt: Enforce}, 13: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: [32]byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}}
return M{1: {Expected: []byte{0x74, 0x5f, 0x2f, 0xb4, 0x23, 0x5e, 0x46, 0x47, 0xaa, 0x0a, 0xd5, 0xac, 0xe7, 0x81, 0xcd, 0x92, 0x9e, 0xb6, 0x8c, 0x28, 0x87, 0x0e, 0x7d, 0xd5, 0xd1, 0xa1, 0x53, 0x58, 0x54, 0x32, 0x5e, 0x56}, ValidationOpt: WarnOnly}, 2: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 3: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 4: {Expected: []byte{0xb0, 0x9c, 0x1d, 0xff, 0x20, 0xbd, 0x38, 0x9e, 0xc9, 0xd0, 0x56, 0x53, 0x19, 0xa6, 0x55, 0x54, 0x08, 0x42, 0x6e, 0xe1, 0x72, 0x7b, 0x29, 0xbb, 0xb7, 0x16, 0xeb, 0xe9, 0x72, 0x9f, 0x81, 0xba}, ValidationOpt: Enforce}, 6: {Expected: []byte{0x3d, 0x45, 0x8c, 0xfe, 0x55, 0xcc, 0x03, 0xea, 0x1f, 0x44, 0x3f, 0x15, 0x62, 0xbe, 0xec, 0x8d, 0xf5, 0x1c, 0x75, 0xe1, 0x4a, 0x9f, 0xcf, 0x9a, 0x72, 0x34, 0xa1, 0x3f, 0x19, 0x8e, 0x79, 0x69}, ValidationOpt: WarnOnly}, 7: {Expected: []byte{0xb1, 0xe9, 0xb3, 0x05, 0x32, 0x5c, 0x51, 0xb9, 0x3d, 0xa5, 0x8c, 0xbf, 0x7f, 0x92, 0x51, 0x2d, 0x8e, 0xeb, 0xfa, 0x01, 0x14, 0x3e, 0x4d, 0x88, 0x44, 0xe4, 0x0e, 0x06, 0x2e, 0x9b, 0x6c, 0xd5}, ValidationOpt: WarnOnly}, 8: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: []byte{0x64, 0x68, 0x17, 0x07, 0x27, 0x97, 0xdd, 0xc1, 0xd4, 0xf2, 0xcf, 0x76, 0x46, 0xfe, 0x59, 0xf1, 0x7c, 0x13, 0xc4, 0x1a, 0x7b, 0x59, 0x76, 0xd6, 0xa5, 0x40, 0xda, 0xe1, 0x1d, 0x71, 0x3d, 0x8c}, ValidationOpt: Enforce}, 11: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: []byte{0xd7, 0x65, 0x82, 0xf1, 0x70, 0xcb, 0x84, 0x81, 0xc2, 0x55, 0x5c, 0x76, 0xae, 0x30, 0x39, 0xe9, 0xe1, 0x69, 0xfe, 0xcb, 0x05, 0x91, 0x2a, 0xa3, 0xb3, 0xcd, 0x7d, 0xc3, 0xec, 0xed, 0x0d, 0xa5}, ValidationOpt: Enforce}, 13: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 14: {Expected: []byte{0xd7, 0xc4, 0xcc, 0x7f, 0xf7, 0x93, 0x30, 0x22, 0xf0, 0x13, 0xe0, 0x3b, 0xde, 0xe8, 0x75, 0xb9, 0x17, 0x20, 0xb5, 0xb8, 0x6c, 0xf1, 0x75, 0x3c, 0xad, 0x83, 0x0f, 0x95, 0xe7, 0x91, 0x92, 0x6f}, ValidationOpt: WarnOnly}, 15: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}}
case cloudprovider.QEMU:
return M{4: {Expected: [32]byte{0xbd, 0x7c, 0x7a, 0x6f, 0x43, 0xd9, 0xa7, 0x16, 0x4a, 0xf5, 0x17, 0xb1, 0xe5, 0x43, 0x2c, 0x14, 0x31, 0xe2, 0xd5, 0xd1, 0x01, 0xe9, 0x5e, 0x7f, 0x52, 0x73, 0x76, 0xda, 0xc2, 0x0b, 0xe8, 0x70}, ValidationOpt: Enforce}, 8: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: [32]byte{0xe1, 0x13, 0x34, 0x51, 0x4d, 0xf4, 0x9a, 0xf5, 0x6a, 0x2b, 0x13, 0x45, 0x3b, 0x3e, 0xbd, 0xc8, 0x96, 0xb0, 0xac, 0x1f, 0x36, 0x4c, 0x5c, 0x68, 0x34, 0xd4, 0x4a, 0x43, 0x6b, 0x80, 0x54, 0xe5}, ValidationOpt: Enforce}, 11: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: [32]byte{0x2c, 0x45, 0x39, 0xc4, 0x40, 0x95, 0xa3, 0x2a, 0x4e, 0xb2, 0xde, 0x12, 0xb2, 0x04, 0x2a, 0x34, 0xaa, 0x13, 0xaf, 0x5f, 0x30, 0x79, 0xef, 0x95, 0x2a, 0x0f, 0xca, 0xaa, 0x07, 0x6a, 0x27, 0x2d}, ValidationOpt: Enforce}, 13: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 15: {Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}}
return M{4: {Expected: []byte{0xbd, 0x7c, 0x7a, 0x6f, 0x43, 0xd9, 0xa7, 0x16, 0x4a, 0xf5, 0x17, 0xb1, 0xe5, 0x43, 0x2c, 0x14, 0x31, 0xe2, 0xd5, 0xd1, 0x01, 0xe9, 0x5e, 0x7f, 0x52, 0x73, 0x76, 0xda, 0xc2, 0x0b, 0xe8, 0x70}, ValidationOpt: Enforce}, 8: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 9: {Expected: []byte{0xe1, 0x13, 0x34, 0x51, 0x4d, 0xf4, 0x9a, 0xf5, 0x6a, 0x2b, 0x13, 0x45, 0x3b, 0x3e, 0xbd, 0xc8, 0x96, 0xb0, 0xac, 0x1f, 0x36, 0x4c, 0x5c, 0x68, 0x34, 0xd4, 0x4a, 0x43, 0x6b, 0x80, 0x54, 0xe5}, ValidationOpt: Enforce}, 11: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 12: {Expected: []byte{0x2c, 0x45, 0x39, 0xc4, 0x40, 0x95, 0xa3, 0x2a, 0x4e, 0xb2, 0xde, 0x12, 0xb2, 0x04, 0x2a, 0x34, 0xaa, 0x13, 0xaf, 0x5f, 0x30, 0x79, 0xef, 0x95, 0x2a, 0x0f, 0xca, 0xaa, 0x07, 0x6a, 0x27, 0x2d}, ValidationOpt: Enforce}, 13: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}, 15: {Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ValidationOpt: Enforce}}
default:
return nil

View File

@ -9,6 +9,7 @@ package measurements
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
@ -30,14 +31,14 @@ func TestMarshal(t *testing.T) {
}{
"measurement": {
m: Measurement{
Expected: [32]byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53},
Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53},
},
wantYAML: "expected: \"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35\"\nwarnOnly: false",
wantJSON: `{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35","warnOnly":false}`,
},
"warn only": {
m: Measurement{
Expected: [32]byte{1, 2, 3, 4}, // implicitly padded with 0s
Expected: []byte{1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
ValidationOpt: WarnOnly,
},
wantYAML: "expected: \"0102030400000000000000000000000000000000000000000000000000000000\"\nwarnOnly: true",
@ -81,10 +82,10 @@ func TestUnmarshal(t *testing.T) {
inputJSON: `{"2":{"expected":"/V3p3zUOO8RBCsBrv+XM3rk/U7nvUSOfdSzmnbxgDzU="},"3":{"expected":"1aRJbSHeyaUljdsZxv61O7TTwEY/5gfySI3fTxAG754="}}`,
wantMeasurements: M{
2: {
Expected: [32]byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53},
Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53},
},
3: {
Expected: [32]byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158},
Expected: []byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158},
},
},
},
@ -93,10 +94,22 @@ func TestUnmarshal(t *testing.T) {
inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9e"}}`,
wantMeasurements: M{
2: {
Expected: [32]byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53},
Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53},
},
3: {
Expected: [32]byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158},
Expected: []byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158},
},
},
},
"valid measurements hex 48 bytes": {
inputYAML: "2:\n expected: \"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35fd5de9df350e3bc4410ac06bbfe5ccde\"\n3:\n expected: \"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9efd5de9df350e3bc4410ac06bbfe5ccde\"",
inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35fd5de9df350e3bc4410ac06bbfe5ccde"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9efd5de9df350e3bc4410ac06bbfe5ccde"}}`,
wantMeasurements: M{
2: {
Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53, 253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222},
},
3: {
Expected: []byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158, 253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222},
},
},
},
@ -105,10 +118,10 @@ func TestUnmarshal(t *testing.T) {
inputJSON: `{"2":{"expected":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},"3":{"expected":"AQIDBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}`,
wantMeasurements: M{
2: {
Expected: [32]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
Expected: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
3: {
Expected: [32]byte{1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
Expected: []byte{1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
},
},
@ -122,10 +135,10 @@ func TestUnmarshal(t *testing.T) {
inputJSON: `{"2":"/V3p3zUOO8RBCsBrv+XM3rk/U7nvUSOfdSzmnbxgDzU=","3":"1aRJbSHeyaUljdsZxv61O7TTwEY/5gfySI3fTxAG754="}`,
wantMeasurements: M{
2: {
Expected: [32]byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53},
Expected: []byte{253, 93, 233, 223, 53, 14, 59, 196, 65, 10, 192, 107, 191, 229, 204, 222, 185, 63, 83, 185, 239, 81, 35, 159, 117, 44, 230, 157, 188, 96, 15, 53},
},
3: {
Expected: [32]byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158},
Expected: []byte{213, 164, 73, 109, 33, 222, 201, 165, 37, 141, 219, 25, 198, 254, 181, 59, 180, 211, 192, 70, 63, 230, 7, 242, 72, 141, 223, 79, 16, 6, 239, 158},
},
},
},
@ -134,6 +147,11 @@ func TestUnmarshal(t *testing.T) {
inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463f"}}`,
wantErr: true,
},
"mixed length hex": {
inputYAML: "2:\n expected: \"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35fd5de9df350e3bc4410ac06bbfe5ccde\"\n3:\n expected: \"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9e\"",
inputJSON: `{"2":{"expected":"fd5de9df350e3bc4410ac06bbfe5ccdeb93f53b9ef51239f752ce69dbc600f35fd5de9df350e3bc4410ac06bbfe5ccde"},"3":{"expected":"d5a4496d21dec9a5258ddb19c6feb53bb4d3c0463fe607f2488ddf4f1006ef9e"}}`,
wantErr: true,
},
"invalid length base64": {
inputYAML: "2:\n expected: \"AA==\"\n3:\n expected: \"AQIDBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\"",
inputJSON: `{"2":{"expected":"AA=="},"3":{"expected":"AQIDBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="}}`,
@ -170,6 +188,7 @@ func TestUnmarshal(t *testing.T) {
err := json.Unmarshal([]byte(tc.inputJSON), &m)
if tc.wantErr {
fmt.Println(err)
assert.Error(err, "json.Unmarshal should have failed")
} else {
require.NoError(err, "json.Unmarshal failed")
@ -200,10 +219,10 @@ func TestEncodeM(t *testing.T) {
},
"output is sorted": {
m: M{
3: {},
1: {},
11: {},
2: {},
3: WithAllBytes(0, false),
1: WithAllBytes(0, false),
11: WithAllBytes(0, false),
2: WithAllBytes(0, false),
},
want: `1:
expected: "0000000000000000000000000000000000000000000000000000000000000000"
@ -605,7 +624,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0x00,
warnOnly: true,
wantMeasurement: Measurement{
Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
ValidationOpt: WarnOnly,
},
},
@ -613,7 +632,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0x00,
warnOnly: false,
wantMeasurement: Measurement{
Expected: [32]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
Expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
ValidationOpt: Enforce,
},
},
@ -621,7 +640,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0x01,
warnOnly: true,
wantMeasurement: Measurement{
Expected: [32]byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
Expected: []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
ValidationOpt: WarnOnly,
},
},
@ -629,7 +648,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0x01,
warnOnly: false,
wantMeasurement: Measurement{
Expected: [32]byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
Expected: []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
ValidationOpt: Enforce,
},
},
@ -637,7 +656,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0xFF,
warnOnly: true,
wantMeasurement: Measurement{
Expected: [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
Expected: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
ValidationOpt: WarnOnly,
},
},
@ -645,7 +664,7 @@ func TestWithAllBytes(t *testing.T) {
b: 0xFF,
warnOnly: false,
wantMeasurement: Measurement{
Expected: [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
Expected: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
ValidationOpt: Enforce,
},
},

View File

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

View File

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

View File

@ -10,6 +10,7 @@ import (
"context"
"crypto"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/variant"
@ -24,7 +25,7 @@ type Validator struct {
}
// NewValidator initializes a new QEMU validator with the provided PCR values.
func NewValidator(cfg *config.QEMUVTPM, log vtpm.AttestationLogger) *Validator {
func NewValidator(cfg *config.QEMUVTPM, log attestation.Logger) *Validator {
return &Validator{
Validator: vtpm.NewValidator(
cfg.Measurements,

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

View File

@ -10,7 +10,6 @@ import (
"bytes"
"context"
"crypto"
"crypto/sha256"
"encoding/json"
"fmt"
"io"
@ -21,6 +20,7 @@ import (
tpmServer "github.com/google/go-tpm-tools/server"
"github.com/google/go-tpm/tpm2"
"github.com/edgelesssys/constellation/v2/internal/attestation"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
)
@ -66,12 +66,6 @@ type (
ValidateCVM func(attestation AttestationDocument, state *attest.MachineState) error
)
// AttestationLogger is a logger used to print warnings and infos during attestation validation.
type AttestationLogger interface {
Infof(format string, args ...any)
Warnf(format string, args ...any)
}
// AttestationDocument contains the TPM attestation with signed user data.
type AttestationDocument struct {
// Attestation contains the TPM event log, PCR values and quotes, and public key of the key used to sign the attestation.
@ -87,16 +81,16 @@ type Issuer struct {
openTPM TPMOpenFunc
getAttestationKey GetTPMAttestationKey
getInstanceInfo GetInstanceInfo
log AttestationLogger
log attestation.Logger
}
// NewIssuer returns a new Issuer.
func NewIssuer(
openTPM TPMOpenFunc, getAttestationKey GetTPMAttestationKey,
getInstanceInfo GetInstanceInfo, log AttestationLogger,
getInstanceInfo GetInstanceInfo, log attestation.Logger,
) *Issuer {
if log == nil {
log = &nopAttestationLogger{}
log = &attestation.NOPLogger{}
}
return &Issuer{
openTPM: openTPM,
@ -129,7 +123,7 @@ func (i *Issuer) Issue(ctx context.Context, userData []byte, nonce []byte) (res
defer aK.Close()
// Create an attestation using the loaded key
extraData := makeExtraData(userData, nonce)
extraData := attestation.MakeExtraData(userData, nonce)
tpmAttestation, err := aK.Attest(tpmClient.AttestOpts{Nonce: extraData})
if err != nil {
return nil, fmt.Errorf("creating attestation: %w", err)
@ -162,15 +156,15 @@ type Validator struct {
getTrustedKey GetTPMTrustedAttestationPublicKey
validateCVM ValidateCVM
log AttestationLogger
log attestation.Logger
}
// NewValidator returns a new Validator.
func NewValidator(expected measurements.M, getTrustedKey GetTPMTrustedAttestationPublicKey,
validateCVM ValidateCVM, log AttestationLogger,
validateCVM ValidateCVM, log attestation.Logger,
) *Validator {
if log == nil {
log = &nopAttestationLogger{}
log = &attestation.NOPLogger{}
}
return &Validator{
expected: expected,
@ -194,7 +188,7 @@ func (v *Validator) Validate(ctx context.Context, attDocRaw []byte, nonce []byte
return nil, fmt.Errorf("unmarshaling TPM attestation document: %w", err)
}
extraData := makeExtraData(attDoc.UserData, nonce)
extraData := attestation.MakeExtraData(attDoc.UserData, nonce)
// Verify and retrieve the trusted attestation public key using the provided instance info
aKP, err := v.getTrustedKey(ctx, attDoc, extraData)
@ -276,25 +270,9 @@ func GetSelectedMeasurements(open TPMOpenFunc, selection tpm2.PCRSelection) (mea
return nil, fmt.Errorf("invalid measurement: invalid length: %d", len(pcr))
}
m[i] = measurements.Measurement{
Expected: *(*[32]byte)(pcr),
Expected: pcr,
}
}
return m, nil
}
func makeExtraData(userData []byte, nonce []byte) []byte {
data := append([]byte{}, userData...)
data = append(data, nonce...)
digest := sha256.Sum256(data)
return digest[:]
}
// nopAttestationLogger is a no-op implementation of AttestationLogger.
type nopAttestationLogger struct{}
// Infof is a no-op.
func (nopAttestationLogger) Infof(string, ...interface{}) {}
// Warnf is a no-op.
func (nopAttestationLogger) Warnf(string, ...interface{}) {}

View File

@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/edgelesssys/constellation/v2/internal/attestation/initialize"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
tpmsim "github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
@ -100,7 +101,7 @@ func TestValidate(t *testing.T) {
require.Equal(challenge, out)
// validation must fail after bootstrapping (change of enforced PCR)
require.NoError(MarkNodeAsBootstrapped(tpmOpen, []byte{2}))
require.NoError(initialize.MarkNodeAsBootstrapped(tpmOpen, []byte{2}))
attDocBootstrappedRaw, err := issuer.Issue(ctx, challenge, nonce)
require.NoError(err)
_, err = validator.Validate(ctx, attDocBootstrappedRaw, nonce)
@ -121,19 +122,19 @@ func TestValidate(t *testing.T) {
0: measurements.WithAllBytes(0x00, measurements.WarnOnly),
1: measurements.WithAllBytes(0x00, measurements.WarnOnly),
2: measurements.Measurement{
Expected: [32]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20},
Expected: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20},
ValidationOpt: measurements.WarnOnly,
},
3: measurements.Measurement{
Expected: [32]byte{0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40},
Expected: []byte{0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40},
ValidationOpt: measurements.WarnOnly,
},
4: measurements.Measurement{
Expected: [32]byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60},
Expected: []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60},
ValidationOpt: measurements.WarnOnly,
},
5: measurements.Measurement{
Expected: [32]byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80},
Expected: []byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80},
ValidationOpt: measurements.WarnOnly,
},
}
@ -202,7 +203,7 @@ func TestValidate(t *testing.T) {
validator: NewValidator(
measurements.M{
0: measurements.Measurement{
Expected: [32]byte{0xFF},
Expected: []byte{0xFF},
ValidationOpt: measurements.Enforce,
},
},

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 (
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/goleak"
)
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
func TestNOPTPM(t *testing.T) {
assert := assert.New(t)
assert.NoError(MarkNodeAsBootstrapped(OpenNOPTPM, []byte{0x0, 0x1, 0x2, 0x3}))
}

View File

@ -885,6 +885,37 @@ func (c QEMUVTPM) EqualTo(other AttestationCfg) (bool, error) {
return c.Measurements.EqualTo(otherCfg.Measurements), nil
}
// QEMUTDX is the configuration for QEMU TDX attestation.
type QEMUTDX struct {
// description: |
// Expected TDX measurements.
Measurements measurements.M `json:"measurements" yaml:"measurements" validate:"required,no_placeholders"`
}
// GetVariant returns qemu-tdx as the variant.
func (QEMUTDX) GetVariant() variant.Variant {
return variant.QEMUTDX{}
}
// GetMeasurements returns the measurements used for attestation.
func (c QEMUTDX) GetMeasurements() measurements.M {
return c.Measurements
}
// SetMeasurements updates a config's measurements using the given measurements.
func (c *QEMUTDX) SetMeasurements(m measurements.M) {
c.Measurements = m
}
// EqualTo returns true if the config is equal to the given config.
func (c QEMUTDX) EqualTo(other AttestationCfg) (bool, error) {
otherCfg, ok := other.(*QEMUTDX)
if !ok {
return false, fmt.Errorf("cannot compare %T with %T", c, other)
}
return c.Measurements.EqualTo(otherCfg.Measurements), nil
}
func toPtr[T any](v T) *T {
return &v
}

View File

@ -43,6 +43,7 @@ const (
azureSEVSNP = "azure-sev-snp"
azureTrustedLaunch = "azure-trustedlaunch"
qemuVTPM = "qemu-vtpm"
qemuTDX = "qemu-tdx"
)
// Getter returns an ASN.1 Object Identifier.
@ -72,6 +73,8 @@ func FromString(oid string) (Variant, error) {
return AzureTrustedLaunch{}, nil
case qemuVTPM:
return QEMUVTPM{}, nil
case qemuTDX:
return QEMUTDX{}, nil
}
return nil, fmt.Errorf("unknown OID: %q", oid)
}
@ -183,3 +186,22 @@ func (QEMUVTPM) String() string {
func (QEMUVTPM) Equal(other Getter) bool {
return other.OID().Equal(QEMUVTPM{}.OID())
}
// QEMUTDX holds the QEMU TDX OID.
// Placeholder for dev-cloud integration.
type QEMUTDX struct{}
// OID returns the struct's object identifier.
// Placeholder for dev-cloud integration.
func (QEMUTDX) OID() asn1.ObjectIdentifier {
return asn1.ObjectIdentifier{1, 3, 9900, 5, 99}
}
func (QEMUTDX) String() string {
return qemuTDX
}
// Equal returns true if the other variant is also QEMUTDX.
func (QEMUTDX) Equal(other Getter) bool {
return other.OID().Equal(QEMUTDX{}.OID())
}