Skip to content

Commit 9f82f8d

Browse files
committed
Make sure SNP ARK is always loaded from config, or fetched from AMD KDS
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
1 parent 049a2af commit 9f82f8d

File tree

6 files changed

+68
-43
lines changed

6 files changed

+68
-43
lines changed

internal/attestation/aws/snp/validator.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,11 @@ func (a *awsValidator) validate(attestation vtpm.AttestationDocument, ask *x509.
191191
func getVerifyOpts(att *sevsnp.Attestation) (*verify.Options, error) {
192192
ask, err := x509.ParseCertificate(att.CertificateChain.AskCert)
193193
if err != nil {
194-
return &verify.Options{}, fmt.Errorf("parsing VLEK certificate: %w", err)
194+
return nil, fmt.Errorf("parsing ASK certificate: %w", err)
195195
}
196196
ark, err := x509.ParseCertificate(att.CertificateChain.ArkCert)
197197
if err != nil {
198-
return &verify.Options{}, fmt.Errorf("parsing VLEK certificate: %w", err)
198+
return nil, fmt.Errorf("parsing ARK certificate: %w", err)
199199
}
200200

201201
verifyOpts := &verify.Options{

internal/attestation/azure/snp/validator.go

+30-16
Original file line numberDiff line numberDiff line change
@@ -116,25 +116,11 @@ func (v *Validator) getTrustedKey(ctx context.Context, attDoc vtpm.AttestationDo
116116
return nil, fmt.Errorf("parsing attestation report: %w", err)
117117
}
118118

119-
// ASK, as cached in joinservice or reported from THIM / KDS.
120-
ask, err := x509.ParseCertificate(att.CertificateChain.AskCert)
119+
verifyOpts, err := getVerifyOpts(att)
121120
if err != nil {
122-
return nil, fmt.Errorf("parsing ASK certificate: %w", err)
121+
return nil, fmt.Errorf("getting verify options: %w", err)
123122
}
124123

125-
verifyOpts := &verify.Options{
126-
TrustedRoots: map[string][]*trust.AMDRootCerts{
127-
"Milan": {
128-
{
129-
Product: "Milan",
130-
ProductCerts: &trust.ProductCerts{
131-
Ask: ask,
132-
Ark: trustedArk,
133-
},
134-
},
135-
},
136-
},
137-
}
138124
if err := v.attestationVerifier.SNPAttestation(att, verifyOpts); err != nil {
139125
return nil, fmt.Errorf("verifying SNP attestation: %w", err)
140126
}
@@ -252,3 +238,31 @@ type maaValidator interface {
252238
type hclAkValidator interface {
253239
Validate(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error
254240
}
241+
242+
func getVerifyOpts(att *spb.Attestation) (*verify.Options, error) {
243+
// ASK, as cached in joinservice or reported from THIM / KDS.
244+
ask, err := x509.ParseCertificate(att.CertificateChain.AskCert)
245+
if err != nil {
246+
return nil, fmt.Errorf("parsing ASK certificate: %w", err)
247+
}
248+
ark, err := x509.ParseCertificate(att.CertificateChain.ArkCert)
249+
if err != nil {
250+
return nil, fmt.Errorf("parsing ARK certificate: %w", err)
251+
}
252+
253+
verifyOpts := &verify.Options{
254+
TrustedRoots: map[string][]*trust.AMDRootCerts{
255+
"Milan": {
256+
{
257+
Product: "Milan",
258+
ProductCerts: &trust.ProductCerts{
259+
Ask: ask,
260+
Ark: ark,
261+
},
262+
},
263+
},
264+
},
265+
}
266+
267+
return verifyOpts, nil
268+
}

internal/attestation/gcp/snp/issuer.go

+26-11
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) {
5858
// getInstanceInfo generates an extended SNP report, i.e. the report and any loaded certificates.
5959
// Report generation is triggered by sending ioctl syscalls to the SNP guest device, the AMD PSP generates the report.
6060
// The returned bytes will be written into the attestation document.
61-
func getInstanceInfo(_ context.Context, tpm io.ReadWriteCloser, extraData []byte) ([]byte, error) {
61+
func getInstanceInfo(_ context.Context, _ io.ReadWriteCloser, extraData []byte) ([]byte, error) {
6262
if len(extraData) > 64 {
6363
return nil, fmt.Errorf("extra data too long: %d, should be 64 bytes at most", len(extraData))
6464
}
@@ -76,7 +76,7 @@ func getInstanceInfo(_ context.Context, tpm io.ReadWriteCloser, extraData []byte
7676
return nil, fmt.Errorf("getting extended report: %w", err)
7777
}
7878

79-
vcek, err := pemEncodedVCEK(certs)
79+
vcek, certChain, err := pemEncodedVCEK(certs)
8080
if err != nil {
8181
return nil, fmt.Errorf("parsing vcek: %w", err)
8282
}
@@ -89,6 +89,7 @@ func getInstanceInfo(_ context.Context, tpm io.ReadWriteCloser, extraData []byte
8989
raw, err := json.Marshal(snp.InstanceInfo{
9090
AttestationReport: report,
9191
ReportSigner: vcek,
92+
CertChain: certChain,
9293
GCP: gceInstanceInfo,
9394
})
9495
if err != nil {
@@ -124,30 +125,44 @@ func gceInstanceInfo() (*attest.GCEInstanceInfo, error) {
124125
}, nil
125126
}
126127

127-
// pemEncodedVCEK takes a marshalled SNP certificate table and returns the PEM-encoded VCEK certificate.
128+
// preFetchCerts takes a marshalled SNP certificate table and returns the PEM-encoded VCEK certificate and,
129+
// if present, the ASK of the SNP certificate chain.
128130
// AMD documentation on certificate tables can be found in section 4.1.8.1, revision 2.03 "SEV-ES Guest-Hypervisor Communication Block Standardization".
129131
// https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56421.pdf
130-
func pemEncodedVCEK(certs []byte) ([]byte, error) {
132+
func pemEncodedVCEK(certs []byte) (vcekPEM []byte, certChain []byte, err error) {
131133
certTable := abi.CertTable{}
132134
if err := certTable.Unmarshal(certs); err != nil {
133-
return nil, fmt.Errorf("unmarshalling SNP certificate table: %w", err)
135+
return nil, nil, fmt.Errorf("unmarshalling SNP certificate table: %w", err)
134136
}
135137

136138
vcekRaw, err := certTable.GetByGUIDString(abi.VcekGUID)
137139
if err != nil {
138-
return nil, fmt.Errorf("getting VCEK certificate: %w", err)
140+
return nil, nil, fmt.Errorf("getting VCEK certificate: %w", err)
139141
}
140142

141143
// An optional check for certificate well-formedness. vcekRaw == cert.Raw.
142-
cert, err := x509.ParseCertificate(vcekRaw)
144+
vcek, err := x509.ParseCertificate(vcekRaw)
143145
if err != nil {
144-
return nil, fmt.Errorf("parsing certificate: %w", err)
146+
return nil, nil, fmt.Errorf("parsing certificate: %w", err)
145147
}
146148

147-
certPEM := pem.EncodeToMemory(&pem.Block{
149+
vcekPEM = pem.EncodeToMemory(&pem.Block{
148150
Type: "CERTIFICATE",
149-
Bytes: cert.Raw,
151+
Bytes: vcek.Raw,
150152
})
151153

152-
return certPEM, nil
154+
var askPEM []byte
155+
if askRaw, err := certTable.GetByGUIDString(abi.AskGUID); err == nil {
156+
ask, err := x509.ParseCertificate(askRaw)
157+
if err != nil {
158+
return nil, nil, fmt.Errorf("parsing ASK certificate: %w", err)
159+
}
160+
161+
askPEM = pem.EncodeToMemory(&pem.Block{
162+
Type: "CERTIFICATE",
163+
Bytes: ask.Raw,
164+
})
165+
}
166+
167+
return vcekPEM, askPEM, nil
153168
}

internal/attestation/gcp/snp/validator.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,11 @@ func (a *gcpValidator) validate(attestation vtpm.AttestationDocument, ask *x509.
205205
func getVerifyOpts(att *sevsnp.Attestation) (*verify.Options, error) {
206206
ask, err := x509.ParseCertificate(att.CertificateChain.AskCert)
207207
if err != nil {
208-
return &verify.Options{}, fmt.Errorf("parsing ASK certificate: %w", err)
208+
return nil, fmt.Errorf("parsing ASK certificate: %w", err)
209209
}
210210
ark, err := x509.ParseCertificate(att.CertificateChain.ArkCert)
211211
if err != nil {
212-
return &verify.Options{}, fmt.Errorf("parsing ARK certificate: %w", err)
212+
return nil, fmt.Errorf("parsing ARK certificate: %w", err)
213213
}
214214

215215
verifyOpts := &verify.Options{

internal/attestation/snp/BUILD.bazel

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ go_library(
88
visibility = ["//:__subpackages__"],
99
deps = [
1010
"//internal/attestation",
11-
"//internal/constants",
1211
"@com_github_google_go_sev_guest//abi",
1312
"@com_github_google_go_sev_guest//kds",
1413
"@com_github_google_go_sev_guest//proto/sevsnp",

internal/attestation/snp/snp.go

+8-11
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"fmt"
1616

1717
"github.com/edgelesssys/constellation/v2/internal/attestation"
18-
"github.com/edgelesssys/constellation/v2/internal/constants"
1918
"github.com/google/go-sev-guest/abi"
2019
"github.com/google/go-sev-guest/kds"
2120
spb "github.com/google/go-sev-guest/proto/sevsnp"
@@ -97,7 +96,7 @@ func (a *InstanceInfo) addReportSigner(att *spb.Attestation, report *spb.Report,
9796

9897
// AttestationWithCerts returns a formatted version of the attestation report and its certificates from the instanceInfo.
9998
// Certificates are retrieved in the following precedence:
100-
// 1. ASK or ARK from issuer. On Azure: THIM. One AWS: not prefilled.
99+
// 1. ASK from issuer. On Azure: THIM. One AWS: not prefilled. On GCP: prefilled.
101100
// 2. ASK or ARK from fallbackCerts.
102101
// 3. ASK or ARK from AMD KDS.
103102
func (a *InstanceInfo) AttestationWithCerts(getter trust.HTTPSGetter,
@@ -122,30 +121,28 @@ func (a *InstanceInfo) AttestationWithCerts(getter trust.HTTPSGetter,
122121
return nil, fmt.Errorf("adding report signer: %w", err)
123122
}
124123

125-
// If the certificate chain from THIM is present, parse it and format it.
126-
ask, ark, err := a.ParseCertChain()
124+
// If a certificate chain was pre-fetched by the Issuer, parse it and format it.
125+
// Make sure to only use the ask, since using an ark from the Issuer would invalidate security guarantees.
126+
ask, _, err := a.ParseCertChain()
127127
if err != nil {
128128
logger.Warn(fmt.Sprintf("Error parsing certificate chain: %v", err))
129129
}
130130
if ask != nil {
131-
logger.Info("Using ASK certificate from Azure THIM")
131+
logger.Info("Using ASK certificate from pre-fetched certificate chain")
132132
att.CertificateChain.AskCert = ask.Raw
133133
}
134-
if ark != nil {
135-
logger.Info("Using ARK certificate from Azure THIM")
136-
att.CertificateChain.ArkCert = ark.Raw
137-
}
138134

139135
// If a cached ASK or an ARK from the Constellation config is present, use it.
140136
if att.CertificateChain.AskCert == nil && fallbackCerts.ask != nil {
141137
logger.Info("Using cached ASK certificate")
142138
att.CertificateChain.AskCert = fallbackCerts.ask.Raw
143139
}
144140
if att.CertificateChain.ArkCert == nil && fallbackCerts.ark != nil {
145-
logger.Info(fmt.Sprintf("Using ARK certificate from %s", constants.ConfigFilename))
141+
logger.Info("Using cached ARK certificate")
146142
att.CertificateChain.ArkCert = fallbackCerts.ark.Raw
147143
}
148-
// Otherwise, retrieve it from AMD KDS.
144+
145+
// Otherwise, retrieve missing certificates from AMD KDS.
149146
if att.CertificateChain.AskCert == nil || att.CertificateChain.ArkCert == nil {
150147
logger.Info(fmt.Sprintf(
151148
"Certificate chain not fully present (ARK present: %t, ASK present: %t), falling back to retrieving it from AMD KDS",

0 commit comments

Comments
 (0)