@@ -88,8 +88,9 @@ pub(crate) static CERT_CHAINS: LazyLock<HashMap<ProcessorGeneration, VendorCerti
8888 map
8989 } ) ;
9090
91- #[ derive( Default , Debug ) ]
92- pub struct Snp { }
91+ pub struct Snp {
92+ cache : Arc < dyn Cache + Send + Sync > ,
93+ }
9394
9495#[ derive( Clone , Debug ) ]
9596pub ( crate ) enum VendorEndorsementKey {
@@ -220,7 +221,7 @@ impl Verifier for Snp {
220221 // No certificate chain provided, so we need to request the VCEK from KDS
221222 _ => {
222223 // Get VCEK from KDS
223- let vcek_buf = fetch_vcek_from_kds ( report, proc_gen. clone ( ) )
224+ let vcek_buf = self . fetch_vcek_from_kds ( report, proc_gen. clone ( ) )
224225 . await
225226 . context ( "Failed to fetch VCEK from KDS" ) ?;
226227 let vcek = Certificate :: from_bytes ( & vcek_buf)
@@ -287,6 +288,100 @@ impl Verifier for Snp {
287288 }
288289}
289290
291+ impl Snp {
292+ pub fn new ( cache : Arc < dyn Cache + Send + Sync > ) -> Self {
293+ Self {
294+ cache,
295+ }
296+ }
297+
298+ /// Fetches VCEK in DER format from AMD KDS.
299+ /// If the VCEK is retrieved successfully, it is added to the cache,
300+ /// which is cheked prior to fetching the cert.
301+ /// If the VCEK cannot be fetched from the VCEK, this method
302+ /// does not retry.
303+ async fn fetch_vcek_from_kds (
304+ & self ,
305+ att_report : AttestationReport ,
306+ proc_gen : ProcessorGeneration ,
307+ ) -> Result < Vec < u8 > > {
308+ // Use attestation report to get data for URL
309+ let hw_id: String = if att_report. chip_id . as_slice ( ) != [ 0 ; 64 ] {
310+ match proc_gen {
311+ ProcessorGeneration :: Turin => {
312+ let shorter_bytes: & [ u8 ] = & att_report. chip_id [ 0 ..8 ] ;
313+ hex:: encode ( shorter_bytes)
314+ }
315+ _ => hex:: encode ( att_report. chip_id ) ,
316+ }
317+ } else {
318+ bail ! ( "Hardware ID is 0s on attestation report. Confirm that MASK_CHIP_ID is set to 0 to request from VCEK from KDS." ) ;
319+ } ;
320+
321+ // Request VCEK from KDS
322+ let vcek_url: String = match proc_gen {
323+ ProcessorGeneration :: Turin => {
324+ let fmc = if let Some ( fmc) = att_report. reported_tcb . fmc {
325+ fmc
326+ } else {
327+ bail ! ( "A Turin processor must have a fmc value" ) ;
328+ } ;
329+ format ! (
330+ "{KDS_CERT_SITE}{KDS_VCEK}/{}/\
331+ {hw_id}?fmcSPL={:02}&blSPL={:02}&teeSPL={:02}&snpSPL={:02}&ucodeSPL={:02}",
332+ proc_gen,
333+ fmc,
334+ att_report. reported_tcb. bootloader,
335+ att_report. reported_tcb. tee,
336+ att_report. reported_tcb. snp,
337+ att_report. reported_tcb. microcode
338+ )
339+ }
340+ _ => {
341+ format ! (
342+ "{KDS_CERT_SITE}{KDS_VCEK}/{}/\
343+ {hw_id}?blSPL={:02}&teeSPL={:02}&snpSPL={:02}&ucodeSPL={:02}",
344+ proc_gen,
345+ att_report. reported_tcb. bootloader,
346+ att_report. reported_tcb. tee,
347+ att_report. reported_tcb. snp,
348+ att_report. reported_tcb. microcode
349+ )
350+ }
351+ } ;
352+
353+ // Check cache for VCEK
354+ let vcek_cache_key = format ! ( "snp_verifier_{vcek_url}" ) ;
355+ if let Some ( cached_vcek_b64) = self . cache . get ( vcek_cache_key. clone ( ) ) . await {
356+ let vcek: Vec < u8 > = STANDARD . decode ( cached_vcek_b64) ?;
357+ return Ok ( vcek)
358+ }
359+
360+ // VCEK in DER format
361+ let vcek_rsp: ReqwestResponse = get ( vcek_url. clone ( ) )
362+ . await
363+ . context ( "Unable to send request for VCEK" ) ?;
364+
365+ match vcek_rsp. status ( ) {
366+ StatusCode :: OK => {
367+ let vcek_rsp_bytes: Vec < u8 > = vcek_rsp
368+ . bytes ( )
369+ . await
370+ . context ( "Unable to parse VCEK" ) ?
371+ . to_vec ( ) ;
372+
373+ // Add fetched VCEK to cache
374+ let vcek_b64 = STANDARD . encode ( & vcek_rsp_bytes) ;
375+ self . cache . set ( vcek_cache_key, vcek_b64) . await ?;
376+
377+ Ok ( vcek_rsp_bytes)
378+ }
379+
380+ status => bail ! ( "Unable to fetch VCEK from URL: {status:?}, {vcek_url:?}" ) ,
381+ }
382+ }
383+ }
384+
290385/// Retrieves the octet string value for a given OID from a certificate's extensions.
291386/// Supports both raw and DER-encoded formats.
292387pub ( crate ) fn get_oid_octets < const N : usize > (
@@ -421,75 +516,6 @@ pub(crate) fn get_common_name(cert: &x509::X509) -> Result<String> {
421516 Ok ( e. data ( ) . as_utf8 ( ) ?. to_string ( ) )
422517}
423518
424- /// Asynchronously fetches the VCEK from the Key Distribution Service (KDS) using the provided attestation report.
425- /// Returns the VCEK in DER format as part of a certificate table entry.
426- async fn fetch_vcek_from_kds (
427- att_report : AttestationReport ,
428- proc_gen : ProcessorGeneration ,
429- ) -> Result < Vec < u8 > > {
430- // Use attestation report to get data for URL
431- let hw_id: String = if att_report. chip_id . as_slice ( ) != [ 0 ; 64 ] {
432- match proc_gen {
433- ProcessorGeneration :: Turin => {
434- let shorter_bytes: & [ u8 ] = & att_report. chip_id [ 0 ..8 ] ;
435- hex:: encode ( shorter_bytes)
436- }
437- _ => hex:: encode ( att_report. chip_id ) ,
438- }
439- } else {
440- bail ! ( "Hardware ID is 0s on attestation report. Confirm that MASK_CHIP_ID is set to 0 to request from VCEK from KDS." ) ;
441- } ;
442-
443- // Request VCEK from KDS
444- let vcek_url: String = match proc_gen {
445- ProcessorGeneration :: Turin => {
446- let fmc = if let Some ( fmc) = att_report. reported_tcb . fmc {
447- fmc
448- } else {
449- bail ! ( "A Turin processor must have a fmc value" ) ;
450- } ;
451- format ! (
452- "{KDS_CERT_SITE}{KDS_VCEK}/{}/\
453- {hw_id}?fmcSPL={:02}&blSPL={:02}&teeSPL={:02}&snpSPL={:02}&ucodeSPL={:02}",
454- proc_gen,
455- fmc,
456- att_report. reported_tcb. bootloader,
457- att_report. reported_tcb. tee,
458- att_report. reported_tcb. snp,
459- att_report. reported_tcb. microcode
460- )
461- }
462- _ => {
463- format ! (
464- "{KDS_CERT_SITE}{KDS_VCEK}/{}/\
465- {hw_id}?blSPL={:02}&teeSPL={:02}&snpSPL={:02}&ucodeSPL={:02}",
466- proc_gen,
467- att_report. reported_tcb. bootloader,
468- att_report. reported_tcb. tee,
469- att_report. reported_tcb. snp,
470- att_report. reported_tcb. microcode
471- )
472- }
473- } ;
474- // VCEK in DER format
475- let vcek_rsp: ReqwestResponse = get ( vcek_url. clone ( ) )
476- . await
477- . context ( "Unable to send request for VCEK" ) ?;
478-
479- match vcek_rsp. status ( ) {
480- StatusCode :: OK => {
481- let vcek_rsp_bytes: Vec < u8 > = vcek_rsp
482- . bytes ( )
483- . await
484- . context ( "Unable to parse VCEK" ) ?
485- . to_vec ( ) ;
486- Ok ( vcek_rsp_bytes)
487- }
488-
489- status => bail ! ( "Unable to fetch VCEK from URL: {status:?}, {vcek_url:?}" ) ,
490- }
491- }
492-
493519/// Determines the processor model based on the family and model IDs from the attestation report.
494520fn get_processor_generation ( att_report : & AttestationReport ) -> Result < ProcessorGeneration > {
495521 let cpu_fam = att_report
0 commit comments