diff --git a/tss-esapi/src/context/tpm_commands/non_volatile_storage.rs b/tss-esapi/src/context/tpm_commands/non_volatile_storage.rs index b1b7fb68..b8f2c4a3 100644 --- a/tss-esapi/src/context/tpm_commands/non_volatile_storage.rs +++ b/tss-esapi/src/context/tpm_commands/non_volatile_storage.rs @@ -6,7 +6,7 @@ use crate::{ interface_types::reserved_handles::{NvAuth, Provision}, structures::{Auth, MaxNvBuffer, Name, NvPublic}, tss2_esys::{ - Esys_NV_DefineSpace, Esys_NV_Increment, Esys_NV_Read, Esys_NV_ReadPublic, + Esys_NV_DefineSpace, Esys_NV_Extend, Esys_NV_Increment, Esys_NV_Read, Esys_NV_ReadPublic, Esys_NV_UndefineSpace, Esys_NV_UndefineSpaceSpecial, Esys_NV_Write, }, Context, Result, ReturnCode, @@ -698,7 +698,116 @@ impl Context { ) } - // Missing function: NV_Extend + /// Extends data to the NV memory associated with a nv index. + /// + /// # Details + /// This method is used to extend a value to the nv memory in the TPM. + /// + /// Please beware that this method requires an authorization session handle to be present. + /// + /// Any NV index (that is not already used) can be defined as an extend type. However various specifications define + /// indexes that have specific purposes or are reserved, for example the TCG PC Client Platform Firmware Profile + /// Specification Section 3.3.6 defines indexes within the 0x01c40200-0x01c402ff range for instance measurements. + /// Section 2.2 of TCG Registry of Reserved TPM 2.0 Handles and Localities provides additional context for specific + /// NV index ranges. + /// + /// # Arguments + /// * `auth_handle` - Handle indicating the source of authorization value. + /// * `nv_index_handle` - The [NvIndexHandle] associated with NV memory + /// which will be extended by data hashed with the previous data. + /// * `data` - The data, in the form of a [MaxNvBuffer], that is to be written. + /// + /// # Example + /// ```rust + /// # use tss_esapi::{ + /// # Context, TctiNameConf, attributes::{SessionAttributes, NvIndexAttributes}, + /// # handles::NvIndexTpmHandle, interface_types::algorithm::HashingAlgorithm, + /// # structures::{SymmetricDefinition, NvPublic}, + /// # constants::SessionType, constants::nv_index_type::NvIndexType, + /// # }; + /// use tss_esapi::{ + /// interface_types::reserved_handles::{Provision, NvAuth}, structures::MaxNvBuffer, + /// }; + /// + /// # // Create context + /// # let mut context = + /// # Context::new( + /// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"), + /// # ).expect("Failed to create Context"); + /// # + /// # let session = context + /// # .start_auth_session( + /// # None, + /// # None, + /// # None, + /// # SessionType::Hmac, + /// # SymmetricDefinition::AES_256_CFB, + /// # tss_esapi::interface_types::algorithm::HashingAlgorithm::Sha256, + /// # ) + /// # .expect("Failed to create session") + /// # .expect("Received invalid handle"); + /// # let (session_attributes, session_attributes_mask) = SessionAttributes::builder() + /// # .with_decrypt(true) + /// # .with_encrypt(true) + /// # .build(); + /// # context.tr_sess_set_attributes(session, session_attributes, session_attributes_mask) + /// # .expect("Failed to set attributes on session"); + /// # context.set_sessions((Some(session), None, None)); + /// # + /// # let nv_index = NvIndexTpmHandle::new(0x01500028) + /// # .expect("Failed to create NV index tpm handle"); + /// # + /// // Create NV index attributes + /// let owner_nv_index_attributes = NvIndexAttributes::builder() + /// .with_owner_write(true) + /// .with_owner_read(true) + /// .with_orderly(true) + /// .with_nv_index_type(NvIndexType::Extend) + /// .build() + /// .expect("Failed to create owner nv index attributes"); + /// + /// // Create owner nv public. + /// let owner_nv_public = NvPublic::builder() + /// .with_nv_index(nv_index) + /// .with_index_name_algorithm(HashingAlgorithm::Sha256) + /// .with_index_attributes(owner_nv_index_attributes) + /// .with_data_area_size(32) + /// .build() + /// .expect("Failed to build NvPublic for owner"); + /// + /// let nv_index_handle = context + /// .nv_define_space(Provision::Owner, None, owner_nv_public.clone()) + /// .expect("Call to nv_define_space failed"); + /// + /// let data = MaxNvBuffer::try_from(vec![0x0]).unwrap(); + /// let result = context.nv_extend(NvAuth::Owner, nv_index_handle, data); + /// + /// # context + /// # .nv_undefine_space(Provision::Owner, nv_index_handle) + /// # .expect("Call to nv_undefine_space failed"); + /// ``` + pub fn nv_extend( + &mut self, + auth_handle: NvAuth, + nv_index_handle: NvIndexHandle, + data: MaxNvBuffer, + ) -> Result<()> { + ReturnCode::ensure_success( + unsafe { + Esys_NV_Extend( + self.mut_context(), + AuthHandle::from(auth_handle).into(), + nv_index_handle.into(), + self.required_session_1()?, + self.optional_session_2(), + self.optional_session_3(), + &data.into(), + ) + }, + |ret| error!("Error when extending NV: {:#010X}", ret), + ) + } + // Missing function: NV_SetBits // Missing function: NV_WriteLock // Missing function: NV_GlobalWriteLock diff --git a/tss-esapi/tests/integration_tests/context_tests/tpm_commands/non_volatile_storage_tests.rs b/tss-esapi/tests/integration_tests/context_tests/tpm_commands/non_volatile_storage_tests.rs index 7e6f232c..9e12bc75 100644 --- a/tss-esapi/tests/integration_tests/context_tests/tpm_commands/non_volatile_storage_tests.rs +++ b/tss-esapi/tests/integration_tests/context_tests/tpm_commands/non_volatile_storage_tests.rs @@ -421,3 +421,125 @@ mod test_nv_increment { assert_eq!(first_value + 1, second_value); } } + +mod test_nv_extend { + use crate::common::create_ctx_with_session; + use tss_esapi::{ + attributes::NvIndexAttributesBuilder, + constants::nv_index_type::NvIndexType, + handles::NvIndexTpmHandle, + interface_types::{ + algorithm::HashingAlgorithm, + reserved_handles::{NvAuth, Provision}, + }, + structures::{MaxNvBuffer, NvPublicBuilder}, + }; + + #[test] + fn test_nv_extend() { + let mut context = create_ctx_with_session(); + let nv_index = NvIndexTpmHandle::new(0x01500029).unwrap(); + + // Create owner nv public. + let owner_nv_index_attributes = NvIndexAttributesBuilder::new() + .with_owner_write(true) + .with_owner_read(true) + .with_orderly(true) + .with_nv_index_type(NvIndexType::Extend) + .build() + .expect("Failed to create owner nv index attributes"); + + let owner_nv_public = NvPublicBuilder::new() + .with_nv_index(nv_index) + .with_index_name_algorithm(HashingAlgorithm::Sha256) + .with_index_attributes(owner_nv_index_attributes) + .with_data_area_size(32) + .build() + .expect("Failed to build NvPublic for owner"); + + let owner_nv_index_handle = context + .nv_define_space(Provision::Owner, None, owner_nv_public) + .expect("Call to nv_define_space failed"); + + // Attempt to read an un-"written"/uninitialized NV index that is defined as extend type + let nv_read_result = context.nv_read(NvAuth::Owner, owner_nv_index_handle, 32, 0); + assert!(nv_read_result.is_err()); + + // Extend NV index with data + let data = MaxNvBuffer::try_from(vec![0x0]).unwrap(); + context + .nv_extend(NvAuth::Owner, owner_nv_index_handle, data) + .expect("Failed to extend NV index"); + + // Validate the new state of the index, which was extended by the data + let nv_read_result = context.nv_read(NvAuth::Owner, owner_nv_index_handle, 32, 0); + let read_data = nv_read_result.expect("Call to nv_read failed"); + + // Expected value is sha256([0; 32] + [0; 1]) + assert_eq!( + [ + 0x7f, 0x9c, 0x9e, 0x31, 0xac, 0x82, 0x56, 0xca, 0x2f, 0x25, 0x85, 0x83, 0xdf, 0x26, + 0x2d, 0xbc, 0x7d, 0x6f, 0x68, 0xf2, 0xa0, 0x30, 0x43, 0xd5, 0xc9, 0x9a, 0x4a, 0xe5, + 0xa7, 0x39, 0x6c, 0xe9 + ], + read_data.as_ref() + ); + + // Clean up defined NV index + context + .nv_undefine_space(Provision::Owner, owner_nv_index_handle) + .expect("Call to nv_undefine_space failed"); + + // Create platform nv public that is cleared on TPM reset/shutdown + let platform_nv_index_attributes = NvIndexAttributesBuilder::new() + .with_pp_write(true) + .with_pp_read(true) + .with_orderly(true) + .with_platform_create(true) + .with_nv_index_type(NvIndexType::Extend) + .with_clear_stclear(true) + .build() + .expect("Failed to create owner nv index attributes"); + + let platform_nv_public = NvPublicBuilder::new() + .with_nv_index(nv_index) + .with_index_name_algorithm(HashingAlgorithm::Sha256) + .with_index_attributes(platform_nv_index_attributes) + .with_data_area_size(32) + .build() + .expect("Failed to build NvPublic for owner"); + + let platform_nv_index_handle = context + .nv_define_space(Provision::Platform, None, platform_nv_public) + .expect("Call to nv_define_space failed"); + + // Attempt to read an un-"written"/uninitialized NV index that is defined as extend type + let nv_read_result = context.nv_read(NvAuth::Platform, platform_nv_index_handle, 32, 0); + assert!(nv_read_result.is_err()); + + // Extend NV index with data + let data = MaxNvBuffer::try_from(vec![0x0]).unwrap(); + context + .nv_extend(NvAuth::Platform, platform_nv_index_handle, data) + .expect("Failed to extend NV index"); + + // Validate the new state of the index, which was extended by the data + let nv_read_result = context.nv_read(NvAuth::Platform, platform_nv_index_handle, 32, 0); + let read_data = nv_read_result.expect("Call to nv_read failed"); + + // Expected value is sha256([0; 32] + [0; 1]) + assert_eq!( + [ + 0x7f, 0x9c, 0x9e, 0x31, 0xac, 0x82, 0x56, 0xca, 0x2f, 0x25, 0x85, 0x83, 0xdf, 0x26, + 0x2d, 0xbc, 0x7d, 0x6f, 0x68, 0xf2, 0xa0, 0x30, 0x43, 0xd5, 0xc9, 0x9a, 0x4a, 0xe5, + 0xa7, 0x39, 0x6c, 0xe9 + ], + read_data.as_ref() + ); + + // Clean up defined NV index + context + .nv_undefine_space(Provision::Platform, platform_nv_index_handle) + .expect("Call to nv_undefine_space failed"); + } +}