mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-09-29 12:18:40 -04:00
attestation: add snp package
The package holds code shared between SNP-based attestation implementations on AWS and Azure .
This commit is contained in:
parent
635a5d2c0a
commit
5ce55e3449
15 changed files with 608 additions and 489 deletions
|
@ -15,11 +15,11 @@ go_library(
|
|||
deps = [
|
||||
"//internal/attestation",
|
||||
"//internal/attestation/idkeydigest",
|
||||
"//internal/attestation/snp",
|
||||
"//internal/attestation/variant",
|
||||
"//internal/attestation/vtpm",
|
||||
"//internal/cloud/azure",
|
||||
"//internal/config",
|
||||
"//internal/constants",
|
||||
"@com_github_edgelesssys_go_azguestattestation//maa",
|
||||
"@com_github_google_go_sev_guest//abi",
|
||||
"@com_github_google_go_sev_guest//kds",
|
||||
|
@ -48,9 +48,10 @@ go_test(
|
|||
}),
|
||||
deps = [
|
||||
"//internal/attestation",
|
||||
"//internal/attestation/azure/snp/testdata",
|
||||
"//internal/attestation/idkeydigest",
|
||||
"//internal/attestation/simulator",
|
||||
"//internal/attestation/snp",
|
||||
"//internal/attestation/snp/testdata",
|
||||
"//internal/attestation/vtpm",
|
||||
"//internal/config",
|
||||
"//internal/logger",
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"io"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/snp"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/go-azguestattestation/maa"
|
||||
|
@ -65,7 +66,7 @@ func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, us
|
|||
}
|
||||
}
|
||||
|
||||
instanceInfo := azureInstanceInfo{
|
||||
instanceInfo := snp.InstanceInfo{
|
||||
VCEK: params.VcekCert,
|
||||
CertChain: params.VcekChain,
|
||||
AttestationReport: params.SNPReport,
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/snp"
|
||||
"github.com/edgelesssys/go-azguestattestation/maa"
|
||||
tpmclient "github.com/google/go-tpm-tools/client"
|
||||
"github.com/google/go-tpm/legacy/tpm2"
|
||||
|
@ -99,7 +100,7 @@ func TestGetSNPAttestation(t *testing.T) {
|
|||
assert.Equal(data, maa.gotTokenData)
|
||||
}
|
||||
|
||||
var instanceInfo azureInstanceInfo
|
||||
var instanceInfo snp.InstanceInfo
|
||||
err = json.Unmarshal(attestationJSON, &instanceInfo)
|
||||
require.NoError(err)
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "testdata",
|
||||
srcs = ["testdata.go"],
|
||||
embedsrcs = [
|
||||
"attestation.bin",
|
||||
"vcek.cert",
|
||||
"certchain.pem",
|
||||
"runtimedata.bin",
|
||||
"vcek.pem",
|
||||
],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp/testdata",
|
||||
visibility = ["//:__subpackages__"],
|
||||
)
|
Binary file not shown.
|
@ -1,74 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC
|
||||
BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS
|
||||
BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg
|
||||
Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp
|
||||
Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy
|
||||
MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS
|
||||
BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j
|
||||
ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg
|
||||
W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta
|
||||
1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2
|
||||
SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0
|
||||
60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05
|
||||
gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg
|
||||
bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs
|
||||
+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi
|
||||
Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ
|
||||
eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18
|
||||
fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j
|
||||
WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI
|
||||
rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG
|
||||
KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG
|
||||
SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI
|
||||
AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel
|
||||
ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw
|
||||
STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK
|
||||
dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq
|
||||
zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp
|
||||
KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e
|
||||
pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq
|
||||
HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh
|
||||
3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn
|
||||
JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH
|
||||
CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4
|
||||
AFZEAwoKCQ==
|
||||
-----END CERTIFICATE-----
|
|
@ -1 +0,0 @@
|
|||
{"keys":[{"kid":"HCLAkPub","key_ops":["encrypt"],"kty":"RSA","e":"AQAB","n":"tyFqvAAAf2GFelkW7VcRhJnA2YvY6LjBzeUN2vaMZnZth_tFnWMkK5AXtusyCNel3uip3VGZzTaz5X2tGVjGrs-MVHcap9QdwqUXVW3g9OQ_tEbixcx7-xblUJQkGEQfnbbSdnPI2lvLzOs1Z_0vjeDAxvSQrmags6nY-cJAWH-pgDVJyHtpsUSs_QBWklazDso5WHmnMt9s9MuilWXox0RSyXnUeaQyhY1ju7RTScRnVXuNy67z_EJnwM92drwFb8AUd0SJ_9ohvEYl4aZRDECGo0VrjcSHU-JGJeuWC5VhDBR5EOoCVBBgqe9e83v_lJxI3WLe2ovSIZIIzAmbSQ"}],"vm-configuration":{"console-enabled":true,"current-time":1661455391,"secure-boot":false,"tpm-enabled":true,"vmUniqueId":"B6C98C3B-4EC7-4DA6-BD2F-7D98D20D7B75"}}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// Package testdata contains testing data for an attestation process.
|
||||
package testdata
|
||||
|
||||
import _ "embed"
|
||||
|
||||
// AttestationBytes is an example attestation report from a Constellation VM.
|
||||
//
|
||||
//go:embed attestation.bin
|
||||
var AttestationReport []byte
|
||||
|
||||
// AzureThimVCEK is an example VCEK certificate (PEM, as returned from Azure THIM) for the AttestationReport.
|
||||
//
|
||||
//go:embed vcek.pem
|
||||
var AzureThimVCEK []byte
|
||||
|
||||
// AmdKdsVCEK is an example VCEK certificate (DER, as returned from AMD KDS) for the AttestationReport.
|
||||
//
|
||||
//go:embed vcek.cert
|
||||
var AmdKdsVCEK []byte
|
||||
|
||||
// RuntimeData is an example runtime data from the TPM for the AttestationReport.
|
||||
//
|
||||
//go:embed runtimedata.bin
|
||||
var RuntimeData []byte
|
||||
|
||||
// CertChain is a valid certificate chain (PEM, as returned from Azure THIM) for the VCEK certificate.
|
||||
//
|
||||
//go:embed certchain.pem
|
||||
var CertChain []byte
|
BIN
internal/attestation/azure/snp/testdata/vcek.cert
vendored
BIN
internal/attestation/azure/snp/testdata/vcek.cert
vendored
Binary file not shown.
31
internal/attestation/azure/snp/testdata/vcek.pem
vendored
31
internal/attestation/azure/snp/testdata/vcek.pem
vendored
|
@ -1,31 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
8H3kABPv6hqrFftRNbargQ==
|
||||
-----END CERTIFICATE-----
|
|
@ -15,16 +15,15 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/snp"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/google/go-sev-guest/abi"
|
||||
"github.com/google/go-sev-guest/kds"
|
||||
spb "github.com/google/go-sev-guest/proto/sevsnp"
|
||||
|
@ -79,7 +78,7 @@ func NewValidator(cfg *config.AzureSEVSNP, log attestation.Logger) *Validator {
|
|||
log = nopAttestationLogger{}
|
||||
}
|
||||
v := &Validator{
|
||||
hclValidator: &azureInstanceInfo{},
|
||||
hclValidator: &attestationKey{},
|
||||
maa: newMAAClient(),
|
||||
config: cfg,
|
||||
log: log,
|
||||
|
@ -106,18 +105,15 @@ func (v *Validator) getTrustedKey(ctx context.Context, attDoc vtpm.AttestationDo
|
|||
trustedArk := (*x509.Certificate)(&v.config.AMDRootKey) // ARK, specified in the Constellation config
|
||||
|
||||
// fallback certificates, used if not present in THIM response.
|
||||
cachedCerts := sevSnpCerts{
|
||||
ask: trustedAsk,
|
||||
ark: trustedArk,
|
||||
}
|
||||
cachedCerts := snp.NewCertificateChain(trustedAsk, trustedArk)
|
||||
|
||||
// transform the instanceInfo received from Microsoft into a verifiable attestation report format.
|
||||
var instanceInfo azureInstanceInfo
|
||||
var instanceInfo snp.InstanceInfo
|
||||
if err := json.Unmarshal(attDoc.InstanceInfo, &instanceInfo); err != nil {
|
||||
return nil, fmt.Errorf("unmarshalling instanceInfo: %w", err)
|
||||
}
|
||||
|
||||
att, err := instanceInfo.attestationWithCerts(v.log, v.getter, cachedCerts)
|
||||
att, err := instanceInfo.AttestationWithCerts(v.log, v.getter, cachedCerts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing attestation report: %w", err)
|
||||
}
|
||||
|
@ -192,7 +188,7 @@ func (v *Validator) getTrustedKey(ctx context.Context, attDoc vtpm.AttestationDo
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = v.hclValidator.validateAk(instanceInfo.RuntimeData, att.Report.ReportData, pubArea.RSAParameters); err != nil {
|
||||
if err = v.hclValidator.validate(instanceInfo.RuntimeData, att.Report.ReportData, pubArea.RSAParameters); err != nil {
|
||||
return nil, fmt.Errorf("validating HCLAkPub: %w", err)
|
||||
}
|
||||
|
||||
|
@ -239,201 +235,17 @@ func (v *Validator) checkIDKeyDigest(ctx context.Context, report *spb.Attestatio
|
|||
return nil
|
||||
}
|
||||
|
||||
// azureInstanceInfo contains the necessary information to establish trust in
|
||||
// an Azure CVM.
|
||||
type azureInstanceInfo struct {
|
||||
// VCEK is the PEM-encoded VCEK certificate for the attestation report.
|
||||
VCEK []byte
|
||||
// CertChain is the PEM-encoded certificate chain for the attestation report.
|
||||
CertChain []byte
|
||||
// AttestationReport is the attestation report from the vTPM (NVRAM) of the CVM.
|
||||
AttestationReport []byte
|
||||
// RuntimeData is the Azure runtime data from the vTPM (NVRAM) of the CVM.
|
||||
RuntimeData []byte
|
||||
// MAAToken is the token of the MAA for the attestation report, used as a fallback
|
||||
// if the IDKeyDigest cannot be verified.
|
||||
MAAToken string
|
||||
type attestationKey struct {
|
||||
PublicPart []akPub `json:"keys"`
|
||||
}
|
||||
|
||||
// attestationWithCerts returns a formatted version of the attestation report and its certificates from the instanceInfo.
|
||||
// Certificates are retrieved in the following precedence:
|
||||
// 1. ASK or ARK from THIM
|
||||
// 2. ASK or ARK from fallbackCerts
|
||||
// 3. ASK or ARK from AMD KDS.
|
||||
func (a *azureInstanceInfo) attestationWithCerts(logger attestation.Logger, getter trust.HTTPSGetter,
|
||||
fallbackCerts sevSnpCerts,
|
||||
) (*spb.Attestation, error) {
|
||||
report, err := abi.ReportToProto(a.AttestationReport)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting report to proto: %w", err)
|
||||
}
|
||||
|
||||
// Product info as reported through CPUID[EAX=1]
|
||||
sevProduct := &spb.SevProduct{Name: spb.SevProduct_SEV_PRODUCT_MILAN, Stepping: 0} // Milan-B0
|
||||
productName := kds.ProductString(sevProduct)
|
||||
|
||||
att := &spb.Attestation{
|
||||
Report: report,
|
||||
CertificateChain: &spb.CertificateChain{},
|
||||
Product: sevProduct,
|
||||
}
|
||||
|
||||
// If the VCEK certificate is present, parse it and format it.
|
||||
vcek, err := a.parseVCEK()
|
||||
if err != nil {
|
||||
logger.Warnf("Error parsing VCEK: %v", err)
|
||||
}
|
||||
if vcek != nil {
|
||||
att.CertificateChain.VcekCert = vcek.Raw
|
||||
} else {
|
||||
// Otherwise, retrieve it from AMD KDS.
|
||||
logger.Infof("VCEK certificate not present, falling back to retrieving it from AMD KDS")
|
||||
vcekURL := kds.VCEKCertURL(productName, report.GetChipId(), kds.TCBVersion(report.GetReportedTcb()))
|
||||
vcek, err := getter.Get(vcekURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("retrieving VCEK certificate from AMD KDS: %w", err)
|
||||
}
|
||||
att.CertificateChain.VcekCert = vcek
|
||||
}
|
||||
|
||||
// If the certificate chain from THIM is present, parse it and format it.
|
||||
ask, ark, err := a.parseCertChain()
|
||||
if err != nil {
|
||||
logger.Warnf("Error parsing certificate chain: %v", err)
|
||||
}
|
||||
if ask != nil {
|
||||
logger.Infof("Using ASK certificate from Azure THIM")
|
||||
att.CertificateChain.AskCert = ask.Raw
|
||||
}
|
||||
if ark != nil {
|
||||
logger.Infof("Using ARK certificate from Azure THIM")
|
||||
att.CertificateChain.ArkCert = ark.Raw
|
||||
}
|
||||
|
||||
// If a cached ASK or an ARK from the Constellation config is present, use it.
|
||||
if att.CertificateChain.AskCert == nil && fallbackCerts.ask != nil {
|
||||
logger.Infof("Using cached ASK certificate")
|
||||
att.CertificateChain.AskCert = fallbackCerts.ask.Raw
|
||||
}
|
||||
if att.CertificateChain.ArkCert == nil && fallbackCerts.ark != nil {
|
||||
logger.Infof("Using ARK certificate from %s", constants.ConfigFilename)
|
||||
att.CertificateChain.ArkCert = fallbackCerts.ark.Raw
|
||||
}
|
||||
// Otherwise, retrieve it from AMD KDS.
|
||||
if att.CertificateChain.AskCert == nil || att.CertificateChain.ArkCert == nil {
|
||||
logger.Infof(
|
||||
"Certificate chain not fully present (ARK present: %t, ASK present: %t), falling back to retrieving it from AMD KDS",
|
||||
(att.CertificateChain.ArkCert != nil),
|
||||
(att.CertificateChain.AskCert != nil),
|
||||
)
|
||||
kdsCertChain, err := trust.GetProductChain(productName, abi.VcekReportSigner, getter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("retrieving certificate chain from AMD KDS: %w", err)
|
||||
}
|
||||
if att.CertificateChain.AskCert == nil && kdsCertChain.Ask != nil {
|
||||
logger.Infof("Using ASK certificate from AMD KDS")
|
||||
att.CertificateChain.AskCert = kdsCertChain.Ask.Raw
|
||||
}
|
||||
if att.CertificateChain.ArkCert == nil && kdsCertChain.Ask != nil {
|
||||
logger.Infof("Using ARK certificate from AMD KDS")
|
||||
att.CertificateChain.ArkCert = kdsCertChain.Ark.Raw
|
||||
}
|
||||
}
|
||||
|
||||
return att, nil
|
||||
}
|
||||
|
||||
type sevSnpCerts struct {
|
||||
ask *x509.Certificate
|
||||
ark *x509.Certificate
|
||||
}
|
||||
|
||||
// parseCertChain parses the certificate chain from the instanceInfo into x509-formatted ASK and ARK certificates.
|
||||
// If less than 2 certificates are present, only the present certificate is returned.
|
||||
// If more than 2 certificates are present, an error is returned.
|
||||
func (a *azureInstanceInfo) parseCertChain() (ask, ark *x509.Certificate, retErr error) {
|
||||
rest := bytes.TrimSpace(a.CertChain)
|
||||
|
||||
i := 1
|
||||
var block *pem.Block
|
||||
for block, rest = pem.Decode(rest); block != nil; block, rest = pem.Decode(rest) {
|
||||
if i > 2 {
|
||||
retErr = fmt.Errorf("parse certificate %d: more than 2 certificates in chain", i)
|
||||
return
|
||||
}
|
||||
|
||||
if block.Type != "CERTIFICATE" {
|
||||
retErr = fmt.Errorf("parse certificate %d: expected PEM block type 'CERTIFICATE', got '%s'", i, block.Type)
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
retErr = fmt.Errorf("parse certificate %d: %w", i, err)
|
||||
return
|
||||
}
|
||||
|
||||
// https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/57230.pdf
|
||||
// Table 6 and 7
|
||||
switch cert.Subject.CommonName {
|
||||
case "SEV-Milan":
|
||||
ask = cert
|
||||
case "ARK-Milan":
|
||||
ark = cert
|
||||
default:
|
||||
retErr = fmt.Errorf("parse certificate %d: unexpected subject CN %s", i, cert.Subject.CommonName)
|
||||
return
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
switch {
|
||||
case i == 1:
|
||||
retErr = fmt.Errorf("no PEM blocks found")
|
||||
case len(rest) != 0:
|
||||
retErr = fmt.Errorf("remaining PEM block is not a valid certificate: %s", rest)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseVCEK parses the VCEK certificate from the instanceInfo into an x509-formatted certificate.
|
||||
// If the VCEK certificate is not present, nil is returned.
|
||||
func (a *azureInstanceInfo) parseVCEK() (*x509.Certificate, error) {
|
||||
newlinesTrimmed := bytes.TrimSpace(a.VCEK)
|
||||
if len(newlinesTrimmed) == 0 {
|
||||
// VCEK is not present.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
block, rest := pem.Decode(newlinesTrimmed)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("no PEM blocks found")
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
return nil, fmt.Errorf("received more data than expected")
|
||||
}
|
||||
if block.Type != "CERTIFICATE" {
|
||||
return nil, fmt.Errorf("expected PEM block type 'CERTIFICATE', got '%s'", block.Type)
|
||||
}
|
||||
|
||||
vcek, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing VCEK certificate: %w", err)
|
||||
}
|
||||
|
||||
return vcek, nil
|
||||
}
|
||||
|
||||
// validateAk validates that the attestation key from the TPM is trustworthy. The steps are:
|
||||
// validate validates that the attestation key from the TPM is trustworthy. The steps are:
|
||||
// 1. runtime data read from the TPM has the same sha256 digest as reported in `report_data` of the SNP report.
|
||||
// 2. modulus reported in runtime data matches modulus from key at idx 0x81000003.
|
||||
// 3. exponent reported in runtime data matches exponent from key at idx 0x81000003.
|
||||
// The function is currently tested manually on a Azure Ubuntu CVM.
|
||||
func (a *azureInstanceInfo) validateAk(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error {
|
||||
var runtimeData runtimeData
|
||||
if err := json.Unmarshal(runtimeDataRaw, &runtimeData); err != nil {
|
||||
func (a *attestationKey) validate(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error {
|
||||
if err := json.Unmarshal(runtimeDataRaw, a); err != nil {
|
||||
return fmt.Errorf("unmarshalling json: %w", err)
|
||||
}
|
||||
|
||||
|
@ -445,10 +257,10 @@ func (a *azureInstanceInfo) validateAk(runtimeDataRaw []byte, reportData []byte,
|
|||
return errors.New("unexpected runtimeData digest in TPM")
|
||||
}
|
||||
|
||||
if len(runtimeData.Keys) < 1 {
|
||||
if len(a.PublicPart) < 1 {
|
||||
return errors.New("did not receive any keys in runtime data")
|
||||
}
|
||||
rawN, err := base64.RawURLEncoding.DecodeString(runtimeData.Keys[0].N)
|
||||
rawN, err := base64.RawURLEncoding.DecodeString(a.PublicPart[0].N)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decoding modulus string: %w", err)
|
||||
}
|
||||
|
@ -456,7 +268,7 @@ func (a *azureInstanceInfo) validateAk(runtimeDataRaw []byte, reportData []byte,
|
|||
return fmt.Errorf("unexpected modulus value in TPM")
|
||||
}
|
||||
|
||||
rawE, err := base64.RawURLEncoding.DecodeString(runtimeData.Keys[0].E)
|
||||
rawE, err := base64.RawURLEncoding.DecodeString(a.PublicPart[0].E)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decoding exponent string: %w", err)
|
||||
}
|
||||
|
@ -478,17 +290,13 @@ func (a *azureInstanceInfo) validateAk(runtimeDataRaw []byte, reportData []byte,
|
|||
// The HCL is written by Azure, and sits between the Hypervisor and CVM OS.
|
||||
// The HCL runs in the protected context of the CVM.
|
||||
type hclAkValidator interface {
|
||||
validateAk(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error
|
||||
validate(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error
|
||||
}
|
||||
|
||||
// akPub are the public parameters of an RSA attestation key.
|
||||
type akPub struct {
|
||||
E string
|
||||
N string
|
||||
}
|
||||
|
||||
type runtimeData struct {
|
||||
Keys []akPub
|
||||
E string `json:"e"`
|
||||
N string `json:"n"`
|
||||
}
|
||||
|
||||
// nopAttestationLogger is a no-op implementation of AttestationLogger.
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
|
@ -19,13 +18,13 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp/testdata"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/snp"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/snp/testdata"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
|
@ -69,264 +68,6 @@ func TestNewValidator(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestParseCertChain tests the parsing of the certificate chain.
|
||||
func TestParseCertChain(t *testing.T) {
|
||||
defaultCertChain := testdata.CertChain
|
||||
askOnly := strings.Split(string(defaultCertChain), "-----END CERTIFICATE-----")[0] + "-----END CERTIFICATE-----"
|
||||
arkOnly := strings.Split(string(defaultCertChain), "-----END CERTIFICATE-----")[1] + "-----END CERTIFICATE-----"
|
||||
|
||||
testCases := map[string]struct {
|
||||
certChain []byte
|
||||
wantAsk bool
|
||||
wantArk bool
|
||||
wantErr bool
|
||||
}{
|
||||
"success": {
|
||||
certChain: defaultCertChain,
|
||||
wantAsk: true,
|
||||
wantArk: true,
|
||||
},
|
||||
"empty cert chain": {
|
||||
certChain: []byte{},
|
||||
wantErr: true,
|
||||
},
|
||||
"more than two certificates": {
|
||||
certChain: append(defaultCertChain, defaultCertChain...),
|
||||
wantErr: true,
|
||||
},
|
||||
"invalid certificate": {
|
||||
certChain: []byte("invalid"),
|
||||
wantErr: true,
|
||||
},
|
||||
"ark missing": {
|
||||
certChain: []byte(askOnly),
|
||||
wantAsk: true,
|
||||
},
|
||||
"ask missing": {
|
||||
certChain: []byte(arkOnly),
|
||||
wantArk: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
instanceInfo := &azureInstanceInfo{
|
||||
CertChain: tc.certChain,
|
||||
}
|
||||
|
||||
ask, ark, err := instanceInfo.parseCertChain()
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.wantAsk, ask != nil)
|
||||
assert.Equal(tc.wantArk, ark != nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseVCEK tests the parsing of the VCEK certificate.
|
||||
func TestParseVCEK(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
VCEK []byte
|
||||
wantVCEK bool
|
||||
wantErr bool
|
||||
}{
|
||||
"success": {
|
||||
VCEK: testdata.AzureThimVCEK,
|
||||
wantVCEK: true,
|
||||
},
|
||||
"empty": {
|
||||
VCEK: []byte{},
|
||||
},
|
||||
"malformed": {
|
||||
VCEK: testdata.AzureThimVCEK[:len(testdata.AzureThimVCEK)-100],
|
||||
wantErr: true,
|
||||
},
|
||||
"invalid": {
|
||||
VCEK: []byte("invalid"),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
instanceInfo := &azureInstanceInfo{
|
||||
VCEK: tc.VCEK,
|
||||
}
|
||||
|
||||
vcek, err := instanceInfo.parseVCEK()
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.wantVCEK, vcek != nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestInstanceInfoAttestation tests the basic unmarshalling of the attestation report and the ASK / ARK precedence.
|
||||
func TestInstanceInfoAttestation(t *testing.T) {
|
||||
defaultReport := testdata.AttestationReport
|
||||
testdataArk, testdataAsk := mustCertChainToPem(t, testdata.CertChain)
|
||||
exampleCert := &x509.Certificate{
|
||||
Raw: []byte{1, 2, 3},
|
||||
}
|
||||
cfg := config.DefaultForAzureSEVSNP()
|
||||
|
||||
testCases := map[string]struct {
|
||||
report []byte
|
||||
vcek []byte
|
||||
certChain []byte
|
||||
fallbackCerts sevSnpCerts
|
||||
getter *stubHTTPSGetter
|
||||
expectedArk *x509.Certificate
|
||||
expectedAsk *x509.Certificate
|
||||
wantErr bool
|
||||
}{
|
||||
"success": {
|
||||
report: defaultReport,
|
||||
vcek: testdata.AzureThimVCEK,
|
||||
certChain: testdata.CertChain,
|
||||
expectedArk: testdataArk,
|
||||
expectedAsk: testdataAsk,
|
||||
},
|
||||
"retrieve vcek": {
|
||||
report: defaultReport,
|
||||
certChain: testdata.CertChain,
|
||||
getter: newStubHTTPSGetter(
|
||||
&urlResponseMatcher{
|
||||
vcekResponse: testdata.AmdKdsVCEK,
|
||||
wantVcekRequest: true,
|
||||
},
|
||||
nil,
|
||||
),
|
||||
expectedArk: testdataArk,
|
||||
expectedAsk: testdataAsk,
|
||||
},
|
||||
"retrieve certchain": {
|
||||
report: defaultReport,
|
||||
vcek: testdata.AzureThimVCEK,
|
||||
getter: newStubHTTPSGetter(
|
||||
&urlResponseMatcher{
|
||||
certChainResponse: testdata.CertChain,
|
||||
wantCertChainRequest: true,
|
||||
},
|
||||
nil,
|
||||
),
|
||||
expectedArk: testdataArk,
|
||||
expectedAsk: testdataAsk,
|
||||
},
|
||||
"use fallback certs": {
|
||||
report: defaultReport,
|
||||
vcek: testdata.AzureThimVCEK,
|
||||
fallbackCerts: sevSnpCerts{
|
||||
ask: exampleCert,
|
||||
ark: exampleCert,
|
||||
},
|
||||
getter: newStubHTTPSGetter(
|
||||
&urlResponseMatcher{},
|
||||
nil,
|
||||
),
|
||||
expectedArk: exampleCert,
|
||||
expectedAsk: exampleCert,
|
||||
},
|
||||
"use certchain with fallback certs": {
|
||||
report: defaultReport,
|
||||
certChain: testdata.CertChain,
|
||||
vcek: testdata.AzureThimVCEK,
|
||||
fallbackCerts: sevSnpCerts{
|
||||
ask: &x509.Certificate{},
|
||||
ark: &x509.Certificate{},
|
||||
},
|
||||
getter: newStubHTTPSGetter(
|
||||
&urlResponseMatcher{},
|
||||
nil,
|
||||
),
|
||||
expectedArk: testdataArk,
|
||||
expectedAsk: testdataAsk,
|
||||
},
|
||||
"retrieve vcek and certchain": {
|
||||
report: defaultReport,
|
||||
getter: newStubHTTPSGetter(
|
||||
&urlResponseMatcher{
|
||||
certChainResponse: testdata.CertChain,
|
||||
vcekResponse: testdata.AmdKdsVCEK,
|
||||
wantCertChainRequest: true,
|
||||
wantVcekRequest: true,
|
||||
},
|
||||
nil,
|
||||
),
|
||||
expectedArk: testdataArk,
|
||||
expectedAsk: testdataAsk,
|
||||
},
|
||||
"report too short": {
|
||||
report: defaultReport[:len(defaultReport)-100],
|
||||
wantErr: true,
|
||||
},
|
||||
"corrupted report": {
|
||||
report: defaultReport[10 : len(defaultReport)-10],
|
||||
wantErr: true,
|
||||
},
|
||||
"certificate fetch error": {
|
||||
report: defaultReport,
|
||||
getter: newStubHTTPSGetter(nil, assert.AnError),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
// This is important. Without this call, the trust module caches certificates across testcases.
|
||||
defer trust.ClearProductCertCache()
|
||||
|
||||
instanceInfo := azureInstanceInfo{
|
||||
AttestationReport: tc.report,
|
||||
CertChain: tc.certChain,
|
||||
VCEK: tc.vcek,
|
||||
}
|
||||
|
||||
att, err := instanceInfo.attestationWithCerts(logger.NewTest(t), tc.getter, tc.fallbackCerts)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
require.NoError(err)
|
||||
assert.NotNil(att)
|
||||
assert.NotNil(att.CertificateChain)
|
||||
assert.NotNil(att.Report)
|
||||
|
||||
assert.Equal(hex.EncodeToString(att.Report.IdKeyDigest[:]), "57e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc1")
|
||||
|
||||
// This is a canary for us: If this fails in the future we possibly downgraded a SVN.
|
||||
// See https://github.com/google/go-sev-guest/blob/14ac50e9ffcc05cd1d12247b710c65093beedb58/validate/validate.go#L336 for decomposition of the values.
|
||||
tcbValues := kds.DecomposeTCBVersion(kds.TCBVersion(att.Report.GetLaunchTcb()))
|
||||
assert.True(tcbValues.BlSpl >= cfg.BootloaderVersion.Value)
|
||||
assert.True(tcbValues.TeeSpl >= cfg.TEEVersion.Value)
|
||||
assert.True(tcbValues.SnpSpl >= cfg.SNPVersion.Value)
|
||||
assert.True(tcbValues.UcodeSpl >= cfg.MicrocodeVersion.Value)
|
||||
assert.Equal(tc.expectedArk.Raw, att.CertificateChain.ArkCert)
|
||||
assert.Equal(tc.expectedAsk.Raw, att.CertificateChain.AskCert)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mustCertChainToPem(t *testing.T, certchain []byte) (ark, ask *x509.Certificate) {
|
||||
t.Helper()
|
||||
a := azureInstanceInfo{CertChain: certchain}
|
||||
ask, ark, err := a.parseCertChain()
|
||||
require.NoError(t, err)
|
||||
return ark, ask
|
||||
}
|
||||
|
||||
type stubHTTPSGetter struct {
|
||||
urlResponseMatcher *urlResponseMatcher // maps responses to requested URLs
|
||||
err error
|
||||
|
@ -488,18 +229,18 @@ func TestValidateAk(t *testing.T) {
|
|||
n := base64.RawURLEncoding.EncodeToString(key.PublicArea().RSAParameters.ModulusRaw)
|
||||
|
||||
ak := akPub{E: e, N: n}
|
||||
runtimeData := runtimeData{Keys: []akPub{ak}}
|
||||
runtimeData := attestationKey{PublicPart: []akPub{ak}}
|
||||
|
||||
defaultRuntimeDataRaw, err := json.Marshal(runtimeData)
|
||||
require.NoError(err)
|
||||
defaultInstanceInfo := azureInstanceInfo{RuntimeData: defaultRuntimeDataRaw}
|
||||
defaultInstanceInfo := snp.InstanceInfo{RuntimeData: defaultRuntimeDataRaw}
|
||||
|
||||
sig := sha256.Sum256(defaultRuntimeDataRaw)
|
||||
defaultReportData := sig[:]
|
||||
defaultRsaParams := key.PublicArea().RSAParameters
|
||||
|
||||
testCases := map[string]struct {
|
||||
instanceInfo azureInstanceInfo
|
||||
instanceInfo snp.InstanceInfo
|
||||
runtimeDataRaw []byte
|
||||
reportData []byte
|
||||
rsaParameters *tpm2.RSAParams
|
||||
|
@ -552,7 +293,8 @@ func TestValidateAk(t *testing.T) {
|
|||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
err = tc.instanceInfo.validateAk(tc.runtimeDataRaw, tc.reportData, tc.rsaParameters)
|
||||
ak := attestationKey{}
|
||||
err = ak.validate(tc.runtimeDataRaw, tc.reportData, tc.rsaParameters)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
|
@ -985,7 +727,7 @@ func TestTrustedKeyFromSNP(t *testing.T) {
|
|||
// This is important. Without this call, the trust module caches certificates across testcases.
|
||||
defer trust.ClearProductCertCache()
|
||||
|
||||
instanceInfo, err := newStubAzureInstanceInfo(tc.vcek, tc.certChain, tc.report, tc.runtimeData)
|
||||
instanceInfo, err := newStubInstanceInfo(tc.vcek, tc.certChain, tc.report, tc.runtimeData)
|
||||
require.NoError(err)
|
||||
|
||||
statement, err := json.Marshal(instanceInfo)
|
||||
|
@ -1004,7 +746,7 @@ func TestTrustedKeyFromSNP(t *testing.T) {
|
|||
}
|
||||
|
||||
validator := &Validator{
|
||||
hclValidator: &instanceInfo,
|
||||
hclValidator: &stubAttestationKey{},
|
||||
config: defaultCfg,
|
||||
log: logger.NewTest(t),
|
||||
getter: tc.getter,
|
||||
|
@ -1050,25 +792,25 @@ func (v *stubAttestationValidator) SNPAttestation(attestation *spb.Attestation,
|
|||
return validate.SnpAttestation(attestation, options)
|
||||
}
|
||||
|
||||
type stubAzureInstanceInfo struct {
|
||||
type stubInstanceInfo struct {
|
||||
AttestationReport []byte
|
||||
RuntimeData []byte
|
||||
VCEK []byte
|
||||
CertChain []byte
|
||||
}
|
||||
|
||||
func newStubAzureInstanceInfo(vcek, certChain []byte, report, runtimeData string) (stubAzureInstanceInfo, error) {
|
||||
func newStubInstanceInfo(vcek, certChain []byte, report, runtimeData string) (stubInstanceInfo, error) {
|
||||
validReport, err := hex.DecodeString(report)
|
||||
if err != nil {
|
||||
return stubAzureInstanceInfo{}, fmt.Errorf("invalid hex string report: %s", err)
|
||||
return stubInstanceInfo{}, fmt.Errorf("invalid hex string report: %s", err)
|
||||
}
|
||||
|
||||
decodedRuntime, err := hex.DecodeString(runtimeData)
|
||||
if err != nil {
|
||||
return stubAzureInstanceInfo{}, fmt.Errorf("invalid hex string runtimeData: %s", err)
|
||||
return stubInstanceInfo{}, fmt.Errorf("invalid hex string runtimeData: %s", err)
|
||||
}
|
||||
|
||||
return stubAzureInstanceInfo{
|
||||
return stubInstanceInfo{
|
||||
AttestationReport: validReport,
|
||||
RuntimeData: decodedRuntime,
|
||||
VCEK: vcek,
|
||||
|
@ -1076,9 +818,12 @@ func newStubAzureInstanceInfo(vcek, certChain []byte, report, runtimeData string
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *stubAzureInstanceInfo) validateAk(runtimeDataRaw []byte, reportData []byte, _ *tpm2.RSAParams) error {
|
||||
var runtimeData runtimeData
|
||||
if err := json.Unmarshal(runtimeDataRaw, &runtimeData); err != nil {
|
||||
type stubAttestationKey struct {
|
||||
PublicPart []akPub
|
||||
}
|
||||
|
||||
func (s *stubAttestationKey) validate(runtimeDataRaw []byte, reportData []byte, _ *tpm2.RSAParams) error {
|
||||
if err := json.Unmarshal(runtimeDataRaw, s); err != nil {
|
||||
return fmt.Errorf("unmarshalling json: %w", err)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue