diff --git a/tss-esapi-sys/src/bindings/x86_64-unknown-linux-gnu.rs b/tss-esapi-sys/src/bindings/x86_64-unknown-linux-gnu.rs index 05d3f51c..73683ac7 100644 --- a/tss-esapi-sys/src/bindings/x86_64-unknown-linux-gnu.rs +++ b/tss-esapi-sys/src/bindings/x86_64-unknown-linux-gnu.rs @@ -11538,6 +11538,7 @@ extern "C" { objectHandle: *mut ESYS_TR, outPrivate: *mut *mut TPM2B_PRIVATE, outPublic: *mut *mut TPM2B_PUBLIC, + // Is this missing NAME? ) -> TSS2_RC; } extern "C" { diff --git a/tss-esapi/src/context/tpm_commands/object_commands.rs b/tss-esapi/src/context/tpm_commands/object_commands.rs index 4feefea2..7251a7f4 100644 --- a/tss-esapi/src/context/tpm_commands/object_commands.rs +++ b/tss-esapi/src/context/tpm_commands/object_commands.rs @@ -3,22 +3,29 @@ mod create_command_input; mod create_command_output; +mod create_loaded_command_input; +mod create_loaded_command_output; + use crate::{ context::handle_manager::HandleDropAction, handles::{KeyHandle, ObjectHandle, TpmHandle}, interface_types::reserved_handles::Hierarchy, structures::{ - Auth, CreateKeyResult, Data, Digest, EncryptedSecret, IdObject, Name, PcrSelectionList, - Private, Public, Sensitive, SensitiveData, + Auth, CreateKeyResult, CreateLoadedKeyResult, Data, Digest, EncryptedSecret, IdObject, + Name, PcrSelectionList, Private, Public, Sensitive, SensitiveData, }, tss2_esys::{ - Esys_ActivateCredential, Esys_Create, Esys_Load, Esys_LoadExternal, Esys_MakeCredential, - Esys_ObjectChangeAuth, Esys_ReadPublic, Esys_Unseal, + Esys_ActivateCredential, Esys_Create, Esys_CreateLoaded, Esys_Load, Esys_LoadExternal, + Esys_MakeCredential, Esys_ObjectChangeAuth, Esys_ReadPublic, Esys_Unseal, }, Context, Result, ReturnCode, }; use create_command_input::CreateCommandInputHandler; use create_command_output::CreateCommandOutputHandler; + +use create_loaded_command_input::CreateLoadedCommandInputHandler; +use create_loaded_command_output::CreateLoadedCommandOutputHandler; + use log::error; use std::convert::{TryFrom, TryInto}; use std::ptr::{null, null_mut}; @@ -338,5 +345,79 @@ impl Context { Private::try_from(Context::ffi_data_to_owned(out_private_ptr)) } - // Missing function: CreateLoaded + /// Create a key and load it in the TPM. + /// + /// This function allows the creation of three distinct object types. This is determined + /// by the type of [KeyHandle] that is provided to the `parent_handle` argument. The key + /// types are: + /// + /// * Primary - Created when [KeyHandle] is a primary seed + /// * Ordinary - Created when [KeyHandle] is a storage parent + /// * Derived - Created when [KeyHandle] is a derivation parent + /// + /// _notes_ + /// + /// When creating a derived key, the value for `sensitive_data_origin` in `public` must be + /// `false`. + /// + /// # Parameters + /// * `parent_handle` - The [KeyHandle] of the parent for the new object that is being created. + /// * `auth_value` - The value used to be used for authorize usage of the object. + /// * `sensitive_data` - The data that is to be sealed, a key or derivation values. + /// * `public` - The public part of the object that is being created. + /// + /// # Errors + /// * if either of the slices is larger than the maximum size of the native objects, a + /// `WrongParamSize` wrapper error is returned + // TODO: Fix when compacting the arguments into a struct + #[allow(clippy::too_many_arguments)] + pub fn create_loaded( + &mut self, + parent_handle: KeyHandle, + auth_value: Option, + sensitive_data: Option, + public: Public, + ) -> Result { + let input_parameters = CreateLoadedCommandInputHandler::create( + parent_handle, + auth_value, + sensitive_data, + public, + )?; + + let mut output_parameters = CreateLoadedCommandOutputHandler::new(); + + ReturnCode::ensure_success( + unsafe { + Esys_CreateLoaded( + // esysContext + self.mut_context(), + // parent_handle + input_parameters.ffi_in_parent_handle(), + // session handles + self.optional_session_1(), + self.optional_session_2(), + self.optional_session_3(), + // inSensitive + input_parameters.ffi_in_sensitive(), + // inPublic + input_parameters.ffi_in_public(), + // objectHandle + output_parameters.ffi_out_object_handle(), + // outPrivate + output_parameters.ffi_out_private_ptr(), + // outPublic + output_parameters.ffi_out_public_ptr(), + // Per TPM Part3 12.9.2 Table 35, name is an output + // value, but appears not to be in our bindings that are generated. + // output_parameters.ffi_out_name_ptr(), + ) + }, + |ret| { + error!("Error in creating derived key: {:#010X}", ret); + }, + )?; + + output_parameters.try_into() + } } diff --git a/tss-esapi/src/context/tpm_commands/object_commands/create_loaded_command_input.rs b/tss-esapi/src/context/tpm_commands/object_commands/create_loaded_command_input.rs new file mode 100644 index 00000000..b14a85b6 --- /dev/null +++ b/tss-esapi/src/context/tpm_commands/object_commands/create_loaded_command_input.rs @@ -0,0 +1,92 @@ +// Copyright 2024 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + ffi::data_zeroize::FfiDataZeroize, + handles::KeyHandle, + structures::{Auth, Public, SensitiveCreate, SensitiveData}, + tss2_esys::{ESYS_TR, TPM2B_SENSITIVE_CREATE, TPM2B_TEMPLATE}, + Result, +}; +use std::convert::TryInto; +use zeroize::Zeroize; + +/// Struct that handles the input of the +/// to the Esys_CreateLoaded command and zeroizes +/// the data when it gets dropped. +pub struct CreateLoadedCommandInputHandler { + ffi_in_parent_handle: ESYS_TR, + ffi_in_sensitive: TPM2B_SENSITIVE_CREATE, + // Per Part 3 12.9.1 note 1: + // In the general descriptions of TPM2_Create() and TPM2_CreatePrimary() the validations refer to a + // TPMT_PUBLIC structure that is in inPublic. For TPM2_CreateLoaded(), inPublic is a + // TPM2B_TEMPLATE that may contain a TPMT_PUBLIC that is used for object creation. For object + // derivation, the unique field can contain a label and context that are used in the derivation process. + // To allow both the TPMT_PUBLIC and the derivation variation, a TPM2B_TEMPLATE is used. When + // referring to the checks in TPM2_Create() and TPM2_CreatePrimary(), TPM2B_TEMPLATE should + // be assumed to contain a TPMT_PUBLIC. + ffi_in_public: TPM2B_TEMPLATE, +} + +impl CreateLoadedCommandInputHandler { + /// Creates the CreateLoadedCommandInputHandler from the inputs + /// of the 'create' [crate::Context] method. + /// + /// # Details + /// Consumes the input parameters and converts them into their + /// TSS counterpart and zeroizes all the data when dropped. + /// + /// # Arguments + /// See the input arguments of 'crate' [crate::Context] method. + /// + /// # Returns + /// The created CreateLoadedCommandInputHandler. + /// + /// # Errors + /// WrapperErrors if the conversions to the TSS types fails. + pub(crate) fn create( + parent_handle: KeyHandle, + auth_value: Option, + sensitive_data: Option, + public: Public, + ) -> Result { + Ok(Self { + ffi_in_parent_handle: parent_handle.into(), + ffi_in_sensitive: SensitiveCreate::new( + auth_value.unwrap_or_default(), + sensitive_data.unwrap_or_default(), + ) + .try_into()?, + ffi_in_public: public.try_into()?, + }) + } + + /// The 'parentHandle' input parameter + pub const fn ffi_in_parent_handle(&self) -> ESYS_TR { + self.ffi_in_parent_handle + } + + /// The 'inSensitive' input parameter. + pub const fn ffi_in_sensitive(&self) -> &TPM2B_SENSITIVE_CREATE { + &self.ffi_in_sensitive + } + + /// The 'inPublic' input parameter. + pub const fn ffi_in_public(&self) -> &TPM2B_TEMPLATE { + &self.ffi_in_public + } +} + +impl Zeroize for CreateLoadedCommandInputHandler { + fn zeroize(&mut self) { + self.ffi_in_parent_handle.zeroize(); + self.ffi_in_sensitive.ffi_data_zeroize(); + self.ffi_in_public.ffi_data_zeroize(); + } +} + +impl Drop for CreateLoadedCommandInputHandler { + fn drop(&mut self) { + self.zeroize(); + } +} diff --git a/tss-esapi/src/context/tpm_commands/object_commands/create_loaded_command_output.rs b/tss-esapi/src/context/tpm_commands/object_commands/create_loaded_command_output.rs new file mode 100644 index 00000000..090c03b6 --- /dev/null +++ b/tss-esapi/src/context/tpm_commands/object_commands/create_loaded_command_output.rs @@ -0,0 +1,83 @@ +// Copyright 2024 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + handles::ObjectHandle, + structures::{CreateLoadedKeyResult, Private, Public}, + tss2_esys::{ESYS_TR, TPM2B_PRIVATE, TPM2B_PUBLIC}, + Error, Result, +}; +use std::convert::TryFrom; +use std::ptr::null_mut; + +/// Struct that handles the output of the +/// Create Esys_CreateLoaded command and zeroizes +/// the FFI data. +pub(crate) struct CreateLoadedCommandOutputHandler { + ffi_out_object_handle: ESYS_TR, + // object + ffi_out_public_ptr: *mut TPM2B_PUBLIC, + ffi_out_private_ptr: *mut TPM2B_PRIVATE, + // name + // ffi_out_name_ptr: *mut TPM2B_NAME, +} + +/// Creates a new CreateLoadedCommandOutputHandler +impl CreateLoadedCommandOutputHandler { + pub(crate) fn new() -> Self { + let ffi_out_object_handle = ObjectHandle::None.into(); + Self { + ffi_out_object_handle, + ffi_out_public_ptr: null_mut(), + ffi_out_private_ptr: null_mut(), + // ffi_out_name_ptr: null_mut(), + } + } + + /// A reference to the where 'objectHandle' output parameter pointer shall be stored. + pub fn ffi_out_object_handle(&mut self) -> &mut ESYS_TR { + &mut self.ffi_out_object_handle + } + + /// A reference to the where 'outPrivate' output parameter pointer shall be stored. + pub fn ffi_out_private_ptr(&mut self) -> &mut *mut TPM2B_PRIVATE { + &mut self.ffi_out_private_ptr + } + + /// A reference to the where 'outPublic' output parameter pointer shall be stored. + pub fn ffi_out_public_ptr(&mut self) -> &mut *mut TPM2B_PUBLIC { + &mut self.ffi_out_public_ptr + } + + /* + /// A reference to the where 'name' output parameter pointer shall be stored. + pub fn ffi_out_name_ptr(&mut self) -> &mut *mut TPM2B_NAME { + &mut self.ffi_out_name_ptr + } + */ +} + +impl TryFrom for CreateLoadedKeyResult { + type Error = Error; + + fn try_from( + ffi_data_handler: CreateLoadedCommandOutputHandler, + ) -> Result { + let object_handle = ObjectHandle::from(ffi_data_handler.ffi_out_object_handle); + + let out_private_owned = + crate::ffi::to_owned_with_zeroized_source(ffi_data_handler.ffi_out_private_ptr); + let out_public_owned = + crate::ffi::to_owned_with_zeroized_source(ffi_data_handler.ffi_out_public_ptr); + + // let out_name_owned = + // crate::ffi::to_owned_with_zeroized_source(ffi_data_handler.ffi_out_name_ptr); + + Ok(CreateLoadedKeyResult { + object_handle, + out_private: Private::try_from(out_private_owned)?, + out_public: Public::try_from(out_public_owned)?, + // out_name: Name::try_from(out_name_owned)?, + }) + } +} diff --git a/tss-esapi/src/ffi/data_zeroize.rs b/tss-esapi/src/ffi/data_zeroize.rs index 0c4a1d71..0d876ed4 100644 --- a/tss-esapi/src/ffi/data_zeroize.rs +++ b/tss-esapi/src/ffi/data_zeroize.rs @@ -10,7 +10,7 @@ use crate::{ TPM2B_ID_OBJECT, TPM2B_IV, TPM2B_MAX_BUFFER, TPM2B_MAX_NV_BUFFER, TPM2B_NAME, TPM2B_PRIVATE, TPM2B_PRIVATE_KEY_RSA, TPM2B_PRIVATE_VENDOR_SPECIFIC, TPM2B_PUBLIC, TPM2B_PUBLIC_KEY_RSA, TPM2B_SENSITIVE_CREATE, TPM2B_SENSITIVE_DATA, TPM2B_SYM_KEY, - TPML_PCR_SELECTION, TPMS_CREATION_DATA, TPMS_ECC_PARMS, TPMS_ECC_POINT, + TPM2B_TEMPLATE, TPML_PCR_SELECTION, TPMS_CREATION_DATA, TPMS_ECC_PARMS, TPMS_ECC_POINT, TPMS_KEYEDHASH_PARMS, TPMS_PCR_SELECTION, TPMS_RSA_PARMS, TPMS_SCHEME_ECDAA, TPMS_SCHEME_HASH, TPMS_SCHEME_XOR, TPMS_SENSITIVE_CREATE, TPMS_SYMCIPHER_PARMS, TPMT_ECC_SCHEME, TPMT_KDF_SCHEME, TPMT_KEYEDHASH_SCHEME, TPMT_PUBLIC, TPMT_RSA_SCHEME, @@ -121,6 +121,7 @@ implement_ffi_data_zeroizer_trait_for_buffer_type!(TPM2B_PRIVATE_VENDOR_SPECIFIC implement_ffi_data_zeroizer_trait_for_buffer_type!(TPM2B_PUBLIC_KEY_RSA); implement_ffi_data_zeroizer_trait_for_buffer_type!(TPM2B_SENSITIVE_DATA); implement_ffi_data_zeroizer_trait_for_buffer_type!(TPM2B_SYM_KEY); +implement_ffi_data_zeroizer_trait_for_buffer_type!(TPM2B_TEMPLATE); implement_ffi_data_zeroizer_trait_for_named_field_structured_buffer_type!( TPM2B_CREATION_DATA, creationData diff --git a/tss-esapi/src/structures/mod.rs b/tss-esapi/src/structures/mod.rs index 734f3bd5..7de8ae6b 100644 --- a/tss-esapi/src/structures/mod.rs +++ b/tss-esapi/src/structures/mod.rs @@ -29,6 +29,7 @@ pub use names::name::Name; ///////////////////////////////////////////////////////// mod result; pub use result::CreateKeyResult; +pub use result::CreateLoadedKeyResult; pub use result::CreatePrimaryKeyResult; ///////////////////////////////////////////////////////// /// The sized buffers section diff --git a/tss-esapi/src/structures/result.rs b/tss-esapi/src/structures/result.rs index 4bdc2dc3..e7d64450 100644 --- a/tss-esapi/src/structures/result.rs +++ b/tss-esapi/src/structures/result.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - handles::KeyHandle, + handles::{KeyHandle, ObjectHandle}, structures::{CreationData, CreationTicket, Digest, Private, Public}, }; @@ -15,6 +15,14 @@ pub struct CreateKeyResult { pub creation_ticket: CreationTicket, } +#[allow(missing_debug_implementations)] +pub struct CreateLoadedKeyResult { + pub object_handle: ObjectHandle, + pub out_private: Private, + pub out_public: Public, + // pub out_name: Name, +} + #[allow(missing_debug_implementations)] pub struct CreatePrimaryKeyResult { pub key_handle: KeyHandle, diff --git a/tss-esapi/src/structures/tagged/public.rs b/tss-esapi/src/structures/tagged/public.rs index 693d86d6..1bfbb10a 100644 --- a/tss-esapi/src/structures/tagged/public.rs +++ b/tss-esapi/src/structures/tagged/public.rs @@ -9,7 +9,7 @@ use crate::{ interface_types::algorithm::{HashingAlgorithm, PublicAlgorithm}, structures::{Digest, EccPoint, PublicKeyRsa, SymmetricCipherParameters}, traits::{impl_mu_standard, Marshall, UnMarshall}, - tss2_esys::{TPM2B_PUBLIC, TPMT_PUBLIC}, + tss2_esys::{TPM2B_PUBLIC, TPM2B_TEMPLATE, TPMT_PUBLIC}, Error, Result, ReturnCode, WrapperErrorKind, }; @@ -567,3 +567,40 @@ impl TryFrom for TPM2B_PUBLIC { }) } } + +impl TryFrom for TPM2B_TEMPLATE { + type Error = Error; + + fn try_from(public: Public) -> Result { + // Now sure how to handle this better. + let mut size = 0; + let mut buffer: [u8; 612usize] = [0; 612]; + let public_area = TPMT_PUBLIC::from(public); + + ReturnCode::ensure_success( + unsafe { + crate::tss2_esys::Tss2_MU_TPMT_PUBLIC_Marshal( + &public_area, + buffer.as_mut_ptr(), + Public::BUFFER_SIZE.try_into().map_err(|e| { + error!("Failed to convert size of buffer to TSS size_t type: {}", e); + Error::local_error(WrapperErrorKind::InvalidParam) + })?, + &mut size, + ) + }, + |ret| error!("Failed to marshal Public: {}", ret), + )?; + + Ok(TPM2B_TEMPLATE { + size: size.try_into().map_err(|e| { + error!( + "Failed to convert size of buffer from TSS size_t type: {}", + e + ); + Error::local_error(WrapperErrorKind::InvalidParam) + })?, + buffer, + }) + } +} diff --git a/tss-esapi/tests/integration_tests/context_tests/tpm_commands/object_commands_tests.rs b/tss-esapi/tests/integration_tests/context_tests/tpm_commands/object_commands_tests.rs index a6b037ed..db723ac3 100644 --- a/tss-esapi/tests/integration_tests/context_tests/tpm_commands/object_commands_tests.rs +++ b/tss-esapi/tests/integration_tests/context_tests/tpm_commands/object_commands_tests.rs @@ -427,3 +427,154 @@ mod test_unseal { assert!(unsealed == testbytes); } } + +mod test_create_loaded { + use crate::common::{create_ctx_with_session, decryption_key_pub}; + use std::convert::{TryFrom, TryInto}; + use tss_esapi::{ + attributes::{ObjectAttributesBuilder, SessionAttributesBuilder}, + constants::SessionType, + interface_types::{ + algorithm::{HashingAlgorithm, PublicAlgorithm, SymmetricMode}, + key_bits::AesKeyBits, + reserved_handles::Hierarchy, + }, + structures::{ + CreateLoadedKeyResult, Digest, KeyedHashScheme, PublicBuilder, + PublicKeyedHashParameters, SymmetricCipherParameters, SymmetricDefinition, + SymmetricDefinitionObject, + }, + }; + + #[test] + fn test_create_loaded_tpm_alg_kdf1_sp800_108() { + let mut context = create_ctx_with_session(); + + let (session_attributes, session_attributes_mask) = SessionAttributesBuilder::new().build(); + + let object_attributes = ObjectAttributesBuilder::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_st_clear(false) + .with_sensitive_data_origin(true) + .with_user_with_auth(true) + .with_sign_encrypt(false) + .with_decrypt(true) + .with_restricted(true) + .build() + .expect("Failed to build object attributes"); + + let primary_pub = PublicBuilder::new() + // This key is a symmetric key. + .with_public_algorithm(PublicAlgorithm::SymCipher) + .with_name_hashing_algorithm(HashingAlgorithm::Sha256) + .with_object_attributes(object_attributes) + .with_symmetric_cipher_parameters(SymmetricCipherParameters::new( + SymmetricDefinitionObject::AES_128_CFB, + )) + .with_symmetric_cipher_unique_identifier(Digest::default()) + .build() + .unwrap(); + + // Create primary. + let primary_key_handle = context + .create_primary(Hierarchy::Owner, primary_pub, None, None, None, None) + .unwrap() + .key_handle; + + // Create Derivation Parent + // - How to mark an object as a derivation parent? From what I read + // in the spec, a derivation parent is just when != (primary || storage) + let derive_parent_object_attributes = ObjectAttributesBuilder::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_st_clear(false) + .with_sensitive_data_origin(true) + .with_user_with_auth(true) + // Architecture 25.1.5 table 24. + .with_sign_encrypt(false) + .with_decrypt(true) + .with_restricted(true) + .build() + .expect("Failed to build object attributes"); + + // How can I set this to KeyDerivationFunctionScheme::Kdf1Sp800_108(HashScheme::Sha256) + + let derive_parent_public = PublicBuilder::new() + .with_public_algorithm(PublicAlgorithm::KeyedHash) + .with_name_hashing_algorithm(HashingAlgorithm::Sha256) + .with_object_attributes(derive_parent_object_attributes) + .with_keyed_hash_parameters(PublicKeyedHashParameters::new( + KeyedHashScheme::HMAC_SHA_256, + )) + .with_keyed_hash_unique_identifier(Digest::default()) + .build() + .expect("Failed to build derive parent public"); + + // We should be able to create and load this now. And look, like magic, + // it's created and loaded in one operation! + let create_loaded_result = context + .create_loaded(primary_key_handle, None, None, derive_parent_public) + .expect("Failed to create derivation parent."); + + let CreateLoadedKeyResult { + object_handle: derive_parent_handle, + out_private: derive_parent_private, + out_public: derive_parent_public, + } = create_loaded_result; + + context + .flush_context(primary_key_handle.into()) + .expect("Unable to unload primary key"); + + // Create the derived key + let derived_object_attributes = ObjectAttributesBuilder::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_st_clear(false) + // Must be false on a derived key. + .with_sensitive_data_origin(false) + .with_user_with_auth(true) + // The key is used only for signing. + .with_sign_encrypt(true) + .with_decrypt(true) + .with_restricted(false) + .build() + .expect("Failed to build object attributes"); + + let aes_params = SymmetricCipherParameters::new(SymmetricDefinitionObject::Aes { + key_bits: AesKeyBits::Aes128, + mode: SymmetricMode::Cbc, + }); + + let derivation_params = Digest::try_from(b"testinputs".to_vec()).unwrap(); + + let derived_public = PublicBuilder::new() + .with_public_algorithm(PublicAlgorithm::SymCipher) + .with_name_hashing_algorithm(HashingAlgorithm::Sha256) + .with_object_attributes(derived_object_attributes) + .with_symmetric_cipher_parameters(aes_params) + .with_symmetric_cipher_unique_identifier(derivation_params) + .build() + .expect("Failed to build derive parent public"); + + // We should be able to create and load this now. And look, like magic, + // it's created and loaded in one operation! + let create_loaded_result = context + .create_loaded(derive_parent_handle.into(), None, None, derived_public) + .expect("Failed to create derivation parent."); + + let CreateLoadedKeyResult { + object_handle: derived_handle, + out_private: derived_private, + out_public: derived_public, + } = create_loaded_result; + + // Derivation params are in the inPublic.unique Field, or inPrivate.data + + // Sensitive dataOrigin must be clear for derivation. + + // TPM_ALG_KDF1_SP800_56A == ECDH + // TPM_ALG_KDF1_SP800_108 == CMAC/HMAC/KMAC derivation + } +}