@@ -42,16 +42,24 @@ var (
42
42
// expiration dates is at https://kdsintf.amd.com/vcek/v1/Milan/cert_chain
43
43
//go:embed ask_ark_milan.sevcert
44
44
askArkMilanBytes []byte
45
+
46
+ // A cache of product certificate KDS results per product.
47
+ prodCacheMu sync.Mutex
48
+ productCertCache map [string ]* ProductCerts
45
49
)
46
50
51
+ // ProductCerts contains the root key and signing key devoted to a given product line.
52
+ type ProductCerts struct {
53
+ Ask * x509.Certificate
54
+ Ark * x509.Certificate
55
+ }
56
+
47
57
// AMDRootCerts encapsulates the certificates that represent root of trust in AMD.
48
58
type AMDRootCerts struct {
49
59
// Product is the expected CPU product name, e.g., Milan, Turin, Genoa.
50
60
Product string
51
- // AskX509 is an X.509 certificate for the AMD SEV signing key (ASK)
52
- AskX509 * x509.Certificate
53
- // ArkX509 is an X.509 certificate for the AMD root key (ARK).
54
- ArkX509 * x509.Certificate
61
+ // ProductCerts contains the root key and signing key devoted to a given product line.
62
+ ProductCerts * ProductCerts
55
63
// AskSev is the AMD certificate representation of the AMD signing key that certifies
56
64
// versioned chip endoresement keys. If present, the information must match AskX509.
57
65
AskSev * abi.AskCert
@@ -71,6 +79,16 @@ type HTTPSGetter interface {
71
79
Get (url string ) ([]byte , error )
72
80
}
73
81
82
+ // AttestationRecreationErr represents a problem with fetching or interpreting associated
83
+ // certificates for a given attestation report. This is typically due to network unreliability.
84
+ type AttestationRecreationErr struct {
85
+ Msg string
86
+ }
87
+
88
+ func (e * AttestationRecreationErr ) Error () string {
89
+ return e .Msg
90
+ }
91
+
74
92
// SimpleHTTPSGetter implements the HTTPSGetter interface with http.Get.
75
93
type SimpleHTTPSGetter struct {}
76
94
@@ -149,26 +167,26 @@ func (r *AMDRootCerts) Unmarshal(data []byte) error {
149
167
return nil
150
168
}
151
169
152
- // FromDER populates the AMDRootCerts from DER-formatted certificates for both the ASK and the ARK.
153
- func (r * AMDRootCerts ) FromDER (ask []byte , ark []byte ) error {
170
+ // FromDER populates the ProductCerts from DER-formatted certificates for both the ASK and the ARK.
171
+ func (r * ProductCerts ) FromDER (ask []byte , ark []byte ) error {
154
172
askCert , err := x509 .ParseCertificate (ask )
155
173
if err != nil {
156
174
return fmt .Errorf ("could not parse ASK certificate: %v" , err )
157
175
}
158
- r .AskX509 = askCert
176
+ r .Ask = askCert
159
177
160
178
arkCert , err := x509 .ParseCertificate (ark )
161
179
if err != nil {
162
180
logger .Errorf ("could not parse ARK certificate: %v" , err )
163
181
}
164
- r .ArkX509 = arkCert
182
+ r .Ark = arkCert
165
183
return nil
166
184
}
167
185
168
186
// FromKDSCertBytes populates r's AskX509 and ArkX509 certificates from the two PEM-encoded
169
187
// certificates in data. This is the format the Key Distribution Service (KDS) uses, e.g.,
170
188
// https://kdsintf.amd.com/vcek/v1/Milan/cert_chain
171
- func (r * AMDRootCerts ) FromKDSCertBytes (data []byte ) error {
189
+ func (r * ProductCerts ) FromKDSCertBytes (data []byte ) error {
172
190
ask , ark , err := kds .ParseProductCertChain (data )
173
191
if err != nil {
174
192
return err
@@ -178,7 +196,7 @@ func (r *AMDRootCerts) FromKDSCertBytes(data []byte) error {
178
196
179
197
// FromKDSCert populates r's AskX509 and ArkX509 certificates from the certificate format AMD's Key
180
198
// Distribution Service (KDS) uses, e.g., https://kdsintf.amd.com/vcek/v1/Milan/cert_chain
181
- func (r * AMDRootCerts ) FromKDSCert (path string ) error {
199
+ func (r * ProductCerts ) FromKDSCert (path string ) error {
182
200
certBytes , err := os .ReadFile (path )
183
201
if err != nil {
184
202
return err
@@ -188,17 +206,97 @@ func (r *AMDRootCerts) FromKDSCert(path string) error {
188
206
189
207
// X509Options returns the ASK and ARK as the only intermediate and root certificates of an x509
190
208
// verification options object, or nil if either key's x509 certificate is not present in r.
191
- func (r * AMDRootCerts ) X509Options () * x509.VerifyOptions {
192
- if r .AskX509 == nil || r .ArkX509 == nil {
209
+ func (r * ProductCerts ) X509Options () * x509.VerifyOptions {
210
+ if r .Ask == nil || r .Ark == nil {
193
211
return nil
194
212
}
195
213
roots := x509 .NewCertPool ()
196
- roots .AddCert (r .ArkX509 )
214
+ roots .AddCert (r .Ark )
197
215
intermediates := x509 .NewCertPool ()
198
- intermediates .AddCert (r .AskX509 )
216
+ intermediates .AddCert (r .Ask )
199
217
return & x509.VerifyOptions {Roots : roots , Intermediates : intermediates }
200
218
}
201
219
220
+ // ClearProductCertCache clears the product certificate cache. This is useful for testing with
221
+ // multiple roots of trust.
222
+ func ClearProductCertCache () {
223
+ prodCacheMu .Lock ()
224
+ productCertCache = nil
225
+ prodCacheMu .Unlock ()
226
+ }
227
+
228
+ // GetProductChain returns the ASK and ARK certificates of the given product, either from getter
229
+ // or from a cache of the results from the last successful call.
230
+ func GetProductChain (product string , getter HTTPSGetter ) (* ProductCerts , error ) {
231
+ if productCertCache == nil {
232
+ prodCacheMu .Lock ()
233
+ productCertCache = make (map [string ]* ProductCerts )
234
+ prodCacheMu .Unlock ()
235
+ }
236
+ result , ok := productCertCache [product ]
237
+ if ! ok {
238
+ logger .Infof ("Getting product cert chain for %s" , product )
239
+ askark , err := getter .Get (kds .ProductCertChainURL (product ))
240
+ if err != nil {
241
+ return nil , & AttestationRecreationErr {
242
+ Msg : fmt .Sprintf ("could not download ASK and ARK certificates: %v" , err ),
243
+ }
244
+ }
245
+ logger .Infof ("Product chain in %s" , string (askark ))
246
+ ask , ark , err := kds .ParseProductCertChain (askark )
247
+ if err != nil {
248
+ // Treat a bad parse as a network error since it's likely due to an incomplete transfer.
249
+ return nil , & AttestationRecreationErr {Msg : fmt .Sprintf ("could not parse root cert_chain: %v" , err )}
250
+ }
251
+ askCert , err := x509 .ParseCertificate (ask )
252
+ if err != nil {
253
+ return nil , & AttestationRecreationErr {Msg : fmt .Sprintf ("could not parse ASK cert: %v" , err )}
254
+ }
255
+ arkCert , err := x509 .ParseCertificate (ark )
256
+ if err != nil {
257
+ return nil , & AttestationRecreationErr {Msg : fmt .Sprintf ("could not parse ARK cert: %v" , err )}
258
+ }
259
+ result = & ProductCerts {Ask : askCert , Ark : arkCert }
260
+ prodCacheMu .Lock ()
261
+ productCertCache [product ] = result
262
+ prodCacheMu .Unlock ()
263
+ }
264
+ return result , nil
265
+ }
266
+
267
+ // Forward all the ProductCerts operations from the AMDRootCerts struct to follow the
268
+ // Law of Demeter.
269
+
270
+ // FromDER populates the AMDRootCerts from DER-formatted certificates for both the ASK and the ARK.
271
+ func (r * AMDRootCerts ) FromDER (ask []byte , ark []byte ) error {
272
+ r .ProductCerts = & ProductCerts {}
273
+ return r .ProductCerts .FromDER (ask , ark )
274
+ }
275
+
276
+ // FromKDSCertBytes populates r's AskX509 and ArkX509 certificates from the two PEM-encoded
277
+ // certificates in data. This is the format the Key Distribution Service (KDS) uses, e.g.,
278
+ // https://kdsintf.amd.com/vcek/v1/Milan/cert_chain
279
+ func (r * AMDRootCerts ) FromKDSCertBytes (data []byte ) error {
280
+ r .ProductCerts = & ProductCerts {}
281
+ return r .ProductCerts .FromKDSCertBytes (data )
282
+ }
283
+
284
+ // FromKDSCert populates r's AskX509 and ArkX509 certificates from the certificate format AMD's Key
285
+ // Distribution Service (KDS) uses, e.g., https://kdsintf.amd.com/vcek/v1/Milan/cert_chain
286
+ func (r * AMDRootCerts ) FromKDSCert (path string ) error {
287
+ r .ProductCerts = & ProductCerts {}
288
+ return r .ProductCerts .FromKDSCert (path )
289
+ }
290
+
291
+ // X509Options returns the ASK and ARK as the only intermediate and root certificates of an x509
292
+ // verification options object, or nil if either key's x509 certificate is not present in r.
293
+ func (r * AMDRootCerts ) X509Options () * x509.VerifyOptions {
294
+ if r .ProductCerts == nil {
295
+ return nil
296
+ }
297
+ return r .ProductCerts .X509Options ()
298
+ }
299
+
202
300
// Parse ASK, ARK certificates from the embedded AMD certificate file.
203
301
func init () {
204
302
milanCerts := new (AMDRootCerts )
0 commit comments