Skip to content

Commit ecdd815

Browse files
author
Nathan Rossi
committed
Add definition for NV_Extend
Add a implementation for NV_Extend, which is used with a NV Index defined with the NvIndexType::Extend to provide a PCR equivalent that uses NV memory. The hashing algorithm used is defined by the index name algorithm. An example is included within the documentation for the function, setting up the NV index with the required type and typical attributes for performing a extend of the index. An integration test is also added for this function. Testing the same owner auth scenario of the example, and a platform auth variant, validating the result of the NV index after the extend operation in both tests. Signed-off-by: Nathan Rossi <[email protected]>
1 parent a33e447 commit ecdd815

File tree

2 files changed

+233
-2
lines changed

2 files changed

+233
-2
lines changed

tss-esapi/src/context/tpm_commands/non_volatile_storage.rs

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
interface_types::reserved_handles::{NvAuth, Provision},
77
structures::{Auth, MaxNvBuffer, Name, NvPublic},
88
tss2_esys::{
9-
Esys_NV_DefineSpace, Esys_NV_Increment, Esys_NV_Read, Esys_NV_ReadPublic,
9+
Esys_NV_DefineSpace, Esys_NV_Extend, Esys_NV_Increment, Esys_NV_Read, Esys_NV_ReadPublic,
1010
Esys_NV_UndefineSpace, Esys_NV_UndefineSpaceSpecial, Esys_NV_Write,
1111
},
1212
Context, Result, ReturnCode,
@@ -698,7 +698,116 @@ impl Context {
698698
)
699699
}
700700

701-
// Missing function: NV_Extend
701+
/// Extends data to the NV memory associated with a nv index.
702+
///
703+
/// # Details
704+
/// This method is used to extend a value to the nv memory in the TPM.
705+
///
706+
/// Please beware that this method requires an authorization session handle to be present.
707+
///
708+
/// Any NV index (that is not already used) can be defined as an extend type. However various specifications define
709+
/// indexes that have specific purposes or are reserved, for example the TCG PC Client Platform Firmware Profile
710+
/// Specification Section 3.3.6 defines indexes within the 0x01c40200-0x01c402ff range for instance measurements.
711+
/// Section 2.2 of TCG Registry of Reserved TPM 2.0 Handles and Localities provides additional context for specific
712+
/// NV index ranges.
713+
///
714+
/// # Arguments
715+
/// * `auth_handle` - Handle indicating the source of authorization value.
716+
/// * `nv_index_handle` - The [NvIndexHandle] associated with NV memory
717+
/// which will be extended by data hashed with the previous data.
718+
/// * `data` - The data, in the form of a [MaxNvBuffer], that is to be written.
719+
///
720+
/// # Example
721+
/// ```rust
722+
/// # use tss_esapi::{
723+
/// # Context, TctiNameConf, attributes::{SessionAttributes, NvIndexAttributes},
724+
/// # handles::NvIndexTpmHandle, interface_types::algorithm::HashingAlgorithm,
725+
/// # structures::{SymmetricDefinition, NvPublic},
726+
/// # constants::SessionType, constants::nv_index_type::NvIndexType,
727+
/// # };
728+
/// use tss_esapi::{
729+
/// interface_types::reserved_handles::{Provision, NvAuth}, structures::MaxNvBuffer,
730+
/// };
731+
///
732+
/// # // Create context
733+
/// # let mut context =
734+
/// # Context::new(
735+
/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
736+
/// # ).expect("Failed to create Context");
737+
/// #
738+
/// # let session = context
739+
/// # .start_auth_session(
740+
/// # None,
741+
/// # None,
742+
/// # None,
743+
/// # SessionType::Hmac,
744+
/// # SymmetricDefinition::AES_256_CFB,
745+
/// # tss_esapi::interface_types::algorithm::HashingAlgorithm::Sha256,
746+
/// # )
747+
/// # .expect("Failed to create session")
748+
/// # .expect("Received invalid handle");
749+
/// # let (session_attributes, session_attributes_mask) = SessionAttributes::builder()
750+
/// # .with_decrypt(true)
751+
/// # .with_encrypt(true)
752+
/// # .build();
753+
/// # context.tr_sess_set_attributes(session, session_attributes, session_attributes_mask)
754+
/// # .expect("Failed to set attributes on session");
755+
/// # context.set_sessions((Some(session), None, None));
756+
/// #
757+
/// # let nv_index = NvIndexTpmHandle::new(0x01500028)
758+
/// # .expect("Failed to create NV index tpm handle");
759+
/// #
760+
/// // Create NV index attributes
761+
/// let owner_nv_index_attributes = NvIndexAttributes::builder()
762+
/// .with_owner_write(true)
763+
/// .with_owner_read(true)
764+
/// .with_orderly(true)
765+
/// .with_nv_index_type(NvIndexType::Extend)
766+
/// .build()
767+
/// .expect("Failed to create owner nv index attributes");
768+
///
769+
/// // Create owner nv public.
770+
/// let owner_nv_public = NvPublic::builder()
771+
/// .with_nv_index(nv_index)
772+
/// .with_index_name_algorithm(HashingAlgorithm::Sha256)
773+
/// .with_index_attributes(owner_nv_index_attributes)
774+
/// .with_data_area_size(32)
775+
/// .build()
776+
/// .expect("Failed to build NvPublic for owner");
777+
///
778+
/// let nv_index_handle = context
779+
/// .nv_define_space(Provision::Owner, None, owner_nv_public.clone())
780+
/// .expect("Call to nv_define_space failed");
781+
///
782+
/// let data = MaxNvBuffer::try_from(vec![0x0]).unwrap();
783+
/// let result = context.nv_extend(NvAuth::Owner, nv_index_handle, data);
784+
///
785+
/// # context
786+
/// # .nv_undefine_space(Provision::Owner, nv_index_handle)
787+
/// # .expect("Call to nv_undefine_space failed");
788+
/// ```
789+
pub fn nv_extend(
790+
&mut self,
791+
auth_handle: NvAuth,
792+
nv_index_handle: NvIndexHandle,
793+
data: MaxNvBuffer,
794+
) -> Result<()> {
795+
ReturnCode::ensure_success(
796+
unsafe {
797+
Esys_NV_Extend(
798+
self.mut_context(),
799+
AuthHandle::from(auth_handle).into(),
800+
nv_index_handle.into(),
801+
self.required_session_1()?,
802+
self.optional_session_2(),
803+
self.optional_session_3(),
804+
&data.into(),
805+
)
806+
},
807+
|ret| error!("Error when extending NV: {:#010X}", ret),
808+
)
809+
}
810+
702811
// Missing function: NV_SetBits
703812
// Missing function: NV_WriteLock
704813
// Missing function: NV_GlobalWriteLock

tss-esapi/tests/integration_tests/context_tests/tpm_commands/non_volatile_storage_tests.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,125 @@ mod test_nv_increment {
421421
assert_eq!(first_value + 1, second_value);
422422
}
423423
}
424+
425+
mod test_nv_extend {
426+
use crate::common::create_ctx_with_session;
427+
use tss_esapi::{
428+
attributes::NvIndexAttributesBuilder,
429+
constants::nv_index_type::NvIndexType,
430+
handles::NvIndexTpmHandle,
431+
interface_types::{
432+
algorithm::HashingAlgorithm,
433+
reserved_handles::{NvAuth, Provision},
434+
},
435+
structures::{MaxNvBuffer, NvPublicBuilder},
436+
};
437+
438+
#[test]
439+
fn test_nv_extend() {
440+
let mut context = create_ctx_with_session();
441+
let nv_index = NvIndexTpmHandle::new(0x01500029).unwrap();
442+
443+
// Create owner nv public.
444+
let owner_nv_index_attributes = NvIndexAttributesBuilder::new()
445+
.with_owner_write(true)
446+
.with_owner_read(true)
447+
.with_orderly(true)
448+
.with_nv_index_type(NvIndexType::Extend)
449+
.build()
450+
.expect("Failed to create owner nv index attributes");
451+
452+
let owner_nv_public = NvPublicBuilder::new()
453+
.with_nv_index(nv_index)
454+
.with_index_name_algorithm(HashingAlgorithm::Sha256)
455+
.with_index_attributes(owner_nv_index_attributes)
456+
.with_data_area_size(32)
457+
.build()
458+
.expect("Failed to build NvPublic for owner");
459+
460+
let owner_nv_index_handle = context
461+
.nv_define_space(Provision::Owner, None, owner_nv_public)
462+
.expect("Call to nv_define_space failed");
463+
464+
// Attempt to read an un-"written"/uninitialized NV index that is defined as extend type
465+
let nv_read_result = context.nv_read(NvAuth::Owner, owner_nv_index_handle, 32, 0);
466+
assert!(nv_read_result.is_err());
467+
468+
// Extend NV index with data
469+
let data = MaxNvBuffer::try_from(vec![0x0]).unwrap();
470+
context
471+
.nv_extend(NvAuth::Owner, owner_nv_index_handle, data)
472+
.expect("Failed to extend NV index");
473+
474+
// Validate the new state of the index, which was extended by the data
475+
let nv_read_result = context.nv_read(NvAuth::Owner, owner_nv_index_handle, 32, 0);
476+
let read_data = nv_read_result.expect("Call to nv_read failed");
477+
478+
// Expected value is sha256([0; 32] + [0; 1])
479+
assert_eq!(
480+
[
481+
0x7f, 0x9c, 0x9e, 0x31, 0xac, 0x82, 0x56, 0xca, 0x2f, 0x25, 0x85, 0x83, 0xdf, 0x26,
482+
0x2d, 0xbc, 0x7d, 0x6f, 0x68, 0xf2, 0xa0, 0x30, 0x43, 0xd5, 0xc9, 0x9a, 0x4a, 0xe5,
483+
0xa7, 0x39, 0x6c, 0xe9
484+
],
485+
read_data.as_ref()
486+
);
487+
488+
// Clean up defined NV index
489+
context
490+
.nv_undefine_space(Provision::Owner, owner_nv_index_handle)
491+
.expect("Call to nv_undefine_space failed");
492+
493+
// Create platform nv public that is cleared on TPM reset/shutdown
494+
let platform_nv_index_attributes = NvIndexAttributesBuilder::new()
495+
.with_pp_write(true)
496+
.with_pp_read(true)
497+
.with_orderly(true)
498+
.with_platform_create(true)
499+
.with_nv_index_type(NvIndexType::Extend)
500+
.with_clear_stclear(true)
501+
.build()
502+
.expect("Failed to create owner nv index attributes");
503+
504+
let platform_nv_public = NvPublicBuilder::new()
505+
.with_nv_index(nv_index)
506+
.with_index_name_algorithm(HashingAlgorithm::Sha256)
507+
.with_index_attributes(platform_nv_index_attributes)
508+
.with_data_area_size(32)
509+
.build()
510+
.expect("Failed to build NvPublic for owner");
511+
512+
let platform_nv_index_handle = context
513+
.nv_define_space(Provision::Platform, None, platform_nv_public)
514+
.expect("Call to nv_define_space failed");
515+
516+
// Attempt to read an un-"written"/uninitialized NV index that is defined as extend type
517+
let nv_read_result = context.nv_read(NvAuth::Platform, platform_nv_index_handle, 32, 0);
518+
assert!(nv_read_result.is_err());
519+
520+
// Extend NV index with data
521+
let data = MaxNvBuffer::try_from(vec![0x0]).unwrap();
522+
context
523+
.nv_extend(NvAuth::Platform, platform_nv_index_handle, data)
524+
.expect("Failed to extend NV index");
525+
526+
// Validate the new state of the index, which was extended by the data
527+
let nv_read_result = context.nv_read(NvAuth::Platform, platform_nv_index_handle, 32, 0);
528+
let read_data = nv_read_result.expect("Call to nv_read failed");
529+
530+
// Expected value is sha256([0; 32] + [0; 1])
531+
assert_eq!(
532+
[
533+
0x7f, 0x9c, 0x9e, 0x31, 0xac, 0x82, 0x56, 0xca, 0x2f, 0x25, 0x85, 0x83, 0xdf, 0x26,
534+
0x2d, 0xbc, 0x7d, 0x6f, 0x68, 0xf2, 0xa0, 0x30, 0x43, 0xd5, 0xc9, 0x9a, 0x4a, 0xe5,
535+
0xa7, 0x39, 0x6c, 0xe9
536+
],
537+
read_data.as_ref()
538+
);
539+
540+
// Clean up defined NV index
541+
context
542+
.nv_undefine_space(Provision::Platform, platform_nv_index_handle)
543+
.expect("Call to nv_undefine_space failed");
544+
}
545+
}

0 commit comments

Comments
 (0)