@@ -28,7 +28,7 @@ use tss_esapi::{
28
28
abstraction:: {
29
29
ak,
30
30
cipher:: Cipher ,
31
- ek,
31
+ ek, nv ,
32
32
pcr:: { read_all, PcrData } ,
33
33
DefaultKey ,
34
34
} ,
@@ -37,6 +37,7 @@ use tss_esapi::{
37
37
} ,
38
38
constants:: {
39
39
response_code:: Tss2ResponseCodeKind , session_type:: SessionType ,
40
+ CapabilityType ,
40
41
} ,
41
42
handles:: {
42
43
AuthHandle , KeyHandle , ObjectHandle , PcrHandle , PersistentTpmHandle ,
@@ -46,14 +47,14 @@ use tss_esapi::{
46
47
algorithm:: { AsymmetricAlgorithm , HashingAlgorithm , PublicAlgorithm } ,
47
48
ecc:: EccCurve ,
48
49
key_bits:: RsaKeyBits ,
49
- resource_handles:: Hierarchy ,
50
+ resource_handles:: { Hierarchy , NvAuth } ,
50
51
session_handles:: AuthSession ,
51
52
structure_tags:: AttestationType ,
52
53
} ,
53
54
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 ,
57
58
PcrSelectionListBuilder , PcrSlot , Private as TssPrivate ,
58
59
Public as TssPublic , PublicBuilder , PublicEccParametersBuilder ,
59
60
PublicKeyRsa , PublicRsaParametersBuilder , RsaExponent , RsaScheme ,
@@ -116,6 +117,9 @@ const IAK_AUTH_POLICY_SHA256: [u8; 32] = [
116
117
] ;
117
118
const UNIQUE_IAK : [ u8 ; 3 ] = [ 0x49 , 0x41 , 0x4b ] ;
118
119
120
+ const RSA_EK_CERTIFICATE_CHAIN_START : u32 = 0x01c00100 ;
121
+ const RSA_EK_CERTIFICATE_CHAIN_END : u32 = 0x01c001ff ;
122
+
119
123
/// TpmError wraps all possible errors raised in tpm.rs
120
124
#[ derive( Error , Debug ) ]
121
125
pub enum TpmError {
@@ -444,6 +448,39 @@ pub struct EKResult {
444
448
pub key_handle : KeyHandle ,
445
449
pub ek_cert : Option < Vec < u8 > > ,
446
450
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
+ }
447
484
}
448
485
449
486
/// Holds the output of create_ak.
@@ -612,10 +649,25 @@ impl Context<'_> {
612
649
let ( tpm_pub, _, _) = ctx
613
650
. read_public ( key_handle)
614
651
. 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
+
615
666
Ok ( EKResult {
616
667
key_handle,
617
668
ek_cert : cert,
618
669
public : tpm_pub,
670
+ ek_chain : chain,
619
671
} )
620
672
}
621
673
@@ -1947,6 +1999,116 @@ pub fn check_pubkey_match_cert(
1947
1999
}
1948
2000
}
1949
2001
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
+
1950
2112
pub mod testing {
1951
2113
use super :: * ;
1952
2114
#[ cfg( feature = "testing" ) ]
0 commit comments