@@ -31,12 +31,14 @@ use tss_esapi::{
31
31
ek,
32
32
pcr:: { read_all, PcrData } ,
33
33
DefaultKey ,
34
+ nv,
34
35
} ,
35
36
attributes:: {
36
37
object:: ObjectAttributesBuilder , session:: SessionAttributesBuilder ,
37
38
} ,
38
39
constants:: {
39
40
response_code:: Tss2ResponseCodeKind , session_type:: SessionType ,
41
+ CapabilityType
40
42
} ,
41
43
handles:: {
42
44
AuthHandle , KeyHandle , ObjectHandle , PcrHandle , PersistentTpmHandle ,
@@ -46,7 +48,10 @@ use tss_esapi::{
46
48
algorithm:: { AsymmetricAlgorithm , HashingAlgorithm , PublicAlgorithm } ,
47
49
ecc:: EccCurve ,
48
50
key_bits:: RsaKeyBits ,
49
- resource_handles:: Hierarchy ,
51
+ resource_handles:: {
52
+ Hierarchy ,
53
+ NvAuth ,
54
+ } ,
50
55
session_handles:: AuthSession ,
51
56
structure_tags:: AttestationType ,
52
57
} ,
@@ -58,7 +63,7 @@ use tss_esapi::{
58
63
Public as TssPublic , PublicBuilder , PublicEccParametersBuilder ,
59
64
PublicKeyRsa , PublicRsaParametersBuilder , RsaExponent , RsaScheme ,
60
65
Signature , SignatureScheme , SymmetricDefinitionObject , Ticket ,
61
- VerifiedTicket ,
66
+ VerifiedTicket , CapabilityData
62
67
} ,
63
68
tcti_ldr:: TctiNameConf ,
64
69
traits:: Marshall ,
@@ -116,6 +121,9 @@ const IAK_AUTH_POLICY_SHA256: [u8; 32] = [
116
121
] ;
117
122
const UNIQUE_IAK : [ u8 ; 3 ] = [ 0x49 , 0x41 , 0x4b ] ;
118
123
124
+ const RSA_EK_CERTIFICATE_CHAIN_START : u32 = 0x01c00100 ;
125
+ const RSA_EK_CERTIFICATE_CHAIN_END : u32 = 0x01c001ff ;
126
+
119
127
/// TpmError wraps all possible errors raised in tpm.rs
120
128
#[ derive( Error , Debug ) ]
121
129
pub enum TpmError {
@@ -444,6 +452,39 @@ pub struct EKResult {
444
452
pub key_handle : KeyHandle ,
445
453
pub ek_cert : Option < Vec < u8 > > ,
446
454
pub public : TssPublic ,
455
+ pub ek_chain : Option < Vec < u8 > > ,
456
+ }
457
+
458
+ impl EKResult {
459
+ pub fn to_pem ( & self ) -> Option < String > {
460
+ let mut ca_chain: Vec < Vec < u8 > > = Vec :: new ( ) ;
461
+
462
+ match & self . ek_chain {
463
+ Some ( chain) => {
464
+ ca_chain. extend ( split_der_certificates ( & chain) ) ;
465
+ }
466
+ None => {
467
+ debug ! ( "* No EK certificate chain" ) ;
468
+ }
469
+ }
470
+
471
+ match & self . ek_cert {
472
+ Some ( cert) => {
473
+ ca_chain. push ( cert. clone ( ) ) ;
474
+ }
475
+ None => {
476
+ debug ! ( "* No EK certificate" ) ;
477
+ }
478
+ }
479
+
480
+ match der_to_pem ( ca_chain) {
481
+ Ok ( pem) => Some ( pem) ,
482
+ Err ( err) => {
483
+ error ! ( "Failed to transform certificate chain to PEM format, due to {err:?}" ) ;
484
+ None
485
+ }
486
+ }
487
+ }
447
488
}
448
489
449
490
/// Holds the output of create_ak.
@@ -612,10 +653,25 @@ impl Context<'_> {
612
653
let ( tpm_pub, _, _) = ctx
613
654
. read_public ( key_handle)
614
655
. map_err ( |source| TpmError :: TSSReadPublicError { source } ) ?;
656
+
657
+ let chain = match read_ek_ca_chain ( & mut ctx) {
658
+ Ok ( der_data) => {
659
+ if ! der_data. is_empty ( ) {
660
+ info ! ( "Found EK certificate chain in TPM NVRAM" )
661
+ }
662
+ Some ( der_data)
663
+ } ,
664
+ Err ( _) => {
665
+ warn ! ( "Failed reading EK certificate chain from TPM NVRAM" ) ;
666
+ None
667
+ }
668
+ } ;
669
+
615
670
Ok ( EKResult {
616
671
key_handle,
617
672
ek_cert : cert,
618
673
public : tpm_pub,
674
+ ek_chain : chain,
619
675
} )
620
676
}
621
677
@@ -1947,6 +2003,113 @@ pub fn check_pubkey_match_cert(
1947
2003
}
1948
2004
}
1949
2005
2006
+ /// Find certificates (DER format) in binary data and split them
2007
+ ///
2008
+ /// # Arguments
2009
+ ///
2010
+ /// `der_data`: Binary data containing certificates in DER format
2011
+ ///
2012
+ /// # Returns
2013
+ ///
2014
+ /// 'Vec<Vec<u8>>', a vector ob certificates in DER format
2015
+ pub fn split_der_certificates (
2016
+ der_data : & [ u8 ]
2017
+ ) -> Vec < Vec < u8 > >
2018
+ {
2019
+ let mut certificates = Vec :: new ( ) ;
2020
+ let mut offset = 0 ;
2021
+ while offset < der_data. len ( ) {
2022
+ // Check if the current byte indicates the start of a sequence (0x30)
2023
+ if der_data[ offset] != 0x30 {
2024
+ break ; // Not a valid certificate start
2025
+ }
2026
+ // Read the length of the sequence
2027
+ let length_byte = der_data[ offset + 1 ] ;
2028
+ let cert_length = if length_byte & 0x80 == 0 {
2029
+ // Short form length
2030
+ length_byte as usize + 2 // +2 for the tag and length byte
2031
+ } else {
2032
+ // Long form length
2033
+ let length_of_length = ( length_byte & 0x7F ) as usize ;
2034
+ let length_bytes = & der_data[ offset + 2 ..offset + 2 + length_of_length] ;
2035
+ let cert_length = length_bytes. iter ( ) . fold ( 0 , |acc, & b| ( acc << 8 ) | b as usize ) ;
2036
+ cert_length + 2 + length_of_length // +2 for the tag and length byte
2037
+ } ;
2038
+ // Extract the certificate
2039
+ let cert = der_data[ offset..offset + cert_length] . to_vec ( ) ;
2040
+ certificates. push ( cert) ;
2041
+ // Move the offset to the next certificate
2042
+ offset += cert_length;
2043
+ }
2044
+ certificates
2045
+ }
2046
+
2047
+ /// Convert a vector of der certificates into a single string with all certificates in PEM format.
2048
+ ///
2049
+ /// # Arguments
2050
+ ///
2051
+ /// `der_certificates`: Vector of certificates in DER format
2052
+ ///
2053
+ /// # Returns
2054
+ ///
2055
+ /// A `String` containing all concatenated certificates in PEM format (order is maintained)
2056
+ pub fn der_to_pem (
2057
+ der_certificates : Vec < Vec < u8 > >
2058
+ ) -> std:: result:: Result < String , Box < dyn std:: error:: Error > > {
2059
+ let mut pem_string = String :: new ( ) ;
2060
+ for der in der_certificates. iter ( ) . rev ( ) {
2061
+ // Convert DER to X509
2062
+ let cert = X509 :: from_der ( & der) ?;
2063
+ // Convert X509 to PEM format
2064
+ let pem = cert. to_pem ( ) ?;
2065
+ // Append the PEM string to the result
2066
+ pem_string. push_str ( & String :: from_utf8 ( pem) ?) ;
2067
+ }
2068
+ Ok ( pem_string)
2069
+ }
2070
+
2071
+ /// Read certificate chain from TPM.
2072
+ ///
2073
+ /// Read content of NV Handle 0x01c00100 - 0x01c001ff
2074
+ ///
2075
+ /// # Returns
2076
+ ///
2077
+ /// `Vec<u8>', binary data of certificate chain
2078
+ pub fn read_ek_ca_chain ( context : & mut tss_esapi:: Context ) -> tss_esapi:: Result < Vec < u8 > > {
2079
+ let mut result: Vec < u8 > = Vec :: new ( ) ;
2080
+
2081
+ // Get handles for NV-Index in range 0x01c00100 - 0x01c001ff
2082
+ let ( capabilities, _) = context. get_capability (
2083
+ CapabilityType :: Handles ,
2084
+ RSA_EK_CERTIFICATE_CHAIN_START ,
2085
+ RSA_EK_CERTIFICATE_CHAIN_END - RSA_EK_CERTIFICATE_CHAIN_START ,
2086
+ ) ?;
2087
+
2088
+ if let CapabilityData :: Handles ( handle_list) = capabilities {
2089
+ for handle in handle_list. iter ( ) {
2090
+ if let TpmHandle :: NvIndex ( nv_idx) = handle {
2091
+ // Attempt to get the NV authorization handle
2092
+ let nv_auth_handle = context. execute_without_session ( |ctx| {
2093
+ ctx. tr_from_tpm_public ( * handle)
2094
+ . map ( |v| NvAuth :: NvIndex ( v. into ( ) ) )
2095
+ } ) ?;
2096
+
2097
+ // Read the full NV data
2098
+ let data = context. execute_with_nullauth_session ( |ctx| {
2099
+ nv:: read_full ( ctx, nv_auth_handle, * nv_idx)
2100
+ } ) ?;
2101
+
2102
+ result. extend ( data) ;
2103
+ } else {
2104
+ // Handle other types of handles if necessary
2105
+ break ; // Skip non-NvIndex handles
2106
+ }
2107
+ }
2108
+ }
2109
+
2110
+ Ok ( result) // Return the accumulated result
2111
+ }
2112
+
1950
2113
pub mod testing {
1951
2114
use super :: * ;
1952
2115
#[ cfg( feature = "testing" ) ]
0 commit comments