Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

20240604 506 create loaded #529

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tss-esapi-sys/src/bindings/x86_64-unknown-linux-gnu.rs
Original file line number Diff line number Diff line change
@@ -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" {
91 changes: 86 additions & 5 deletions tss-esapi/src/context/tpm_commands/object_commands.rs
Original file line number Diff line number Diff line change
@@ -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<Auth>,
sensitive_data: Option<SensitiveData>,
public: Public,
) -> Result<CreateLoadedKeyResult> {
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()
}
}
Original file line number Diff line number Diff line change
@@ -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<Auth>,
sensitive_data: Option<SensitiveData>,
public: Public,
) -> Result<Self> {
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();
}
}
Original file line number Diff line number Diff line change
@@ -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<CreateLoadedCommandOutputHandler> for CreateLoadedKeyResult {
type Error = Error;

fn try_from(
ffi_data_handler: CreateLoadedCommandOutputHandler,
) -> Result<CreateLoadedKeyResult> {
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)?,
})
}
}
3 changes: 2 additions & 1 deletion tss-esapi/src/ffi/data_zeroize.rs
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions tss-esapi/src/structures/mod.rs
Original file line number Diff line number Diff line change
@@ -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
10 changes: 9 additions & 1 deletion tss-esapi/src/structures/result.rs
Original file line number Diff line number Diff line change
@@ -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,
39 changes: 38 additions & 1 deletion tss-esapi/src/structures/tagged/public.rs
Original file line number Diff line number Diff line change
@@ -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<Public> for TPM2B_PUBLIC {
})
}
}

impl TryFrom<Public> for TPM2B_TEMPLATE {
type Error = Error;

fn try_from(public: Public) -> Result<Self> {
// 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,
})
}
}
Original file line number Diff line number Diff line change
@@ -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()
Copy link
Collaborator

@Superhepper Superhepper Jun 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really not my area of expertise but it seems to me as it is not as simple as setting the object attributes of the key. The kind of object you create will depend on the object attributes of its parent not only the object attributes of the object it self.

Look at TCG TPM2, r1p59, Part3 Commands, Section 12.9.1

Hrmm but the error code suggests:

TPM_RC_SCHEME

inconsistent attributes decrypt, sign, restricted and key's scheme ID;
or hash algorithm is inconsistent with the scheme ID for keyed hash
object

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I did read that section. The problem I hit was that part 3 12.9.1 doesn't actually define a derivation parent - that's actually defined in part 1 4.19 as "loadable key used to derive other keys; a TPM_ALG_KEYEDHASH Parent Key" and part 1 25.1.5 table 24 states "Asymmetric keys and
symmetric keys with these attributes are Storage Parents, and hash objects with these attributes are
Derivation Parents. " where the attributes are sign=clear, decrypt=set, restricted=set.

I think the comment I wrote here needs to be updated to reflect this, but I still think the attributes I used on the key were correct :)

.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
}
}