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