@@ -28,7 +28,7 @@ use tss_esapi::{
2828 abstraction:: {
2929 ak,
3030 cipher:: Cipher ,
31- ek,
31+ ek, nv ,
3232 pcr:: { read_all, PcrData } ,
3333 DefaultKey ,
3434 } ,
@@ -37,6 +37,7 @@ use tss_esapi::{
3737 } ,
3838 constants:: {
3939 response_code:: Tss2ResponseCodeKind , session_type:: SessionType ,
40+ CapabilityType ,
4041 } ,
4142 handles:: {
4243 AuthHandle , KeyHandle , ObjectHandle , PcrHandle , PersistentTpmHandle ,
@@ -46,14 +47,14 @@ use tss_esapi::{
4647 algorithm:: { AsymmetricAlgorithm , HashingAlgorithm , PublicAlgorithm } ,
4748 ecc:: EccCurve ,
4849 key_bits:: RsaKeyBits ,
49- resource_handles:: Hierarchy ,
50+ resource_handles:: { Hierarchy , NvAuth } ,
5051 session_handles:: AuthSession ,
5152 structure_tags:: AttestationType ,
5253 } ,
5354 structures:: {
54- Attest , AttestInfo , Auth , Data , Digest , DigestValues , EccParameter ,
55- EccPoint , EccScheme , EncryptedSecret , HashScheme , IdObject ,
56- KeyDerivationFunctionScheme , Name , PcrSelectionList ,
55+ Attest , AttestInfo , Auth , CapabilityData , Data , Digest , DigestValues ,
56+ EccParameter , EccPoint , EccScheme , EncryptedSecret , HashScheme ,
57+ IdObject , KeyDerivationFunctionScheme , Name , PcrSelectionList ,
5758 PcrSelectionListBuilder , PcrSlot , Private as TssPrivate ,
5859 Public as TssPublic , PublicBuilder , PublicEccParametersBuilder ,
5960 PublicKeyRsa , PublicRsaParametersBuilder , RsaExponent , RsaScheme ,
@@ -116,6 +117,9 @@ const IAK_AUTH_POLICY_SHA256: [u8; 32] = [
116117] ;
117118const UNIQUE_IAK : [ u8 ; 3 ] = [ 0x49 , 0x41 , 0x4b ] ;
118119
120+ const RSA_EK_CERTIFICATE_CHAIN_START : u32 = 0x01c00100 ;
121+ const RSA_EK_CERTIFICATE_CHAIN_END : u32 = 0x01c001ff ;
122+
119123/// TpmError wraps all possible errors raised in tpm.rs
120124#[ derive( Error , Debug ) ]
121125pub enum TpmError {
@@ -444,6 +448,39 @@ pub struct EKResult {
444448 pub key_handle : KeyHandle ,
445449 pub ek_cert : Option < Vec < u8 > > ,
446450 pub public : TssPublic ,
451+ pub ek_chain : Option < Vec < u8 > > ,
452+ }
453+
454+ impl EKResult {
455+ pub fn to_pem ( & self ) -> Option < String > {
456+ let mut ca_chain: Vec < Vec < u8 > > = Vec :: new ( ) ;
457+
458+ match & self . ek_chain {
459+ Some ( chain) => {
460+ ca_chain. extend ( split_der_certificates ( chain) ) ;
461+ }
462+ None => {
463+ debug ! ( "* No EK certificate chain" ) ;
464+ }
465+ }
466+
467+ match & self . ek_cert {
468+ Some ( cert) => {
469+ ca_chain. push ( cert. clone ( ) ) ;
470+ }
471+ None => {
472+ debug ! ( "* No EK certificate" ) ;
473+ }
474+ }
475+
476+ match der_to_pem ( ca_chain) {
477+ Ok ( pem) => Some ( pem) ,
478+ Err ( err) => {
479+ error ! ( "Failed to transform certificate chain to PEM format, due to {err:?}" ) ;
480+ None
481+ }
482+ }
483+ }
447484}
448485
449486/// Holds the output of create_ak.
@@ -612,10 +649,25 @@ impl Context<'_> {
612649 let ( tpm_pub, _, _) = ctx
613650 . read_public ( key_handle)
614651 . map_err ( |source| TpmError :: TSSReadPublicError { source } ) ?;
652+
653+ let chain = match read_ek_ca_chain ( & mut ctx) {
654+ Ok ( der_data) => {
655+ if !der_data. is_empty ( ) {
656+ info ! ( "Found EK certificate chain in TPM NVRAM" )
657+ }
658+ Some ( der_data)
659+ }
660+ Err ( _) => {
661+ warn ! ( "Failed reading EK certificate chain from TPM NVRAM" ) ;
662+ None
663+ }
664+ } ;
665+
615666 Ok ( EKResult {
616667 key_handle,
617668 ek_cert : cert,
618669 public : tpm_pub,
670+ ek_chain : chain,
619671 } )
620672 }
621673
@@ -1947,6 +1999,116 @@ pub fn check_pubkey_match_cert(
19471999 }
19482000}
19492001
2002+ /// Find certificates (DER format) in binary data and split them
2003+ ///
2004+ /// # Arguments
2005+ ///
2006+ /// `der_data`: Binary data containing certificates in DER format
2007+ ///
2008+ /// # Returns
2009+ ///
2010+ /// 'Vec<Vec<u8>>', a vector ob certificates in DER format
2011+ pub fn split_der_certificates ( der_data : & [ u8 ] ) -> Vec < Vec < u8 > > {
2012+ let mut certificates = Vec :: new ( ) ;
2013+ let mut offset = 0 ;
2014+ while offset < der_data. len ( ) {
2015+ // Check if the current byte indicates the start of a sequence (0x30)
2016+ if der_data[ offset] != 0x30 {
2017+ break ; // Not a valid certificate start
2018+ }
2019+ // Read the length of the sequence
2020+ let length_byte = der_data[ offset + 1 ] ;
2021+ let cert_length = if length_byte & 0x80 == 0 {
2022+ // Short form length
2023+ length_byte as usize + 2 // +2 for the tag and length byte
2024+ } else {
2025+ // Long form length
2026+ let length_of_length = ( length_byte & 0x7F ) as usize ;
2027+ let length_bytes =
2028+ & der_data[ offset + 2 ..offset + 2 + length_of_length] ;
2029+ let cert_length = length_bytes
2030+ . iter ( )
2031+ . fold ( 0 , |acc, & b| ( acc << 8 ) | b as usize ) ;
2032+ cert_length + 2 + length_of_length // +2 for the tag and length byte
2033+ } ;
2034+ // Extract the certificate
2035+ let cert = der_data[ offset..offset + cert_length] . to_vec ( ) ;
2036+ certificates. push ( cert) ;
2037+ // Move the offset to the next certificate
2038+ offset += cert_length;
2039+ }
2040+ certificates
2041+ }
2042+
2043+ /// Convert a vector of der certificates into a single string with all certificates in PEM format.
2044+ ///
2045+ /// # Arguments
2046+ ///
2047+ /// `der_certificates`: Vector of certificates in DER format
2048+ ///
2049+ /// # Returns
2050+ ///
2051+ /// A `String` containing all concatenated certificates in PEM format (order is maintained)
2052+ pub fn der_to_pem (
2053+ der_certificates : Vec < Vec < u8 > > ,
2054+ ) -> std:: result:: Result < String , Box < dyn std:: error:: Error > > {
2055+ let mut pem_string = String :: new ( ) ;
2056+ for der in der_certificates. iter ( ) . rev ( ) {
2057+ // Convert DER to X509
2058+ let cert = X509 :: from_der ( der) ?;
2059+ // Convert X509 to PEM format
2060+ let pem = cert. to_pem ( ) ?;
2061+ // Append the PEM string to the result
2062+ pem_string. push_str ( & String :: from_utf8 ( pem) ?) ;
2063+ }
2064+ Ok ( pem_string)
2065+ }
2066+
2067+ /// Read certificate chain from TPM.
2068+ ///
2069+ /// Read content of NV Handle 0x01c00100 - 0x01c001ff
2070+ ///
2071+ /// # Returns
2072+ ///
2073+ /// `Vec<u8>', binary data of certificate chain
2074+ pub fn read_ek_ca_chain (
2075+ context : & mut tss_esapi:: Context ,
2076+ ) -> tss_esapi:: Result < Vec < u8 > > {
2077+ let mut result: Vec < u8 > = Vec :: new ( ) ;
2078+
2079+ // Get handles for NV-Index in range 0x01c00100 - 0x01c001ff
2080+ let ( capabilities, _) = context. get_capability (
2081+ CapabilityType :: Handles ,
2082+ RSA_EK_CERTIFICATE_CHAIN_START ,
2083+ RSA_EK_CERTIFICATE_CHAIN_END - RSA_EK_CERTIFICATE_CHAIN_START ,
2084+ ) ?;
2085+
2086+ if let CapabilityData :: Handles ( handle_list) = capabilities {
2087+ for handle in handle_list. iter ( ) {
2088+ if let TpmHandle :: NvIndex ( nv_idx) = handle {
2089+ // Attempt to get the NV authorization handle
2090+ let nv_auth_handle =
2091+ context. execute_without_session ( |ctx| {
2092+ ctx. tr_from_tpm_public ( * handle)
2093+ . map ( |v| NvAuth :: NvIndex ( v. into ( ) ) )
2094+ } ) ?;
2095+
2096+ // Read the full NV data
2097+ let data = context. execute_with_nullauth_session ( |ctx| {
2098+ nv:: read_full ( ctx, nv_auth_handle, * nv_idx)
2099+ } ) ?;
2100+
2101+ result. extend ( data) ;
2102+ } else {
2103+ // Handle other types of handles if necessary
2104+ break ; // Skip non-NvIndex handles
2105+ }
2106+ }
2107+ }
2108+
2109+ Ok ( result) // Return the accumulated result
2110+ }
2111+
19502112pub mod testing {
19512113 use super :: * ;
19522114 #[ cfg( feature = "testing" ) ]
0 commit comments