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