mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
AB#2444 Verify Azure trusted launch attestation keys (#203)
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
parent
a60e76e91f
commit
acdcb535c0
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Loadbalancer for control-plane recovery
|
- Loadbalancer for control-plane recovery
|
||||||
- K8s conformance mode
|
- K8s conformance mode
|
||||||
- Local cluster creation based on QEMU
|
- Local cluster creation based on QEMU
|
||||||
|
- Verification of Azure trusted launch attestation keys
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
<!-- For changes in existing functionality. -->
|
<!-- For changes in existing functionality. -->
|
||||||
|
5
go.mod
5
go.mod
@ -30,7 +30,10 @@ replace (
|
|||||||
k8s.io/sample-apiserver v0.0.0 => k8s.io/sample-apiserver v0.24.6
|
k8s.io/sample-apiserver v0.0.0 => k8s.io/sample-apiserver v0.24.6
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/google/go-attestation => github.com/malt3/go-attestation v0.0.0-20220816131639-92b6394e4e0e
|
replace (
|
||||||
|
github.com/google/go-attestation => github.com/malt3/go-attestation v0.0.0-20220816131639-92b6394e4e0e
|
||||||
|
github.com/google/go-tpm-tools => github.com/daniel-weisse/go-tpm-tools v0.0.0-20220929072523-22862750ed86
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute v1.7.0
|
cloud.google.com/go/compute v1.7.0
|
||||||
|
8
go.sum
8
go.sum
@ -441,6 +441,8 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ
|
|||||||
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
|
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||||
github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc=
|
github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc=
|
||||||
|
github.com/daniel-weisse/go-tpm-tools v0.0.0-20220929072523-22862750ed86 h1:fJzK2MkfHItDnNVEgCUQfaH0SzcNBkpTB31XCBd3YUk=
|
||||||
|
github.com/daniel-weisse/go-tpm-tools v0.0.0-20220929072523-22862750ed86/go.mod h1:22JvWmHcD5w55cs+nMeqDGDxgNS15/2pDq2cLqnc3rc=
|
||||||
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
|
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
|
||||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -755,14 +757,8 @@ github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOm
|
|||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
|
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
|
||||||
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
|
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
|
||||||
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
|
|
||||||
github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
|
|
||||||
github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=
|
github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=
|
||||||
github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4=
|
github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4=
|
||||||
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
|
|
||||||
github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
|
|
||||||
github.com/google/go-tpm-tools v0.3.8 h1:ecZgxez5lyKWjnkK8lP3ru4rkgLyIM7pPY36FOFnAx4=
|
|
||||||
github.com/google/go-tpm-tools v0.3.8/go.mod h1:rp+rDmmDCnWiMmxOTF3ypWxpChEQ4vwA6wtAIq09Qtc=
|
|
||||||
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||||
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
||||||
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||||
|
@ -25,6 +25,7 @@ const (
|
|||||||
lenSnpReport = 0x4a0
|
lenSnpReport = 0x4a0
|
||||||
lenSnpReportRuntimeDataPadding = 0x14
|
lenSnpReportRuntimeDataPadding = 0x14
|
||||||
tpmReportIdx = 0x01400001
|
tpmReportIdx = 0x01400001
|
||||||
|
tpmAkIdx = 0x81000003
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetIdKeyDigest reads the idkeydigest from the snp report saved in the TPM's non-volatile memory.
|
// GetIdKeyDigest reads the idkeydigest from the snp report saved in the TPM's non-volatile memory.
|
||||||
@ -37,7 +38,7 @@ func GetIdKeyDigest(open vtpm.TPMOpenFunc) ([]byte, error) {
|
|||||||
|
|
||||||
reportRaw, err := tpm2.NVReadEx(tpm, tpmReportIdx, tpm2.HandleOwner, "", 0)
|
reportRaw, err := tpm2.NVReadEx(tpm, tpmReportIdx, tpm2.HandleOwner, "", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("reading idx %x from TMP: %w", tpmReportIdx, err)
|
return nil, fmt.Errorf("reading idx %x from TPM: %w", tpmReportIdx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
report, err := newSNPReportFromBytes(reportRaw[lenHclHeader:])
|
report, err := newSNPReportFromBytes(reportRaw[lenHclHeader:])
|
||||||
@ -107,30 +108,9 @@ func getInstanceInfo(reportGetter tpmReportGetter, imdsAPI imdsApi) func(tpm io.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func hclAkTemplate() tpm2.Public {
|
|
||||||
akFlags := tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth | tpm2.FlagNoDA | tpm2.FlagRestricted | tpm2.FlagSign
|
|
||||||
return tpm2.Public{
|
|
||||||
Type: tpm2.AlgRSA,
|
|
||||||
NameAlg: tpm2.AlgSHA256,
|
|
||||||
Attributes: akFlags,
|
|
||||||
RSAParameters: &tpm2.RSAParams{
|
|
||||||
Sign: &tpm2.SigScheme{
|
|
||||||
Alg: tpm2.AlgRSASSA,
|
|
||||||
Hash: tpm2.AlgSHA256,
|
|
||||||
},
|
|
||||||
KeyBits: 2048,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAttestationKey reads the attesation key put into the TPM during early boot.
|
// getAttestationKey reads the attesation key put into the TPM during early boot.
|
||||||
func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) {
|
func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) {
|
||||||
// A minor drawback of `NewCachedKey` is that it will transparently create/overwrite a key if it does not find one matching the template at the given index.
|
ak, err := tpmclient.LoadCachedKey(tpm, tpmAkIdx)
|
||||||
// We actually wouldn't want to continue at this point if we realize that the key at the index is not present, due to
|
|
||||||
// easier debuggability. If `NewCachedKey` creates a new key, attestation will fail at the validator.
|
|
||||||
// The function in tpmclient that doesn't create a new key, ReadPublic, can't be used as we would have to create
|
|
||||||
// a tpmclient.Key object manually, which we can't since there is no constructor exported.
|
|
||||||
ak, err := tpmclient.NewCachedKey(tpm, tpm2.HandleOwner, hclAkTemplate(), 0x81000003)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("reading HCL attestation key from TPM: %w", err)
|
return nil, fmt.Errorf("reading HCL attestation key from TPM: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
tpmclient "github.com/google/go-tpm-tools/client"
|
||||||
"github.com/google/go-tpm-tools/simulator"
|
"github.com/google/go-tpm-tools/simulator"
|
||||||
|
"github.com/google/go-tpm/tpm2"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -91,12 +93,34 @@ func TestGetSNPAttestation(t *testing.T) {
|
|||||||
// Testing anything else will only verify that the simulator works as expected, since getAkPub
|
// Testing anything else will only verify that the simulator works as expected, since getAkPub
|
||||||
// only retrieves the attestation key from the TPM.
|
// only retrieves the attestation key from the TPM.
|
||||||
func TestGetHCLAttestationKey(t *testing.T) {
|
func TestGetHCLAttestationKey(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
tpm, err := simulator.Get()
|
tpm, err := simulator.Get()
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
defer tpm.Close()
|
defer tpm.Close()
|
||||||
|
|
||||||
|
// we should receive an error if no key was saved at index `tpmAkIdx`
|
||||||
|
_, err = getAttestationKey(tpm)
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
// create a key at the index
|
||||||
|
tpmAk, err := tpmclient.NewCachedKey(tpm, tpm2.HandleOwner, tpm2.Public{
|
||||||
|
Type: tpm2.AlgRSA,
|
||||||
|
NameAlg: tpm2.AlgSHA256,
|
||||||
|
Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth | tpm2.FlagNoDA | tpm2.FlagRestricted | tpm2.FlagSign,
|
||||||
|
RSAParameters: &tpm2.RSAParams{
|
||||||
|
Sign: &tpm2.SigScheme{
|
||||||
|
Alg: tpm2.AlgRSASSA,
|
||||||
|
Hash: tpm2.AlgSHA256,
|
||||||
|
},
|
||||||
|
KeyBits: 2048,
|
||||||
|
},
|
||||||
|
}, tpmAkIdx)
|
||||||
|
require.NoError(err)
|
||||||
|
defer tpmAk.Close()
|
||||||
|
|
||||||
|
// we should now be able to retrieve the key
|
||||||
_, err = getAttestationKey(tpm)
|
_, err = getAttestationKey(tpm)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
}
|
}
|
||||||
|
@ -7,31 +7,117 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
package trustedlaunch
|
package trustedlaunch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||||
tpmclient "github.com/google/go-tpm-tools/client"
|
tpmclient "github.com/google/go-tpm-tools/client"
|
||||||
|
"github.com/google/go-tpm/tpm2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tpmAkIdx = 0x81000003
|
||||||
|
tpmAkCertIdx = 0x1C101D0
|
||||||
)
|
)
|
||||||
|
|
||||||
// Issuer for Azure trusted launch TPM attestation.
|
// Issuer for Azure trusted launch TPM attestation.
|
||||||
type Issuer struct {
|
type Issuer struct {
|
||||||
oid.AzureTrustedLaunch
|
oid.AzureTrustedLaunch
|
||||||
*vtpm.Issuer
|
*vtpm.Issuer
|
||||||
|
hClient httpClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIssuer initializes a new Azure Issuer.
|
// NewIssuer initializes a new Azure Issuer.
|
||||||
func NewIssuer() *Issuer {
|
func NewIssuer() *Issuer {
|
||||||
return &Issuer{
|
i := &Issuer{
|
||||||
Issuer: vtpm.NewIssuer(
|
hClient: &http.Client{},
|
||||||
vtpm.OpenVTPM,
|
|
||||||
tpmclient.AttestationKeyRSA,
|
|
||||||
getAttestation,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
i.Issuer = vtpm.NewIssuer(
|
||||||
|
vtpm.OpenVTPM,
|
||||||
|
getAttestationKey,
|
||||||
|
i.getAttestationCert,
|
||||||
|
)
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAttestation returns nil.
|
// akSigner holds the attestation key certificate and the corresponding CA certificate.
|
||||||
func getAttestation(tpm io.ReadWriteCloser) ([]byte, error) {
|
type akSigner struct {
|
||||||
return nil, nil
|
AkCert []byte
|
||||||
|
CA []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAttestationCert returns the DER encoded certificate of the TPM's attestation key and it's CA.
|
||||||
|
func (i *Issuer) getAttestationCert(tpm io.ReadWriteCloser) ([]byte, error) {
|
||||||
|
certDER, err := tpm2.NVReadEx(tpm, tpmAkCertIdx, tpm2.HandleOwner, "", 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("reading attestation key certificate from TPM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := x509.ParseCertificate(certDER)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing attestation key certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to load the CA certificate from the issuing certificate URL
|
||||||
|
// any error is ignored and the next URL is tried
|
||||||
|
// if no CA certificate can be loaded, an error is returned
|
||||||
|
var caCert *x509.Certificate
|
||||||
|
for _, caCertURL := range cert.IssuingCertificateURL {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||||
|
defer cancel()
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, caCertURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resp, err := i.hClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
caCertDER, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
caCert, err = x509.ParseCertificate(caCertDER)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if caCert == nil {
|
||||||
|
return nil, errors.New("failed to load CA certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
signerInfo := akSigner{
|
||||||
|
AkCert: certDER,
|
||||||
|
CA: caCert.Raw,
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(signerInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAttestationKey reads the Azure trusted launch attesation key.
|
||||||
|
func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) {
|
||||||
|
ak, err := tpmclient.LoadCachedKey(tpm, tpmAkIdx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("reading attestation key from TPM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ak, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpClient interface {
|
||||||
|
Do(req *http.Request) (*http.Response, error)
|
||||||
}
|
}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) Edgeless Systems GmbH
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package trustedlaunch
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetSNPAttestation(t *testing.T) {
|
|
||||||
testCases := map[string]struct {
|
|
||||||
tpmFunc vtpm.TPMOpenFunc
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
"success": {
|
|
||||||
tpmFunc: simulator.OpenSimulatedTPM,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, tc := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
tpm, err := tc.tpmFunc()
|
|
||||||
require.NoError(err)
|
|
||||||
defer tpm.Close()
|
|
||||||
|
|
||||||
_, err = getAttestation(tpm)
|
|
||||||
if tc.wantErr {
|
|
||||||
assert.Error(err)
|
|
||||||
} else {
|
|
||||||
assert.NoError(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
250
internal/attestation/azure/trustedlaunch/trustedlaunch_test.go
Normal file
250
internal/attestation/azure/trustedlaunch/trustedlaunch_test.go
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package trustedlaunch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/crypto"
|
||||||
|
tpmclient "github.com/google/go-tpm-tools/client"
|
||||||
|
"github.com/google/go-tpm/tpm2"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetAttestationCert(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
tpm, err := simulator.OpenSimulatedTPM()
|
||||||
|
require.NoError(err)
|
||||||
|
defer tpm.Close()
|
||||||
|
|
||||||
|
// create key in TPM
|
||||||
|
tpmAk, err := tpmclient.NewCachedKey(tpm, tpm2.HandleOwner, tpm2.Public{
|
||||||
|
Type: tpm2.AlgRSA,
|
||||||
|
NameAlg: tpm2.AlgSHA256,
|
||||||
|
Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth | tpm2.FlagNoDA | tpm2.FlagRestricted | tpm2.FlagSign,
|
||||||
|
RSAParameters: &tpm2.RSAParams{
|
||||||
|
Sign: &tpm2.SigScheme{
|
||||||
|
Alg: tpm2.AlgRSASSA,
|
||||||
|
Hash: tpm2.AlgSHA256,
|
||||||
|
},
|
||||||
|
KeyBits: 2048,
|
||||||
|
},
|
||||||
|
}, tpmAkIdx)
|
||||||
|
require.NoError(err)
|
||||||
|
defer tpmAk.Close()
|
||||||
|
akPub, err := tpmAk.PublicArea().Encode()
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
// root certificate
|
||||||
|
rootKey, rootTemplate := fillCertTemplate(t, &x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "root CA"},
|
||||||
|
IsCA: true,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||||
|
})
|
||||||
|
rootCert := newTestCert(t, rootTemplate, rootTemplate, rootKey.Public(), rootKey)
|
||||||
|
|
||||||
|
// intermediate certificate
|
||||||
|
intermediateKey, intermediateTemplate := fillCertTemplate(t, &x509.Certificate{
|
||||||
|
Subject: pkix.Name{CommonName: "intermediate CA"},
|
||||||
|
Issuer: rootTemplate.Subject,
|
||||||
|
IsCA: true,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||||
|
})
|
||||||
|
intermediateCert := newTestCert(t, intermediateTemplate, rootTemplate, intermediateKey.Public(), rootKey)
|
||||||
|
|
||||||
|
// define NV index once to avoid the need for fancy error handling later
|
||||||
|
require.NoError(tpm2.NVDefineSpace(
|
||||||
|
tpm, tpm2.HandleOwner, tpmAkCertIdx, "", "", []byte{},
|
||||||
|
tpm2.AttrOwnerWrite|tpm2.AttrOwnerRead|tpm2.AttrAuthRead|tpm2.AttrAuthWrite|tpm2.AttrNoDA, 1,
|
||||||
|
))
|
||||||
|
|
||||||
|
defaultAkCertFunc := func(*testing.T) *x509.Certificate {
|
||||||
|
t.Helper()
|
||||||
|
_, certTemplate := fillCertTemplate(t, &x509.Certificate{
|
||||||
|
IssuingCertificateURL: []string{
|
||||||
|
"192.0.2.1/ca.crt",
|
||||||
|
},
|
||||||
|
Subject: pkix.Name{CommonName: "AK Certificate"},
|
||||||
|
Issuer: intermediateCert.Subject,
|
||||||
|
})
|
||||||
|
return newTestCert(t, certTemplate, intermediateCert, tpmAk.PublicKey(), intermediateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
crlServer roundTripFunc
|
||||||
|
getAkCert func(*testing.T) *x509.Certificate
|
||||||
|
wantIssueErr bool
|
||||||
|
wantValidateErr bool
|
||||||
|
}{
|
||||||
|
"success": {
|
||||||
|
crlServer: func(req *http.Request) *http.Response {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: io.NopCloser(bytes.NewReader(intermediateCert.Raw)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAkCert: defaultAkCertFunc,
|
||||||
|
},
|
||||||
|
"intermediate cert is fetched from multiple URLs": {
|
||||||
|
crlServer: func(req *http.Request) *http.Response {
|
||||||
|
if req.URL.String() == "192.0.2.1/ca.crt" {
|
||||||
|
return &http.Response{StatusCode: http.StatusNotFound}
|
||||||
|
}
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: io.NopCloser(bytes.NewReader(intermediateCert.Raw)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAkCert: func(*testing.T) *x509.Certificate {
|
||||||
|
t.Helper()
|
||||||
|
_, certTemplate := fillCertTemplate(t, &x509.Certificate{
|
||||||
|
IssuingCertificateURL: []string{
|
||||||
|
"192.0.2.1/ca.crt",
|
||||||
|
"192.0.2.2/ca.crt",
|
||||||
|
},
|
||||||
|
Subject: pkix.Name{CommonName: "AK Certificate"},
|
||||||
|
Issuer: intermediateCert.Subject,
|
||||||
|
})
|
||||||
|
return newTestCert(t, certTemplate, intermediateCert, tpmAk.PublicKey(), intermediateKey)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"intermediate cert cannot be fetched": {
|
||||||
|
crlServer: func(req *http.Request) *http.Response {
|
||||||
|
return &http.Response{StatusCode: http.StatusNotFound}
|
||||||
|
},
|
||||||
|
getAkCert: defaultAkCertFunc,
|
||||||
|
wantIssueErr: true,
|
||||||
|
},
|
||||||
|
"intermediate cert is not signed by root cert": {
|
||||||
|
crlServer: func(req *http.Request) *http.Response {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: io.NopCloser(bytes.NewReader(rootCert.Raw)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAkCert: defaultAkCertFunc,
|
||||||
|
wantValidateErr: true,
|
||||||
|
},
|
||||||
|
"ak does not match ak cert public key": {
|
||||||
|
crlServer: func(req *http.Request) *http.Response {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: io.NopCloser(bytes.NewReader(intermediateCert.Raw)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getAkCert: func(*testing.T) *x509.Certificate {
|
||||||
|
t.Helper()
|
||||||
|
key, certTemplate := fillCertTemplate(t, &x509.Certificate{
|
||||||
|
IssuingCertificateURL: []string{
|
||||||
|
"192.0.2.1/ca.crt",
|
||||||
|
},
|
||||||
|
Subject: pkix.Name{CommonName: "AK Certificate"},
|
||||||
|
Issuer: intermediateCert.Subject,
|
||||||
|
})
|
||||||
|
return newTestCert(t, certTemplate, intermediateCert, key.Public(), intermediateKey)
|
||||||
|
},
|
||||||
|
wantValidateErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
akCert := tc.getAkCert(t).Raw
|
||||||
|
|
||||||
|
// write akCert to TPM
|
||||||
|
require.NoError(tpm2.NVUndefineSpace(tpm, "", tpm2.HandleOwner, tpmAkCertIdx))
|
||||||
|
require.NoError(tpm2.NVDefineSpace(
|
||||||
|
tpm, tpm2.HandleOwner, tpmAkCertIdx, "", "", []byte{},
|
||||||
|
tpm2.AttrOwnerWrite|tpm2.AttrOwnerRead|tpm2.AttrAuthRead|tpm2.AttrAuthWrite|tpm2.AttrNoDA,
|
||||||
|
uint16(len(akCert)),
|
||||||
|
))
|
||||||
|
require.NoError(tpm2.NVWrite(tpm, tpm2.HandleOwner, tpmAkCertIdx, "", akCert, 0))
|
||||||
|
|
||||||
|
issuer := NewIssuer()
|
||||||
|
issuer.hClient = newTestClient(tc.crlServer)
|
||||||
|
|
||||||
|
certs, err := issuer.getAttestationCert(tpm)
|
||||||
|
if tc.wantIssueErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
validator := NewValidator(map[uint32][]byte{}, []uint32{}, nil)
|
||||||
|
cert, err := x509.ParseCertificate(rootCert.Raw)
|
||||||
|
require.NoError(err)
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
roots.AddCert(cert)
|
||||||
|
validator.roots = roots
|
||||||
|
|
||||||
|
key, err := validator.verifyAttestationKey(akPub, certs)
|
||||||
|
if tc.wantValidateErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NoError(err)
|
||||||
|
rsaKey, ok := key.(*rsa.PublicKey)
|
||||||
|
require.True(ok)
|
||||||
|
assert.True(rsaKey.Equal(tpmAk.PublicKey()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type roundTripFunc func(req *http.Request) *http.Response
|
||||||
|
|
||||||
|
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
return f(req), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTestClient returns *http.Client with Transport replaced to avoid making real calls.
|
||||||
|
func newTestClient(fn roundTripFunc) *http.Client {
|
||||||
|
return &http.Client{
|
||||||
|
Transport: fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestCert(t *testing.T, template *x509.Certificate, parent *x509.Certificate, pub, priv any) *x509.Certificate {
|
||||||
|
t.Helper()
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
certDER, err := x509.CreateCertificate(rand.Reader, template, parent, pub, priv)
|
||||||
|
require.NoError(err)
|
||||||
|
cert, err := x509.ParseCertificate(certDER)
|
||||||
|
require.NoError(err)
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillCertTemplate(t *testing.T, template *x509.Certificate) (*rsa.PrivateKey, *x509.Certificate) {
|
||||||
|
t.Helper()
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
require.NoError(err, "generating root key failed")
|
||||||
|
|
||||||
|
serialNumber, err := crypto.GenerateCertificateSerialNumber()
|
||||||
|
require.NoError(err)
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
template.SerialNumber = serialNumber
|
||||||
|
template.NotBefore = now.Add(-2 * time.Hour)
|
||||||
|
template.NotAfter = now.Add(24 * 365 * time.Hour)
|
||||||
|
return key, template
|
||||||
|
}
|
@ -8,42 +8,104 @@ package trustedlaunch
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||||
|
certutil "github.com/edgelesssys/constellation/v2/internal/crypto"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||||
"github.com/google/go-tpm/tpm2"
|
"github.com/google/go-tpm/tpm2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ameRoot is the AME root CA certificate used to sign Azure's AME Infra CA certificates.
|
||||||
|
// The certificate can be found at http://crl.microsoft.com/pkiinfra/certs/AMERoot_ameroot.crt.
|
||||||
|
var ameRoot = mustParseX509("-----BEGIN CERTIFICATE-----\nMIIFVjCCAz6gAwIBAgIQJdrLVcnGd4FAnlaUgt5N/jANBgkqhkiG9w0BAQsFADA8\nMRMwEQYKCZImiZPyLGQBGRYDR0JMMRMwEQYKCZImiZPyLGQBGRYDQU1FMRAwDgYD\nVQQDEwdhbWVyb290MB4XDTE2MDUyNDIyNTI1NFoXDTI2MDUyNDIyNTcwM1owPDET\nMBEGCgmSJomT8ixkARkWA0dCTDETMBEGCgmSJomT8ixkARkWA0FNRTEQMA4GA1UE\nAxMHYW1lcm9vdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALv4uChY\noVuO+bxBOcn8v4FajoGkxo0YgVwEqEPDVPI6vzmnEqHVhQ1GMVeDyiRrgQT1vCk1\nHMMzo9LlWowPrzbXOwjOTFbXc36+UU41yNN2GeNa49RXbAkfbzKE/SYLfbqOD0dN\nZLwvOhgIb25oA1eAxW/DI/hvJLLKh2SscvkIyd3o2BUeFm7NtyYG/buCKJh8lOq8\n0iBwRoEoInb0vhorHaswSMmqY1g+AJndY/M7uGUqkhDGBhLu53bU9wbUPHsEI+wa\nq6WypCijZYT+C4BS5GJrEPZ2O92pztd+ULqhzNRoPj5RuElUww7+z5RnbCaupyBY\nOmmJMH30EiRSq8dK/irixXXwJraSywR5kyfmAkv6GYWlRlxFUiK3/co47JLA3TDK\nN0wfutbpqxdZQYyGfO2nZrr5JbKfSU0sMtOZDkK6hlafV++hfkVSvFfNHE5B5uN1\nMK6agl1dzi28HfJT9aO7cmjGxl1SJ5qoCvcwZNQ2SPHFdrslcwXEFOMDaEzVOA3V\n7j3+6lrT8sHXg0sErkcd8lrBImfzhLxM/Wh8CgOUNeUu3flUoxmFv3el+QWalSNy\n2SXs2NgWuYE5Iog7CHD/xCnoEnZwwjqLkrro4hYWE4Xj3VlA2Eq+VxqJOgdyFl3m\nckSZ08OcwLeprY4+2GEvCXNGNdXUmNNgk2PvAgMBAAGjVDBSMAsGA1UdDwQEAwIB\nhjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBQpXlFeZK40ueusnA2njHUB\n0QkLKDAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQsFAAOCAgEAcznFDnJx\nsXaazFY1DuIPvUaiWS7ELxAVXMGZ7ROjLrDq1FNYVewL4emDqyEIEMFncec8rqyk\nVBvLQA5YqMCxQWJpL0SlgRSknzLh9ZVcQw1TshC49/XV2N/CLOuyInEQwS//46so\nT20Cf8UGUiOK472LZlvM4KchyDR3FTNtmMg0B/LKVjevpX9sk5MiyjjLUj3jtPIP\n7jpsfZDd/BNsg/89kpsIF5O64I7iYFj3MHu9o4UJcEX0hRt7OzUxqa9THTssvzE5\nVkWo8Rtou2T5TobKV6Rr5Ob9wchLXqVtCyZF16voEKheBnalhGUvErI/6VtBwLb7\n13C0JkKLBNMen+HClNliicVIaubnpY2g+AqxOgKBHiZnzq2HhE1qqEUf4VfqahNU\niaXtbtyo54f2dCf9UL9uG9dllN3nxBE/Y/aWF6E1M8Bslj1aYAtfUQ/xlhEXCly6\nzohw697i3XFUt76RwvfW8quvqdH9Mx0PBpYo4wJJRwAecSJQNy6wIJhAuDgOemXJ\nYViBi/bDnhPcFEVQxsypQSw91BUw7Mxh+W59H5MC25SAIw9fLMT9LRqSYpPyasNp\n4nACjR+bv/6cI+ICOrGmD2mrk2c4dNnYpDx96FfX/Y158RV0wotqIglACk6m1qyo\nyTra6P0Kvo6xz4KaVm8F7VDzUP+heAAhPAs=\n-----END CERTIFICATE-----\n")
|
||||||
|
|
||||||
// Validator for Azure trusted launch VM attestation.
|
// Validator for Azure trusted launch VM attestation.
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
oid.AzureTrustedLaunch
|
oid.AzureTrustedLaunch
|
||||||
*vtpm.Validator
|
*vtpm.Validator
|
||||||
|
roots *x509.CertPool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewValidator initializes a new Azure validator with the provided PCR values.
|
// NewValidator initializes a new Azure validator with the provided PCR values.
|
||||||
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.WarnLogger) *Validator {
|
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.WarnLogger) *Validator {
|
||||||
return &Validator{
|
rootPool := x509.NewCertPool()
|
||||||
Validator: vtpm.NewValidator(
|
rootPool.AddCert(ameRoot)
|
||||||
|
v := &Validator{roots: rootPool}
|
||||||
|
v.Validator = vtpm.NewValidator(
|
||||||
pcrs,
|
pcrs,
|
||||||
enforcedPCRs,
|
enforcedPCRs,
|
||||||
trustedKey,
|
v.verifyAttestationKey,
|
||||||
validateVM,
|
validateVM,
|
||||||
vtpm.VerifyPKCS1v15,
|
vtpm.VerifyPKCS1v15,
|
||||||
log,
|
log,
|
||||||
),
|
)
|
||||||
}
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// trustedKey returns the key encoded in the given TPMT_PUBLIC message.
|
// verifyAttestationKey establishes trust in an attestation key.
|
||||||
func trustedKey(akPub, instanceInfo []byte) (crypto.PublicKey, error) {
|
// It does so by verifying the certificate chain of the attestation key certificate.
|
||||||
|
func (v *Validator) verifyAttestationKey(akPub, instanceInfo []byte) (crypto.PublicKey, error) {
|
||||||
pubArea, err := tpm2.DecodePublic(akPub)
|
pubArea, err := tpm2.DecodePublic(akPub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("decoding attestation key public area: %w", err)
|
||||||
}
|
}
|
||||||
return pubArea.Key()
|
|
||||||
|
var akSigner akSigner
|
||||||
|
if err := json.Unmarshal(instanceInfo, &akSigner); err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshaling attestation key signer info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
akCert, err := x509.ParseCertificate(akSigner.AkCert)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing attestation key certificate: %w", err)
|
||||||
|
}
|
||||||
|
akCertCA, err := x509.ParseCertificate(akSigner.CA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing attestation key CA certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
intermediates := x509.NewCertPool()
|
||||||
|
intermediates.AddCert(akCertCA)
|
||||||
|
|
||||||
|
if _, err := akCert.Verify(x509.VerifyOptions{
|
||||||
|
Roots: v.roots,
|
||||||
|
Intermediates: intermediates,
|
||||||
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
||||||
|
}); err != nil {
|
||||||
|
return nil, fmt.Errorf("verifying attestation key certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, err := pubArea.Key()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting public key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyRSA, ok := pubKey.(*rsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("attestation key is not an RSA key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pubKeyRSA.Equal(akCert.PublicKey) {
|
||||||
|
return nil, errors.New("certificate public key does not match attestation key")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pubKeyRSA, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateVM returns nil.
|
// validateVM returns nil.
|
||||||
func validateVM(attestation vtpm.AttestationDocument) error {
|
func validateVM(attestation vtpm.AttestationDocument) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mustParseX509(pem string) *x509.Certificate {
|
||||||
|
cert, err := certutil.PemToX509Cert([]byte(pem))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) Edgeless Systems GmbH
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package trustedlaunch
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
|
||||||
"github.com/google/go-tpm-tools/client"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTrustedKeyFromSNP(t *testing.T) {
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
tpm, err := simulator.OpenSimulatedTPM()
|
|
||||||
require.NoError(err)
|
|
||||||
defer tpm.Close()
|
|
||||||
key, err := client.AttestationKeyRSA(tpm)
|
|
||||||
require.NoError(err)
|
|
||||||
defer key.Close()
|
|
||||||
akPub, err := key.PublicArea().Encode()
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
key []byte
|
|
||||||
instanceInfo []byte
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
"success": {
|
|
||||||
key: akPub,
|
|
||||||
instanceInfo: []byte{},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, tc := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
key, err := trustedKey(tc.key, tc.instanceInfo)
|
|
||||||
if tc.wantErr {
|
|
||||||
assert.Error(err)
|
|
||||||
} else {
|
|
||||||
assert.NoError(err)
|
|
||||||
assert.NotNil(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidateAzureCVM(t *testing.T) {
|
|
||||||
testCases := map[string]struct {
|
|
||||||
attDoc vtpm.AttestationDocument
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
"success": {
|
|
||||||
attDoc: vtpm.AttestationDocument{},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, tc := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
err := validateVM(tc.attDoc)
|
|
||||||
if tc.wantErr {
|
|
||||||
assert.Error(err)
|
|
||||||
} else {
|
|
||||||
assert.NoError(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -45,7 +45,7 @@ const (
|
|||||||
// These images are built in a way that they support all versions currently listed in VersionConfigs.
|
// These images are built in a way that they support all versions currently listed in VersionConfigs.
|
||||||
KonnectivityAgentImage = "us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-agent:v0.0.32"
|
KonnectivityAgentImage = "us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-agent:v0.0.32"
|
||||||
KonnectivityServerImage = "registry.k8s.io/kas-network-proxy/proxy-server:v0.0.32"
|
KonnectivityServerImage = "registry.k8s.io/kas-network-proxy/proxy-server:v0.0.32"
|
||||||
JoinImage = "ghcr.io/edgelesssys/constellation/join-service:v2.0.0"
|
JoinImage = "ghcr.io/edgelesssys/constellation/join-service:v2.1.0-pre.0.20220928143744-c46d6e390f2d"
|
||||||
AccessManagerImage = "ghcr.io/edgelesssys/constellation/access-manager:v2.0.0"
|
AccessManagerImage = "ghcr.io/edgelesssys/constellation/access-manager:v2.0.0"
|
||||||
KmsImage = "ghcr.io/edgelesssys/constellation/kmsserver:v2.0.0"
|
KmsImage = "ghcr.io/edgelesssys/constellation/kmsserver:v2.0.0"
|
||||||
VerificationImage = "ghcr.io/edgelesssys/constellation/verification-service:v2.0.0"
|
VerificationImage = "ghcr.io/edgelesssys/constellation/verification-service:v2.0.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user