tpm/
tpm_helper.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! The module includes the helper functions for sending TPM commands.
5
6use crate::TPM_AZURE_AIK_HANDLE;
7use crate::TPM_GUEST_SECRET_HANDLE;
8use crate::TPM_NV_INDEX_AIK_CERT;
9use crate::TPM_NV_INDEX_ATTESTATION_REPORT;
10use crate::TPM_NV_INDEX_MITIGATED;
11use crate::TPM_RSA_SRK_HANDLE;
12use crate::TpmRsa2kPublic;
13use crate::expected_ak_attributes;
14use crate::tpm20proto;
15use crate::tpm20proto::AlgIdEnum;
16use crate::tpm20proto::CommandCodeEnum;
17use crate::tpm20proto::MAX_DIGEST_BUFFER_SIZE;
18use crate::tpm20proto::ReservedHandle;
19use crate::tpm20proto::ResponseCode;
20use crate::tpm20proto::ResponseValidationError;
21use crate::tpm20proto::SessionTagEnum;
22use crate::tpm20proto::TPM20_RH_ENDORSEMENT;
23use crate::tpm20proto::TPM20_RH_OWNER;
24use crate::tpm20proto::TPM20_RH_PLATFORM;
25use crate::tpm20proto::TPM20_RS_PW;
26use crate::tpm20proto::TpmProtoError;
27use crate::tpm20proto::TpmaNvBits;
28use crate::tpm20proto::TpmaObjectBits;
29use crate::tpm20proto::protocol::CreatePrimaryReply;
30use crate::tpm20proto::protocol::ImportReply;
31use crate::tpm20proto::protocol::LoadReply;
32use crate::tpm20proto::protocol::NvReadPublicReply;
33use crate::tpm20proto::protocol::PcrSelection;
34use crate::tpm20proto::protocol::ReadPublicReply;
35use crate::tpm20proto::protocol::StartupType;
36use crate::tpm20proto::protocol::Tpm2bBuffer;
37use crate::tpm20proto::protocol::Tpm2bPublic;
38use crate::tpm20proto::protocol::TpmCommand;
39use crate::tpm20proto::protocol::TpmsNvPublic;
40use crate::tpm20proto::protocol::TpmsRsaParams;
41use crate::tpm20proto::protocol::TpmtPublic;
42use crate::tpm20proto::protocol::TpmtRsaScheme;
43use crate::tpm20proto::protocol::TpmtSymDefObject;
44use crate::tpm20proto::protocol::common::CmdAuth;
45use cvm_tracing::CVM_ALLOWED;
46use inspect::InspectMut;
47use ms_tpm_20_ref::MsTpm20RefPlatform;
48use thiserror::Error;
49use zerocopy::FromZeros;
50use zerocopy::IntoBytes;
51
52// The size of command and response buffers.
53// DEVNOTE: The specification only requires the size to be large
54// enough for the command and response fit into the buffer. We
55// would need to scale this value up in case it is not sufficient.
56const TPM_PAGE_SIZE: usize = 4096;
57const MAX_NV_BUFFER_SIZE: usize = MAX_DIGEST_BUFFER_SIZE;
58const MAX_NV_INDEX_SIZE: u16 = 4096;
59// Scale this with maximum attestation payload
60const MAX_ATTESTATION_INDEX_SIZE: u16 = 2600;
61
62const RSA_2K_MODULUS_BITS: u16 = 2048;
63const RSA_2K_MODULUS_SIZE: usize = (RSA_2K_MODULUS_BITS / 8) as usize;
64const RSA_2K_EXPONENT_SIZE: usize = 3;
65
66/// TPM command debug information used by error logs.
67#[derive(Debug)]
68pub struct CommandDebugInfo {
69    /// Command code
70    pub command_code: CommandCodeEnum,
71    /// Optional authorization handle in the command request
72    pub auth_handle: Option<ReservedHandle>,
73    /// Optional nv index in the command request
74    pub nv_index: Option<u32>,
75}
76
77#[derive(Error, Debug)]
78pub enum TpmHelperError {
79    #[error("TPM command error - command code: {:?}, auth handle: {:#x?}, nv index: {:#x?}",
80        {.command_debug_info.command_code}, {.command_debug_info.auth_handle}, {.command_debug_info.nv_index})]
81    TpmCommandError {
82        command_debug_info: CommandDebugInfo,
83        #[source]
84        error: TpmCommandError,
85    },
86    #[error("failed to export rsa public from ak handle {ak_handle:#x?}")]
87    ExportRsaPublicFromAkHandle {
88        ak_handle: u32,
89        #[source]
90        error: TpmHelperUtilityError,
91    },
92    #[error("failed to create ak pub template")]
93    CreateAkPubTemplateFailed(#[source] TpmHelperUtilityError),
94    #[error("failed to create ek pub template")]
95    CreateEkPubTemplateFailed(#[source] TpmHelperUtilityError),
96    #[error("failed to export rsa public from newly created primary object")]
97    ExportRsaPublicFromPrimaryObject(#[source] TpmHelperUtilityError),
98    #[error("nv index {0:#x} without owner read flag")]
99    NoOwnerReadFlag(u32),
100    #[error(
101        "nv index {nv_index:#x} without auth write ({auth_write}) or platform created ({platform_created}) flag"
102    )]
103    InvalidPermission {
104        nv_index: u32,
105        auth_write: bool,
106        platform_created: bool,
107    },
108    #[error(
109        "input size {input_size} to nv write exceeds the allocated size {allocated_size} of nv index {nv_index:#x}"
110    )]
111    NvWriteInputTooLarge {
112        nv_index: u32,
113        input_size: usize,
114        allocated_size: usize,
115    },
116    #[error("failed to find SRK {0:#x} from tpm")]
117    SrkNotFound(u32),
118    #[error("failed to deserialize guest secret key into TPM Import command")]
119    DeserializeGuestSecretKey,
120}
121
122#[derive(Error, Debug)]
123pub enum TpmCommandError {
124    #[error("failed to execute the TPM command")]
125    TpmExecuteCommand(#[source] ms_tpm_20_ref::Error),
126    #[error("invalid response from the TPM command")]
127    InvalidResponse(#[source] ResponseValidationError),
128    #[error("invalid input parameter for the TPM command")]
129    InvalidInputParameter(#[source] TpmProtoError),
130    #[error("TPM command failed, response code: {response_code:#x}")]
131    TpmCommandFailed { response_code: u32 },
132    #[error("failed to create the TPM command struct")]
133    TpmCommandCreationFailed(#[source] TpmProtoError),
134}
135
136#[derive(Error, Debug)]
137pub enum TpmHelperUtilityError {
138    #[error("the RSA exponent returned by TPM is unexpected")]
139    UnexpectedRsaExponent,
140    #[error("the size of RSA modulus returned by TPM is unexpected")]
141    UnexpectedRsaModulusSize,
142    #[error("invalid input parameter")]
143    InvalidInputParameter(#[source] TpmProtoError),
144}
145
146#[derive(InspectMut)]
147pub struct TpmEngineHelper {
148    /// An TPM engine instance.
149    #[inspect(skip)]
150    pub tpm_engine: MsTpm20RefPlatform,
151    /// Buffer used to hold the command response.
152    pub reply_buffer: [u8; TPM_PAGE_SIZE],
153}
154
155/// Action of the `evict_or_persist`.
156enum EvictOrPersist {
157    /// Evict a persistent handle from nv ram
158    Evict(ReservedHandle),
159    /// Persist a transient object into nv ram
160    Persist {
161        from: ReservedHandle,
162        to: ReservedHandle,
163    },
164}
165
166/// State of the NV index returned by `read_from_nv_index`
167#[derive(Debug)]
168pub enum NvIndexState {
169    /// The NV index is available to read
170    Available,
171    /// The NV index does not exist
172    Unallocated,
173    /// The NV index existed but uninitialized
174    Uninitialized,
175}
176
177enum AkCertType {
178    None,
179    PlatformOwned(Vec<u8>),
180    OwnerOwned,
181}
182
183impl TpmEngineHelper {
184    // === Helper functions built on top of TPM commands === //
185
186    /// Initialize the TPM instance and perform self-tests using Startup and SelfTest commands.
187    /// This function should only be invoked after an TPM reset.
188    pub fn initialize_tpm_engine(&mut self) -> Result<(), TpmHelperError> {
189        // Set TPM to the default state.
190        self.startup(StartupType::Clear)
191            .map_err(|error| TpmHelperError::TpmCommandError {
192                command_debug_info: CommandDebugInfo {
193                    command_code: CommandCodeEnum::Startup,
194                    auth_handle: None,
195                    nv_index: None,
196                },
197                error,
198            })?;
199
200        // Perform capabilities test
201        self.self_test(true)
202            .map_err(|error| TpmHelperError::TpmCommandError {
203                command_debug_info: CommandDebugInfo {
204                    command_code: CommandCodeEnum::SelfTest,
205                    auth_handle: None,
206                    nv_index: None,
207                },
208                error,
209            })?;
210
211        Ok(())
212    }
213
214    /// Clear the TPM context under the platform hierarchy using ClearControl and Clear commands.
215    /// This function should only be invoked under platform hierarchy (before it's cleared by
216    /// the HierarchyControl command).
217    ///
218    /// Returns the response code in `u32`.
219    pub fn clear_tpm_platform_context(&mut self) -> Result<u32, TpmHelperError> {
220        // Use clear control to enable the execution of clear
221        if let Err(error) = self.clear_control(TPM20_RH_PLATFORM, false) {
222            if let TpmCommandError::TpmCommandFailed { response_code } = error {
223                tracelimit::error_ratelimited!(
224                    CVM_ALLOWED,
225                    err = &error as &dyn std::error::Error,
226                    "tpm ClearControlCmd failed"
227                );
228
229                // Return the error code to be written to `last_ppi_state`
230                return Ok(response_code);
231            } else {
232                // Unexpected failure
233                return Err(TpmHelperError::TpmCommandError {
234                    command_debug_info: CommandDebugInfo {
235                        command_code: CommandCodeEnum::ClearControl,
236                        auth_handle: Some(TPM20_RH_PLATFORM),
237                        nv_index: None,
238                    },
239                    error,
240                });
241            }
242        }
243
244        // Clear the context associated with `TPM20_RH_PLATFORM`.
245        match self.clear(TPM20_RH_PLATFORM) {
246            Err(error) => {
247                if let TpmCommandError::TpmCommandFailed { response_code } = error {
248                    tracelimit::error_ratelimited!(
249                        CVM_ALLOWED,
250                        err = &error as &dyn std::error::Error,
251                        "tpm ClearCmd failed"
252                    );
253
254                    // Return the error code to be written to `last_ppi_state`
255                    Ok(response_code)
256                } else {
257                    // Unexpected failure
258                    Err(TpmHelperError::TpmCommandError {
259                        command_debug_info: CommandDebugInfo {
260                            command_code: CommandCodeEnum::Clear,
261                            auth_handle: Some(TPM20_RH_PLATFORM),
262                            nv_index: None,
263                        },
264                        error,
265                    })?
266                }
267            }
268            // Return `tpm20proto::ResponseCode::Success`
269            Ok(response_code) => Ok(response_code),
270        }
271    }
272
273    /// Refresh TPM endorsement primary seed (ESP) and platform primary seed (PPS) using ChangeEPS
274    /// and ChangePPS commands.
275    pub fn refresh_tpm_seeds(&mut self) -> Result<(), TpmHelperError> {
276        // Refresh endorsement primary seed (EPS)
277        self.change_seed(TPM20_RH_PLATFORM, CommandCodeEnum::ChangeEPS)
278            .map_err(|error| TpmHelperError::TpmCommandError {
279                command_debug_info: CommandDebugInfo {
280                    command_code: CommandCodeEnum::ChangeEPS,
281                    auth_handle: Some(TPM20_RH_PLATFORM),
282                    nv_index: None,
283                },
284                error,
285            })?;
286
287        // Refresh platform primary seed (PPS)
288        self.change_seed(TPM20_RH_PLATFORM, CommandCodeEnum::ChangePPS)
289            .map_err(|error| TpmHelperError::TpmCommandError {
290                command_debug_info: CommandDebugInfo {
291                    command_code: CommandCodeEnum::ChangePPS,
292                    auth_handle: Some(TPM20_RH_PLATFORM),
293                    nv_index: None,
294                },
295                error,
296            })?;
297
298        Ok(())
299    }
300
301    /// Create and persist an Attestation Key (AK) in the tpm.
302    ///
303    /// # Arguments
304    /// * `force_create`: Whether to remove the existing AK and re-create one.
305    ///
306    /// Returns the AK public in `TpmRsa2kPublic`, and a bool indicating whether AKCert
307    /// renewal is allowed.
308    pub fn create_ak_pub(
309        &mut self,
310        force_create: bool,
311    ) -> Result<(TpmRsa2kPublic, bool), TpmHelperError> {
312        if let Some(res) = self.find_object(TPM_AZURE_AIK_HANDLE)? {
313            if force_create {
314                // Remove existing key before creating a new one
315                self.evict_or_persist_handle(EvictOrPersist::Evict(TPM_AZURE_AIK_HANDLE))?;
316            } else {
317                let expected_attributes = expected_ak_attributes();
318
319                // If an existing key has the wrong attributes, deny renewing the AKCert later.
320                // This prevents an attack where the VTL0 admin can replace the AK with their own
321                // and get a signed AKCert.
322                let actual_attributes = res.out_public.public_area.object_attributes;
323                if actual_attributes != expected_attributes {
324                    tracing::warn!(
325                        CVM_ALLOWED,
326                        attrs = actual_attributes.0.get(),
327                        "incorrect AK attributes; denying AKCert renewal"
328                    );
329                }
330
331                return export_rsa_public(&res.out_public)
332                    .map_err(|error| TpmHelperError::ExportRsaPublicFromAkHandle {
333                        ak_handle: TPM_AZURE_AIK_HANDLE.0.get(),
334                        error,
335                    })
336                    .map(|ak_pub| (ak_pub, actual_attributes == expected_attributes));
337            }
338        }
339
340        let in_public = ak_pub_template().map_err(TpmHelperError::CreateAkPubTemplateFailed)?;
341
342        self.create_key_object(in_public, Some(TPM_AZURE_AIK_HANDLE))
343            .map(|res| (res, true))
344    }
345
346    /// Create Windows-style Endorsement key (EK) based on the template from the TPM specification. Note that
347    /// this function does not persist the EK in the tpm platform. Instead, EK will be created and persisted
348    /// using the same template by other software component during guest OS boot.
349    ///
350    /// Returns the EK public in `TpmRsa2kPublic`.
351    pub fn create_ek_pub(&mut self) -> Result<TpmRsa2kPublic, TpmHelperError> {
352        let in_public = ek_pub_template().map_err(TpmHelperError::CreateEkPubTemplateFailed)?;
353
354        self.create_key_object(in_public, None)
355    }
356
357    /// Create EK or AK based on the public key template.
358    ///
359    /// # Arguments
360    /// `in_public` - The public key template.
361    /// `ak_handle` - To determine if this is EK or AK.
362    ///
363    /// Returns the created RSA public in `TpmRsa2kPublic`.
364    fn create_key_object(
365        &mut self,
366        in_public: TpmtPublic,
367        ak_handle: Option<ReservedHandle>,
368    ) -> Result<TpmRsa2kPublic, TpmHelperError> {
369        let res = match self.create_primary(TPM20_RH_ENDORSEMENT, in_public) {
370            Err(error) => {
371                if let TpmCommandError::TpmCommandFailed { response_code: _ } = error {
372                    // Guest might cause the command to fail (e.g., taking the ownership of a hierarchy).
373                    // Making this failure as non-fatal.
374                    tracelimit::error_ratelimited!(
375                        CVM_ALLOWED,
376                        err = &error as &dyn std::error::Error,
377                        "tpm CreatePrimaryCmd failed"
378                    );
379
380                    return Ok(TpmRsa2kPublic {
381                        modulus: [0u8; RSA_2K_MODULUS_SIZE],
382                        exponent: [0u8; RSA_2K_EXPONENT_SIZE],
383                    });
384                } else {
385                    // Unexpected failure
386                    return Err(TpmHelperError::TpmCommandError {
387                        command_debug_info: CommandDebugInfo {
388                            command_code: CommandCodeEnum::CreatePrimary,
389                            auth_handle: Some(TPM20_RH_ENDORSEMENT),
390                            nv_index: None,
391                        },
392                        error,
393                    });
394                }
395            }
396            Ok(res) => res,
397        };
398
399        if res.out_public.size.get() == 0 {
400            // Guest might cause the command to fail (e.g., taking the ownership of a hierarchy).
401            // Making this failure as non-fatal.
402            tracelimit::error_ratelimited!(
403                CVM_ALLOWED,
404                "No public data in CreatePrimaryCmd response"
405            );
406
407            return Ok(TpmRsa2kPublic {
408                modulus: [0u8; RSA_2K_MODULUS_SIZE],
409                exponent: [0u8; RSA_2K_EXPONENT_SIZE],
410            });
411        }
412
413        let rsa_public = if let Some(ak_handle) = ak_handle {
414            // Make a persistent copy of the transient object
415            self.evict_or_persist_handle(EvictOrPersist::Persist {
416                from: res.object_handle,
417                to: ak_handle,
418            })?;
419
420            export_rsa_public(&res.out_public)
421        } else {
422            // EK already exists, we just re-compute the public key
423            export_rsa_public(&res.out_public)
424        }
425        .map_err(TpmHelperError::ExportRsaPublicFromPrimaryObject)?;
426
427        if let Err(error) = self.flush_context(res.object_handle) {
428            if let TpmCommandError::TpmCommandFailed { response_code: _ } = error {
429                // Guest might cause the command to fail (e.g., taking the ownership of a hierarchy).
430                // Making this failure as non-fatal.
431                tracelimit::error_ratelimited!(
432                    CVM_ALLOWED,
433                    err = &error as &dyn std::error::Error,
434                    "tpm FlushContextCmd failed"
435                );
436            } else {
437                // Unexpected failure
438                return Err(TpmHelperError::TpmCommandError {
439                    command_debug_info: CommandDebugInfo {
440                        command_code: CommandCodeEnum::FlushContext,
441                        auth_handle: None,
442                        nv_index: Some(res.object_handle.0.get()),
443                    },
444                    error,
445                });
446            }
447        }
448
449        Ok(rsa_public)
450    }
451
452    /// Evict a persistent object from or persist a transient object to nv ram using EvictControl
453    /// command.
454    fn evict_or_persist_handle(&mut self, action: EvictOrPersist) -> Result<(), TpmHelperError> {
455        let (object_handle, persistent_handle) = match action {
456            EvictOrPersist::Evict(handle) => (handle, handle),
457            EvictOrPersist::Persist { from, to } => (from, to),
458        };
459
460        if let Err(error) = self.evict_control(TPM20_RH_OWNER, object_handle, persistent_handle) {
461            if let TpmCommandError::TpmCommandFailed { response_code: _ } = error {
462                // Guest might cause the command to fail (e.g., taking the ownership of a hierarchy).
463                // Making this failure as non-fatal.
464                tracelimit::error_ratelimited!(
465                    CVM_ALLOWED,
466                    err = &error as &dyn std::error::Error,
467                    "tpm EvictControlCmd failed"
468                );
469            } else {
470                // Unexpected failure
471                return Err(TpmHelperError::TpmCommandError {
472                    command_debug_info: CommandDebugInfo {
473                        command_code: CommandCodeEnum::EvictControl,
474                        auth_handle: Some(TPM20_RH_OWNER),
475                        nv_index: Some(object_handle.0.get()),
476                    },
477                    error,
478                });
479            }
480        }
481
482        Ok(())
483    }
484
485    /// Read the existing AK cert and clear the nv index if:
486    ///  - the nv index is present, and is platform owned
487    ///  - the nv index is present, but has no data
488    ///
489    /// Owner owned nv index is left as-is.
490    fn take_existing_ak_cert(&mut self) -> Result<AkCertType, TpmHelperError> {
491        let mut output = vec![0; MAX_NV_INDEX_SIZE as usize];
492
493        // Read the AK cert from the index. If the index is not owner owned, the
494        // index will be removed.
495        match self.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut output)? {
496            NvIndexState::Available => {
497                let res = self
498                    .find_nv_index(TPM_NV_INDEX_AIK_CERT)?
499                    .expect("nv index exists");
500                let nv_bits = TpmaNvBits::from(res.nv_public.nv_public.attributes.0.get());
501                let size = res.nv_public.nv_public.data_size.get();
502
503                // Resize the output vector to match exactly what the nv index
504                // size is.
505                assert!(size <= MAX_NV_INDEX_SIZE);
506                output.resize(size as usize, 0);
507
508                let platform_cert = nv_bits.nv_platformcreate();
509                tracing::info!(platform_cert, "AK cert nv index with available data");
510
511                if nv_bits.nv_platformcreate() {
512                    tracing::info!("clearing platform owned AK cert");
513                    self.nv_undefine_space(TPM20_RH_PLATFORM, TPM_NV_INDEX_AIK_CERT)
514                        .map_err(|error| TpmHelperError::TpmCommandError {
515                            command_debug_info: CommandDebugInfo {
516                                command_code: CommandCodeEnum::NV_UndefineSpace,
517                                auth_handle: Some(TPM20_RH_PLATFORM),
518                                nv_index: Some(TPM_NV_INDEX_AIK_CERT),
519                            },
520                            error,
521                        })?;
522
523                    Ok(AkCertType::PlatformOwned(output))
524                } else {
525                    tracing::info!("Existing AK cert is owner-defined");
526                    Ok(AkCertType::OwnerOwned)
527                }
528            }
529            NvIndexState::Uninitialized => {
530                tracing::info!("AK cert nv index allocated but uninitialized");
531
532                self.nv_undefine_space(TPM20_RH_PLATFORM, TPM_NV_INDEX_AIK_CERT)
533                    .map_err(|error| TpmHelperError::TpmCommandError {
534                        command_debug_info: CommandDebugInfo {
535                            command_code: CommandCodeEnum::NV_UndefineSpace,
536                            auth_handle: Some(TPM20_RH_PLATFORM),
537                            nv_index: Some(TPM_NV_INDEX_AIK_CERT),
538                        },
539                        error,
540                    })?;
541
542                Ok(AkCertType::None)
543            }
544            NvIndexState::Unallocated => {
545                tracing::info!("AK cert nv index not allocated yet");
546                Ok(AkCertType::None)
547            }
548        }
549    }
550
551    /// Allocate NV indices under platform hierarchy that are necessary for guest
552    /// attestation.
553    ///
554    /// # Arguments
555    /// * `auth_value`: The password used during the NV indices allocation.
556    /// * `preserve_ak_cert`: Whether to preserve the previous AK cert into newly-create NV index.
557    /// * `support_attestation_report`: Whether to allocate NV index for attestation report.
558    /// * `mitigate_legacy_akcert`: If this VM should be attempted to be mitigated.
559    ///
560    pub fn allocate_guest_attestation_nv_indices(
561        &mut self,
562        auth_value: u64,
563        preserve_ak_cert: bool,
564        support_attestation_report: bool,
565        mitigate_legacy_akcert: bool,
566    ) -> Result<(), TpmHelperError> {
567        if mitigate_legacy_akcert && self.has_mitigation_marker() {
568            // VM has a small-vTPM mitigation marker. Don't touch anything, but
569            // log whether the AK cert exists, as that previous write might have
570            // failed.
571            let mut output = vec![0u8; MAX_NV_INDEX_SIZE as usize];
572            let r = self.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut output);
573            tracing::warn!("VM has 16k vTPM mitigation marker");
574            match r {
575                Err(e) => tracing::error!(
576                    err = &e as &dyn std::error::Error,
577                    "error reading AKCert index with mitigation marker"
578                ),
579                Ok(NvIndexState::Available) => {
580                    let res = self
581                        .find_nv_index(TPM_NV_INDEX_AIK_CERT)?
582                        .expect("akcert nv index present");
583                    let nv_bits = TpmaNvBits::from(res.nv_public.nv_public.attributes.0.get());
584                    let size = res.nv_public.nv_public.data_size.get();
585
586                    tracing::info!(?nv_bits, size, "AKCert index exists");
587
588                    if nv_bits.nv_platformcreate() {
589                        tracing::info!("AKCert index is platform owned; restoring owner auth");
590                        let existing_cert = self.take_existing_ak_cert()?;
591                        if let AkCertType::PlatformOwned(cert) = existing_cert {
592                            self.nv_define_space(
593                                TPM20_RH_OWNER,
594                                0,
595                                TPM_NV_INDEX_AIK_CERT,
596                                cert.len() as u16,
597                            )
598                            .map_err(|error| {
599                                TpmHelperError::TpmCommandError {
600                                    command_debug_info: CommandDebugInfo {
601                                        command_code: CommandCodeEnum::NV_DefineSpace,
602                                        auth_handle: Some(TPM20_RH_OWNER),
603                                        nv_index: Some(TPM_NV_INDEX_AIK_CERT),
604                                    },
605                                    error,
606                                }
607                            })?;
608
609                            self.nv_write(TPM20_RH_OWNER, None, TPM_NV_INDEX_AIK_CERT, &cert)
610                                .map_err(|error| TpmHelperError::TpmCommandError {
611                                    command_debug_info: CommandDebugInfo {
612                                        command_code: CommandCodeEnum::NV_Write,
613                                        auth_handle: Some(TPM20_RH_OWNER),
614                                        nv_index: Some(TPM_NV_INDEX_AIK_CERT),
615                                    },
616                                    error,
617                                })?;
618                        }
619                    }
620                }
621                Ok(NvIndexState::Uninitialized) => {
622                    tracing::warn!("AKCert index uninitialized with mitigation marker")
623                }
624                Ok(NvIndexState::Unallocated) => {
625                    tracing::warn!("AKCert index unallocated with mitigation marker")
626                }
627            }
628
629            return Ok(());
630        } else {
631            tracing::info!(
632                "No small-vTPM mitigation marker; proceeding to resize AKCert index if needed"
633            );
634        }
635
636        let previous_ak_cert = self.take_existing_ak_cert()?;
637
638        match previous_ak_cert {
639            AkCertType::None => {
640                let size = MAX_NV_INDEX_SIZE;
641
642                tracing::info!(
643                    nv_index = format!("{:x}", TPM_NV_INDEX_AIK_CERT),
644                    size,
645                    "Allocate nv index for AK cert"
646                );
647
648                self.nv_define_space(TPM20_RH_PLATFORM, auth_value, TPM_NV_INDEX_AIK_CERT, size)
649                    .map_err(|error| TpmHelperError::TpmCommandError {
650                        command_debug_info: CommandDebugInfo {
651                            command_code: CommandCodeEnum::NV_DefineSpace,
652                            auth_handle: Some(TPM20_RH_PLATFORM),
653                            nv_index: Some(TPM_NV_INDEX_AIK_CERT),
654                        },
655                        error,
656                    })?;
657            }
658            AkCertType::PlatformOwned(mut cert) => {
659                let will_mitigate_cert =
660                    mitigate_legacy_akcert && cert.len() == MAX_NV_INDEX_SIZE as usize;
661
662                if will_mitigate_cert {
663                    self.write_mitigation_marker(auth_value);
664                }
665
666                let size = if will_mitigate_cert {
667                    // To save space in the NVRAM, if the AKCert index contents
668                    // look like a DER-encoded X.509 certificate, use its actual
669                    // size (plus 4 bytes for the DER header).
670                    if let &[0x30, 0x82, len0, len1, ..] = cert.as_slice() {
671                        let len = u16::from_be_bytes([len0, len1]);
672                        let parsed_size = len.saturating_add(4).min(MAX_NV_INDEX_SIZE);
673                        tracing::warn!(parsed_size, "redefining AKCert index with limited size");
674                        assert!(parsed_size as usize <= cert.len());
675                        cert.resize(parsed_size as usize, 0);
676                        parsed_size
677                    } else {
678                        MAX_NV_INDEX_SIZE
679                    }
680                } else {
681                    MAX_NV_INDEX_SIZE
682                };
683
684                tracing::info!(
685                    nv_index = format!("{:x}", TPM_NV_INDEX_AIK_CERT),
686                    size,
687                    "allocate nv index for previous platform AK cert"
688                );
689
690                let (handle, auth, write_auth_handle) = if will_mitigate_cert {
691                    (TPM20_RH_OWNER, None, TPM20_RH_OWNER)
692                } else {
693                    (
694                        TPM20_RH_PLATFORM,
695                        Some(auth_value),
696                        ReservedHandle(TPM_NV_INDEX_AIK_CERT.into()),
697                    )
698                };
699
700                let result = self
701                    .nv_define_space(handle, auth.unwrap_or(0), TPM_NV_INDEX_AIK_CERT, size)
702                    .map_err(|error| TpmHelperError::TpmCommandError {
703                        command_debug_info: CommandDebugInfo {
704                            command_code: CommandCodeEnum::NV_DefineSpace,
705                            auth_handle: Some(handle),
706                            nv_index: Some(TPM_NV_INDEX_AIK_CERT),
707                        },
708                        error,
709                    });
710
711                match result {
712                    Err(e) => {
713                        tracing::error!(
714                            error = &e as &dyn std::error::Error,
715                            "Failed to allocate AK cert nv index"
716                        );
717
718                        // Unless this VM was mitigated, bubble this error up to
719                        // the caller.
720                        if !will_mitigate_cert {
721                            return Err(e);
722                        }
723                    }
724                    Ok(_) => {
725                        tracing::info!("Successfully allocated AK cert nv index");
726
727                        if preserve_ak_cert {
728                            // For resiliency, write the previous AK cert to the
729                            // newly created nv index in case the following
730                            // boot-time AK cert request fails.
731                            tracing::info!("Preserve previous AK cert across boot");
732
733                            self.nv_write(write_auth_handle, auth, TPM_NV_INDEX_AIK_CERT, &cert)
734                                .map_err(|error| TpmHelperError::TpmCommandError {
735                                    command_debug_info: CommandDebugInfo {
736                                        command_code: CommandCodeEnum::NV_Write,
737                                        auth_handle: Some(ReservedHandle(
738                                            TPM_NV_INDEX_AIK_CERT.into(),
739                                        )),
740                                        nv_index: Some(TPM_NV_INDEX_AIK_CERT),
741                                    },
742                                    error,
743                                })?;
744                        }
745                    }
746                }
747            }
748            AkCertType::OwnerOwned => {
749                // Owner owned AK certs are left as-is.
750            }
751        }
752
753        // Allocate `TPM_NV_INDEX_ATTESTATION_REPORT` if `support_attestation_report` is true
754        if support_attestation_report {
755            // Attempt to remove previous `TPM_NV_INDEX_ATTESTATION_REPORT` allocation before the allocation
756            if self
757                .find_nv_index(TPM_NV_INDEX_ATTESTATION_REPORT)?
758                .is_some()
759            {
760                self.nv_undefine_space(TPM20_RH_PLATFORM, TPM_NV_INDEX_ATTESTATION_REPORT)
761                    .map_err(|error| TpmHelperError::TpmCommandError {
762                        command_debug_info: CommandDebugInfo {
763                            command_code: CommandCodeEnum::NV_UndefineSpace,
764                            auth_handle: Some(TPM20_RH_PLATFORM),
765                            nv_index: Some(TPM_NV_INDEX_ATTESTATION_REPORT),
766                        },
767                        error,
768                    })?;
769            }
770
771            tracing::info!(
772                nv_index = format!("{:x}", TPM_NV_INDEX_ATTESTATION_REPORT),
773                size = MAX_ATTESTATION_INDEX_SIZE,
774                "Allocate nv index for attestation report",
775            );
776
777            self.nv_define_space(
778                TPM20_RH_PLATFORM,
779                auth_value,
780                TPM_NV_INDEX_ATTESTATION_REPORT,
781                MAX_ATTESTATION_INDEX_SIZE,
782            )
783            .map_err(|error| TpmHelperError::TpmCommandError {
784                command_debug_info: CommandDebugInfo {
785                    command_code: CommandCodeEnum::NV_DefineSpace,
786                    auth_handle: Some(TPM20_RH_PLATFORM),
787                    nv_index: Some(TPM_NV_INDEX_ATTESTATION_REPORT),
788                },
789                error,
790            })?;
791        }
792
793        Ok(())
794    }
795
796    fn has_mitigation_marker(&mut self) -> bool {
797        self.find_nv_index(TPM_NV_INDEX_MITIGATED)
798            .is_ok_and(|v| v.is_some())
799    }
800
801    fn write_mitigation_marker(&mut self, auth_value: u64) {
802        match self.nv_define_space(TPM20_RH_PLATFORM, auth_value, TPM_NV_INDEX_MITIGATED, 1) {
803            Ok(_) => {
804                tracing::warn!(TPM_NV_INDEX_MITIGATED, "wrote tpm mitigation marker");
805            }
806            Err(e) => {
807                tracing::error!(
808                    error = &e as &dyn std::error::Error,
809                    "failed to write mitigation marker"
810                );
811            }
812        }
813    }
814
815    /// Check if the nv index is present using NV_ReadPublic command.
816    ///
817    /// Returns Ok(Some(NvReadPublicReply)) if nv index is present.
818    /// Returns Ok(None) if nv index is not present.
819    fn find_nv_index(
820        &mut self,
821        nv_index: u32,
822    ) -> Result<Option<NvReadPublicReply>, TpmHelperError> {
823        match self.nv_read_public(nv_index) {
824            Err(error) => {
825                if let TpmCommandError::TpmCommandFailed { response_code } = error {
826                    if response_code == (ResponseCode::Handle as u32 | ResponseCode::Rc1 as u32) {
827                        // nv index not found
828                        Ok(None)
829                    } else {
830                        // Unexpected response code
831                        Err(TpmHelperError::TpmCommandError {
832                            command_debug_info: CommandDebugInfo {
833                                command_code: CommandCodeEnum::NV_ReadPublic,
834                                auth_handle: None,
835                                nv_index: Some(nv_index),
836                            },
837                            error,
838                        })?
839                    }
840                } else {
841                    // Unexpected failure
842                    Err(TpmHelperError::TpmCommandError {
843                        command_debug_info: CommandDebugInfo {
844                            command_code: CommandCodeEnum::NV_ReadPublic,
845                            auth_handle: None,
846                            nv_index: Some(nv_index),
847                        },
848                        error,
849                    })?
850                }
851            }
852            Ok(res) => Ok(Some(res)),
853        }
854    }
855
856    /// Write data to a NV index that is password-based and platform-created.
857    /// If the data size is less than the size of the index, the function applies
858    /// zero padding and ensure the entire NV space is filled.
859    ///
860    /// # Arguments
861    /// * `auth_value` - The authorization value for the password-based index.
862    /// * `nv_index` - The target NV index.
863    /// * `data` - The data to write.
864    ///
865    pub fn write_to_nv_index(
866        &mut self,
867        auth_value: u64,
868        nv_index: u32,
869        data: &[u8],
870    ) -> Result<(), TpmHelperError> {
871        let res =
872            self.nv_read_public(nv_index)
873                .map_err(|error| TpmHelperError::TpmCommandError {
874                    command_debug_info: CommandDebugInfo {
875                        command_code: CommandCodeEnum::NV_ReadPublic,
876                        auth_handle: None,
877                        nv_index: Some(nv_index),
878                    },
879                    error,
880                })?;
881
882        let nv_bits = TpmaNvBits::from(res.nv_public.nv_public.attributes.0.get());
883        let nv_index_size = res.nv_public.nv_public.data_size.get();
884
885        // Validate the input size against the nv index size
886        let data = match data.len().cmp(&nv_index_size.into()) {
887            std::cmp::Ordering::Greater => Err(TpmHelperError::NvWriteInputTooLarge {
888                nv_index,
889                input_size: data.len(),
890                allocated_size: nv_index_size.into(),
891            })?,
892            std::cmp::Ordering::Less => {
893                // Ensure the nv index is filled by padding 0's.
894                let mut data = data.to_vec();
895                data.resize(nv_index_size.into(), 0);
896                data
897            }
898            std::cmp::Ordering::Equal => data.to_vec(),
899        };
900
901        // Always expect nv index to be password-based and platform-created given that
902        // the index is always created or re-created at boot-time.
903        if !nv_bits.nv_authwrite() || !nv_bits.nv_platformcreate() {
904            return Err(TpmHelperError::InvalidPermission {
905                nv_index,
906                auth_write: nv_bits.nv_authwrite(),
907                platform_created: nv_bits.nv_platformcreate(),
908            });
909        }
910
911        self.nv_write(
912            ReservedHandle(nv_index.into()),
913            Some(auth_value),
914            nv_index,
915            &data,
916        )
917        .map_err(|error| TpmHelperError::TpmCommandError {
918            command_debug_info: CommandDebugInfo {
919                command_code: CommandCodeEnum::NV_Write,
920                auth_handle: Some(ReservedHandle(nv_index.into())),
921                nv_index: Some(nv_index),
922            },
923            error,
924        })?;
925
926        Ok(())
927    }
928
929    /// Read data from a owner-defined NV Index if the index is present.
930    ///
931    /// # Arguments
932    /// * `nv_index` - The target NV index.
933    /// * `data` - The data to write.
934    ///
935    /// Returns Ok(NvIndexState::Available) if the index is present and read succeeds.
936    /// Returns Ok(NvIndexState::Unallocated) if the index is not present.
937    /// Returns Ok(NvIndexState::Uninitialized) if the index is present but uninitialized.
938    pub fn read_from_nv_index(
939        &mut self,
940        nv_index: u32,
941        data: &mut [u8],
942    ) -> Result<NvIndexState, TpmHelperError> {
943        let Some(res) = self.find_nv_index(nv_index)? else {
944            // nv index may not exist before guest makes a request
945            return Ok(NvIndexState::Unallocated);
946        };
947
948        let nv_bits = TpmaNvBits::from(res.nv_public.nv_public.attributes.0.get());
949        if !nv_bits.nv_ownerread() {
950            Err(TpmHelperError::NoOwnerReadFlag(nv_index))?
951        }
952
953        let nv_index_size = res.nv_public.nv_public.data_size.get();
954        match self.nv_read(TPM20_RH_OWNER, nv_index, nv_index_size, data) {
955            Err(error) => {
956                if let TpmCommandError::TpmCommandFailed { response_code } = error {
957                    if response_code == ResponseCode::NvUninitialized as u32 {
958                        Ok(NvIndexState::Uninitialized)
959                    } else {
960                        // Unexpected response code
961                        Err(TpmHelperError::TpmCommandError {
962                            command_debug_info: CommandDebugInfo {
963                                command_code: CommandCodeEnum::NV_Read,
964                                auth_handle: Some(TPM20_RH_OWNER),
965                                nv_index: Some(nv_index),
966                            },
967                            error,
968                        })?
969                    }
970                } else {
971                    // Unexpected failure
972                    Err(TpmHelperError::TpmCommandError {
973                        command_debug_info: CommandDebugInfo {
974                            command_code: CommandCodeEnum::NV_Read,
975                            auth_handle: Some(TPM20_RH_OWNER),
976                            nv_index: Some(nv_index),
977                        },
978                        error,
979                    })?
980                }
981            }
982            Ok(_) => Ok(NvIndexState::Available),
983        }
984    }
985
986    /// Check if the object is present using ReadPublic command.
987    ///
988    /// Returns Ok(Some(ReadPublicReply)) if the object is present.
989    /// Returns Ok(None) if nv index is not present.
990    pub fn find_object(
991        &mut self,
992        object_handle: ReservedHandle,
993    ) -> Result<Option<ReadPublicReply>, TpmHelperError> {
994        match self.read_public(object_handle) {
995            Err(error) => {
996                if let TpmCommandError::TpmCommandFailed { response_code } = error {
997                    if response_code == (ResponseCode::Handle as u32 | ResponseCode::Rc1 as u32) {
998                        // nv index not found
999                        Ok(None)
1000                    } else {
1001                        // Unexpected response code
1002                        Err(TpmHelperError::TpmCommandError {
1003                            command_debug_info: CommandDebugInfo {
1004                                command_code: CommandCodeEnum::ReadPublic,
1005                                auth_handle: None,
1006                                nv_index: Some(object_handle.0.get()),
1007                            },
1008                            error,
1009                        })?
1010                    }
1011                } else {
1012                    // Unexpected failure
1013                    Err(TpmHelperError::TpmCommandError {
1014                        command_debug_info: CommandDebugInfo {
1015                            command_code: CommandCodeEnum::ReadPublic,
1016                            auth_handle: None,
1017                            nv_index: Some(object_handle.0.get()),
1018                        },
1019                        error,
1020                    })?
1021                }
1022            }
1023            Ok(res) => Ok(Some(res)),
1024        }
1025    }
1026
1027    /// Initialize the guest secret key with the given data
1028    /// blob using Import, Load, and EvictControl commands.
1029    ///
1030    /// # Arguments
1031    /// * `guest_secret_key`: The guest secret key data blob.
1032    ///   The format of the data blob is expected to be:
1033    ///   (TPM2B_PUBLIC || TPM2B_PRIVATE || TPM2B_ENCRYPTED_SECRET)
1034    ///
1035    pub fn initialize_guest_secret_key(
1036        &mut self,
1037        guest_secret_key: &[u8],
1038    ) -> Result<(), TpmHelperError> {
1039        use crate::tpm20proto::protocol::ImportCmd;
1040
1041        if self.find_object(TPM_GUEST_SECRET_HANDLE)?.is_some() {
1042            // ECC key found, early return.
1043            return Ok(());
1044        };
1045
1046        if self.find_object(TPM_RSA_SRK_HANDLE)?.is_none() {
1047            // SRK not found, return an error.
1048            return Err(TpmHelperError::SrkNotFound(TPM_RSA_SRK_HANDLE.0.get()));
1049        };
1050
1051        // Deserialize the guest secret key data blob
1052        let import_command = ImportCmd::deserialize_no_wrapping_key(guest_secret_key)
1053            .ok_or(TpmHelperError::DeserializeGuestSecretKey)?;
1054
1055        // Import the key under `TPM_RSA_SRK_HANDLE`
1056        let import_reply = self
1057            .import(
1058                TPM_RSA_SRK_HANDLE,
1059                &import_command.object_public,
1060                &import_command.duplicate,
1061                &import_command.in_sym_seed,
1062            )
1063            .map_err(|error| TpmHelperError::TpmCommandError {
1064                command_debug_info: CommandDebugInfo {
1065                    command_code: CommandCodeEnum::Import,
1066                    auth_handle: None,
1067                    nv_index: None,
1068                },
1069                error,
1070            })?;
1071
1072        // Load the imported key
1073        let load_reply = self
1074            .load(
1075                TPM_RSA_SRK_HANDLE,
1076                &import_reply.out_private,
1077                &import_command.object_public,
1078            )
1079            .map_err(|error| TpmHelperError::TpmCommandError {
1080                command_debug_info: CommandDebugInfo {
1081                    command_code: CommandCodeEnum::Load,
1082                    auth_handle: None,
1083                    nv_index: None,
1084                },
1085                error,
1086            })?;
1087
1088        // Persist the imported key into TPM
1089        self.evict_or_persist_handle(EvictOrPersist::Persist {
1090            from: load_reply.object_handle,
1091            to: TPM_GUEST_SECRET_HANDLE,
1092        })?;
1093
1094        Ok(())
1095    }
1096
1097    // === TPM commands === //
1098
1099    /// Helper function to send Startup command.
1100    ///
1101    /// # Arguments
1102    /// * `startup_type`: The requested type to the command.
1103    ///
1104    pub fn startup(&mut self, startup_type: StartupType) -> Result<(), TpmCommandError> {
1105        use tpm20proto::protocol::StartupCmd;
1106
1107        let session_tag = SessionTagEnum::NoSessions;
1108        let mut cmd = StartupCmd::new(session_tag.into(), startup_type);
1109
1110        self.tpm_engine
1111            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1112            .map_err(TpmCommandError::TpmExecuteCommand)?;
1113
1114        match StartupCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1115            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1116            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1117                response_code: res.header.response_code.get(),
1118            })?,
1119            Ok((_res, true)) => Ok(()),
1120        }
1121    }
1122
1123    /// Helper function to send SelfTest command.
1124    ///
1125    /// # Arguments
1126    /// * `full_test`*: Perform full test or not.
1127    ///
1128    pub fn self_test(&mut self, full_test: bool) -> Result<(), TpmCommandError> {
1129        use tpm20proto::protocol::SelfTestCmd;
1130
1131        let session_tag = SessionTagEnum::NoSessions;
1132
1133        // Perform full test by default
1134        let mut cmd = SelfTestCmd::new(session_tag.into(), full_test);
1135
1136        self.tpm_engine
1137            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1138            .map_err(TpmCommandError::TpmExecuteCommand)?;
1139
1140        match SelfTestCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1141            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1142            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1143                response_code: res.header.response_code.get(),
1144            })?,
1145            Ok((_res, true)) => Ok(()),
1146        }
1147    }
1148
1149    /// Helper function to send HierarchyControl command.
1150    ///
1151    /// # Arguments
1152    /// * `auth_handle`: The authorization handle used in the command.
1153    /// * `hierarchy`: The hierarchy to control.
1154    /// * `state`: Enable the target hierarchy or not.
1155    ///
1156    pub fn hierarchy_control(
1157        &mut self,
1158        auth_handle: ReservedHandle,
1159        hierarchy: ReservedHandle,
1160        state: bool,
1161    ) -> Result<(), TpmCommandError> {
1162        use tpm20proto::protocol::HierarchyControlCmd;
1163
1164        let session_tag = SessionTagEnum::Sessions;
1165        let mut cmd = HierarchyControlCmd::new(
1166            session_tag.into(),
1167            auth_handle,
1168            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1169            hierarchy,
1170            state,
1171        );
1172
1173        self.tpm_engine
1174            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1175            .map_err(TpmCommandError::TpmExecuteCommand)?;
1176
1177        match HierarchyControlCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1178            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1179            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1180                response_code: res.header.response_code.get(),
1181            })?,
1182            Ok((_res, true)) => Ok(()),
1183        }
1184    }
1185
1186    /// Helper function to send ClearControl command.
1187    ///
1188    /// # Arguments
1189    /// * `auth_handle`: The authorization handle used in the command.
1190    /// * `disable`: Disable the execution of the Control command or not.
1191    ///
1192    pub fn clear_control(
1193        &mut self,
1194        auth_handle: ReservedHandle,
1195        disable: bool,
1196    ) -> Result<(), TpmCommandError> {
1197        use tpm20proto::protocol::ClearControlCmd;
1198
1199        let session_tag = SessionTagEnum::Sessions;
1200        let mut cmd = ClearControlCmd::new(
1201            session_tag.into(),
1202            auth_handle,
1203            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1204            disable,
1205        );
1206
1207        self.tpm_engine
1208            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1209            .map_err(TpmCommandError::TpmExecuteCommand)?;
1210
1211        match ClearControlCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1212            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1213            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1214                response_code: res.header.response_code.get(),
1215            })?,
1216            Ok((_res, true)) => Ok(()),
1217        }
1218    }
1219
1220    /// Helper function to send Clear command.
1221    ///
1222    /// # Arguments
1223    /// * `auth_handle`: The authorization handle used in the command.
1224    ///
1225    /// Returns the response code of the command (write back into `last_ppi_state`).
1226    pub fn clear(&mut self, auth_handle: ReservedHandle) -> Result<u32, TpmCommandError> {
1227        use tpm20proto::protocol::ClearCmd;
1228
1229        let session_tag = SessionTagEnum::Sessions;
1230        let mut cmd = ClearCmd::new(
1231            session_tag.into(),
1232            auth_handle,
1233            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1234        );
1235
1236        self.tpm_engine
1237            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1238            .map_err(TpmCommandError::TpmExecuteCommand)?;
1239
1240        match ClearCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1241            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1242            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1243                response_code: res.header.response_code.get(),
1244            })?,
1245            Ok((res, true)) => Ok(res.header.response_code.get()),
1246        }
1247    }
1248
1249    /// Helper function to send PcrAllocate command.
1250    ///
1251    /// # Arguments
1252    /// * `supported_pcr_banks` - 5-bit bitmap for supported PCR banks.
1253    /// * `pcr_banks_to_allocate` - 5-bit bitmap for PCR banks to be allocate.
1254    ///
1255    /// Returns the response code of the command (write back into `last_ppi_state`).
1256    pub fn pcr_allocate(
1257        &mut self,
1258        auth_handle: ReservedHandle,
1259        supported_pcr_banks: u32,
1260        pcr_banks_to_allocate: u32,
1261    ) -> Result<u32, TpmCommandError> {
1262        use tpm20proto::protocol::PcrAllocateCmd;
1263
1264        let mut pcr_selections = Vec::new(); // TODO: replace with smallvec<5>?
1265        for (alg_hash, alg_id) in PcrAllocateCmd::HASH_ALG_TO_ID {
1266            if (alg_hash & supported_pcr_banks) != 0 {
1267                pcr_selections.push(PcrSelection {
1268                    hash: alg_id,
1269                    size_of_select: 3,
1270                    bitmap: if (alg_hash & pcr_banks_to_allocate) != 0 {
1271                        [0xff, 0xff, 0xff]
1272                    } else {
1273                        [0x00, 0x00, 0x00]
1274                    },
1275                })
1276            }
1277        }
1278
1279        let session_tag = SessionTagEnum::Sessions;
1280        let cmd = PcrAllocateCmd::new(
1281            session_tag.into(),
1282            auth_handle,
1283            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1284            &pcr_selections,
1285        )
1286        .map_err(TpmCommandError::TpmCommandCreationFailed)?;
1287
1288        self.tpm_engine
1289            .execute_command(&mut cmd.serialize(), &mut self.reply_buffer)
1290            .map_err(TpmCommandError::TpmExecuteCommand)?;
1291
1292        match PcrAllocateCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1293            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1294            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1295                response_code: res.header.response_code.get(),
1296            })?,
1297            Ok((res, true)) => Ok(res.header.response_code.get()),
1298        }
1299    }
1300
1301    /// Helper function to send ChangeEPS and ChangePPS commands.
1302    ///
1303    /// # Arguments
1304    /// * `auth_handle`: The authorization handle used in the command.
1305    /// * `command_code`: The command corresponding to the seed to refresh (ChangeEPS or ChangePPS).
1306    ///
1307    pub fn change_seed(
1308        &mut self,
1309        auth_handle: ReservedHandle,
1310        command_code: CommandCodeEnum,
1311    ) -> Result<(), TpmCommandError> {
1312        use crate::tpm20proto::protocol::ChangeSeedCmd;
1313
1314        assert!(matches!(
1315            command_code,
1316            CommandCodeEnum::ChangeEPS | CommandCodeEnum::ChangePPS
1317        ));
1318
1319        let session_tag = SessionTagEnum::Sessions;
1320        let mut cmd = ChangeSeedCmd::new(
1321            session_tag.into(),
1322            auth_handle,
1323            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1324            command_code,
1325        );
1326
1327        self.tpm_engine
1328            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1329            .map_err(TpmCommandError::TpmExecuteCommand)?;
1330
1331        match ChangeSeedCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1332            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1333            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1334                response_code: res.header.response_code.get(),
1335            })?,
1336            Ok((_res, true)) => Ok(()),
1337        }
1338    }
1339
1340    /// Helper function to send ReadPublic command.
1341    ///
1342    /// # Arguments
1343    /// * `object_handle` - The handle to read.
1344    ///
1345    /// Returns Ok(ReadPublicReply) if the command succeeds. Returns
1346    /// Err(TpmCommandError) otherwise.
1347    pub fn read_public(
1348        &mut self,
1349        object_handle: ReservedHandle,
1350    ) -> Result<ReadPublicReply, TpmCommandError> {
1351        use tpm20proto::protocol::ReadPublicCmd;
1352
1353        let session_tag = SessionTagEnum::NoSessions;
1354        let mut cmd = ReadPublicCmd::new(session_tag.into(), object_handle);
1355
1356        self.tpm_engine
1357            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1358            .map_err(TpmCommandError::TpmExecuteCommand)?;
1359
1360        match ReadPublicCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1361            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1362            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1363                response_code: res.header.response_code.get(),
1364            })?,
1365            Ok((res, true)) => Ok(res),
1366        }
1367    }
1368
1369    /// Helper function to send FlushContext command.
1370    ///
1371    /// # Arguments
1372    /// * `flush_handle` - The handle to flush.
1373    ///
1374    pub fn flush_context(&mut self, flush_handle: ReservedHandle) -> Result<(), TpmCommandError> {
1375        use tpm20proto::protocol::FlushContextCmd;
1376
1377        let mut cmd = FlushContextCmd::new(flush_handle);
1378
1379        self.tpm_engine
1380            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1381            .map_err(TpmCommandError::TpmExecuteCommand)?;
1382
1383        match FlushContextCmd::base_validate_reply(&self.reply_buffer, cmd.header.session_tag) {
1384            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1385            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1386                response_code: res.header.response_code.get(),
1387            })?,
1388            Ok((_res, true)) => Ok(()),
1389        }
1390    }
1391
1392    /// Helper function to send EvictControl command.
1393    ///
1394    /// # Arguments
1395    /// * `auth_handle`: The authorization handle used in the command.
1396    /// * `object_handle` - Transient object handle.
1397    /// * `persistent_handle` - Handle for persisted object.
1398    ///
1399    pub fn evict_control(
1400        &mut self,
1401        auth_handle: ReservedHandle,
1402        object_handle: ReservedHandle,
1403        persistent_handle: ReservedHandle,
1404    ) -> Result<(), TpmCommandError> {
1405        use tpm20proto::protocol::EvictControlCmd;
1406
1407        let session_tag = SessionTagEnum::Sessions;
1408        let mut cmd = EvictControlCmd::new(
1409            session_tag.into(),
1410            auth_handle,
1411            object_handle,
1412            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1413            persistent_handle,
1414        );
1415
1416        self.tpm_engine
1417            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1418            .map_err(TpmCommandError::TpmExecuteCommand)?;
1419
1420        match EvictControlCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1421            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1422            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1423                response_code: res.header.response_code.get(),
1424            })?,
1425            Ok((_res, true)) => Ok(()),
1426        }
1427    }
1428
1429    /// Helper function to send NV_ReadPublic command.
1430    ///
1431    /// # Arguments
1432    /// * `nv_index` - The NV index to read.
1433    ///
1434    /// Returns Ok(NvReadPublicReply) if the command succeeds. Returns
1435    /// Err(TpmCommandError) otherwise.
1436    pub fn nv_read_public(&mut self, nv_index: u32) -> Result<NvReadPublicReply, TpmCommandError> {
1437        use tpm20proto::protocol::NvReadPublicCmd;
1438
1439        let session_tag = SessionTagEnum::NoSessions;
1440        let mut cmd = NvReadPublicCmd::new(session_tag.into(), nv_index);
1441
1442        self.tpm_engine
1443            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1444            .map_err(TpmCommandError::TpmExecuteCommand)?;
1445
1446        match NvReadPublicCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1447            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1448            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1449                response_code: res.header.response_code.get(),
1450            })?,
1451            Ok((res, true)) => Ok(res),
1452        }
1453    }
1454
1455    /// Helper function to send NV_UndefineSpace command.
1456    ///
1457    /// # Arguments
1458    /// * `auth_handle`: The authorization handle used in the command.
1459    /// * `nv_index` - The NV Index to undefine.
1460    ///
1461    pub fn nv_undefine_space(
1462        &mut self,
1463        auth_handle: ReservedHandle,
1464        nv_index: u32,
1465    ) -> Result<(), TpmCommandError> {
1466        use tpm20proto::protocol::NvUndefineSpaceCmd;
1467
1468        let session_tag = SessionTagEnum::Sessions;
1469        let mut cmd = NvUndefineSpaceCmd::new(
1470            session_tag.into(),
1471            auth_handle,
1472            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1473            nv_index,
1474        );
1475
1476        self.tpm_engine
1477            .execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
1478            .map_err(TpmCommandError::TpmExecuteCommand)?;
1479
1480        match NvUndefineSpaceCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1481            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1482            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1483                response_code: res.header.response_code.get(),
1484            })?,
1485            Ok((_res, true)) => Ok(()),
1486        }
1487    }
1488
1489    /// Helper function to send NV_DefineSpace command, which defines the attributes
1490    /// of an NV Index and causes the TPM to reserve space to hold the data associated
1491    /// with the index.
1492    ///
1493    /// # Arguments
1494    /// * `auth_handle`: The authorization handle used in the command.
1495    /// * `auth_value` - The password associated with the allocated NV index.
1496    /// * `nv_index` - The NV index to allocate.
1497    /// * `nv_index_size` - Size of NV index to allocate.
1498    ///
1499    pub fn nv_define_space(
1500        &mut self,
1501        auth_handle: ReservedHandle,
1502        auth_value: u64,
1503        nv_index: u32,
1504        nv_index_size: u16,
1505    ) -> Result<(), TpmCommandError> {
1506        use tpm20proto::protocol::NvDefineSpaceCmd;
1507
1508        let session_tag = SessionTagEnum::Sessions;
1509
1510        // Use password-based authorization and allow owner to read
1511        let attributes = if auth_handle == TPM20_RH_PLATFORM {
1512            TpmaNvBits::new()
1513                .with_nv_authread(true)
1514                .with_nv_authwrite(true)
1515                .with_nv_ownerread(true)
1516                .with_nv_platformcreate(true)
1517                .with_nv_no_da(true)
1518        } else {
1519            TpmaNvBits::new()
1520                .with_nv_ownerread(true)
1521                .with_nv_ownerwrite(true)
1522                .with_nv_authread(true)
1523                .with_nv_authwrite(true)
1524        };
1525
1526        let public_info = TpmsNvPublic::new(
1527            nv_index,
1528            AlgIdEnum::SHA256.into(),
1529            attributes,
1530            &[],
1531            nv_index_size,
1532        )
1533        .map_err(TpmCommandError::InvalidInputParameter)?;
1534
1535        let cmd = NvDefineSpaceCmd::new(
1536            session_tag.into(),
1537            auth_handle,
1538            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1539            auth_value,
1540            public_info,
1541        )
1542        .map_err(TpmCommandError::TpmCommandCreationFailed)?;
1543
1544        self.tpm_engine
1545            .execute_command(&mut cmd.serialize(), &mut self.reply_buffer)
1546            .map_err(TpmCommandError::TpmExecuteCommand)?;
1547
1548        match NvDefineSpaceCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1549            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1550            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1551                response_code: res.header.response_code.get(),
1552            })?,
1553            Ok((_res, true)) => Ok(()),
1554        }
1555    }
1556
1557    /// Helper function to send CreatePrimary command.
1558    ///
1559    /// # Arguments
1560    /// * `auth_handle`: The authorization handle used in the command.
1561    /// * `in_public` - The public template used to create the primary.
1562    ///
1563    pub fn create_primary(
1564        &mut self,
1565        auth_handle: ReservedHandle,
1566        in_public: TpmtPublic,
1567    ) -> Result<CreatePrimaryReply, TpmCommandError> {
1568        use tpm20proto::protocol::CreatePrimaryCmd;
1569
1570        let session_tag = SessionTagEnum::Sessions;
1571        let cmd = CreatePrimaryCmd::new(
1572            session_tag.into(),
1573            auth_handle,
1574            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1575            &[],
1576            &[],
1577            in_public,
1578            &[],
1579            &[],
1580        )
1581        .map_err(TpmCommandError::TpmCommandCreationFailed)?;
1582
1583        self.tpm_engine
1584            .execute_command(&mut cmd.serialize(), &mut self.reply_buffer)
1585            .map_err(TpmCommandError::TpmExecuteCommand)?;
1586
1587        match CreatePrimaryCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1588            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1589            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1590                response_code: res.header.response_code.get(),
1591            })?,
1592            Ok((res, true)) => Ok(res),
1593        }
1594    }
1595
1596    /// Helper function to send NV_Write command.
1597    ///
1598    /// # Arguments
1599    /// * `auth_handle`: The authorization handle used in the command.
1600    /// * `auth_value` - The optional password associated with the NV index.
1601    /// * `nv_index` - The NV index to write.
1602    /// * `data` - The data to be written to the NV index.
1603    ///
1604    pub fn nv_write(
1605        &mut self,
1606        auth_handle: ReservedHandle,
1607        auth_value: Option<u64>,
1608        nv_index: u32,
1609        data: &[u8],
1610    ) -> Result<(), TpmCommandError> {
1611        use tpm20proto::protocol::NvWriteCmd;
1612
1613        let session_tag = SessionTagEnum::Sessions;
1614
1615        let mut cmd = if let Some(auth_value) = auth_value {
1616            // Password-based authorization (the NV index was created at boot-time)
1617            NvWriteCmd::new(
1618                session_tag.into(),
1619                auth_handle,
1620                CmdAuth::new(TPM20_RS_PW, 0, 0, size_of_val(&auth_value) as u16),
1621                auth_value,
1622                nv_index,
1623                &[],
1624                0,
1625            )
1626        } else {
1627            // Owner write (the NV index was pre-provisioned)
1628            NvWriteCmd::new(
1629                session_tag.into(),
1630                auth_handle,
1631                CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1632                0,
1633                nv_index,
1634                &[],
1635                0,
1636            )
1637        }
1638        .map_err(TpmCommandError::TpmCommandCreationFailed)?;
1639
1640        let mut transferred_bytes = 0;
1641        while transferred_bytes < data.len() {
1642            let bytes_remaining = data.len() - transferred_bytes;
1643            let bytes_to_transfer = std::cmp::min(bytes_remaining, MAX_NV_BUFFER_SIZE);
1644            let data_to_transfer = &data[transferred_bytes..transferred_bytes + bytes_to_transfer];
1645
1646            cmd.update_write_data(data_to_transfer, transferred_bytes as u16)
1647                .map_err(TpmCommandError::InvalidInputParameter)?;
1648
1649            self.tpm_engine
1650                .execute_command(&mut cmd.serialize(), &mut self.reply_buffer)
1651                .map_err(TpmCommandError::TpmExecuteCommand)?;
1652
1653            match NvWriteCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1654                Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1655                Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1656                    response_code: res.header.response_code.get(),
1657                })?,
1658                Ok((_res, true)) => {}
1659            }
1660
1661            transferred_bytes += bytes_to_transfer;
1662        }
1663
1664        Ok(())
1665    }
1666
1667    /// Helper function to send NV_Read command.
1668    ///
1669    /// # Arguments
1670    /// * `auth_handle`: The authorization handle used in the command.
1671    /// * `nv_index` - The NV index to read.
1672    /// * `nv_index_size` - Size of NV index.
1673    /// * `data` - The output buffer to hold the data read from the NV index.
1674    ///
1675    pub fn nv_read(
1676        &mut self,
1677        auth_handle: ReservedHandle,
1678        nv_index: u32,
1679        nv_index_size: u16,
1680        data: &mut [u8],
1681    ) -> Result<(), TpmCommandError> {
1682        use tpm20proto::protocol::NvReadCmd;
1683
1684        let session_tag = SessionTagEnum::Sessions;
1685        let mut nv_read = NvReadCmd::new(
1686            session_tag.into(),
1687            auth_handle,
1688            nv_index,
1689            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1690            0,
1691            0,
1692        );
1693
1694        let mut transferred_bytes = 0;
1695        let total_bytes = std::cmp::min(nv_index_size, data.len() as u16);
1696
1697        while transferred_bytes < total_bytes {
1698            let bytes_remaining = total_bytes - transferred_bytes;
1699            let bytes_to_transfer = std::cmp::min(bytes_remaining, MAX_NV_BUFFER_SIZE as u16);
1700
1701            nv_read.update_read_parameters(bytes_to_transfer, transferred_bytes);
1702
1703            self.tpm_engine
1704                .execute_command(nv_read.as_mut_bytes(), &mut self.reply_buffer)
1705                .map_err(TpmCommandError::TpmExecuteCommand)?;
1706
1707            let res = match NvReadCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1708                Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1709                Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1710                    response_code: res.header.response_code.get(),
1711                })?,
1712                Ok((res, true)) => res,
1713            };
1714
1715            data[transferred_bytes as usize..(transferred_bytes + bytes_to_transfer) as usize]
1716                .copy_from_slice(&res.data.buffer[..bytes_to_transfer as usize]);
1717            transferred_bytes += bytes_to_transfer;
1718        }
1719
1720        Ok(())
1721    }
1722
1723    /// Helper function to send Import command.
1724    ///
1725    /// # Arguments
1726    /// * `auth_handle`: The authorization handle used in the command.
1727    /// * `object_public` - The public part of the key to be imported.
1728    /// * `duplicate` - The private part of the key to be imported.
1729    /// * `in_sym_seed` - The value associated with `duplicate`.
1730    ///
1731    fn import(
1732        &mut self,
1733        auth_handle: ReservedHandle,
1734        object_public: &Tpm2bPublic,
1735        duplicate: &Tpm2bBuffer,
1736        in_sym_seed: &Tpm2bBuffer,
1737    ) -> Result<ImportReply, TpmCommandError> {
1738        use tpm20proto::protocol::ImportCmd;
1739
1740        // Assuming there is no inner wrapper
1741        let encryption_key = Tpm2bBuffer::new_zeroed();
1742        let symmetric_alg = TpmtSymDefObject::new(AlgIdEnum::NULL.into(), None, None);
1743
1744        let session_tag = SessionTagEnum::Sessions;
1745        let cmd = ImportCmd::new(
1746            session_tag.into(),
1747            auth_handle,
1748            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1749            &encryption_key,
1750            object_public,
1751            duplicate,
1752            in_sym_seed,
1753            &symmetric_alg,
1754        );
1755
1756        self.tpm_engine
1757            .execute_command(&mut cmd.serialize(), &mut self.reply_buffer)
1758            .map_err(TpmCommandError::TpmExecuteCommand)?;
1759
1760        match ImportCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1761            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1762            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1763                response_code: res.header.response_code.get(),
1764            })?,
1765            Ok((res, true)) => Ok(res),
1766        }
1767    }
1768
1769    /// Helper function to send Load command.
1770    ///
1771    /// # Arguments
1772    /// * `auth_handle`: The authorization handle used in the command.
1773    /// * `in_private` - The private part of the key to be loaded.
1774    /// * `in_public` - The public part of the key to be loaded.
1775    ///
1776    fn load(
1777        &mut self,
1778        auth_handle: ReservedHandle,
1779        in_private: &Tpm2bBuffer,
1780        in_public: &Tpm2bPublic,
1781    ) -> Result<LoadReply, TpmCommandError> {
1782        use tpm20proto::protocol::LoadCmd;
1783
1784        let session_tag = SessionTagEnum::Sessions;
1785        let cmd = LoadCmd::new(
1786            session_tag.into(),
1787            auth_handle,
1788            CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
1789            in_private,
1790            in_public,
1791        );
1792
1793        self.tpm_engine
1794            .execute_command(&mut cmd.serialize(), &mut self.reply_buffer)
1795            .map_err(TpmCommandError::TpmExecuteCommand)?;
1796
1797        match LoadCmd::base_validate_reply(&self.reply_buffer, session_tag) {
1798            Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
1799            Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
1800                response_code: res.header.response_code.get(),
1801            })?,
1802            Ok((res, true)) => Ok(res),
1803        }
1804    }
1805}
1806
1807/// Returns the public template for AK.
1808pub fn ak_pub_template() -> Result<TpmtPublic, TpmHelperUtilityError> {
1809    let symmetric = TpmtSymDefObject::new(AlgIdEnum::NULL.into(), None, None);
1810    let scheme = TpmtRsaScheme::new(AlgIdEnum::RSASSA.into(), Some(AlgIdEnum::SHA256.into()));
1811    let rsa_params = TpmsRsaParams::new(symmetric, scheme, crate::RSA_2K_MODULUS_BITS, 0);
1812
1813    let object_attributes = TpmaObjectBits::new()
1814        .with_fixed_tpm(true)
1815        .with_fixed_parent(true)
1816        .with_sensitive_data_origin(true)
1817        .with_user_with_auth(true)
1818        .with_no_da(true)
1819        .with_restricted(true)
1820        .with_sign_encrypt(true);
1821
1822    let in_public = TpmtPublic::new(
1823        AlgIdEnum::RSA.into(),
1824        AlgIdEnum::SHA256.into(),
1825        object_attributes,
1826        &[],
1827        rsa_params,
1828        &[0u8; crate::RSA_2K_MODULUS_SIZE],
1829    )
1830    .map_err(TpmHelperUtilityError::InvalidInputParameter)?;
1831
1832    Ok(in_public)
1833}
1834
1835/// Returns the public template for the EK.
1836pub fn ek_pub_template() -> Result<TpmtPublic, TpmHelperUtilityError> {
1837    // Create Windows-style EK.
1838    // The following parameters are based on low-range RSA 2048 EK Template.
1839    // See B 3.3 & 6.2, "TCG EK Credential Profile", version 2.5.
1840    const AUTH_POLICY_A_SHA_256: [u8; 32] = [
1841        0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, 0xD7,
1842        0x24, 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, 0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14,
1843        0x69, 0xAA,
1844    ];
1845    let symmetric = TpmtSymDefObject::new(
1846        AlgIdEnum::AES.into(),
1847        Some(128),
1848        Some(AlgIdEnum::CFB.into()),
1849    );
1850    let scheme = TpmtRsaScheme::new(AlgIdEnum::NULL.into(), None);
1851    let rsa_params = TpmsRsaParams::new(symmetric, scheme, crate::RSA_2K_MODULUS_BITS, 0);
1852
1853    let object_attributes = TpmaObjectBits::new()
1854        .with_fixed_tpm(true)
1855        .with_fixed_parent(true)
1856        .with_sensitive_data_origin(true)
1857        .with_admin_with_policy(true)
1858        .with_restricted(true)
1859        .with_decrypt(true);
1860
1861    let in_public = TpmtPublic::new(
1862        AlgIdEnum::RSA.into(),
1863        AlgIdEnum::SHA256.into(),
1864        object_attributes,
1865        &AUTH_POLICY_A_SHA_256,
1866        rsa_params,
1867        &[0u8; crate::RSA_2K_MODULUS_SIZE],
1868    )
1869    .map_err(TpmHelperUtilityError::InvalidInputParameter)?;
1870
1871    Ok(in_public)
1872}
1873
1874/// Helper function for converting `Tpm2bPublic` to `TpmRsa2kPublic`.
1875fn export_rsa_public(public: &Tpm2bPublic) -> Result<TpmRsa2kPublic, TpmHelperUtilityError> {
1876    if public.public_area.parameters.exponent.get() != 0 {
1877        Err(TpmHelperUtilityError::UnexpectedRsaExponent)?
1878    }
1879
1880    // Use the default value (2^16 + 1) when exponent is 0.
1881    // See Table 186, Section 12.2.3.5, "Trusted Platform Module Library Part 2: Structures", revision 1.38.
1882    const DEFAULT_EXPONENT: [u8; RSA_2K_EXPONENT_SIZE] = [0x01, 0x00, 0x01];
1883    let mut modulus = [0u8; RSA_2K_MODULUS_SIZE];
1884    let output = public.public_area.unique.serialize();
1885    let buffer_offset = size_of_val(&public.public_area.unique.size);
1886
1887    if output.len() != buffer_offset + RSA_2K_MODULUS_SIZE {
1888        Err(TpmHelperUtilityError::UnexpectedRsaModulusSize)?
1889    }
1890
1891    modulus.copy_from_slice(&output[buffer_offset..buffer_offset + RSA_2K_MODULUS_SIZE]);
1892
1893    Ok(TpmRsa2kPublic {
1894        exponent: DEFAULT_EXPONENT,
1895        modulus,
1896    })
1897}
1898
1899#[cfg(test)]
1900mod tests {
1901    use super::*;
1902    use crate::TPM_AZURE_AIK_HANDLE;
1903    use crate::TPM_NV_INDEX_AIK_CERT;
1904    use crate::TPM_NV_INDEX_ATTESTATION_REPORT;
1905    use crate::Tpm;
1906    use crate::ak_cert::RequestAkCert;
1907    use crate::ak_cert::TpmAkCertType;
1908    use crate::tpm20proto::ResponseCode;
1909    use crate::tpm20proto::TPM20_HT_PERSISTENT;
1910    use crate::tpm20proto::TPM20_RH_ENDORSEMENT;
1911    use crate::tpm20proto::TPM20_RH_OWNER;
1912    use crate::tpm20proto::TPM20_RH_PLATFORM;
1913    use guestmem::GuestMemory;
1914    use ms_tpm_20_ref::DynResult;
1915    use pal_async::async_test;
1916    use std::sync::Arc;
1917    use std::time::Instant;
1918    use tpm_resources::TpmRegisterLayout;
1919    use tpm20proto::AlgId;
1920    use vmcore::non_volatile_store::EphemeralNonVolatileStore;
1921
1922    const TPM_AZURE_EK_HANDLE: ReservedHandle = ReservedHandle::new(TPM20_HT_PERSISTENT, 0x010001);
1923    const AUTH_VALUE: u64 = 0x7766554433221100;
1924
1925    /// Sample platform callback implementation for testing purposes.
1926    struct TestPlatformCallbacks {
1927        blob: Vec<u8>,
1928        time: Instant,
1929    }
1930
1931    impl ms_tpm_20_ref::PlatformCallbacks for TestPlatformCallbacks {
1932        fn commit_nv_state(&mut self, state: &[u8]) -> DynResult<()> {
1933            self.blob = state.to_vec();
1934
1935            Ok(())
1936        }
1937
1938        fn get_crypt_random(&mut self, buf: &mut [u8]) -> DynResult<usize> {
1939            getrandom::fill(buf).expect("rng failure");
1940
1941            Ok(buf.len())
1942        }
1943
1944        fn monotonic_timer(&mut self) -> std::time::Duration {
1945            self.time.elapsed()
1946        }
1947
1948        fn get_unique_value(&self) -> &'static [u8] {
1949            b"vtpm test"
1950        }
1951    }
1952
1953    fn create_tpm_engine_helper() -> TpmEngineHelper {
1954        let result = MsTpm20RefPlatform::initialize(
1955            Box::new(TestPlatformCallbacks {
1956                blob: vec![],
1957                time: Instant::now(),
1958            }),
1959            ms_tpm_20_ref::InitKind::ColdInit,
1960        );
1961        assert!(result.is_ok());
1962
1963        let tpm_engine = result.unwrap();
1964
1965        TpmEngineHelper {
1966            tpm_engine,
1967            reply_buffer: [0u8; 4096],
1968        }
1969    }
1970
1971    fn restart_tpm_engine(
1972        tpm_engine_helper: &mut TpmEngineHelper,
1973        clear_context: bool,
1974        initialize: bool,
1975    ) {
1976        if clear_context {
1977            let result = tpm_engine_helper.clear_tpm_platform_context();
1978            assert!(result.is_ok());
1979        }
1980
1981        let result = tpm_engine_helper.tpm_engine.reset(None);
1982        assert!(result.is_ok());
1983
1984        if initialize {
1985            let result = tpm_engine_helper.initialize_tpm_engine();
1986            assert!(result.is_ok());
1987        }
1988    }
1989
1990    #[test]
1991    fn test_create_ak_ek_pub() {
1992        let mut tpm_engine_helper = create_tpm_engine_helper();
1993        restart_tpm_engine(&mut tpm_engine_helper, false, true);
1994
1995        // Test creating AK and EK
1996
1997        // Ensure nothing present
1998        assert!(
1999            tpm_engine_helper
2000                .find_object(TPM_AZURE_AIK_HANDLE)
2001                .unwrap()
2002                .is_none()
2003        );
2004        assert!(
2005            tpm_engine_helper
2006                .find_object(TPM_AZURE_EK_HANDLE)
2007                .unwrap()
2008                .is_none()
2009        );
2010
2011        let (ak_pub_first, ek_pub_first) = create_ak_ek_pub(&mut tpm_engine_helper);
2012
2013        // Test creating AK and EK with clearing context
2014
2015        restart_tpm_engine(&mut tpm_engine_helper, true, true);
2016
2017        // Ensure nothing present after context is cleared and tpm reset
2018        assert!(
2019            tpm_engine_helper
2020                .find_object(TPM_AZURE_AIK_HANDLE)
2021                .unwrap()
2022                .is_none()
2023        );
2024        assert!(
2025            tpm_engine_helper
2026                .find_object(TPM_AZURE_EK_HANDLE)
2027                .unwrap()
2028                .is_none()
2029        );
2030
2031        let (ak_pub_second, ek_pub_second) = create_ak_ek_pub(&mut tpm_engine_helper);
2032
2033        // Ensure AK and EK match across reset if seeds do not change
2034        assert_eq!(ak_pub_first, ak_pub_second);
2035        assert_eq!(ek_pub_first, ek_pub_second);
2036
2037        // Test creating AK and EK without clearing context and force_create = false
2038
2039        restart_tpm_engine(&mut tpm_engine_helper, false, true);
2040
2041        // Ensure that AK is persisted across reset without clearing context
2042        assert!(
2043            tpm_engine_helper
2044                .find_object(TPM_AZURE_AIK_HANDLE)
2045                .unwrap()
2046                .is_some()
2047        );
2048        assert!(
2049            tpm_engine_helper
2050                .find_object(TPM_AZURE_EK_HANDLE)
2051                .unwrap()
2052                .is_none()
2053        );
2054
2055        let (ak_pub_third, ek_pub_third) = create_ak_ek_pub(&mut tpm_engine_helper);
2056
2057        // Ensure AK and EK match across reset if seeds do not change
2058        assert_eq!(ak_pub_second, ak_pub_third);
2059        assert_eq!(ek_pub_second, ek_pub_third);
2060
2061        // Test creating AK and EK without clearing context and force_create = true
2062
2063        restart_tpm_engine(&mut tpm_engine_helper, false, true);
2064
2065        // Ensure that AK is persisted across reset without clearing context
2066        assert!(
2067            tpm_engine_helper
2068                .find_object(TPM_AZURE_AIK_HANDLE)
2069                .unwrap()
2070                .is_some()
2071        );
2072        assert!(
2073            tpm_engine_helper
2074                .find_object(TPM_AZURE_EK_HANDLE)
2075                .unwrap()
2076                .is_none()
2077        );
2078
2079        let (ak_pub_fourth, ek_pub_fourth) = create_ak_ek_pub(&mut tpm_engine_helper);
2080
2081        // Ensure AK and EK match across reset if seeds do not change
2082        assert_eq!(ak_pub_third, ak_pub_fourth);
2083        assert_eq!(ek_pub_third, ek_pub_fourth);
2084
2085        // Test creating AK and EK after refreshing TPM seeds
2086
2087        restart_tpm_engine(&mut tpm_engine_helper, false, true);
2088
2089        let result = tpm_engine_helper.refresh_tpm_seeds();
2090        assert!(result.is_ok());
2091
2092        // Ensure nothing present after seeds refreshment
2093        assert!(
2094            tpm_engine_helper
2095                .find_object(TPM_AZURE_AIK_HANDLE)
2096                .unwrap()
2097                .is_none()
2098        );
2099        assert!(
2100            tpm_engine_helper
2101                .find_object(TPM_AZURE_EK_HANDLE)
2102                .unwrap()
2103                .is_none()
2104        );
2105
2106        let (ak_pub_fifth, ek_pub_fifth) = create_ak_ek_pub(&mut tpm_engine_helper);
2107
2108        // Ensure AK and EK mismatch across reset if seeds do change
2109        assert_ne!(ak_pub_fourth, ak_pub_fifth);
2110        assert_ne!(ek_pub_fourth, ek_pub_fifth);
2111    }
2112
2113    fn create_ak_ek_pub(
2114        tpm_engine_helper: &mut TpmEngineHelper,
2115    ) -> (TpmRsa2kPublic, TpmRsa2kPublic) {
2116        let result = tpm_engine_helper.create_ak_pub(false);
2117        assert!(result.is_ok());
2118        let (ak_pub, _) = result.unwrap();
2119
2120        // Ensure `create_ak_pub` persists AK
2121        assert!(
2122            tpm_engine_helper
2123                .find_object(TPM_AZURE_AIK_HANDLE)
2124                .unwrap()
2125                .is_some()
2126        );
2127        assert!(
2128            tpm_engine_helper
2129                .find_object(TPM_AZURE_EK_HANDLE)
2130                .unwrap()
2131                .is_none()
2132        );
2133
2134        let result = tpm_engine_helper.create_ek_pub();
2135        assert!(result.is_ok());
2136        let ek_pub = result.unwrap();
2137
2138        // Ensure `create_ek_pub` does not persist anything
2139        assert!(
2140            tpm_engine_helper
2141                .find_object(TPM_AZURE_AIK_HANDLE)
2142                .unwrap()
2143                .is_some()
2144        );
2145        assert!(
2146            tpm_engine_helper
2147                .find_object(TPM_AZURE_EK_HANDLE)
2148                .unwrap()
2149                .is_none()
2150        );
2151
2152        (ak_pub, ek_pub)
2153    }
2154
2155    #[test]
2156    fn test_allocate_guest_attestation_nv_indices() {
2157        const AK_CERT_INPUT_512: [u8; 512] = [7u8; 512];
2158        const AK_CERT_INPUT_1024: [u8; 1024] = [8u8; 1024];
2159        const ATTESTATION_REPORT_INPUT: [u8; 256] = [6u8; 256];
2160
2161        let mut tpm_engine_helper = create_tpm_engine_helper();
2162        restart_tpm_engine(&mut tpm_engine_helper, false, true);
2163
2164        // Test allocation without initial states and with with preserve_ak_cert = true, support_attestation_report = false
2165        // Expect only the ak cert nv index to be created but with no data
2166        // Do not write AK cert data to index after allocation.
2167        {
2168            let mut ak_cert_output = [0u8; MAX_NV_INDEX_SIZE as usize];
2169            let mut attestation_report_output = [0u8; MAX_ATTESTATION_INDEX_SIZE as usize];
2170
2171            // Ensure both nv indices are not present
2172            let result =
2173                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2174            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2175
2176            let result = tpm_engine_helper.read_from_nv_index(
2177                TPM_NV_INDEX_ATTESTATION_REPORT,
2178                &mut attestation_report_output,
2179            );
2180            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2181
2182            restart_tpm_engine(&mut tpm_engine_helper, true, true);
2183
2184            let result = tpm_engine_helper
2185                .allocate_guest_attestation_nv_indices(AUTH_VALUE, true, false, false);
2186            assert!(result.is_ok());
2187
2188            // Ensure ak cert nv index becomes uninitialized
2189            let result =
2190                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2191            assert!(matches!(result.unwrap(), NvIndexState::Uninitialized));
2192
2193            let result = tpm_engine_helper.read_from_nv_index(
2194                TPM_NV_INDEX_ATTESTATION_REPORT,
2195                &mut attestation_report_output,
2196            );
2197            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2198        }
2199
2200        // Test allocation without initial states and with with preserve_ak_cert = true, support_attestation_report = false
2201        // Expect only the ak cert nv index to be created but with no data
2202        // Write AK cert data to index after allocation.
2203        {
2204            let mut ak_cert_output = [0u8; MAX_NV_INDEX_SIZE as usize];
2205            let mut attestation_report_output = [0u8; MAX_ATTESTATION_INDEX_SIZE as usize];
2206
2207            restart_tpm_engine(&mut tpm_engine_helper, true, true);
2208
2209            // Ensure only ak cert index is present but uninitialized after reboot
2210            let result =
2211                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2212            assert!(matches!(result.unwrap(), NvIndexState::Uninitialized));
2213
2214            let result = tpm_engine_helper.read_from_nv_index(
2215                TPM_NV_INDEX_ATTESTATION_REPORT,
2216                &mut attestation_report_output,
2217            );
2218            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2219
2220            let result = tpm_engine_helper
2221                .allocate_guest_attestation_nv_indices(AUTH_VALUE, true, false, false);
2222            assert!(result.is_ok());
2223
2224            // Ensure only ak cert index remains present but uninitialized
2225            let result =
2226                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2227            assert!(matches!(result.unwrap(), NvIndexState::Uninitialized));
2228
2229            let result = tpm_engine_helper.read_from_nv_index(
2230                TPM_NV_INDEX_ATTESTATION_REPORT,
2231                &mut attestation_report_output,
2232            );
2233            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2234
2235            // Write to ak cert nv
2236            let result = tpm_engine_helper.write_to_nv_index(
2237                AUTH_VALUE,
2238                TPM_NV_INDEX_AIK_CERT,
2239                &AK_CERT_INPUT_512,
2240            );
2241            assert!(result.is_ok());
2242
2243            // Read the data and ensure it is zero-padded
2244            let result =
2245                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2246            assert!(matches!(result.unwrap(), NvIndexState::Available));
2247            let input_with_padding = {
2248                let mut input = AK_CERT_INPUT_512.to_vec();
2249                input.resize(MAX_NV_INDEX_SIZE.into(), 0);
2250                input
2251            };
2252            assert_eq!(&ak_cert_output, input_with_padding.as_slice());
2253        }
2254
2255        // Test allocation after a restart with preserve_ak_cert = true, support_attestation_report = false
2256        // Expect the content of ak cert nv index to be re-created and the ak cert is preserved
2257        {
2258            let mut ak_cert_output = [0u8; MAX_NV_INDEX_SIZE as usize];
2259            let mut attestation_report_output = [0u8; MAX_ATTESTATION_INDEX_SIZE as usize];
2260
2261            restart_tpm_engine(&mut tpm_engine_helper, true, true);
2262
2263            // Ensure only ak cert index remains available after reboot
2264            let result =
2265                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2266            assert!(matches!(result.unwrap(), NvIndexState::Available));
2267
2268            let result = tpm_engine_helper.read_from_nv_index(
2269                TPM_NV_INDEX_ATTESTATION_REPORT,
2270                &mut attestation_report_output,
2271            );
2272            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2273
2274            let result = tpm_engine_helper
2275                .allocate_guest_attestation_nv_indices(AUTH_VALUE, true, false, false);
2276            assert!(result.is_ok());
2277
2278            // Ensure only ak cert index remains available
2279            let result =
2280                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2281            assert!(matches!(result.unwrap(), NvIndexState::Available));
2282
2283            let result = tpm_engine_helper.read_from_nv_index(
2284                TPM_NV_INDEX_ATTESTATION_REPORT,
2285                &mut attestation_report_output,
2286            );
2287            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2288
2289            // Read the data and ensure it is zero-padded
2290            let result =
2291                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2292            assert!(matches!(result.unwrap(), NvIndexState::Available));
2293            let input_with_padding = {
2294                let mut input = AK_CERT_INPUT_512.to_vec();
2295                input.resize(MAX_NV_INDEX_SIZE.into(), 0);
2296                input
2297            };
2298            assert_eq!(&ak_cert_output, input_with_padding.as_slice());
2299
2300            // Write to ak cert nv
2301            let result = tpm_engine_helper.write_to_nv_index(
2302                AUTH_VALUE,
2303                TPM_NV_INDEX_AIK_CERT,
2304                &AK_CERT_INPUT_1024,
2305            );
2306            assert!(result.is_ok());
2307
2308            // Read the data and ensure it is zero-padded
2309            let result =
2310                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2311            assert!(matches!(result.unwrap(), NvIndexState::Available));
2312            let input_with_padding = {
2313                let mut input = AK_CERT_INPUT_1024.to_vec();
2314                input.resize(MAX_NV_INDEX_SIZE.into(), 0);
2315                input
2316            };
2317            assert_eq!(&ak_cert_output, input_with_padding.as_slice());
2318        }
2319
2320        // Test allocation after a restart with preserve_ak_cert = false, support_attestation_report = false
2321        // Expect ak cert nv index to be re-created and the ak cert is not preserved
2322        {
2323            let mut ak_cert_output = [0u8; MAX_NV_INDEX_SIZE as usize];
2324            let mut attestation_report_output = [0u8; MAX_ATTESTATION_INDEX_SIZE as usize];
2325
2326            restart_tpm_engine(&mut tpm_engine_helper, true, true);
2327
2328            // Ensure only ak cert index remains available after reboot
2329            let result =
2330                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2331            assert!(matches!(result.unwrap(), NvIndexState::Available));
2332
2333            let result = tpm_engine_helper.read_from_nv_index(
2334                TPM_NV_INDEX_ATTESTATION_REPORT,
2335                &mut attestation_report_output,
2336            );
2337            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2338
2339            let result = tpm_engine_helper
2340                .allocate_guest_attestation_nv_indices(AUTH_VALUE, false, false, false);
2341            assert!(result.is_ok());
2342
2343            // Ensure read to fail given that the ak cert index is re-created and data is not preserved
2344            let result =
2345                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2346            assert!(matches!(result.unwrap(), NvIndexState::Uninitialized));
2347
2348            let result = tpm_engine_helper.read_from_nv_index(
2349                TPM_NV_INDEX_ATTESTATION_REPORT,
2350                &mut attestation_report_output,
2351            );
2352            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2353
2354            // Write to ak cert nv
2355            let result = tpm_engine_helper.write_to_nv_index(
2356                AUTH_VALUE,
2357                TPM_NV_INDEX_AIK_CERT,
2358                &AK_CERT_INPUT_512,
2359            );
2360            assert!(result.is_ok());
2361
2362            // Read the data and ensure it is zero-padded
2363            let result =
2364                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2365            assert!(matches!(result.unwrap(), NvIndexState::Available));
2366            let input_with_padding = {
2367                let mut input = AK_CERT_INPUT_512.to_vec();
2368                input.resize(MAX_NV_INDEX_SIZE.into(), 0);
2369                input
2370            };
2371            assert_eq!(&ak_cert_output, input_with_padding.as_slice());
2372        }
2373
2374        // Test allocation after a restart preserve_ak_cert = false, support_attestation_report = true
2375        // Expect ak cert nv index to be re-created and attestation report nv index to be created
2376        {
2377            let mut ak_cert_output = [0u8; MAX_NV_INDEX_SIZE as usize];
2378            let mut attestation_report_output = [0u8; MAX_ATTESTATION_INDEX_SIZE as usize];
2379
2380            restart_tpm_engine(&mut tpm_engine_helper, true, true);
2381
2382            // Ensure the state of indices remains the same after reboot
2383            let result =
2384                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2385            assert!(matches!(result.unwrap(), NvIndexState::Available));
2386
2387            let result = tpm_engine_helper.read_from_nv_index(
2388                TPM_NV_INDEX_ATTESTATION_REPORT,
2389                &mut attestation_report_output,
2390            );
2391            assert!(matches!(result.unwrap(), NvIndexState::Unallocated));
2392
2393            let result = tpm_engine_helper
2394                .allocate_guest_attestation_nv_indices(AUTH_VALUE, false, true, false);
2395            assert!(result.is_ok());
2396
2397            // Ensure read to fail given that the ak cert index is re-created and data is not preserved
2398            let result =
2399                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2400            assert!(matches!(result.unwrap(), NvIndexState::Uninitialized));
2401
2402            // Ensure read to fail given that the report index is created but uninitialized
2403            let result = tpm_engine_helper.read_from_nv_index(
2404                TPM_NV_INDEX_ATTESTATION_REPORT,
2405                &mut attestation_report_output,
2406            );
2407            assert!(matches!(result.unwrap(), NvIndexState::Uninitialized));
2408
2409            // Write to ak cert nv
2410            let result = tpm_engine_helper.write_to_nv_index(
2411                AUTH_VALUE,
2412                TPM_NV_INDEX_AIK_CERT,
2413                &AK_CERT_INPUT_512,
2414            );
2415            assert!(result.is_ok());
2416
2417            // Read the data and ensure it is zero-padded
2418            let result =
2419                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2420            assert!(matches!(result.unwrap(), NvIndexState::Available));
2421            let input_with_padding = {
2422                let mut input = AK_CERT_INPUT_512.to_vec();
2423                input.resize(MAX_NV_INDEX_SIZE.into(), 0);
2424                input
2425            };
2426            assert_eq!(&ak_cert_output, input_with_padding.as_slice());
2427
2428            // Write to attestation report nv
2429            let result = tpm_engine_helper.write_to_nv_index(
2430                AUTH_VALUE,
2431                TPM_NV_INDEX_ATTESTATION_REPORT,
2432                &ATTESTATION_REPORT_INPUT,
2433            );
2434            assert!(result.is_ok());
2435
2436            // Read the data and ensure it is zero-padded
2437            let result = tpm_engine_helper.read_from_nv_index(
2438                TPM_NV_INDEX_ATTESTATION_REPORT,
2439                &mut attestation_report_output,
2440            );
2441            assert!(matches!(result.unwrap(), NvIndexState::Available));
2442            let input_with_padding = {
2443                let mut input = ATTESTATION_REPORT_INPUT.to_vec();
2444                input.resize(MAX_ATTESTATION_INDEX_SIZE.into(), 0);
2445                input
2446            };
2447            assert_eq!(&attestation_report_output, input_with_padding.as_slice());
2448        }
2449
2450        // Test allocation after a restart preserve_ak_cert = false, support_attestation_report = true
2451        // Expect both ak cert and attestation report nv indices to be re-created
2452        {
2453            let mut ak_cert_output = [0u8; MAX_NV_INDEX_SIZE as usize];
2454            let mut attestation_report_output = [0u8; MAX_ATTESTATION_INDEX_SIZE as usize];
2455
2456            restart_tpm_engine(&mut tpm_engine_helper, true, true);
2457
2458            // Ensure the state of indices remains the same after reboot
2459            let result =
2460                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2461            assert!(matches!(result.unwrap(), NvIndexState::Available));
2462
2463            let result = tpm_engine_helper.read_from_nv_index(
2464                TPM_NV_INDEX_ATTESTATION_REPORT,
2465                &mut attestation_report_output,
2466            );
2467            assert!(matches!(result.unwrap(), NvIndexState::Available));
2468
2469            let result = tpm_engine_helper
2470                .allocate_guest_attestation_nv_indices(AUTH_VALUE, false, true, false);
2471            assert!(result.is_ok());
2472
2473            // Expect read to return Ok(false) given that the nv index is re-created and data is not preserved
2474            let result =
2475                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2476            assert!(matches!(result.unwrap(), NvIndexState::Uninitialized));
2477
2478            // Expect read to return Ok(false) given that the nv index is re-created and no data has been written
2479            let result = tpm_engine_helper.read_from_nv_index(
2480                TPM_NV_INDEX_ATTESTATION_REPORT,
2481                &mut attestation_report_output,
2482            );
2483            assert!(matches!(result.unwrap(), NvIndexState::Uninitialized));
2484        }
2485    }
2486
2487    #[test]
2488    fn test_read_write_guest_attestation_indices() {
2489        let mut tpm_engine_helper = create_tpm_engine_helper();
2490        restart_tpm_engine(&mut tpm_engine_helper, false, true);
2491
2492        let result =
2493            tpm_engine_helper.allocate_guest_attestation_nv_indices(AUTH_VALUE, true, true, false);
2494        assert!(result.is_ok());
2495
2496        let result = tpm_engine_helper.find_nv_index(TPM_NV_INDEX_AIK_CERT);
2497        assert!(result.is_ok());
2498        assert!(result.unwrap().is_some());
2499
2500        let result = tpm_engine_helper.find_nv_index(TPM_NV_INDEX_ATTESTATION_REPORT);
2501        assert!(result.is_ok());
2502        assert!(result.unwrap().is_some());
2503
2504        // Test writing to ak cert nv index with data size equal to index size
2505        {
2506            let ak_cert_input_equal = [7u8; MAX_NV_INDEX_SIZE as usize];
2507            let result = tpm_engine_helper.write_to_nv_index(
2508                AUTH_VALUE,
2509                TPM_NV_INDEX_AIK_CERT,
2510                &ak_cert_input_equal,
2511            );
2512            assert!(result.is_ok());
2513
2514            let mut ak_cert_output = [0u8; MAX_NV_INDEX_SIZE as usize];
2515            let result =
2516                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2517            assert!(result.is_ok());
2518            assert_eq!(&ak_cert_output, &ak_cert_input_equal);
2519        }
2520
2521        // Test writing to ak cert nv index with data size less than index size
2522        {
2523            let ak_cert_input_less = [7u8; MAX_NV_INDEX_SIZE as usize - 1024];
2524            let result = tpm_engine_helper.write_to_nv_index(
2525                AUTH_VALUE,
2526                TPM_NV_INDEX_AIK_CERT,
2527                &ak_cert_input_less,
2528            );
2529            assert!(result.is_ok());
2530
2531            // Read the data and ensure it is zero-padded
2532            let mut ak_cert_output = [0u8; MAX_NV_INDEX_SIZE as usize];
2533            let result =
2534                tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2535            assert!(result.is_ok());
2536            let input_with_padding = {
2537                let mut input = ak_cert_input_less.to_vec();
2538                input.resize(MAX_NV_INDEX_SIZE.into(), 0);
2539                input
2540            };
2541            assert_eq!(&ak_cert_output, input_with_padding.as_slice());
2542        }
2543
2544        // Test writing to ak cert nv index with data size larger than index size
2545        {
2546            let ak_cert_input_larger = [7u8; MAX_NV_INDEX_SIZE as usize + 1024];
2547            let result = tpm_engine_helper.write_to_nv_index(
2548                AUTH_VALUE,
2549                TPM_NV_INDEX_AIK_CERT,
2550                &ak_cert_input_larger,
2551            );
2552            assert!(result.is_err());
2553            let err = result.unwrap_err();
2554            if let TpmHelperError::NvWriteInputTooLarge {
2555                nv_index,
2556                input_size,
2557                allocated_size,
2558            } = err
2559            {
2560                assert_eq!(nv_index, TPM_NV_INDEX_AIK_CERT);
2561                assert_eq!(input_size, ak_cert_input_larger.len());
2562                assert_eq!(allocated_size, MAX_NV_INDEX_SIZE as usize);
2563            } else {
2564                panic!()
2565            }
2566        }
2567
2568        // Test writing to ak cert nv index with wrong authorization value
2569        {
2570            let ak_cert_input_larger = [7u8; MAX_NV_INDEX_SIZE as usize];
2571            let result = tpm_engine_helper.write_to_nv_index(
2572                0,
2573                TPM_NV_INDEX_AIK_CERT,
2574                &ak_cert_input_larger,
2575            );
2576            assert!(result.is_err());
2577            let err = result.unwrap_err();
2578            if let TpmHelperError::TpmCommandError {
2579                command_debug_info,
2580                error: command_error,
2581            } = err
2582            {
2583                assert_eq!(command_debug_info.nv_index, Some(TPM_NV_INDEX_AIK_CERT));
2584                assert_eq!(
2585                    command_debug_info.auth_handle,
2586                    Some(ReservedHandle(TPM_NV_INDEX_AIK_CERT.into()))
2587                );
2588                assert_eq!(command_debug_info.command_code, CommandCodeEnum::NV_Write);
2589                assert!(matches!(
2590                    command_error,
2591                    TpmCommandError::TpmCommandFailed { response_code: _ }
2592                ));
2593            }
2594        }
2595
2596        // Test writing to attestation report nv index with data size equal to index size
2597        {
2598            let report_input_equal = [7u8; MAX_ATTESTATION_INDEX_SIZE as usize];
2599            let result = tpm_engine_helper.write_to_nv_index(
2600                AUTH_VALUE,
2601                TPM_NV_INDEX_ATTESTATION_REPORT,
2602                &report_input_equal,
2603            );
2604            assert!(result.is_ok());
2605
2606            let mut report_output = [0u8; MAX_ATTESTATION_INDEX_SIZE as usize];
2607            let result = tpm_engine_helper
2608                .read_from_nv_index(TPM_NV_INDEX_ATTESTATION_REPORT, &mut report_output);
2609            assert!(result.is_ok());
2610            assert_eq!(&report_output, &report_input_equal);
2611        }
2612
2613        // Test writing to attestation report nv index with data size less than index size
2614        {
2615            let report_input_less = [7u8; MAX_ATTESTATION_INDEX_SIZE as usize - 1024];
2616            let result = tpm_engine_helper.write_to_nv_index(
2617                AUTH_VALUE,
2618                TPM_NV_INDEX_ATTESTATION_REPORT,
2619                &report_input_less,
2620            );
2621            assert!(result.is_ok());
2622
2623            // Read the data and ensure it is zero-padded
2624            let mut report_output = [0u8; MAX_ATTESTATION_INDEX_SIZE as usize];
2625            let result = tpm_engine_helper
2626                .read_from_nv_index(TPM_NV_INDEX_ATTESTATION_REPORT, &mut report_output);
2627            assert!(result.is_ok());
2628            let input_with_padding = {
2629                let mut input = report_input_less.to_vec();
2630                input.resize(MAX_ATTESTATION_INDEX_SIZE.into(), 0);
2631                input
2632            };
2633            assert_eq!(&report_output, input_with_padding.as_slice());
2634        }
2635
2636        // Test writing to attestation report nv index with data size larger than index size
2637        {
2638            let report_input_larger = [7u8; MAX_ATTESTATION_INDEX_SIZE as usize + 1024];
2639            let result = tpm_engine_helper.write_to_nv_index(
2640                AUTH_VALUE,
2641                TPM_NV_INDEX_ATTESTATION_REPORT,
2642                &report_input_larger,
2643            );
2644            assert!(result.is_err());
2645            let err = result.unwrap_err();
2646            if let TpmHelperError::NvWriteInputTooLarge {
2647                nv_index,
2648                input_size,
2649                allocated_size,
2650            } = err
2651            {
2652                assert_eq!(nv_index, TPM_NV_INDEX_ATTESTATION_REPORT);
2653                assert_eq!(input_size, report_input_larger.len());
2654                assert_eq!(allocated_size, MAX_ATTESTATION_INDEX_SIZE as usize);
2655            }
2656        }
2657
2658        // Test writing to attestation report nv index with wrong authorization value
2659        {
2660            let ak_cert_input_larger = [7u8; MAX_NV_INDEX_SIZE as usize];
2661            let result = tpm_engine_helper.write_to_nv_index(
2662                0,
2663                TPM_NV_INDEX_ATTESTATION_REPORT,
2664                &ak_cert_input_larger,
2665            );
2666            assert!(result.is_err());
2667            let err = result.unwrap_err();
2668            if let TpmHelperError::TpmCommandError {
2669                command_debug_info,
2670                error: command_error,
2671            } = err
2672            {
2673                assert_eq!(
2674                    command_debug_info.nv_index,
2675                    Some(TPM_NV_INDEX_ATTESTATION_REPORT)
2676                );
2677                assert_eq!(
2678                    command_debug_info.auth_handle,
2679                    Some(ReservedHandle(TPM_NV_INDEX_ATTESTATION_REPORT.into()))
2680                );
2681                assert_eq!(command_debug_info.command_code, CommandCodeEnum::NV_Write);
2682                assert!(matches!(
2683                    command_error,
2684                    TpmCommandError::TpmCommandFailed { response_code: _ }
2685                ));
2686            }
2687        }
2688    }
2689
2690    #[test]
2691    fn test_with_pre_provisioned_state() {
2692        // The blob file generated by the TpmEngFWInit (internal) tool.
2693        let tpm_state_blob = include_bytes!("../test_data/vTpmState.blob");
2694
2695        let mut tpm_engine_helper = create_tpm_engine_helper();
2696
2697        let result = tpm_engine_helper.tpm_engine.reset(Some(tpm_state_blob));
2698        assert!(result.is_ok());
2699
2700        let result = tpm_engine_helper.initialize_tpm_engine();
2701        assert!(result.is_ok());
2702
2703        // Ensure AK cert is provisioned
2704        let result = tpm_engine_helper.nv_read_public(TPM_NV_INDEX_AIK_CERT);
2705        assert!(result.is_ok());
2706        let nv_read_public_reply = result.unwrap();
2707
2708        // The provisioned nv size is less than the created one
2709        let nv_size = nv_read_public_reply.nv_public.nv_public.data_size.get();
2710        assert!(nv_size < MAX_NV_INDEX_SIZE);
2711
2712        // Ensure AK is provisioned
2713        assert!(
2714            tpm_engine_helper
2715                .find_object(TPM_AZURE_AIK_HANDLE)
2716                .unwrap()
2717                .is_some()
2718        );
2719
2720        let mut provisioned_ak_cert = [0u8; MAX_NV_INDEX_SIZE as usize];
2721        let result =
2722            tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut provisioned_ak_cert);
2723        assert!(matches!(result.unwrap(), NvIndexState::Available));
2724
2725        // Ensure allocate_guest_attestation_nv_indices with preserve_ak_cert = true preserves the ak cert data
2726        let result =
2727            tpm_engine_helper.allocate_guest_attestation_nv_indices(AUTH_VALUE, true, false, false);
2728        assert!(result.is_ok());
2729
2730        // Ensure nv index has the same size
2731        let result = tpm_engine_helper.nv_read_public(TPM_NV_INDEX_AIK_CERT);
2732        assert!(result.is_ok());
2733        let nv_read_public_reply = result.unwrap();
2734        assert!(nv_read_public_reply.nv_public.nv_public.data_size.get() == nv_size);
2735
2736        let mut provisioned_ak_cert_after_call = [0u8; MAX_NV_INDEX_SIZE as usize];
2737        let result = tpm_engine_helper
2738            .read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut provisioned_ak_cert_after_call);
2739        assert!(matches!(result.unwrap(), NvIndexState::Available));
2740        assert_eq!(provisioned_ak_cert_after_call, provisioned_ak_cert);
2741
2742        // Test updating the provisioned nv index (with ownerwrite permission)
2743        // Write a very short AKCert that will definitely fit in the already-provisioned space.
2744        let ak_cert_input = [7u8; 10];
2745        let result =
2746            tpm_engine_helper.nv_write(TPM20_RH_OWNER, None, TPM_NV_INDEX_AIK_CERT, &ak_cert_input);
2747        assert!(result.is_ok());
2748
2749        // Ensure the data is overwritten
2750        let mut ak_cert_output = [0u8; MAX_NV_INDEX_SIZE as usize];
2751        let result =
2752            tpm_engine_helper.read_from_nv_index(TPM_NV_INDEX_AIK_CERT, &mut ak_cert_output);
2753        assert!(matches!(result.unwrap(), NvIndexState::Available));
2754
2755        assert_ne!(&ak_cert_output, &provisioned_ak_cert);
2756
2757        // Ensure that write_to_nv_index fails because the nv index is not platform-defined
2758        let ak_cert_input = [8u8; 10];
2759        let result =
2760            tpm_engine_helper.write_to_nv_index(AUTH_VALUE, TPM_NV_INDEX_AIK_CERT, &ak_cert_input);
2761        assert!(result.is_err());
2762        assert!(matches!(
2763            result.unwrap_err(),
2764            TpmHelperError::InvalidPermission {
2765                platform_created: false,
2766                ..
2767            }
2768        ));
2769    }
2770
2771    #[test]
2772    fn test_initialize_guest_secret_key() {
2773        const GUEST_SECRET_KEY_BLOB: [u8; 422] = [
2774            0x01, 0x16, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10,
2775            0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xec, 0x0d, 0xdf, 0xf3,
2776            0xa2, 0x0f, 0xd4, 0x66, 0xe8, 0x53, 0x8a, 0x1c, 0x54, 0x00, 0x69, 0xbe, 0x57, 0xc4,
2777            0x9a, 0x7d, 0x4d, 0xd2, 0xbc, 0xd7, 0x6b, 0x93, 0xe4, 0x15, 0x3f, 0x2f, 0xbb, 0x77,
2778            0xf7, 0x1b, 0x19, 0x88, 0x04, 0xc7, 0x42, 0xda, 0xa2, 0x00, 0xc7, 0x8c, 0x2a, 0xfc,
2779            0x48, 0xa5, 0xe7, 0x3f, 0x4e, 0x06, 0x33, 0xa8, 0xb1, 0xcf, 0x09, 0x8c, 0xfe, 0x3f,
2780            0x91, 0x43, 0xa9, 0x4a, 0x8e, 0x05, 0xe7, 0xf0, 0x57, 0x68, 0xb5, 0x68, 0xe7, 0x7d,
2781            0xb3, 0x5c, 0xd5, 0x6c, 0xb9, 0x48, 0x5e, 0x0f, 0xf9, 0x0f, 0xe9, 0xf9, 0x42, 0x57,
2782            0x08, 0x8c, 0xff, 0x3f, 0x67, 0xd1, 0x9b, 0xb6, 0xa7, 0x7d, 0xa6, 0xa9, 0xcb, 0x00,
2783            0x4b, 0x1d, 0xa6, 0xf3, 0x09, 0xe0, 0x87, 0x12, 0xc6, 0x8b, 0xbe, 0x61, 0xaf, 0xc6,
2784            0x30, 0x35, 0xcc, 0x10, 0x68, 0x8b, 0x76, 0x36, 0x16, 0xcb, 0xce, 0x83, 0x6c, 0x7e,
2785            0x9e, 0x1e, 0x08, 0xc7, 0x20, 0x7d, 0x1d, 0xd4, 0xc4, 0x4f, 0x3a, 0x34, 0x06, 0xe9,
2786            0xae, 0xf5, 0x50, 0xd9, 0x5d, 0xb2, 0x30, 0x74, 0xed, 0x38, 0x74, 0x31, 0x3e, 0x1d,
2787            0xfd, 0x15, 0x26, 0x8f, 0x48, 0x5b, 0x22, 0x2f, 0xa0, 0xc3, 0xd0, 0x1c, 0x56, 0x4f,
2788            0xb1, 0x39, 0xe7, 0x93, 0xc1, 0x3d, 0x2d, 0x42, 0x57, 0x33, 0x4d, 0xdc, 0x90, 0x41,
2789            0x83, 0x6a, 0x21, 0x15, 0xbd, 0x2c, 0x5c, 0xa1, 0xc1, 0xda, 0xf9, 0x4c, 0x15, 0x89,
2790            0x41, 0x84, 0xad, 0xb9, 0xfc, 0xc7, 0x81, 0xa3, 0x93, 0xe9, 0xd8, 0xfc, 0xe3, 0x3f,
2791            0x4d, 0x6f, 0x71, 0x14, 0x9e, 0xe2, 0xe2, 0xfa, 0xa1, 0x8d, 0x3a, 0x80, 0xea, 0x5a,
2792            0xc9, 0x0f, 0x23, 0xb9, 0x3e, 0x36, 0xbb, 0xff, 0x4e, 0x9c, 0x40, 0x6f, 0x1d, 0x75,
2793            0x39, 0x96, 0x9b, 0xac, 0x54, 0xe1, 0x0b, 0x4b, 0x08, 0x3e, 0xd5, 0x94, 0x7d, 0xad,
2794            0x00, 0x8a, 0x00, 0x88, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xf7, 0xca,
2795            0x88, 0xe3, 0x6a, 0x67, 0xbd, 0xb7, 0xfe, 0xc9, 0x49, 0x35, 0x84, 0x23, 0xf3, 0x26,
2796            0x7f, 0xaa, 0xf6, 0xee, 0x14, 0x86, 0x55, 0xbf, 0x26, 0xd3, 0x21, 0x9f, 0x8a, 0xb2,
2797            0x1f, 0x2e, 0x79, 0x69, 0x7b, 0xa0, 0xad, 0x06, 0x2e, 0x13, 0xda, 0x8a, 0x5c, 0x59,
2798            0x98, 0x75, 0xf5, 0xfa, 0x2e, 0x14, 0xe6, 0xef, 0xc2, 0x3c, 0xa6, 0x11, 0x90, 0xf8,
2799            0xc3, 0x6f, 0x7d, 0xc5, 0x4c, 0x5c, 0xe8, 0x6a, 0x7f, 0x24, 0xa0, 0xef, 0x70, 0x5e,
2800            0xc8, 0x92, 0xa2, 0x3c, 0xa8, 0xa4, 0x0b, 0x38, 0xb1, 0xd5, 0xeb, 0x67, 0x8f, 0x76,
2801            0x65, 0x73, 0xd5, 0x6b, 0xb1, 0xad, 0x85, 0xb0, 0x0b, 0x0e, 0x41, 0x6b, 0xba, 0x1c,
2802            0x2a, 0x02, 0x11, 0xb7, 0xb4, 0x72, 0x74, 0xe2, 0x9f, 0x8e, 0x42, 0xa1, 0x38, 0x24,
2803            0x25, 0xc8, 0xcf, 0x53, 0x27, 0x1b, 0x4e, 0xcc, 0x8c, 0x0b, 0x4b, 0x69, 0x3f, 0x7b,
2804            0x00, 0x00,
2805        ];
2806
2807        const GUEST_SECRET_KEY_PUBLIC: [u8; 256] = [
2808            0xec, 0x0d, 0xdf, 0xf3, 0xa2, 0x0f, 0xd4, 0x66, 0xe8, 0x53, 0x8a, 0x1c, 0x54, 0x00,
2809            0x69, 0xbe, 0x57, 0xc4, 0x9a, 0x7d, 0x4d, 0xd2, 0xbc, 0xd7, 0x6b, 0x93, 0xe4, 0x15,
2810            0x3f, 0x2f, 0xbb, 0x77, 0xf7, 0x1b, 0x19, 0x88, 0x04, 0xc7, 0x42, 0xda, 0xa2, 0x00,
2811            0xc7, 0x8c, 0x2a, 0xfc, 0x48, 0xa5, 0xe7, 0x3f, 0x4e, 0x06, 0x33, 0xa8, 0xb1, 0xcf,
2812            0x09, 0x8c, 0xfe, 0x3f, 0x91, 0x43, 0xa9, 0x4a, 0x8e, 0x05, 0xe7, 0xf0, 0x57, 0x68,
2813            0xb5, 0x68, 0xe7, 0x7d, 0xb3, 0x5c, 0xd5, 0x6c, 0xb9, 0x48, 0x5e, 0x0f, 0xf9, 0x0f,
2814            0xe9, 0xf9, 0x42, 0x57, 0x08, 0x8c, 0xff, 0x3f, 0x67, 0xd1, 0x9b, 0xb6, 0xa7, 0x7d,
2815            0xa6, 0xa9, 0xcb, 0x00, 0x4b, 0x1d, 0xa6, 0xf3, 0x09, 0xe0, 0x87, 0x12, 0xc6, 0x8b,
2816            0xbe, 0x61, 0xaf, 0xc6, 0x30, 0x35, 0xcc, 0x10, 0x68, 0x8b, 0x76, 0x36, 0x16, 0xcb,
2817            0xce, 0x83, 0x6c, 0x7e, 0x9e, 0x1e, 0x08, 0xc7, 0x20, 0x7d, 0x1d, 0xd4, 0xc4, 0x4f,
2818            0x3a, 0x34, 0x06, 0xe9, 0xae, 0xf5, 0x50, 0xd9, 0x5d, 0xb2, 0x30, 0x74, 0xed, 0x38,
2819            0x74, 0x31, 0x3e, 0x1d, 0xfd, 0x15, 0x26, 0x8f, 0x48, 0x5b, 0x22, 0x2f, 0xa0, 0xc3,
2820            0xd0, 0x1c, 0x56, 0x4f, 0xb1, 0x39, 0xe7, 0x93, 0xc1, 0x3d, 0x2d, 0x42, 0x57, 0x33,
2821            0x4d, 0xdc, 0x90, 0x41, 0x83, 0x6a, 0x21, 0x15, 0xbd, 0x2c, 0x5c, 0xa1, 0xc1, 0xda,
2822            0xf9, 0x4c, 0x15, 0x89, 0x41, 0x84, 0xad, 0xb9, 0xfc, 0xc7, 0x81, 0xa3, 0x93, 0xe9,
2823            0xd8, 0xfc, 0xe3, 0x3f, 0x4d, 0x6f, 0x71, 0x14, 0x9e, 0xe2, 0xe2, 0xfa, 0xa1, 0x8d,
2824            0x3a, 0x80, 0xea, 0x5a, 0xc9, 0x0f, 0x23, 0xb9, 0x3e, 0x36, 0xbb, 0xff, 0x4e, 0x9c,
2825            0x40, 0x6f, 0x1d, 0x75, 0x39, 0x96, 0x9b, 0xac, 0x54, 0xe1, 0x0b, 0x4b, 0x08, 0x3e,
2826            0xd5, 0x94, 0x7d, 0xad,
2827        ];
2828
2829        // The blob file generated by the TpmEngFWInit (internal) tool.
2830        let tpm_state_blob = include_bytes!("../test_data/vTpmState.blob");
2831
2832        let mut tpm_engine_helper = create_tpm_engine_helper();
2833
2834        let result = tpm_engine_helper.tpm_engine.reset(Some(tpm_state_blob));
2835        assert!(result.is_ok());
2836
2837        let result = tpm_engine_helper.initialize_tpm_engine();
2838        assert!(result.is_ok());
2839
2840        // Ensure SRK is provisioned
2841        assert!(
2842            tpm_engine_helper
2843                .find_object(TPM_RSA_SRK_HANDLE)
2844                .unwrap()
2845                .is_some()
2846        );
2847
2848        // Ensure guest secret key is not initialized yet
2849        assert!(
2850            tpm_engine_helper
2851                .find_object(TPM_GUEST_SECRET_HANDLE)
2852                .unwrap()
2853                .is_none()
2854        );
2855
2856        // Negative test: invalid data blob
2857        let result = tpm_engine_helper.initialize_guest_secret_key(&[]);
2858        assert!(result.is_err());
2859        let err = result.unwrap_err();
2860        assert!(matches!(err, TpmHelperError::DeserializeGuestSecretKey));
2861
2862        // Positive test
2863
2864        // Apply zero paddings to `GUEST_SECRET_KEY_MAX_SIZE`
2865        let data_with_zero_paddings = {
2866            let mut data = GUEST_SECRET_KEY_BLOB.to_vec();
2867            data.resize(2048, 0);
2868
2869            data
2870        };
2871
2872        let result = tpm_engine_helper.initialize_guest_secret_key(&data_with_zero_paddings);
2873        assert!(result.is_ok());
2874
2875        // Ensure guest secret key is initialized
2876        let result = tpm_engine_helper.find_object(TPM_GUEST_SECRET_HANDLE);
2877        assert!(result.is_ok());
2878        let result = result.unwrap();
2879        assert!(result.is_some());
2880
2881        let read_public_reply = result.unwrap();
2882        let unique = read_public_reply.out_public.public_area.unique.serialize();
2883        let offset = size_of_val(&read_public_reply.out_public.public_area.unique.size);
2884        assert_eq!(
2885            &unique[offset..offset + RSA_2K_MODULUS_SIZE],
2886            GUEST_SECRET_KEY_PUBLIC
2887        );
2888
2889        // Negative test: Test without SRK
2890
2891        restart_tpm_engine(&mut tpm_engine_helper, true, true);
2892
2893        // Ensure SRK is not provisioned
2894        assert!(
2895            tpm_engine_helper
2896                .find_object(TPM_RSA_SRK_HANDLE)
2897                .unwrap()
2898                .is_none()
2899        );
2900
2901        // Ensure guest secret key is not initialized yet
2902        assert!(
2903            tpm_engine_helper
2904                .find_object(TPM_GUEST_SECRET_HANDLE)
2905                .unwrap()
2906                .is_none()
2907        );
2908
2909        // Expect to fail due to SRK not found
2910        let result = tpm_engine_helper.initialize_guest_secret_key(&GUEST_SECRET_KEY_BLOB);
2911        assert!(result.is_err());
2912        if let TpmHelperError::SrkNotFound(srk_handle) = result.unwrap_err() {
2913            assert_eq!(srk_handle, TPM_RSA_SRK_HANDLE);
2914        } else {
2915            panic!()
2916        }
2917    }
2918
2919    #[test]
2920    fn test_startup_and_self_test() {
2921        let mut tpm_engine_helper = create_tpm_engine_helper();
2922        restart_tpm_engine(&mut tpm_engine_helper, false, false);
2923
2924        // Negative test for SelfTest (expect to fail before StartUp is called)
2925        let result = tpm_engine_helper.self_test(true);
2926        assert!(result.is_err());
2927        let err = result.unwrap_err();
2928        if let TpmCommandError::TpmCommandFailed { response_code } = err {
2929            assert_ne!(response_code, ResponseCode::Success as u32);
2930        } else {
2931            panic!()
2932        }
2933
2934        // Positive tests
2935        let result = tpm_engine_helper.startup(StartupType::Clear);
2936        assert!(result.is_ok());
2937
2938        let result = tpm_engine_helper.self_test(true);
2939        assert!(result.is_ok());
2940
2941        // Negative test for StartUp
2942        let result = tpm_engine_helper.startup(StartupType::Clear);
2943        assert!(result.is_err());
2944        let err = result.unwrap_err();
2945        if let TpmCommandError::TpmCommandFailed { response_code } = err {
2946            assert_ne!(response_code, ResponseCode::Success as u32);
2947        } else {
2948            panic!()
2949        }
2950    }
2951
2952    #[test]
2953    fn test_change_seed() {
2954        let mut tpm_engine_helper = create_tpm_engine_helper();
2955        restart_tpm_engine(&mut tpm_engine_helper, false, true);
2956
2957        // Positive test
2958        let auth_handle = TPM20_RH_PLATFORM;
2959        let result = tpm_engine_helper.change_seed(auth_handle, CommandCodeEnum::ChangeEPS);
2960        assert!(result.is_ok());
2961
2962        let result = tpm_engine_helper.change_seed(auth_handle, CommandCodeEnum::ChangePPS);
2963        assert!(result.is_ok());
2964
2965        // Negative test
2966        let invalid_auth_handle = ReservedHandle(0.into());
2967        let result = tpm_engine_helper.change_seed(invalid_auth_handle, CommandCodeEnum::ChangeEPS);
2968        assert!(result.is_err());
2969        let err = result.unwrap_err();
2970        if let TpmCommandError::TpmCommandFailed { response_code } = err {
2971            assert_ne!(response_code, ResponseCode::Success as u32);
2972        } else {
2973            panic!()
2974        }
2975
2976        let invalid_auth_handle = ReservedHandle(0.into());
2977        let result = tpm_engine_helper.change_seed(invalid_auth_handle, CommandCodeEnum::ChangePPS);
2978        assert!(result.is_err());
2979        let err = result.unwrap_err();
2980        if let TpmCommandError::TpmCommandFailed { response_code } = err {
2981            assert_ne!(response_code, ResponseCode::Success as u32);
2982        } else {
2983            panic!()
2984        }
2985    }
2986
2987    #[test]
2988    fn test_pcr_allocate() {
2989        let mut tpm_engine_helper = create_tpm_engine_helper();
2990        restart_tpm_engine(&mut tpm_engine_helper, false, true);
2991
2992        // Positive test
2993        let auth_handle = TPM20_RH_PLATFORM;
2994        let result = tpm_engine_helper.pcr_allocate(auth_handle, 0b000011, 0b00001);
2995        assert!(result.is_ok());
2996        let response_code = result.unwrap();
2997        assert_eq!(response_code, ResponseCode::Success as u32);
2998
2999        // Negative test
3000        let invalid_auth_handle = ReservedHandle(0.into());
3001        let result = tpm_engine_helper.pcr_allocate(invalid_auth_handle, 0, 0);
3002        assert!(result.is_err());
3003        let err = result.unwrap_err();
3004        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3005            assert_ne!(response_code, ResponseCode::Success as u32);
3006        } else {
3007            panic!()
3008        }
3009    }
3010
3011    #[test]
3012    fn test_create_primary() {
3013        let mut tpm_engine_helper = create_tpm_engine_helper();
3014        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3015
3016        // Positive tests
3017
3018        // Create EK
3019        let result = ek_pub_template();
3020        assert!(result.is_ok());
3021        let ek_pub_template = result.unwrap();
3022
3023        let auth_handle = TPM20_RH_ENDORSEMENT;
3024        let result = tpm_engine_helper.create_primary(auth_handle, ek_pub_template);
3025        assert!(result.is_ok());
3026        let response = result.unwrap();
3027        assert_ne!(response.out_public.size.get(), 0);
3028
3029        // Create AK
3030        let result = ak_pub_template();
3031        assert!(result.is_ok());
3032        let ak_pub_template = result.unwrap();
3033
3034        let auth_handle = TPM20_RH_ENDORSEMENT;
3035        let result = tpm_engine_helper.create_primary(auth_handle, ak_pub_template);
3036        assert!(result.is_ok());
3037        let response = result.unwrap();
3038        assert_ne!(response.out_public.size.get(), 0);
3039
3040        // Negative test
3041        let invalid_auth_handle = ReservedHandle(0.into());
3042        let template = TpmtPublic::new_zeroed();
3043        let result = tpm_engine_helper.create_primary(invalid_auth_handle, template);
3044        assert!(result.is_err());
3045        let err = result.unwrap_err();
3046        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3047            assert_ne!(response_code, ResponseCode::Success as u32);
3048        } else {
3049            panic!()
3050        }
3051    }
3052
3053    #[test]
3054    fn test_evict_control() {
3055        let ak_handle = TPM_AZURE_AIK_HANDLE;
3056
3057        let mut tpm_engine_helper = create_tpm_engine_helper();
3058        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3059
3060        // Create AK
3061        let result = ak_pub_template();
3062        assert!(result.is_ok());
3063        let ak_pub_template = result.unwrap();
3064
3065        let auth_handle = TPM20_RH_ENDORSEMENT;
3066        let result = tpm_engine_helper.create_primary(auth_handle, ak_pub_template);
3067        assert!(result.is_ok());
3068        let response = result.unwrap();
3069        assert_ne!(response.out_public.size.get(), 0);
3070        let ak_object_handle = response.object_handle;
3071
3072        // Positive test
3073        let auth_handle = TPM20_RH_OWNER;
3074        let result = tpm_engine_helper.evict_control(auth_handle, ak_object_handle, ak_handle);
3075        assert!(result.is_ok());
3076
3077        // Negative test
3078        let invalid_auth_handle = ReservedHandle(0.into());
3079        let result =
3080            tpm_engine_helper.evict_control(invalid_auth_handle, ak_object_handle, ak_handle);
3081        assert!(result.is_err());
3082        let err = result.unwrap_err();
3083        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3084            assert_ne!(response_code, ResponseCode::Success as u32);
3085        } else {
3086            panic!()
3087        }
3088    }
3089
3090    #[test]
3091    fn test_flush_context() {
3092        let mut tpm_engine_helper = create_tpm_engine_helper();
3093        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3094
3095        // Create AK
3096        let result = ak_pub_template();
3097        assert!(result.is_ok());
3098        let ak_pub_template = result.unwrap();
3099
3100        let auth_handle = TPM20_RH_ENDORSEMENT;
3101        let result = tpm_engine_helper.create_primary(auth_handle, ak_pub_template);
3102        assert!(result.is_ok());
3103        let response = result.unwrap();
3104        assert_ne!(response.out_public.size.get(), 0);
3105        let ak_object_handle = response.object_handle;
3106
3107        // Positive test
3108        let result = tpm_engine_helper.flush_context(ak_object_handle);
3109        assert!(result.is_ok());
3110
3111        // Negative test
3112        let invalid_handle = ReservedHandle(0.into());
3113        let result = tpm_engine_helper.flush_context(invalid_handle);
3114        assert!(result.is_err());
3115        let err = result.unwrap_err();
3116        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3117            assert_ne!(response_code, ResponseCode::Success as u32);
3118        } else {
3119            panic!()
3120        }
3121    }
3122
3123    #[test]
3124    fn test_read_public() {
3125        let ak_handle = TPM_AZURE_AIK_HANDLE;
3126
3127        let mut tpm_engine_helper = create_tpm_engine_helper();
3128        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3129
3130        // Create AK
3131        let result = ak_pub_template();
3132        assert!(result.is_ok());
3133        let ak_pub_template = result.unwrap();
3134
3135        let auth_handle = TPM20_RH_ENDORSEMENT;
3136        let result = tpm_engine_helper.create_primary(auth_handle, ak_pub_template);
3137        assert!(result.is_ok());
3138        let response = result.unwrap();
3139        assert_ne!(response.out_public.size.get(), 0);
3140        let ak_object_handle = response.object_handle;
3141
3142        let auth_handle = TPM20_RH_OWNER;
3143        let result = tpm_engine_helper.evict_control(auth_handle, ak_object_handle, ak_handle);
3144        assert!(result.is_ok());
3145
3146        // Positive test
3147        let result = tpm_engine_helper.read_public(ak_handle);
3148        assert!(result.is_ok());
3149
3150        // Negative test
3151        let invalid_object_handle = ReservedHandle((ak_handle.0.get() + 10).into()); // pick an unallocated handle
3152        let result = tpm_engine_helper.read_public(invalid_object_handle);
3153        assert!(result.is_err());
3154        let err = result.unwrap_err();
3155        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3156            assert_eq!(
3157                response_code,
3158                (ResponseCode::Handle as u32 | ResponseCode::Rc1 as u32)
3159            );
3160        } else {
3161            panic!()
3162        }
3163    }
3164
3165    #[test]
3166    fn test_nv_define_space() {
3167        let nv_index = TPM_NV_INDEX_AIK_CERT;
3168        let nv_index_size = MAX_NV_INDEX_SIZE;
3169
3170        let mut tpm_engine_helper = create_tpm_engine_helper();
3171        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3172
3173        // Positive test
3174        let auth_handle = TPM20_RH_PLATFORM;
3175        let result =
3176            tpm_engine_helper.nv_define_space(auth_handle, AUTH_VALUE, nv_index, nv_index_size);
3177        assert!(result.is_ok());
3178
3179        // Negative test
3180        let invalid_auth_handle = ReservedHandle(0.into());
3181        let result = tpm_engine_helper.nv_define_space(
3182            invalid_auth_handle,
3183            AUTH_VALUE,
3184            nv_index,
3185            nv_index_size,
3186        );
3187        assert!(result.is_err());
3188        let err = result.unwrap_err();
3189        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3190            assert_ne!(response_code, ResponseCode::Success as u32);
3191        } else {
3192            panic!()
3193        }
3194    }
3195
3196    #[test]
3197    fn test_nv_read_public() {
3198        let nv_index = TPM_NV_INDEX_AIK_CERT;
3199        let nv_index_size = MAX_NV_INDEX_SIZE;
3200
3201        let mut tpm_engine_helper = create_tpm_engine_helper();
3202        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3203
3204        let auth_handle = TPM20_RH_PLATFORM;
3205        let result =
3206            tpm_engine_helper.nv_define_space(auth_handle, AUTH_VALUE, nv_index, nv_index_size);
3207        assert!(result.is_ok());
3208
3209        // Positive test
3210        let result = tpm_engine_helper.nv_read_public(nv_index);
3211        assert!(result.is_ok());
3212        let response = result.unwrap();
3213
3214        // Check the flags set by `nv_define_space`
3215        let nv_bits = TpmaNvBits::from(response.nv_public.nv_public.attributes.0.get());
3216        assert!(nv_bits.nv_authread());
3217        assert!(nv_bits.nv_authwrite());
3218        assert!(nv_bits.nv_ownerread());
3219        assert!(nv_bits.nv_platformcreate());
3220        assert!(nv_bits.nv_no_da());
3221
3222        // Negative test
3223        let invalid_nv_index = nv_index + 10; // Pick an undefined index
3224        let result = tpm_engine_helper.nv_read_public(invalid_nv_index);
3225        assert!(result.is_err());
3226        let err = result.unwrap_err();
3227        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3228            assert_eq!(
3229                response_code,
3230                (ResponseCode::Handle as u32 | ResponseCode::Rc1 as u32)
3231            );
3232        } else {
3233            panic!()
3234        }
3235    }
3236
3237    #[test]
3238    fn test_nv_read_write() {
3239        let nv_index = TPM_NV_INDEX_AIK_CERT;
3240        let nv_index_size = MAX_NV_INDEX_SIZE;
3241
3242        let mut tpm_engine_helper = create_tpm_engine_helper();
3243        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3244
3245        let auth_handle = TPM20_RH_PLATFORM;
3246        let result =
3247            tpm_engine_helper.nv_define_space(auth_handle, AUTH_VALUE, nv_index, nv_index_size);
3248        assert!(result.is_ok());
3249
3250        // Positive tests
3251
3252        // Write with data size equal to nv_index_size
3253        let input_data = vec![7u8; nv_index_size.into()];
3254        let result = tpm_engine_helper.nv_write(
3255            ReservedHandle(nv_index.into()),
3256            Some(AUTH_VALUE),
3257            nv_index,
3258            input_data.as_ref(),
3259        );
3260        assert!(result.is_ok());
3261
3262        // Read the data
3263        let mut output_data = vec![0u8; nv_index_size.into()];
3264        let result = tpm_engine_helper.nv_read(
3265            TPM20_RH_OWNER,
3266            nv_index,
3267            nv_index_size,
3268            output_data.as_mut(),
3269        );
3270        assert!(result.is_ok());
3271        assert_eq!(input_data, output_data);
3272
3273        // Write with data size smaller to nv_index_size
3274        let data_size = 512;
3275        assert!(data_size < nv_index_size.into());
3276        let input_data = vec![6u8; data_size];
3277        let result = tpm_engine_helper.nv_write(
3278            ReservedHandle(nv_index.into()),
3279            Some(AUTH_VALUE),
3280            nv_index,
3281            input_data.as_ref(),
3282        );
3283        assert!(result.is_ok());
3284
3285        // Read the data
3286        let mut output_data = vec![0u8; nv_index_size.into()];
3287        let result = tpm_engine_helper.nv_read(
3288            TPM20_RH_OWNER,
3289            nv_index,
3290            nv_index_size,
3291            output_data.as_mut(),
3292        );
3293        assert!(result.is_ok());
3294        assert_eq!(input_data, output_data[..data_size]);
3295
3296        // Negative tests
3297
3298        // test nv_write with invalid auth handle
3299        let invalid_auth_handle = ReservedHandle(0.into());
3300        let input_data = vec![7u8; nv_index_size.into()];
3301        let result = tpm_engine_helper.nv_write(
3302            invalid_auth_handle,
3303            Some(AUTH_VALUE),
3304            nv_index,
3305            input_data.as_ref(),
3306        );
3307        assert!(result.is_err());
3308        let err = result.unwrap_err();
3309        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3310            assert_ne!(response_code, ResponseCode::Success as u32);
3311        } else {
3312            panic!()
3313        }
3314
3315        // test nv_read with invalid auth handle
3316        let invalid_auth_handle = ReservedHandle(0.into());
3317        let mut output_data = vec![0u8; nv_index_size.into()];
3318        let result = tpm_engine_helper.nv_read(
3319            invalid_auth_handle,
3320            nv_index,
3321            nv_index_size,
3322            output_data.as_mut(),
3323        );
3324        assert!(result.is_err());
3325        let err = result.unwrap_err();
3326        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3327            assert_ne!(response_code, ResponseCode::Success as u32);
3328        } else {
3329            panic!()
3330        }
3331    }
3332
3333    #[test]
3334    fn test_nv_undefine_space() {
3335        let nv_index = TPM_NV_INDEX_AIK_CERT;
3336        let nv_index_size = MAX_NV_INDEX_SIZE;
3337
3338        let mut tpm_engine_helper = create_tpm_engine_helper();
3339        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3340
3341        let auth_handle = TPM20_RH_PLATFORM;
3342        let result =
3343            tpm_engine_helper.nv_define_space(auth_handle, AUTH_VALUE, nv_index, nv_index_size);
3344
3345        assert!(result.is_ok());
3346        // Positive test
3347        let auth_handle = TPM20_RH_PLATFORM;
3348        let result = tpm_engine_helper.nv_undefine_space(auth_handle, nv_index);
3349        assert!(result.is_ok());
3350
3351        // Negative test
3352        let invalid_auth_handle = ReservedHandle(0.into());
3353        let result = tpm_engine_helper.nv_undefine_space(invalid_auth_handle, nv_index);
3354        assert!(result.is_err());
3355        let err = result.unwrap_err();
3356        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3357            assert_ne!(response_code, ResponseCode::Success as u32);
3358        } else {
3359            panic!()
3360        }
3361    }
3362
3363    #[test]
3364    fn test_clear_control() {
3365        let mut tpm_engine_helper = create_tpm_engine_helper();
3366        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3367
3368        // Positive test
3369        let auth_handle = TPM20_RH_PLATFORM;
3370        let result = tpm_engine_helper.clear_control(auth_handle, false);
3371        assert!(result.is_ok());
3372
3373        // Negative test
3374        let invalid_auth_handle = ReservedHandle(0.into());
3375        let result = tpm_engine_helper.clear_control(invalid_auth_handle, false);
3376        assert!(result.is_err());
3377        let err = result.unwrap_err();
3378        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3379            assert_ne!(response_code, ResponseCode::Success as u32);
3380        } else {
3381            panic!()
3382        }
3383    }
3384
3385    #[test]
3386    fn test_clear() {
3387        let mut tpm_engine_helper = create_tpm_engine_helper();
3388        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3389
3390        // Positive test
3391
3392        // Enable the clear command
3393        let result = tpm_engine_helper.clear_tpm_platform_context();
3394        assert!(result.is_ok());
3395        let response_code = result.unwrap();
3396        assert_eq!(response_code, ResponseCode::Success as u32);
3397
3398        // Negative test
3399
3400        // Disable the clear command
3401        let auth_handle = TPM20_RH_PLATFORM;
3402        let result = tpm_engine_helper.clear_control(auth_handle, true);
3403        assert!(result.is_ok());
3404
3405        let result = tpm_engine_helper.clear(auth_handle);
3406        assert!(result.is_err());
3407        let err = result.unwrap_err();
3408        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3409            assert_ne!(response_code, ResponseCode::Success as u32);
3410        } else {
3411            panic!()
3412        }
3413    }
3414
3415    #[test]
3416    fn test_hierarchy_control() {
3417        let mut tpm_engine_helper = create_tpm_engine_helper();
3418        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3419
3420        // Positive test
3421        let auth_handle = TPM20_RH_PLATFORM;
3422        let result = tpm_engine_helper.hierarchy_control(auth_handle, TPM20_RH_PLATFORM, false);
3423        assert!(result.is_ok());
3424
3425        // Negative test
3426        let invalid_auth_handle = ReservedHandle(0.into());
3427        let result =
3428            tpm_engine_helper.hierarchy_control(invalid_auth_handle, TPM20_RH_PLATFORM, false);
3429        assert!(result.is_err());
3430        let err = result.unwrap_err();
3431        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3432            assert_ne!(response_code, ResponseCode::Success as u32);
3433        } else {
3434            panic!()
3435        }
3436    }
3437
3438    struct TpmtSensitive {
3439        /// TPMI_ALG_PUBLIC
3440        sensitive_type: AlgId,
3441        /// `TPM2B_AUTH`
3442        auth_value: Tpm2bBuffer,
3443        /// `TPM2B_DIGEST`
3444        seed_value: Tpm2bBuffer,
3445        /// `TPM2B_PRIVATE_KEY_RSA`
3446        sensitive: Tpm2bBuffer,
3447    }
3448
3449    impl TpmtSensitive {
3450        fn serialize(&self) -> Vec<u8> {
3451            let mut buffer = Vec::new();
3452
3453            buffer.extend_from_slice(self.sensitive_type.as_bytes());
3454            buffer.extend_from_slice(&self.auth_value.serialize());
3455            buffer.extend_from_slice(&self.seed_value.serialize());
3456            buffer.extend_from_slice(&self.sensitive.serialize());
3457
3458            buffer
3459        }
3460    }
3461
3462    fn generate_rsa() -> (TpmtPublic, Tpm2bBuffer) {
3463        // Using hard-coded value to avoid OpenSSL dependency
3464        // RSA-2k public modulus
3465        const N: [u8; 256] = [
3466            0xbc, 0x85, 0x76, 0x9b, 0x24, 0xf5, 0x55, 0x2b, 0x58, 0x77, 0xf5, 0xbd, 0x3d, 0x15,
3467            0x2f, 0xa4, 0x5b, 0xda, 0x17, 0x74, 0xd7, 0x97, 0x64, 0xd5, 0x64, 0x0a, 0x51, 0xb0,
3468            0x54, 0x98, 0xac, 0x8c, 0xf7, 0xb3, 0xf2, 0x45, 0x32, 0xf9, 0x99, 0xd2, 0x9e, 0xb4,
3469            0xf3, 0x49, 0xb7, 0xf2, 0x27, 0xe3, 0xe4, 0x5d, 0xa6, 0xe2, 0xc2, 0x0f, 0x58, 0x02,
3470            0x65, 0xf7, 0x8e, 0xe7, 0xd0, 0x41, 0x8a, 0xd4, 0xa2, 0x71, 0x7d, 0x0f, 0x27, 0x51,
3471            0x94, 0x9b, 0x5d, 0xd3, 0x0e, 0x05, 0xe0, 0xae, 0x2e, 0x2f, 0x3c, 0xfd, 0x46, 0x28,
3472            0x0a, 0x70, 0x59, 0x74, 0x5a, 0xd7, 0xac, 0x54, 0x92, 0x89, 0xb2, 0xec, 0xb8, 0x38,
3473            0xdf, 0x4d, 0xdb, 0x54, 0xa7, 0x9f, 0x00, 0xba, 0x9b, 0x8d, 0x2e, 0xee, 0x60, 0xd3,
3474            0x47, 0xea, 0x70, 0x53, 0xb9, 0x26, 0x7b, 0x1f, 0x82, 0x33, 0x22, 0x65, 0x7a, 0x60,
3475            0xe0, 0xba, 0xdf, 0x60, 0x55, 0xcc, 0xc2, 0x07, 0x16, 0x7f, 0x6c, 0x07, 0xf0, 0xf8,
3476            0xf5, 0xa6, 0xba, 0xea, 0xc0, 0x6d, 0x45, 0x38, 0x8d, 0xca, 0x0d, 0xa6, 0x98, 0x21,
3477            0xba, 0xdd, 0x27, 0x0f, 0x8d, 0x7e, 0x7c, 0x7a, 0xee, 0x44, 0xc7, 0xa7, 0xd4, 0x3d,
3478            0x39, 0x70, 0x4d, 0xde, 0xb1, 0x72, 0x56, 0x6e, 0xe9, 0x50, 0x69, 0x46, 0x56, 0xd9,
3479            0x83, 0x89, 0x8e, 0xe6, 0xf7, 0x7b, 0xce, 0xf0, 0x75, 0x8e, 0x18, 0xea, 0x22, 0xc5,
3480            0x62, 0xa7, 0x6b, 0x59, 0x80, 0xe8, 0x68, 0xb2, 0x57, 0xdc, 0xfe, 0xd1, 0xe0, 0xda,
3481            0xeb, 0x0f, 0x12, 0x64, 0xb2, 0x7a, 0x1f, 0x1a, 0x97, 0xa9, 0xb6, 0xdd, 0xd7, 0x78,
3482            0x82, 0x90, 0x07, 0xa1, 0x9d, 0x00, 0xff, 0xa9, 0x52, 0xe3, 0x0a, 0xa8, 0xa5, 0x2f,
3483            0xcd, 0xdf, 0x79, 0xec, 0x35, 0xb4, 0x81, 0xad, 0xa9, 0x45, 0x50, 0x30, 0x58, 0x0b,
3484            0xed, 0xdf, 0x10, 0x69,
3485        ];
3486        // RSA-2k private prime
3487        const P: [u8; 128] = [
3488            0xe8, 0x66, 0x31, 0x98, 0xe7, 0xab, 0xd7, 0xbe, 0x1f, 0xa9, 0x13, 0xe2, 0xd0, 0x4d,
3489            0xd0, 0x0a, 0xb0, 0xd1, 0x39, 0xc0, 0xc3, 0x6f, 0x4b, 0xdc, 0x4d, 0xe2, 0x03, 0xf9,
3490            0xd4, 0xd9, 0xb5, 0x47, 0x94, 0x97, 0x5b, 0x51, 0xe3, 0x1a, 0x25, 0x7f, 0x14, 0x50,
3491            0xe8, 0x12, 0x21, 0xd0, 0x0e, 0x51, 0x9a, 0xc3, 0xc5, 0x05, 0x55, 0xe8, 0x31, 0xb8,
3492            0x44, 0xbd, 0x71, 0xa6, 0x5b, 0x88, 0x05, 0x7b, 0x75, 0xd9, 0x75, 0xba, 0x43, 0x55,
3493            0x6a, 0x72, 0x15, 0x0e, 0xd4, 0x09, 0xab, 0x69, 0xee, 0xac, 0x3b, 0x68, 0x13, 0x54,
3494            0x43, 0x63, 0x73, 0xb7, 0x7b, 0x5d, 0x2c, 0x01, 0xb4, 0x1e, 0xfc, 0x88, 0xfe, 0xa6,
3495            0x04, 0x27, 0xba, 0x17, 0x0a, 0x7e, 0xc3, 0xa8, 0xea, 0xb9, 0x37, 0x6d, 0x81, 0x91,
3496            0x6a, 0x70, 0xfa, 0x4f, 0x18, 0xfb, 0xcf, 0x7b, 0x45, 0x12, 0xd7, 0x50, 0x64, 0xd6,
3497            0xc8, 0x73,
3498        ];
3499
3500        let symmetric = TpmtSymDefObject::new(AlgIdEnum::NULL.into(), None, None);
3501        let scheme = TpmtRsaScheme::new(AlgIdEnum::RSASSA.into(), Some(AlgIdEnum::SHA256.into()));
3502        let rsa_params = TpmsRsaParams::new(symmetric, scheme, crate::RSA_2K_MODULUS_BITS, 0);
3503
3504        let object_attributes = TpmaObjectBits::new()
3505            .with_user_with_auth(true)
3506            .with_sign_encrypt(true);
3507
3508        let unique = {
3509            let mut data = [0u8; crate::RSA_2K_MODULUS_SIZE];
3510            data.copy_from_slice(&N);
3511
3512            data
3513        };
3514
3515        let result = TpmtPublic::new(
3516            AlgIdEnum::RSA.into(),
3517            AlgIdEnum::SHA256.into(),
3518            object_attributes,
3519            &[],
3520            rsa_params,
3521            &unique,
3522        );
3523        assert!(result.is_ok());
3524
3525        let rsa_public = result.unwrap();
3526
3527        let result = Tpm2bBuffer::new(&P);
3528        assert!(result.is_ok());
3529        let sensitive = result.unwrap();
3530
3531        let rsa_sensitive = TpmtSensitive {
3532            sensitive_type: AlgIdEnum::RSA.into(),
3533            auth_value: Tpm2bBuffer::new_zeroed(),
3534            seed_value: Tpm2bBuffer::new_zeroed(),
3535            sensitive,
3536        };
3537
3538        let result = Tpm2bBuffer::new(&rsa_sensitive.serialize());
3539        assert!(result.is_ok());
3540        let rsa_private = result.unwrap();
3541
3542        (rsa_public, rsa_private)
3543    }
3544
3545    fn rsa_srk_template() -> Result<TpmtPublic, TpmHelperUtilityError> {
3546        let symmetric = TpmtSymDefObject::new(
3547            AlgIdEnum::AES.into(),
3548            Some(128),
3549            Some(AlgIdEnum::CFB.into()),
3550        );
3551        let scheme = TpmtRsaScheme::new(AlgIdEnum::NULL.into(), None);
3552        let rsa_params = TpmsRsaParams::new(symmetric, scheme, crate::RSA_2K_MODULUS_BITS, 0);
3553
3554        let object_attributes = TpmaObjectBits::new()
3555            .with_fixed_tpm(true)
3556            .with_fixed_parent(true)
3557            .with_sensitive_data_origin(true)
3558            .with_user_with_auth(true)
3559            .with_no_da(true)
3560            .with_restricted(true)
3561            .with_decrypt(true);
3562
3563        let in_public = TpmtPublic::new(
3564            AlgIdEnum::RSA.into(),
3565            AlgIdEnum::SHA256.into(),
3566            object_attributes,
3567            &[],
3568            rsa_params,
3569            &[0u8; crate::RSA_2K_MODULUS_SIZE],
3570        )
3571        .map_err(TpmHelperUtilityError::InvalidInputParameter)?;
3572
3573        Ok(in_public)
3574    }
3575
3576    #[test]
3577    fn test_import_load() {
3578        let mut tpm_engine_helper = create_tpm_engine_helper();
3579        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3580
3581        // Create SRK
3582        let result = rsa_srk_template();
3583        assert!(result.is_ok());
3584        let rsa_srk_template = result.unwrap();
3585
3586        // Positive tests
3587
3588        let auth_handle = TPM20_RH_OWNER;
3589        let result = tpm_engine_helper.create_primary(auth_handle, rsa_srk_template);
3590        assert!(result.is_ok());
3591        let create_primary_reply = result.unwrap();
3592        assert_ne!(create_primary_reply.out_public.size.get(), 0);
3593
3594        let (rsa_public, rsa_private) = generate_rsa();
3595        let object_public = Tpm2bPublic::new(rsa_public);
3596        let result = Tpm2bBuffer::new(&rsa_private.serialize());
3597        assert!(result.is_ok());
3598        let duplicate = result.unwrap();
3599        let in_sym_seed = Tpm2bBuffer::new_zeroed();
3600
3601        let result = tpm_engine_helper.import(
3602            create_primary_reply.object_handle,
3603            &object_public,
3604            &duplicate,
3605            &in_sym_seed,
3606        );
3607        assert!(result.is_ok());
3608        let import_reply = result.unwrap();
3609
3610        let in_public = object_public;
3611        let in_private = import_reply.out_private;
3612
3613        let result =
3614            tpm_engine_helper.load(create_primary_reply.object_handle, &in_private, &in_public);
3615        assert!(result.is_ok());
3616
3617        // Negative tests
3618
3619        let invalid_auth_handle = ReservedHandle(0.into());
3620        let result = tpm_engine_helper.import(
3621            invalid_auth_handle,
3622            &object_public,
3623            &duplicate,
3624            &in_sym_seed,
3625        );
3626        assert!(result.is_err());
3627        let err = result.unwrap_err();
3628        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3629            assert_ne!(response_code, ResponseCode::Success as u32);
3630        } else {
3631            panic!()
3632        }
3633
3634        let result = tpm_engine_helper.load(invalid_auth_handle, &in_private, &in_public);
3635        assert!(result.is_err());
3636        let err = result.unwrap_err();
3637        if let TpmCommandError::TpmCommandFailed { response_code } = err {
3638            assert_ne!(response_code, ResponseCode::Success as u32);
3639        } else {
3640            panic!()
3641        }
3642    }
3643
3644    #[test]
3645    fn test_restore_owner_defined() {
3646        const AK_CERT_INPUT_512: [u8; 512] = [7u8; 512];
3647
3648        let mut tpm_engine_helper = create_tpm_engine_helper();
3649        restart_tpm_engine(&mut tpm_engine_helper, false, true);
3650
3651        // Test allocating a platform-defined AKCert index and mitigating it back to owner-defined.
3652
3653        let result =
3654            tpm_engine_helper.allocate_guest_attestation_nv_indices(AUTH_VALUE, false, true, false);
3655        assert!(result.is_ok());
3656
3657        let result = tpm_engine_helper
3658            .find_nv_index(TPM_NV_INDEX_AIK_CERT)
3659            .expect("find_nv_index should succeed")
3660            .expect("AKCert NV index present");
3661        let nv_bits = TpmaNvBits::from(result.nv_public.nv_public.attributes.0.get());
3662        assert!(nv_bits.nv_platformcreate());
3663
3664        let result = tpm_engine_helper.write_to_nv_index(
3665            AUTH_VALUE,
3666            TPM_NV_INDEX_AIK_CERT,
3667            &AK_CERT_INPUT_512,
3668        );
3669        assert!(result.is_ok());
3670
3671        let result = tpm_engine_helper.nv_define_space(
3672            TPM20_RH_PLATFORM,
3673            AUTH_VALUE,
3674            TPM_NV_INDEX_MITIGATED,
3675            1,
3676        );
3677        assert!(result.is_ok());
3678
3679        // TPM has a platform-defined AKCert index and a mitigation marker. This should restore
3680        // the owner-defined AKCert index.
3681        let result =
3682            tpm_engine_helper.allocate_guest_attestation_nv_indices(AUTH_VALUE, false, true, true);
3683        assert!(result.is_ok());
3684
3685        let result = tpm_engine_helper
3686            .find_nv_index(TPM_NV_INDEX_AIK_CERT)
3687            .expect("find_nv_index should succeed")
3688            .expect("AKCert NV index present");
3689        let nv_bits = TpmaNvBits::from(result.nv_public.nv_public.attributes.0.get());
3690        assert!(!nv_bits.nv_platformcreate());
3691    }
3692
3693    struct TestRequestAkCertHelper {}
3694
3695    #[async_trait::async_trait]
3696    impl RequestAkCert for TestRequestAkCertHelper {
3697        fn create_ak_cert_request(
3698            &self,
3699            _ak_pub_modulus: &[u8],
3700            _ak_pub_exponent: &[u8],
3701            _ek_pub_modulus: &[u8],
3702            _ek_pub_exponent: &[u8],
3703            _guest_input: &[u8],
3704        ) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
3705            Ok(Vec::new())
3706        }
3707
3708        async fn request_ak_cert(
3709            &self,
3710            _request: Vec<u8>,
3711        ) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync + 'static>> {
3712            Ok(Vec::new())
3713        }
3714    }
3715
3716    #[async_test]
3717    async fn test_fix_corrupted_vmgs() {
3718        // Take a corrupt TPM NVRAM and go through OpenHCL TPM init. This should uncorrupt
3719        // the vTPM state and resize the AKCert index to fit its contents.
3720
3721        // To generate a corrupted vTpmState blob:
3722        // 1. Create a test VM with a VMGS file with a 16 kB vTPM blob
3723        // 2. (Depending on how the vTPM blob was created, the AKCert NVRAM index may not
3724        //     contain an actual certificate. If not, create some sort of cert and load it
3725        //     into that index. Do the following steps in the test VM. Note that it should
3726        //     be a DER-encoded X.509 certificate.)
3727        //   a. openssl req -x509 -newkey rsa:4096 -keyout key.der -out cert.der -outform DER -sha256 -days 3650 -nodes -subj "/C=XX/ST=StateName/L=CityName/O=CompanyName/OU=CompanySectionName/CN=CommonNameOrHostname"
3728        //   b. tpm2_nvwrite -C o -i cert.der 0x1c101d0
3729        // 3. Boot the VM with a version of OpenHCL that does not include PR 1452.
3730        // 4. In the guest, fill up the TPM NVRAM space:
3731        //   a. tpm2_nvdefine -s 2048 0x1000001
3732        //   b. tpm2_nvdefine -s 2048 0x1000002
3733        //   c. (repeat until the VM crashes)
3734        // 5. Extract the TPM state from the VMGS file:
3735        //   vmgstool dump -f test.vmgs -i 3 --raw-stdout > vTpmState-corrupt.blob
3736
3737        let tpm_state_blob = include_bytes!("../test_data/vTpmState-corrupt.blob");
3738        let tpm_state_vec = tpm_state_blob.to_vec();
3739        let mut store = EphemeralNonVolatileStore::new_boxed();
3740        store.persist(tpm_state_vec).await.unwrap();
3741
3742        let ppi_store = EphemeralNonVolatileStore::new_boxed();
3743        let gm = GuestMemory::allocate(0x10000);
3744        let monotonic_timer = Box::new(move || std::time::Duration::new(0, 0));
3745
3746        let mut tpm = Tpm::new(
3747            TpmRegisterLayout::IoPort,
3748            gm,
3749            ppi_store,
3750            store,
3751            monotonic_timer,
3752            false,
3753            false,
3754            TpmAkCertType::Trusted(Arc::new(TestRequestAkCertHelper {})),
3755            None,
3756            None,
3757            false,
3758        )
3759        .await
3760        .unwrap();
3761
3762        // Check that the AKCert exists
3763        let result = tpm
3764            .tpm_engine_helper
3765            .find_nv_index(TPM_NV_INDEX_AIK_CERT)
3766            .expect("find_nv_index should succeed")
3767            .expect("AKCert NV index present");
3768
3769        // AKCert should be owner-defined and resized to fit its contents (1419 bytes, in this example)
3770        let nv_bits = TpmaNvBits::from(result.nv_public.nv_public.attributes.0.get());
3771        assert!(!nv_bits.nv_platformcreate());
3772        assert!(result.nv_public.nv_public.data_size.get() == 1419);
3773
3774        // Mitigation marker should be there
3775        tpm.tpm_engine_helper
3776            .find_nv_index(TPM_NV_INDEX_MITIGATED)
3777            .expect("find_nv_index should succeed")
3778            .expect("mitigation marker NV index present");
3779    }
3780}