diff --git a/tss-esapi/src/abstraction/transient/mod.rs b/tss-esapi/src/abstraction/transient/mod.rs index a08c0a59..3c27a3ef 100644 --- a/tss-esapi/src/abstraction/transient/mod.rs +++ b/tss-esapi/src/abstraction/transient/mod.rs @@ -20,10 +20,11 @@ use crate::{ structures::{ Auth, CreateKeyResult, Data, Digest, EccPoint, EccScheme, Public, PublicBuilder, PublicEccParametersBuilder, PublicKeyRsa, PublicRsaParametersBuilder, RsaExponent, - RsaScheme, Signature, SignatureScheme, SymmetricDefinitionObject, VerifiedTicket, + RsaScheme, SavedTpmContext, Signature, SignatureScheme, SymmetricDefinitionObject, + VerifiedTicket, }, tcti_ldr::TctiNameConf, - utils::{create_restricted_decryption_rsa_public, PublicKey, TpmsContext}, + utils::{create_restricted_decryption_rsa_public, PublicKey}, Context, Error, Result, ReturnCode, WrapperErrorKind as ErrorKind, }; @@ -336,7 +337,7 @@ impl TransientKeyContext { /// just a public key. pub fn migrate_key_from_ctx( &mut self, - context: TpmsContext, + context: SavedTpmContext, auth: Option, ) -> Result { self.set_session_attrs()?; diff --git a/tss-esapi/src/context/tpm_commands/context_management.rs b/tss-esapi/src/context/tpm_commands/context_management.rs index 71ac0856..b783de19 100644 --- a/tss-esapi/src/context/tpm_commands/context_management.rs +++ b/tss-esapi/src/context/tpm_commands/context_management.rs @@ -4,12 +4,12 @@ use crate::{ context::handle_manager::HandleDropAction, handles::{handle_conversion::TryIntoNotNone, AuthHandle, ObjectHandle, PersistentTpmHandle}, interface_types::{data_handles::Persistent, reserved_handles::Provision}, + structures::SavedTpmContext, tss2_esys::{Esys_ContextLoad, Esys_ContextSave, Esys_EvictControl, Esys_FlushContext}, - utils::TpmsContext, Context, Result, ReturnCode, }; use log::error; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use std::ptr::null_mut; impl Context { @@ -18,7 +18,7 @@ impl Context { /// # Errors /// * if conversion from `TPMS_CONTEXT` to `TpmsContext` fails, a `WrongParamSize` error will /// be returned - pub fn context_save(&mut self, handle: ObjectHandle) -> Result { + pub fn context_save(&mut self, handle: ObjectHandle) -> Result { let mut context_ptr = null_mut(); ReturnCode::ensure_success( unsafe { Esys_ContextSave(self.mut_context(), handle.into(), &mut context_ptr) }, @@ -26,7 +26,7 @@ impl Context { error!("Error in saving context: {:#010X}", ret); }, )?; - TpmsContext::try_from(Context::ffi_data_to_owned(context_ptr)) + SavedTpmContext::try_from(Context::ffi_data_to_owned(context_ptr)) } /// Load a previously saved context into the TPM and return the object handle. @@ -34,16 +34,11 @@ impl Context { /// # Errors /// * if conversion from `TpmsContext` to the native `TPMS_CONTEXT` fails, a `WrongParamSize` /// error will be returned - pub fn context_load(&mut self, context: TpmsContext) -> Result { + pub fn context_load(&mut self, context: SavedTpmContext) -> Result { let mut esys_loaded_handle = ObjectHandle::None.into(); + let tpm_context = context.into(); ReturnCode::ensure_success( - unsafe { - Esys_ContextLoad( - self.mut_context(), - &context.try_into()?, - &mut esys_loaded_handle, - ) - }, + unsafe { Esys_ContextLoad(self.mut_context(), &tpm_context, &mut esys_loaded_handle) }, |ret| { error!("Error in loading context: {:#010X}", ret); }, diff --git a/tss-esapi/src/interface_types/data_handles.rs b/tss-esapi/src/interface_types/data_handles.rs index 2b3ebf0d..434eb247 100644 --- a/tss-esapi/src/interface_types/data_handles.rs +++ b/tss-esapi/src/interface_types/data_handles.rs @@ -1,3 +1,5 @@ +// Copyright 2020 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 /// This module contains native representations of the TPMI_DH types. use crate::{ handles::{ @@ -8,6 +10,7 @@ use crate::{ Error, Result, WrapperErrorKind, }; use std::convert::TryFrom; + /// Enum representing the 'Object' data handles interface type. /// /// # Details @@ -70,8 +73,7 @@ pub enum Pcr { /// Enum representing the 'Context' data handles interface type. /// /// # Details -/// This corresponds to the TPMI_DH_CONTEXT interface type. -/// This corresponds to the TPMI_DH_CONTEXT interface type. This only +/// This corresponds to the `TPMI_DH_CONTEXT` interface type. This only /// exist for compatibility purposes the specification is not entirely /// clear on whether this should still be used or be completely replaced by /// [Saved]. @@ -116,13 +118,18 @@ impl TryFrom for ContextDataHandle { /// Enum representing the 'Saved' data handles interface type. /// /// # Details -/// This corresponds to the TPMI_DH_SAVED interface type. +/// This corresponds to the `TPMI_DH_SAVED` interface type. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Saved { + /// A HMAC session context. Hmac(HmacSessionTpmHandle), + /// A policy session context. Policy(PolicySessionTpmHandle), + /// An ordinary transient object. Transient, + /// A sequence object. Sequence, + /// A transient object with stClear attribute SET. TransientClear, } @@ -162,3 +169,15 @@ impl TryFrom for Saved { }) } } + +impl From for TPMI_DH_SAVED { + fn from(native: Saved) -> TPMI_DH_SAVED { + match native { + Saved::Hmac(handle) => handle.into(), + Saved::Policy(handle) => handle.into(), + Saved::Transient => TransientTpmHandle::SavedTransient.into(), + Saved::Sequence => TransientTpmHandle::SavedSequence.into(), + Saved::TransientClear => TransientTpmHandle::SavedTransientClear.into(), + } + } +} diff --git a/tss-esapi/src/structures/buffers.rs b/tss-esapi/src/structures/buffers.rs index a67d4471..534f3c3f 100644 --- a/tss-esapi/src/structures/buffers.rs +++ b/tss-esapi/src/structures/buffers.rs @@ -390,3 +390,12 @@ pub mod symmetric_key { pub mod timeout { buffer_type!(Timeout, 8, TPM2B_TIMEOUT); } + +pub mod tpm_context_data { + use crate::tss2_esys::TPMS_CONTEXT_DATA; + buffer_type!( + TpmContextData, + std::mem::size_of::(), + TPM2B_CONTEXT_DATA + ); +} diff --git a/tss-esapi/src/structures/mod.rs b/tss-esapi/src/structures/mod.rs index 2c68b095..734f3bd5 100644 --- a/tss-esapi/src/structures/mod.rs +++ b/tss-esapi/src/structures/mod.rs @@ -41,7 +41,7 @@ pub use self::buffers::{ private_key_rsa::PrivateKeyRsa, private_vendor_specific::PrivateVendorSpecific, public::PublicBuffer, public_key_rsa::PublicKeyRsa, sensitive::SensitiveBuffer, sensitive_create::SensitiveCreateBuffer, sensitive_data::SensitiveData, - symmetric_key::SymmetricKey, timeout::Timeout, + symmetric_key::SymmetricKey, timeout::Timeout, tpm_context_data::TpmContextData, }; ///////////////////////////////////////////////////////// /// The creation section @@ -212,3 +212,8 @@ pub use nv::storage::{NvPublic, NvPublicBuilder}; ///////////////////////////////////////////////////////// mod algorithm; pub use algorithm::symmetric::sensitive_create::SensitiveCreate; +///////////////////////////////////////////////////////// +/// TPM context structures +///////////////////////////////////////////////////////// +mod tpm_context; +pub use tpm_context::SavedTpmContext; diff --git a/tss-esapi/src/structures/tagged/public.rs b/tss-esapi/src/structures/tagged/public.rs index 5f416a88..66e0cc0f 100644 --- a/tss-esapi/src/structures/tagged/public.rs +++ b/tss-esapi/src/structures/tagged/public.rs @@ -495,7 +495,7 @@ impl TryFrom for Public { impl_mu_standard!(Public, TPMT_PUBLIC); impl Serialize for Public { - /// Serialise the [Public] data into it's bytes representation of the TCG + /// Serialize the [Public] data into it's bytes representation of the TCG /// TPMT_PUBLIC structure. fn serialize(&self, serializer: S) -> std::result::Result where diff --git a/tss-esapi/src/structures/tpm_context.rs b/tss-esapi/src/structures/tpm_context.rs new file mode 100644 index 00000000..120f1f5b --- /dev/null +++ b/tss-esapi/src/structures/tpm_context.rs @@ -0,0 +1,104 @@ +// Copyright 2024 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +use crate::{ + handles::TpmHandle, + interface_types::{data_handles::Saved, reserved_handles::Hierarchy}, + structures::TpmContextData, + traits::impl_mu_standard, + traits::{Marshall, UnMarshall}, + tss2_esys::TPMS_CONTEXT, + Error, Result, +}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::convert::TryFrom; + +/// Structure holding the content of a TPM context. +#[derive(Debug, Clone)] +pub struct SavedTpmContext { + sequence: u64, + saved_handle: Saved, + hierarchy: Hierarchy, + context_blob: TpmContextData, +} + +impl SavedTpmContext { + /// The sequence parameter + /// + /// # Details + /// "The sequence parameter is used to differentiate the contexts and to allow the TPM to create a different + /// encryption key for each context." + pub const fn sequence(&self) -> u64 { + self.sequence + } + + /// The saved handle. + pub const fn saved_handle(&self) -> Saved { + self.saved_handle + } + + /// The hierarchy for the saved context. + pub const fn hierarchy(&self) -> Hierarchy { + self.hierarchy + } + + /// The context blob. + /// + /// # Details + /// "This is the hierarchy ([Hierarchy]) for the saved context and determines the proof value used + /// in the construction of the encryption and integrity values for the context. For session and sequence + /// contexts, the hierarchy is [Hierarchy::Null]. The hierarchy for a transient object may be [Hierarchy::Null] + /// but it is not required." + pub fn context_blob(&self) -> &TpmContextData { + &self.context_blob + } +} + +impl TryFrom for SavedTpmContext { + type Error = Error; + + fn try_from(tss: TPMS_CONTEXT) -> Result { + Ok(SavedTpmContext { + sequence: tss.sequence, + saved_handle: Saved::try_from(tss.savedHandle)?, + hierarchy: TpmHandle::try_from(tss.hierarchy).and_then(Hierarchy::try_from)?, + context_blob: TpmContextData::try_from(tss.contextBlob)?, + }) + } +} + +impl From for TPMS_CONTEXT { + fn from(native: SavedTpmContext) -> TPMS_CONTEXT { + TPMS_CONTEXT { + sequence: native.sequence, + savedHandle: native.saved_handle.into(), + hierarchy: TpmHandle::from(native.hierarchy).into(), + contextBlob: native.context_blob.into(), + } + } +} + +impl_mu_standard!(SavedTpmContext, TPMS_CONTEXT); + +impl Serialize for SavedTpmContext { + /// Serialize the [SavedTpmContext] data into it's bytes representation of the TCG + /// TPMS_CONTEXT structure. + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + let bytes = self.marshall().map_err(serde::ser::Error::custom)?; + serializer.serialize_bytes(&bytes) + } +} + +impl<'de> Deserialize<'de> for SavedTpmContext { + /// Deserialize the [SavedTpmContext] data from it's bytes representation of the TCG + /// TPMS_CONTEXT structure. + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + let bytes = >::deserialize(deserializer)?; + Self::unmarshall(&bytes).map_err(serde::de::Error::custom) + } +} diff --git a/tss-esapi/src/utils/mod.rs b/tss-esapi/src/utils/mod.rs index 5d9eff24..17103111 100644 --- a/tss-esapi/src/utils/mod.rs +++ b/tss-esapi/src/utils/mod.rs @@ -19,76 +19,10 @@ use crate::structures::{ EccPoint, EccScheme, Public, PublicBuilder, PublicEccParametersBuilder, PublicKeyRsa, PublicRsaParametersBuilder, RsaExponent, RsaScheme, SymmetricDefinitionObject, }; -use crate::tss2_esys::*; use crate::{Context, Error, Result, WrapperErrorKind}; use serde::{Deserialize, Serialize}; -use std::convert::{TryFrom, TryInto}; -use zeroize::{Zeroize, ZeroizeOnDrop}; - -/// Rust native wrapper for `TPMS_CONTEXT` objects. -/// -/// This structure is intended to help with persisting object contexts. As the main reason for -/// saving the context of an object is to be able to reuse it later, on demand, a serializable -/// structure is most commonly needed. `TpmsContext` implements the `Serialize` and `Deserialize` -/// defined by `serde`. -#[derive(Debug, Serialize, Deserialize, Clone, Zeroize, ZeroizeOnDrop)] -pub struct TpmsContext { - sequence: u64, - saved_handle: TPMI_DH_CONTEXT, - hierarchy: TPMI_RH_HIERARCHY, - context_blob: Vec, -} - -impl TpmsContext { - /// Get a reference to the `context_blob` field - pub fn context_blob(&self) -> &Vec { - &self.context_blob - } -} - -// TODO: Replace with `From` -impl TryFrom for TpmsContext { - type Error = Error; - - fn try_from(tss2_context: TPMS_CONTEXT) -> Result { - let mut context = TpmsContext { - sequence: tss2_context.sequence, - saved_handle: tss2_context.savedHandle, - hierarchy: tss2_context.hierarchy, - context_blob: tss2_context.contextBlob.buffer.to_vec(), - }; - context - .context_blob - .truncate(tss2_context.contextBlob.size.into()); - Ok(context) - } -} - -#[allow(clippy::needless_update)] -impl TryFrom for TPMS_CONTEXT { - type Error = Error; - - fn try_from(context: TpmsContext) -> Result { - let buffer_size = context.context_blob.len(); - if buffer_size > 5188 { - return Err(Error::local_error(WrapperErrorKind::WrongParamSize)); - } - let mut buffer = [0_u8; 5188]; - for (i, val) in context.context_blob.iter().enumerate() { - buffer[i] = *val; - } - Ok(TPMS_CONTEXT { - sequence: context.sequence, - savedHandle: context.saved_handle, - hierarchy: context.hierarchy, - contextBlob: TPM2B_CONTEXT_DATA { - size: buffer_size.try_into().unwrap(), // should not panic given the check above - buffer, - }, - ..Default::default() - }) - } -} +use std::convert::TryFrom; +use zeroize::Zeroize; /// Create the [Public] structure for a restricted decryption key. /// diff --git a/tss-esapi/tests/integration_tests/common/tpm2b_types_equality_checks.rs b/tss-esapi/tests/integration_tests/common/tpm2b_types_equality_checks.rs index c923c62c..e87bbee1 100644 --- a/tss-esapi/tests/integration_tests/common/tpm2b_types_equality_checks.rs +++ b/tss-esapi/tests/integration_tests/common/tpm2b_types_equality_checks.rs @@ -1,8 +1,8 @@ // Copyright 2022 Contributors to the Parsec project. // SPDX-License-Identifier: Apache-2.0 use tss_esapi::tss2_esys::{ - TPM2B_AUTH, TPM2B_DATA, TPM2B_DIGEST, TPM2B_MAX_NV_BUFFER, TPM2B_NAME, TPM2B_SENSITIVE_CREATE, - TPM2B_SENSITIVE_DATA, + TPM2B_AUTH, TPM2B_CONTEXT_DATA, TPM2B_DATA, TPM2B_DIGEST, TPM2B_MAX_NV_BUFFER, TPM2B_NAME, + TPM2B_SENSITIVE_CREATE, TPM2B_SENSITIVE_DATA, }; macro_rules! ensure_sized_buffer_equality { @@ -60,3 +60,10 @@ pub fn ensure_tpm2b_sensitive_create_equality( ); crate::common::ensure_tpms_sensitive_create_equality(&expected.sensitive, &actual.sensitive); } + +pub fn ensure_tpm2b_context_data_equality( + expected: &TPM2B_CONTEXT_DATA, + actual: &TPM2B_CONTEXT_DATA, +) { + ensure_sized_buffer_equality!(expected, actual, buffer, TPM2B_CONTEXT_DATA); +} diff --git a/tss-esapi/tests/integration_tests/common/tpms_types_equality_checks.rs b/tss-esapi/tests/integration_tests/common/tpms_types_equality_checks.rs index da9bef78..f6db3fc7 100644 --- a/tss-esapi/tests/integration_tests/common/tpms_types_equality_checks.rs +++ b/tss-esapi/tests/integration_tests/common/tpms_types_equality_checks.rs @@ -7,7 +7,7 @@ use tss_esapi::{ }, tss2_esys::{ TPMS_ALG_PROPERTY, TPMS_ATTEST, TPMS_CERTIFY_INFO, TPMS_CLOCK_INFO, - TPMS_COMMAND_AUDIT_INFO, TPMS_CREATION_INFO, TPMS_ECC_PARMS, TPMS_EMPTY, + TPMS_COMMAND_AUDIT_INFO, TPMS_CONTEXT, TPMS_CREATION_INFO, TPMS_ECC_PARMS, TPMS_EMPTY, TPMS_KEYEDHASH_PARMS, TPMS_NV_CERTIFY_INFO, TPMS_PCR_SELECTION, TPMS_QUOTE_INFO, TPMS_RSA_PARMS, TPMS_SCHEME_ECDAA, TPMS_SCHEME_HASH, TPMS_SCHEME_HMAC, TPMS_SCHEME_XOR, TPMS_SENSITIVE_CREATE, TPMS_SESSION_AUDIT_INFO, TPMS_SYMCIPHER_PARMS, @@ -316,3 +316,19 @@ pub fn ensure_tpms_empty_equality(expected: &TPMS_EMPTY, actual: &TPMS_EMPTY) { "'empty' value TPMS_EMPTY, mismatch between actual and expected." ); } + +pub fn ensure_tpms_context_equality(expected: &TPMS_CONTEXT, actual: &TPMS_CONTEXT) { + assert_eq!( + expected.sequence, actual.sequence, + "'sequence' value TPMS_CONTEXT, mismatch between actual and expected" + ); + assert_eq!( + expected.savedHandle, actual.savedHandle, + "'savedHandle' value TPMS_CONTEXT, mismatch between actual and expected" + ); + assert_eq!( + expected.hierarchy, actual.hierarchy, + "'hierarchy' value TPMS_CONTEXT, mismatch between actual and expected" + ); + crate::common::ensure_tpm2b_context_data_equality(&expected.contextBlob, &actual.contextBlob); +} diff --git a/tss-esapi/tests/integration_tests/structures_tests/mod.rs b/tss-esapi/tests/integration_tests/structures_tests/mod.rs index cea81812..8869b452 100644 --- a/tss-esapi/tests/integration_tests/structures_tests/mod.rs +++ b/tss-esapi/tests/integration_tests/structures_tests/mod.rs @@ -20,3 +20,4 @@ mod tagged_property_tests; mod tagged_tests; mod time_attest_info_tests; mod time_info_tests; +mod tpm_context_tests; diff --git a/tss-esapi/tests/integration_tests/structures_tests/tpm_context_tests.rs b/tss-esapi/tests/integration_tests/structures_tests/tpm_context_tests.rs new file mode 100644 index 00000000..1ee4dde9 --- /dev/null +++ b/tss-esapi/tests/integration_tests/structures_tests/tpm_context_tests.rs @@ -0,0 +1,40 @@ +use std::convert::TryFrom; +use tss_esapi::{ + handles::TpmHandle, + interface_types::{data_handles::Saved, reserved_handles::Hierarchy}, + structures::{SavedTpmContext, TpmContextData}, + tss2_esys::TPMS_CONTEXT, +}; + +#[test] +fn test_valid_conversions() { + let expected_sequence = 13243546576879u64; + let expected_saved_handle = Saved::Transient; + let expected_hierarchy = Hierarchy::Owner; + let expected_context_blob = + TpmContextData::try_from(vec![1u8, 2u8, 12u8, 23u8, 45u8, 56u8, 98u8]) + .expect("It should be possible to create TpmContextData buffer type from bytes."); + let expected_tpms_context = TPMS_CONTEXT { + sequence: expected_sequence, + savedHandle: expected_saved_handle.into(), + hierarchy: TpmHandle::from(expected_hierarchy).into(), + contextBlob: expected_context_blob.clone().into(), + }; + // Conversion TPMS_CONTEXT -> SavedTpmContext + let actual_saved_tpm_context = SavedTpmContext::try_from(expected_tpms_context).expect( + "It should be possible to convert a valid TPMS_CONTEXT structure into a SavedTpmContext", + ); + assert_eq!(expected_sequence, actual_saved_tpm_context.sequence()); + assert_eq!( + expected_saved_handle, + actual_saved_tpm_context.saved_handle() + ); + assert_eq!(expected_hierarchy, actual_saved_tpm_context.hierarchy()); + assert_eq!( + expected_context_blob, + *actual_saved_tpm_context.context_blob() + ); + // SavedTpmContext -> TPMS_CONTEXT + let actual_tpms_context = TPMS_CONTEXT::from(actual_saved_tpm_context); + crate::common::ensure_tpms_context_equality(&expected_tpms_context, &actual_tpms_context); +}