mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-25 23:49:37 -05:00
verify: query vlek ASK from KDS if not set
The user can choose to supply an intermediate certificate through the config, like they can for the root key. If none is supplied, the KDS is queried for a valid ASK.
This commit is contained in:
parent
07eed0e319
commit
84d8bd8110
@ -105,8 +105,8 @@ func runVerify(cmd *cobra.Command, _ []string) error {
|
|||||||
log: log,
|
log: log,
|
||||||
}
|
}
|
||||||
formatterFactory := func(output string, provider cloudprovider.Provider, log debugLog) (attestationDocFormatter, error) {
|
formatterFactory := func(output string, provider cloudprovider.Provider, log debugLog) (attestationDocFormatter, error) {
|
||||||
if output == "json" && provider != cloudprovider.Azure {
|
if output == "json" && (provider != cloudprovider.Azure && provider != cloudprovider.AWS) {
|
||||||
return nil, errors.New("json output is only supported for Azure")
|
return nil, errors.New("json output is only supported for Azure and AWS")
|
||||||
}
|
}
|
||||||
switch output {
|
switch output {
|
||||||
case "json":
|
case "json":
|
||||||
@ -206,8 +206,7 @@ func (c *verifyCmd) verify(cmd *cobra.Command, verifyClient verifyClient, factor
|
|||||||
cmd.Context(),
|
cmd.Context(),
|
||||||
rawAttestationDoc,
|
rawAttestationDoc,
|
||||||
(conf.Provider.Azure == nil && conf.Provider.AWS == nil),
|
(conf.Provider.Azure == nil && conf.Provider.AWS == nil),
|
||||||
attConfig.GetMeasurements(),
|
attConfig,
|
||||||
maaURL,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("printing attestation document: %w", err)
|
return fmt.Errorf("printing attestation document: %w", err)
|
||||||
@ -254,8 +253,7 @@ func (c *verifyCmd) validateEndpointFlag(cmd *cobra.Command, stateFile *state.St
|
|||||||
// an attestationDocFormatter formats the attestation document.
|
// an attestationDocFormatter formats the attestation document.
|
||||||
type attestationDocFormatter interface {
|
type attestationDocFormatter interface {
|
||||||
// format returns the raw or formatted attestation doc depending on the rawOutput argument.
|
// format returns the raw or formatted attestation doc depending on the rawOutput argument.
|
||||||
format(ctx context.Context, docString string, PCRsOnly bool, expectedPCRs measurements.M,
|
format(ctx context.Context, docString string, PCRsOnly bool, attestationCfg config.AttestationCfg) (string, error)
|
||||||
attestationServiceURL string) (string, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonAttestationDocFormatter struct {
|
type jsonAttestationDocFormatter struct {
|
||||||
@ -264,7 +262,7 @@ type jsonAttestationDocFormatter struct {
|
|||||||
|
|
||||||
// format returns the json formatted attestation doc.
|
// format returns the json formatted attestation doc.
|
||||||
func (f *jsonAttestationDocFormatter) format(ctx context.Context, docString string, _ bool,
|
func (f *jsonAttestationDocFormatter) format(ctx context.Context, docString string, _ bool,
|
||||||
_ measurements.M, attestationServiceURL string,
|
attestationCfg config.AttestationCfg,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
var doc attestationDoc
|
var doc attestationDoc
|
||||||
if err := json.Unmarshal([]byte(docString), &doc); err != nil {
|
if err := json.Unmarshal([]byte(docString), &doc); err != nil {
|
||||||
@ -275,7 +273,7 @@ func (f *jsonAttestationDocFormatter) format(ctx context.Context, docString stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unmarshalling instance info: %w", err)
|
return "", fmt.Errorf("unmarshalling instance info: %w", err)
|
||||||
}
|
}
|
||||||
report, err := verify.NewReport(ctx, instanceInfo, attestationServiceURL, f.log)
|
report, err := verify.NewReport(ctx, instanceInfo, attestationCfg, f.log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("parsing SNP report: %w", err)
|
return "", fmt.Errorf("parsing SNP report: %w", err)
|
||||||
}
|
}
|
||||||
@ -291,7 +289,7 @@ type rawAttestationDocFormatter struct {
|
|||||||
|
|
||||||
// format returns the raw attestation doc.
|
// format returns the raw attestation doc.
|
||||||
func (f *rawAttestationDocFormatter) format(_ context.Context, docString string, _ bool,
|
func (f *rawAttestationDocFormatter) format(_ context.Context, docString string, _ bool,
|
||||||
_ measurements.M, _ string,
|
_ config.AttestationCfg,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
b := &strings.Builder{}
|
b := &strings.Builder{}
|
||||||
b.WriteString("Attestation Document:\n")
|
b.WriteString("Attestation Document:\n")
|
||||||
@ -305,7 +303,7 @@ type defaultAttestationDocFormatter struct {
|
|||||||
|
|
||||||
// format returns the formatted attestation doc.
|
// format returns the formatted attestation doc.
|
||||||
func (f *defaultAttestationDocFormatter) format(ctx context.Context, docString string, PCRsOnly bool,
|
func (f *defaultAttestationDocFormatter) format(ctx context.Context, docString string, PCRsOnly bool,
|
||||||
expectedPCRs measurements.M, attestationServiceURL string,
|
attestationCfg config.AttestationCfg,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
b := &strings.Builder{}
|
b := &strings.Builder{}
|
||||||
b.WriteString("Attestation Document:\n")
|
b.WriteString("Attestation Document:\n")
|
||||||
@ -315,7 +313,7 @@ func (f *defaultAttestationDocFormatter) format(ctx context.Context, docString s
|
|||||||
return "", fmt.Errorf("unmarshal attestation document: %w", err)
|
return "", fmt.Errorf("unmarshal attestation document: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := f.parseQuotes(b, doc.Attestation.Quotes, expectedPCRs); err != nil {
|
if err := f.parseQuotes(b, doc.Attestation.Quotes, attestationCfg.GetMeasurements()); err != nil {
|
||||||
return "", fmt.Errorf("parse quote: %w", err)
|
return "", fmt.Errorf("parse quote: %w", err)
|
||||||
}
|
}
|
||||||
if PCRsOnly {
|
if PCRsOnly {
|
||||||
@ -327,7 +325,7 @@ func (f *defaultAttestationDocFormatter) format(ctx context.Context, docString s
|
|||||||
return "", fmt.Errorf("unmarshalling instance info: %w", err)
|
return "", fmt.Errorf("unmarshalling instance info: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
report, err := verify.NewReport(ctx, instanceInfo, attestationServiceURL, f.log)
|
report, err := verify.NewReport(ctx, instanceInfo, attestationCfg, f.log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("parsing SNP report: %w", err)
|
return "", fmt.Errorf("parsing SNP report: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ type stubAttDocFormatter struct {
|
|||||||
formatErr error
|
formatErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *stubAttDocFormatter) format(_ context.Context, _ string, _ bool, _ measurements.M, _ string) (string, error) {
|
func (f *stubAttDocFormatter) format(_ context.Context, _ string, _ bool, _ config.AttestationCfg) (string, error) {
|
||||||
return "", f.formatErr
|
return "", f.formatErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ func TestFormat(t *testing.T) {
|
|||||||
|
|
||||||
for name, tc := range testCases {
|
for name, tc := range testCases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
_, err := tc.formatter.format(context.Background(), tc.doc, false, nil, "")
|
_, err := tc.formatter.format(context.Background(), tc.doc, false, nil)
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -22,6 +22,13 @@ import (
|
|||||||
"github.com/google/go-sev-guest/verify/trust"
|
"github.com/google/go-sev-guest/verify/trust"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Product returns the SEV product info currently supported by Constellation's SNP attestation.
|
||||||
|
func Product() *spb.SevProduct {
|
||||||
|
// sevProduct is the product info of the SEV platform as reported through CPUID[EAX=1].
|
||||||
|
// It may become necessary in the future to differentiate among CSP vendors.
|
||||||
|
return &spb.SevProduct{Name: spb.SevProduct_SEV_PRODUCT_MILAN, Stepping: 0} // Milan-B0
|
||||||
|
}
|
||||||
|
|
||||||
// InstanceInfo contains the necessary information to establish trust in a SNP CVM.
|
// InstanceInfo contains the necessary information to establish trust in a SNP CVM.
|
||||||
type InstanceInfo struct {
|
type InstanceInfo struct {
|
||||||
// ReportSigner is the PEM-encoded certificate used to validate the attestation report's signature.
|
// ReportSigner is the PEM-encoded certificate used to validate the attestation report's signature.
|
||||||
@ -99,14 +106,12 @@ func (a *InstanceInfo) AttestationWithCerts(getter trust.HTTPSGetter,
|
|||||||
return nil, fmt.Errorf("converting report to proto: %w", err)
|
return nil, fmt.Errorf("converting report to proto: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Product info as reported through CPUID[EAX=1]
|
productName := kds.ProductString(Product())
|
||||||
sevProduct := &spb.SevProduct{Name: spb.SevProduct_SEV_PRODUCT_MILAN, Stepping: 0} // Milan-B0
|
|
||||||
productName := kds.ProductString(sevProduct)
|
|
||||||
|
|
||||||
att := &spb.Attestation{
|
att := &spb.Attestation{
|
||||||
Report: report,
|
Report: report,
|
||||||
CertificateChain: &spb.CertificateChain{},
|
CertificateChain: &spb.CertificateChain{},
|
||||||
Product: sevProduct,
|
Product: Product(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add VCEK/VLEK to attestation object.
|
// Add VCEK/VLEK to attestation object.
|
||||||
|
@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
@ -67,6 +68,11 @@ func unmarshalTypedConfig[T AttestationCfg](data []byte) (AttestationCfg, error)
|
|||||||
// Certificate is a wrapper around x509.Certificate allowing custom marshaling.
|
// Certificate is a wrapper around x509.Certificate allowing custom marshaling.
|
||||||
type Certificate x509.Certificate
|
type Certificate x509.Certificate
|
||||||
|
|
||||||
|
// Equal returns true if the certificates are equal.
|
||||||
|
func (c Certificate) Equal(other Certificate) bool {
|
||||||
|
return bytes.Equal(c.Raw, other.Raw)
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalJSON marshals the certificate to PEM.
|
// MarshalJSON marshals the certificate to PEM.
|
||||||
func (c Certificate) MarshalJSON() ([]byte, error) {
|
func (c Certificate) MarshalJSON() ([]byte, error) {
|
||||||
if len(c.Raw) == 0 {
|
if len(c.Raw) == 0 {
|
||||||
|
@ -1052,7 +1052,7 @@ type AWSSEVSNP struct {
|
|||||||
AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"`
|
AMDRootKey Certificate `json:"amdRootKey" yaml:"amdRootKey"`
|
||||||
// description: |
|
// description: |
|
||||||
// AMD Signing Key certificate used to verify the SEV-SNP VCEK / VLEK certificate.
|
// AMD Signing Key certificate used to verify the SEV-SNP VCEK / VLEK certificate.
|
||||||
AMDSigningKey Certificate `json:"amdSigningKey,omitempty" yaml:"amdSigningKey,omitempty" validate:"len=0"`
|
AMDSigningKey Certificate `json:"amdSigningKey,omitempty" yaml:"amdSigningKey,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AWSNitroTPM is the configuration for AWS Nitro TPM attestation.
|
// AWSNitroTPM is the configuration for AWS Nitro TPM attestation.
|
||||||
|
@ -3,19 +3,16 @@ load("//bazel/go:go_test.bzl", "go_test")
|
|||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "verify",
|
name = "verify",
|
||||||
srcs = [
|
srcs = ["verify.go"],
|
||||||
"certchain.go",
|
|
||||||
"verify.go",
|
|
||||||
],
|
|
||||||
importpath = "github.com/edgelesssys/constellation/v2/internal/verify",
|
importpath = "github.com/edgelesssys/constellation/v2/internal/verify",
|
||||||
visibility = ["//:__subpackages__"],
|
visibility = ["//:__subpackages__"],
|
||||||
deps = [
|
deps = [
|
||||||
"//internal/attestation/snp",
|
"//internal/attestation/snp",
|
||||||
"//internal/constants",
|
"//internal/config",
|
||||||
"//internal/kubernetes/kubectl",
|
|
||||||
"@com_github_golang_jwt_jwt_v5//:jwt",
|
"@com_github_golang_jwt_jwt_v5//:jwt",
|
||||||
"@com_github_google_go_sev_guest//abi",
|
"@com_github_google_go_sev_guest//abi",
|
||||||
"@com_github_google_go_sev_guest//kds",
|
"@com_github_google_go_sev_guest//kds",
|
||||||
|
"@com_github_google_go_sev_guest//verify/trust",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
package verify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getCertChainCache(ctx context.Context, kubectl *kubectl.Kubectl, log debugLog) ([]byte, error) {
|
|
||||||
log.Debugf("Retrieving certificate chain from cache")
|
|
||||||
cm, err := kubectl.GetConfigMap(ctx, constants.ConstellationNamespace, constants.SevSnpCertCacheConfigMapName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("getting certificate chain cache configmap: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var result []byte
|
|
||||||
ask, ok := cm.Data[constants.CertCacheAskKey]
|
|
||||||
if ok {
|
|
||||||
result = append(result, ask...)
|
|
||||||
}
|
|
||||||
ark, ok := cm.Data[constants.CertCacheArkKey]
|
|
||||||
if ok {
|
|
||||||
result = append(result, ark...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
@ -26,17 +26,23 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/snp"
|
"github.com/edgelesssys/constellation/v2/internal/attestation/snp"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/google/go-sev-guest/abi"
|
"github.com/google/go-sev-guest/abi"
|
||||||
"github.com/google/go-sev-guest/kds"
|
"github.com/google/go-sev-guest/kds"
|
||||||
|
"github.com/google/go-sev-guest/verify/trust"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
vcekCert = "VCEK certificate"
|
||||||
|
vlekCert = "VLEK certificate"
|
||||||
|
certificateChain = "certificate chain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Report contains the entire data reported by constellation verify.
|
// Report contains the entire data reported by constellation verify.
|
||||||
type Report struct {
|
type Report struct {
|
||||||
SNPReport SNPReport `json:"snp_report"`
|
SNPReport SNPReport `json:"snp_report"`
|
||||||
ReportSigner []Certificate `json:"vcek"`
|
ReportSigner []Certificate `json:"report_signer"`
|
||||||
CertChain []Certificate `json:"cert_chain"`
|
CertChain []Certificate `json:"cert_chain"`
|
||||||
*AzureReportAddition `json:"azure,omitempty"`
|
*AzureReportAddition `json:"azure,omitempty"`
|
||||||
*AWSReportAddition `json:"aws,omitempty"`
|
*AWSReportAddition `json:"aws,omitempty"`
|
||||||
@ -51,7 +57,7 @@ type AzureReportAddition struct {
|
|||||||
type AWSReportAddition struct{}
|
type AWSReportAddition struct{}
|
||||||
|
|
||||||
// NewReport transforms a snp.InstanceInfo object into a Report.
|
// NewReport transforms a snp.InstanceInfo object into a Report.
|
||||||
func NewReport(ctx context.Context, instanceInfo snp.InstanceInfo, attestationServiceURL string, log debugLog) (Report, error) {
|
func NewReport(ctx context.Context, instanceInfo snp.InstanceInfo, attestationCfg config.AttestationCfg, log debugLog) (Report, error) {
|
||||||
snpReport, err := newSNPReport(instanceInfo.AttestationReport)
|
snpReport, err := newSNPReport(instanceInfo.AttestationReport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Report{}, fmt.Errorf("parsing SNP report: %w", err)
|
return Report{}, fmt.Errorf("parsing SNP report: %w", err)
|
||||||
@ -60,34 +66,28 @@ func NewReport(ctx context.Context, instanceInfo snp.InstanceInfo, attestationSe
|
|||||||
var certTypeName string
|
var certTypeName string
|
||||||
switch snpReport.SignerInfo.SigningKey {
|
switch snpReport.SignerInfo.SigningKey {
|
||||||
case abi.VlekReportSigner.String():
|
case abi.VlekReportSigner.String():
|
||||||
certTypeName = "VLEK certificate"
|
certTypeName = vlekCert
|
||||||
case abi.VcekReportSigner.String():
|
case abi.VcekReportSigner.String():
|
||||||
certTypeName = "VCEK certificate"
|
certTypeName = vcekCert
|
||||||
default:
|
default:
|
||||||
return Report{}, errors.New("unknown report signer")
|
return Report{}, errors.New("unknown report signer")
|
||||||
}
|
}
|
||||||
|
|
||||||
reportSigner, err := newCertificates(certTypeName, instanceInfo.ReportSigner, log)
|
reportSigner, err := newCertificates(certTypeName, instanceInfo.ReportSigner, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Report{}, fmt.Errorf("parsing VCEK certificate: %w", err)
|
return Report{}, fmt.Errorf("parsing %s: %w", certTypeName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if issuer included certChain before parsing. If not included, manually collect from the cluster.
|
// check if issuer included certChain before parsing. If not included, manually collect from the cluster.
|
||||||
var pemCerts []byte
|
rawCerts := instanceInfo.CertChain
|
||||||
if instanceInfo.CertChain == nil {
|
if certTypeName == vlekCert {
|
||||||
client, err := kubectl.NewFromConfig(constants.AdminConfFilename)
|
rawCerts, err = getCertChain(attestationCfg)
|
||||||
if err != nil {
|
|
||||||
return Report{}, fmt.Errorf("creating kubectl client: %w", err)
|
|
||||||
}
|
|
||||||
pemCerts, err = getCertChainCache(ctx, client, log)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Report{}, fmt.Errorf("getting certificate chain cache: %w", err)
|
return Report{}, fmt.Errorf("getting certificate chain cache: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
pemCerts = instanceInfo.CertChain
|
|
||||||
}
|
}
|
||||||
|
|
||||||
certChain, err := newCertificates("Certificate chain", pemCerts, log)
|
certChain, err := newCertificates(certificateChain, rawCerts, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Report{}, fmt.Errorf("parsing certificate chain: %w", err)
|
return Report{}, fmt.Errorf("parsing certificate chain: %w", err)
|
||||||
}
|
}
|
||||||
@ -95,7 +95,11 @@ func NewReport(ctx context.Context, instanceInfo snp.InstanceInfo, attestationSe
|
|||||||
var azure *AzureReportAddition
|
var azure *AzureReportAddition
|
||||||
var aws *AWSReportAddition
|
var aws *AWSReportAddition
|
||||||
if instanceInfo.Azure != nil {
|
if instanceInfo.Azure != nil {
|
||||||
maaToken, err := newMAAToken(ctx, instanceInfo.Azure.MAAToken, attestationServiceURL)
|
cfg, ok := attestationCfg.(*config.AzureSEVSNP)
|
||||||
|
if !ok {
|
||||||
|
return Report{}, fmt.Errorf("expected config type *config.AzureSEVSNP, got %T", attestationCfg)
|
||||||
|
}
|
||||||
|
maaToken, err := newMAAToken(ctx, instanceInfo.Azure.MAAToken, cfg.FirmwareSignerConfig.MAAURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Report{}, fmt.Errorf("parsing MAA token: %w", err)
|
return Report{}, fmt.Errorf("parsing MAA token: %w", err)
|
||||||
}
|
}
|
||||||
@ -113,6 +117,46 @@ func NewReport(ctx context.Context, instanceInfo snp.InstanceInfo, attestationSe
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inverse of newCertificates.
|
||||||
|
// ideally, duplicate encoding/decoding would be removed.
|
||||||
|
// AWS specific.
|
||||||
|
func getCertChain(cfg config.AttestationCfg) ([]byte, error) {
|
||||||
|
awsCfg, ok := cfg.(*config.AWSSEVSNP)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("expected config type *config.AWSSEVSNP, got %T", cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if awsCfg.AMDRootKey.Equal(config.Certificate{}) {
|
||||||
|
return nil, errors.New("no AMD root key configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
if awsCfg.AMDSigningKey.Equal(config.Certificate{}) {
|
||||||
|
certs, err := trust.GetProductChain(kds.ProductString(snp.Product()), abi.VlekReportSigner, trust.DefaultHTTPSGetter())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting product certificate chain: %w", err)
|
||||||
|
}
|
||||||
|
// we want an ASVK, but GetProductChain currently does not use the ASVK field.
|
||||||
|
if certs.Ask == nil {
|
||||||
|
return nil, errors.New("no ASVK certificate available")
|
||||||
|
}
|
||||||
|
awsCfg.AMDSigningKey = config.Certificate(*certs.Ask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ARK
|
||||||
|
certChain := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: awsCfg.AMDRootKey.Raw,
|
||||||
|
})
|
||||||
|
|
||||||
|
// append ASK
|
||||||
|
certChain = append(certChain, pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: awsCfg.AMDSigningKey.Raw,
|
||||||
|
})...)
|
||||||
|
|
||||||
|
return certChain, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FormatString builds a string representation of a report that is inteded for console output.
|
// FormatString builds a string representation of a report that is inteded for console output.
|
||||||
func (r *Report) FormatString(b *strings.Builder) (string, error) {
|
func (r *Report) FormatString(b *strings.Builder) (string, error) {
|
||||||
if len(r.ReportSigner) != 1 {
|
if len(r.ReportSigner) != 1 {
|
||||||
@ -163,7 +207,8 @@ type Certificate struct {
|
|||||||
CertTypeName string `json:"cert_type_name"`
|
CertTypeName string `json:"cert_type_name"`
|
||||||
StructVersion uint8 `json:"struct_version"`
|
StructVersion uint8 `json:"struct_version"`
|
||||||
ProductName string `json:"product_name"`
|
ProductName string `json:"product_name"`
|
||||||
HardwareID []byte `json:"hardware_id"`
|
HardwareID []byte `json:"hardware_id,omitempty"`
|
||||||
|
CspID string `json:"csp_id,omitempty"`
|
||||||
TCBVersion TCBVersion `json:"tcb_version"`
|
TCBVersion TCBVersion `json:"tcb_version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,39 +226,47 @@ func newCertificates(certTypeName string, cert []byte, log debugLog) (certs []Ce
|
|||||||
return certs, fmt.Errorf("parse %s: expected PEM block type 'CERTIFICATE', got '%s'", certTypeName, block.Type)
|
return certs, fmt.Errorf("parse %s: expected PEM block type 'CERTIFICATE', got '%s'", certTypeName, block.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, err := x509.ParseCertificate(block.Bytes)
|
certX509, err := x509.ParseCertificate(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return certs, fmt.Errorf("parse %s: %w", certTypeName, err)
|
return certs, fmt.Errorf("parse %s: %w", certTypeName, err)
|
||||||
}
|
}
|
||||||
if certTypeName == "VCEK certificate" {
|
|
||||||
vcekExts, err := kds.VcekCertificateExtensions(cert)
|
var ext *kds.Extensions
|
||||||
|
switch certTypeName {
|
||||||
|
case vcekCert:
|
||||||
|
ext, err = kds.VcekCertificateExtensions(certX509)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return certs, fmt.Errorf("parsing VCEK certificate extensions: %w", err)
|
return certs, fmt.Errorf("parsing %s extensions: %w", certTypeName, err)
|
||||||
|
}
|
||||||
|
case vlekCert:
|
||||||
|
ext, err = kds.VlekCertificateExtensions(certX509)
|
||||||
|
if err != nil {
|
||||||
|
return certs, fmt.Errorf("parsing %s extensions: %w", certTypeName, err)
|
||||||
}
|
}
|
||||||
certPEM := pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: cert.Raw,
|
|
||||||
})
|
|
||||||
certs = append(certs, Certificate{
|
|
||||||
Certificate: *cert,
|
|
||||||
CertificatePEM: string(certPEM),
|
|
||||||
CertTypeName: certTypeName,
|
|
||||||
StructVersion: vcekExts.StructVersion,
|
|
||||||
ProductName: vcekExts.ProductName,
|
|
||||||
TCBVersion: newTCBVersion(vcekExts.TCBVersion),
|
|
||||||
HardwareID: vcekExts.HWID,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
certPEM := pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: cert.Raw,
|
|
||||||
})
|
|
||||||
certs = append(certs, Certificate{
|
|
||||||
Certificate: *cert,
|
|
||||||
CertificatePEM: string(certPEM),
|
|
||||||
CertTypeName: certTypeName,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
certPEM := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: certX509.Raw,
|
||||||
|
})
|
||||||
|
cert := Certificate{
|
||||||
|
Certificate: *certX509,
|
||||||
|
CertificatePEM: string(certPEM),
|
||||||
|
CertTypeName: certTypeName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ext != nil {
|
||||||
|
cert.StructVersion = ext.StructVersion
|
||||||
|
cert.ProductName = ext.ProductName
|
||||||
|
cert.TCBVersion = newTCBVersion(ext.TCBVersion)
|
||||||
|
if ext.HWID != nil {
|
||||||
|
cert.HardwareID = ext.HWID
|
||||||
|
} else {
|
||||||
|
cert.CspID = ext.CspID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
certs = append(certs, cert)
|
||||||
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
@ -236,12 +289,12 @@ func (c *Certificate) formatString(b *strings.Builder, idx int) error {
|
|||||||
writeIndentfln(b, 2, "Signature Algorithm: %s", c.Certificate.SignatureAlgorithm)
|
writeIndentfln(b, 2, "Signature Algorithm: %s", c.Certificate.SignatureAlgorithm)
|
||||||
writeIndentfln(b, 2, "Public Key Algorithm: %s", c.Certificate.PublicKeyAlgorithm)
|
writeIndentfln(b, 2, "Public Key Algorithm: %s", c.Certificate.PublicKeyAlgorithm)
|
||||||
|
|
||||||
if c.CertTypeName == "VCEK certificate" {
|
if c.CertTypeName == vcekCert {
|
||||||
// Extensions documented in Table 8 and Table 9 of
|
// Extensions documented in Table 8 and Table 9 of
|
||||||
// https://www.amd.com/system/files/TechDocs/57230.pdf
|
// https://www.amd.com/system/files/TechDocs/57230.pdf
|
||||||
vcekExts, err := kds.VcekCertificateExtensions(&c.Certificate)
|
vcekExts, err := kds.VcekCertificateExtensions(&c.Certificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("parsing VCEK certificate extensions: %w", err)
|
return fmt.Errorf("parsing %s extensions: %w", c.CertTypeName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeIndentfln(b, 2, "Struct version: %d", vcekExts.StructVersion)
|
writeIndentfln(b, 2, "Struct version: %d", vcekExts.StructVersion)
|
||||||
|
Loading…
Reference in New Issue
Block a user