1use 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
51const TPM_PAGE_SIZE: usize = 4096;
56const MAX_NV_BUFFER_SIZE: usize = MAX_DIGEST_BUFFER_SIZE;
57const MAX_NV_INDEX_SIZE: u16 = 4096;
58const 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#[derive(Debug)]
67pub struct CommandDebugInfo {
68 pub command_code: CommandCodeEnum,
70 pub auth_handle: Option<ReservedHandle>,
72 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 #[inspect(skip)]
149 pub tpm_engine: MsTpm20RefPlatform,
150 pub reply_buffer: [u8; TPM_PAGE_SIZE],
152}
153
154enum EvictOrPersist {
156 Evict(ReservedHandle),
158 Persist {
160 from: ReservedHandle,
161 to: ReservedHandle,
162 },
163}
164
165#[derive(Debug)]
167pub enum NvIndexState {
168 Available,
170 Unallocated,
172 Uninitialized,
174}
175
176enum AkCertType {
177 None,
178 PlatformOwned(Vec<u8>),
179 OwnerOwned,
180}
181
182impl TpmEngineHelper {
183 pub fn initialize_tpm_engine(&mut self) -> Result<(), TpmHelperError> {
188 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 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 pub fn clear_tpm_platform_context(&mut self) -> Result<u32, TpmHelperError> {
219 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 Ok(response_code);
230 } else {
231 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 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 Ok(response_code)
255 } else {
256 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 Ok(response_code) => Ok(response_code),
269 }
270 }
271
272 pub fn refresh_tpm_seeds(&mut self) -> Result<(), TpmHelperError> {
275 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 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 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 self.evict_or_persist_handle(EvictOrPersist::Evict(TPM_AZURE_AIK_HANDLE))?;
311 } else {
312 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 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 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 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 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 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 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 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 tracelimit::error_ratelimited!(
413 CVM_ALLOWED,
414 err = &error as &dyn std::error::Error,
415 "tpm FlushContextCmd failed"
416 );
417 } else {
418 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 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 tracelimit::error_ratelimited!(
446 CVM_ALLOWED,
447 err = &error as &dyn std::error::Error,
448 "tpm EvictControlCmd failed"
449 );
450 } else {
451 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 fn take_existing_ak_cert(&mut self) -> Result<AkCertType, TpmHelperError> {
472 let mut output = vec![0; MAX_NV_INDEX_SIZE as usize];
473
474 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 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 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 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 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 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 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 }
732 }
733
734 if support_attestation_report {
736 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 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 Ok(None)
810 } else {
811 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 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 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 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 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 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 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 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 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 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 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 Ok(None)
981 } else {
982 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 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 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 return Ok(());
1025 };
1026
1027 if self.find_object(TPM_RSA_SRK_HANDLE)?.is_none() {
1028 return Err(TpmHelperError::SrkNotFound(TPM_RSA_SRK_HANDLE.0.get()));
1030 };
1031
1032 let import_command = ImportCmd::deserialize_no_wrapping_key(guest_secret_key)
1034 .ok_or(TpmHelperError::DeserializeGuestSecretKey)?;
1035
1036 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 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 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 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 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 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 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 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 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 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(); 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
1788pub 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
1816pub fn ek_pub_template() -> Result<TpmtPublic, TpmHelperUtilityError> {
1818 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
1855fn export_rsa_public(public: &Tpm2bPublic) -> Result<TpmRsa2kPublic, TpmHelperUtilityError> {
1857 if public.public_area.parameters.exponent.get() != 0 {
1858 Err(TpmHelperUtilityError::UnexpectedRsaExponent)?
1859 }
1860
1861 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 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 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 restart_tpm_engine(&mut tpm_engine_helper, true, true);
1989
1990 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 assert_eq!(ak_pub_first, ak_pub_second);
2008 assert_eq!(ek_pub_first, ek_pub_second);
2009
2010 restart_tpm_engine(&mut tpm_engine_helper, false, true);
2013
2014 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 assert_eq!(ak_pub_second, ak_pub_third);
2032 assert_eq!(ek_pub_second, ek_pub_third);
2033
2034 restart_tpm_engine(&mut tpm_engine_helper, false, true);
2037
2038 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 assert_eq!(ak_pub_third, ak_pub_fourth);
2056 assert_eq!(ek_pub_third, ek_pub_fourth);
2057
2058 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 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 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 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 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 {
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 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 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 {
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 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 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 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 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 {
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 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 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 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 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 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 {
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 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 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 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 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 {
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 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 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 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 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 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 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 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 {
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 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 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 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 {
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 {
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 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 {
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 {
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 {
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 {
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 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 {
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 {
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 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 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 let nv_size = nv_read_public_reply.nv_public.nv_public.data_size.get();
2683 assert!(nv_size < MAX_NV_INDEX_SIZE);
2684
2685 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 let result =
2700 tpm_engine_helper.allocate_guest_attestation_nv_indices(AUTH_VALUE, true, false, false);
2701 assert!(result.is_ok());
2702
2703 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 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 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 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 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 assert!(
2815 tpm_engine_helper
2816 .find_object(TPM_RSA_SRK_HANDLE)
2817 .unwrap()
2818 .is_some()
2819 );
2820
2821 assert!(
2823 tpm_engine_helper
2824 .find_object(TPM_GUEST_SECRET_HANDLE)
2825 .unwrap()
2826 .is_none()
2827 );
2828
2829 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 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 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 restart_tpm_engine(&mut tpm_engine_helper, true, true);
2865
2866 assert!(
2868 tpm_engine_helper
2869 .find_object(TPM_RSA_SRK_HANDLE)
2870 .unwrap()
2871 .is_none()
2872 );
2873
2874 assert!(
2876 tpm_engine_helper
2877 .find_object(TPM_GUEST_SECRET_HANDLE)
2878 .unwrap()
2879 .is_none()
2880 );
2881
2882 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let result = tpm_engine_helper.flush_context(ak_object_handle);
3082 assert!(result.is_ok());
3083
3084 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 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 let result = tpm_engine_helper.read_public(ak_handle);
3121 assert!(result.is_ok());
3122
3123 let invalid_object_handle = ReservedHandle((ak_handle.0.get() + 10).into()); 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 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 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 let result = tpm_engine_helper.nv_read_public(nv_index);
3184 assert!(result.is_ok());
3185 let response = result.unwrap();
3186
3187 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 let invalid_nv_index = nv_index + 10; 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 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 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 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 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 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 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 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 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 let auth_handle = TPM20_RH_PLATFORM;
3343 let result = tpm_engine_helper.clear_control(auth_handle, false);
3344 assert!(result.is_ok());
3345
3346 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 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 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 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 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 sensitive_type: AlgId,
3414 auth_value: Tpm2bBuffer,
3416 seed_value: Tpm2bBuffer,
3418 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 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 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 let result = rsa_srk_template();
3556 assert!(result.is_ok());
3557 let rsa_srk_template = result.unwrap();
3558
3559 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 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 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 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}