Skip to content

Commit 21d4263

Browse files
authored
Merge pull request #461 from brandsimon/sbr/serialize
Implement tr_serialize and tr_deserialize
2 parents 9d6cdcf + 3386cb2 commit 21d4263

File tree

3 files changed

+218
-22
lines changed

3 files changed

+218
-22
lines changed

tss-esapi/src/context/general_esys_tr.rs

Lines changed: 152 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
// Copyright 2021 Contributors to the Parsec project.
22
// SPDX-License-Identifier: Apache-2.0
33
use crate::{
4+
constants::tss::TPM2_RH_UNASSIGNED,
45
context::handle_manager::HandleDropAction,
6+
ffi::to_owned_bytes,
57
handles::ObjectHandle,
68
handles::{handle_conversion::TryIntoNotNone, TpmHandle},
79
structures::Auth,
810
structures::Name,
9-
tss2_esys::{Esys_TR_Close, Esys_TR_FromTPMPublic, Esys_TR_GetName, Esys_TR_SetAuth},
10-
Context, Result, ReturnCode,
11+
tss2_esys::{
12+
Esys_TR_Close, Esys_TR_Deserialize, Esys_TR_FromTPMPublic, Esys_TR_GetName,
13+
Esys_TR_Serialize, Esys_TR_SetAuth,
14+
},
15+
Context, Error, Result, ReturnCode, WrapperErrorKind,
1116
};
1217
use log::error;
13-
use std::convert::TryFrom;
18+
use std::convert::{TryFrom, TryInto};
1419
use std::ptr::null_mut;
1520
use zeroize::Zeroize;
1621

@@ -372,7 +377,7 @@ impl Context {
372377
#[cfg(has_esys_tr_get_tpm_handle)]
373378
/// Retrieve the `TpmHandle` stored in the given object.
374379
pub fn tr_get_tpm_handle(&mut self, object_handle: ObjectHandle) -> Result<TpmHandle> {
375-
use crate::{constants::tss::TPM2_RH_UNASSIGNED, tss2_esys::Esys_TR_GetTpmHandle};
380+
use crate::tss2_esys::Esys_TR_GetTpmHandle;
376381
let mut tpm_handle = TPM2_RH_UNASSIGNED;
377382
ReturnCode::ensure_success(
378383
unsafe {
@@ -388,6 +393,147 @@ impl Context {
388393
TpmHandle::try_from(tpm_handle)
389394
}
390395

391-
// Missing function: Esys_TR_Serialize
392-
// Missing function: Esys_TR_Deserialize
396+
/// Serialize the metadata of the object identified by `handle` into a new buffer.
397+
///
398+
/// This can subsequently be used to recreate the object in the future.
399+
/// The object can only be recreated in a new context, if it was made persistent
400+
/// with `evict_control`.
401+
///
402+
/// # Arguments
403+
/// * `handle` - A handle to the object which should be serialized.
404+
///
405+
/// # Returns
406+
/// A buffer that can be stored and later deserialized.
407+
///
408+
/// # Errors
409+
/// * if the TPM cannot serialize the handle, a TSS error is returned.
410+
/// * if the buffer length cannot be converted to a `usize`, an `InvalidParam`
411+
/// wrapper error is returned.
412+
///
413+
/// ```rust
414+
/// # use tss_esapi::{
415+
/// # Context, TctiNameConf,
416+
/// # interface_types::resource_handles::Hierarchy,
417+
/// # structures::HashScheme,
418+
/// # utils::create_unrestricted_signing_ecc_public,
419+
/// # interface_types::{
420+
/// # ecc::EccCurve,
421+
/// # algorithm::HashingAlgorithm,
422+
/// # session_handles::AuthSession,
423+
/// # },
424+
/// # structures::EccScheme,
425+
/// # };
426+
/// # let mut context =
427+
/// # Context::new(
428+
/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
429+
/// # ).expect("Failed to create Context");
430+
/// # context.set_sessions((Some(AuthSession::Password), None, None));
431+
/// # let public = create_unrestricted_signing_ecc_public(
432+
/// # EccScheme::EcDsa(HashScheme::new(HashingAlgorithm::Sha256)),
433+
/// # EccCurve::NistP256)
434+
/// # .unwrap();
435+
/// let key_handle = context
436+
/// .create_primary(
437+
/// Hierarchy::Owner,
438+
/// public,
439+
/// None,
440+
/// None,
441+
/// None,
442+
/// None,
443+
/// ).unwrap()
444+
/// .key_handle;
445+
/// let data = context.tr_serialize(key_handle.into()).unwrap();
446+
/// ```
447+
pub fn tr_serialize(&mut self, handle: ObjectHandle) -> Result<Vec<u8>> {
448+
let mut len = 0;
449+
let mut buffer: *mut u8 = null_mut();
450+
ReturnCode::ensure_success(
451+
unsafe { Esys_TR_Serialize(self.mut_context(), handle.into(), &mut buffer, &mut len) },
452+
|ret| {
453+
error!("Error while serializing handle: {}", ret);
454+
},
455+
)?;
456+
Ok(to_owned_bytes(
457+
buffer,
458+
len.try_into().map_err(|e| {
459+
error!("Failed to convert buffer len to usize: {}", e);
460+
Error::local_error(WrapperErrorKind::InvalidParam)
461+
})?,
462+
))
463+
}
464+
465+
/// Deserialize the metadata from `buffer` into a new object.
466+
///
467+
/// This can be used to restore an object from a context in the past.
468+
///
469+
/// # Arguments
470+
/// * `buffer` - The buffer containing the data to restore the object.
471+
/// It can be created using [`tr_serialize`](Self::tr_serialize).
472+
///
473+
/// # Returns
474+
/// A handle to the object that was created from the buffer.
475+
///
476+
/// # Errors
477+
/// * if the TPM cannot deserialize the buffer, a TSS error is returned.
478+
/// * if the buffer length cannot be converted to a `usize`, an `InvalidParam`
479+
/// wrapper error is returned.
480+
///
481+
/// ```rust
482+
/// # use tss_esapi::{
483+
/// # Context, TctiNameConf,
484+
/// # interface_types::resource_handles::Hierarchy,
485+
/// # structures::HashScheme,
486+
/// # utils::create_unrestricted_signing_ecc_public,
487+
/// # interface_types::{
488+
/// # ecc::EccCurve,
489+
/// # algorithm::HashingAlgorithm,
490+
/// # session_handles::AuthSession,
491+
/// # },
492+
/// # structures::EccScheme,
493+
/// # };
494+
/// # let mut context =
495+
/// # Context::new(
496+
/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
497+
/// # ).expect("Failed to create Context");
498+
/// # context.set_sessions((Some(AuthSession::Password), None, None));
499+
/// # let public = create_unrestricted_signing_ecc_public(
500+
/// # EccScheme::EcDsa(HashScheme::new(HashingAlgorithm::Sha256)),
501+
/// # EccCurve::NistP256)
502+
/// # .unwrap();
503+
/// let key_handle = context
504+
/// .create_primary(
505+
/// Hierarchy::Owner,
506+
/// public,
507+
/// None,
508+
/// None,
509+
/// None,
510+
/// None,
511+
/// ).unwrap()
512+
/// .key_handle;
513+
/// # context.set_sessions((None, None, None));
514+
/// let public_key = context.read_public(key_handle).unwrap();
515+
/// let data = context.tr_serialize(key_handle.into()).unwrap();
516+
/// let new_handle = context.tr_deserialize(&data).unwrap();
517+
/// assert_eq!(public_key, context.read_public(new_handle.into()).unwrap());
518+
/// ```
519+
pub fn tr_deserialize(&mut self, buffer: &Vec<u8>) -> Result<ObjectHandle> {
520+
let mut handle = TPM2_RH_UNASSIGNED;
521+
ReturnCode::ensure_success(
522+
unsafe {
523+
Esys_TR_Deserialize(
524+
self.mut_context(),
525+
buffer.as_ptr(),
526+
buffer.len().try_into().map_err(|e| {
527+
error!("Failed to convert buffer len to usize: {}", e);
528+
Error::local_error(WrapperErrorKind::InvalidParam)
529+
})?,
530+
&mut handle,
531+
)
532+
},
533+
|ret| {
534+
error!("Error while deserializing buffer: {}", ret);
535+
},
536+
)?;
537+
Ok(ObjectHandle::from(handle))
538+
}
393539
}

tss-esapi/src/ffi.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,8 @@ where
2525
ffi_data.ffi_data_zeroize();
2626
owned_ffi_data
2727
}
28+
29+
pub(crate) fn to_owned_bytes(ffi_bytes_ptr: *mut u8, size: usize) -> Vec<u8> {
30+
let ffi_bytes = unsafe { MBox::<[u8]>::from_raw_parts(ffi_bytes_ptr, size) };
31+
return Vec::<u8>::from(ffi_bytes.as_ref());
32+
}

tss-esapi/tests/integration_tests/context_tests/general_esys_tr_tests.rs

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1+
use crate::common::{create_ctx_with_session, create_ctx_without_session, decryption_key_pub};
2+
use tss_esapi::{
3+
attributes::NvIndexAttributesBuilder,
4+
constants::{tss::TPM2_NV_INDEX_FIRST, CapabilityType},
5+
handles::{NvIndexHandle, NvIndexTpmHandle, ObjectHandle, PersistentTpmHandle, TpmHandle},
6+
interface_types::{
7+
algorithm::HashingAlgorithm,
8+
dynamic_handles::Persistent,
9+
resource_handles::{Hierarchy, NvAuth, Provision},
10+
session_handles::AuthSession,
11+
},
12+
structures::{Auth, CapabilityData, MaxNvBuffer, NvPublicBuilder},
13+
tss2_esys::TPM2_HANDLE,
14+
Context, Error,
15+
};
16+
17+
use std::convert::TryFrom;
18+
119
mod test_tr_from_tpm_public {
2-
use crate::common::create_ctx_without_session;
3-
use tss_esapi::{
4-
attributes::NvIndexAttributesBuilder,
5-
constants::{tss::TPM2_NV_INDEX_FIRST, CapabilityType},
6-
handles::{NvIndexHandle, NvIndexTpmHandle, ObjectHandle},
7-
interface_types::{
8-
algorithm::HashingAlgorithm,
9-
resource_handles::{NvAuth, Provision},
10-
session_handles::AuthSession,
11-
},
12-
structures::{Auth, CapabilityData, MaxNvBuffer, NvPublicBuilder},
13-
tss2_esys::TPM2_HANDLE,
14-
Context,
15-
};
16-
17-
use std::convert::TryFrom;
20+
use super::*;
1821

1922
fn remove_nv_index_handle_from_tpm(nv_index_tpm_handle: NvIndexTpmHandle, nv_auth: Provision) {
2023
let mut context = create_ctx_without_session();
@@ -445,3 +448,45 @@ mod test_tr_from_tpm_public {
445448
assert_eq!(expected, actual);
446449
}
447450
}
451+
452+
mod test_tr_serialize_tr_deserialize {
453+
use super::*;
454+
455+
#[test]
456+
fn test_tr_serialize_tr_deserialize() -> Result<(), Error> {
457+
let persistent_addr =
458+
PersistentTpmHandle::new(u32::from_be_bytes([0x81, 0x00, 0x00, 0x05]))?;
459+
let persistent = Persistent::Persistent(persistent_addr);
460+
let mut context = create_ctx_with_session();
461+
462+
// Make sure the handle is not already persistent
463+
if let Ok(clear_handle) =
464+
context.tr_from_tpm_public(TpmHandle::Persistent(persistent.into()))
465+
{
466+
context.evict_control(Provision::Owner, clear_handle, persistent)?;
467+
}
468+
469+
let key_handle = context
470+
.create_primary(
471+
Hierarchy::Owner,
472+
decryption_key_pub(),
473+
None,
474+
None,
475+
None,
476+
None,
477+
)?
478+
.key_handle;
479+
let public = context.read_public(key_handle)?;
480+
let persistent_handle =
481+
context.evict_control(Provision::Owner, key_handle.into(), persistent)?;
482+
let data = context.tr_serialize(persistent_handle)?;
483+
484+
drop(context);
485+
// Load handle in a new context
486+
let mut new_context = create_ctx_without_session();
487+
let new_handle = new_context.tr_deserialize(&data)?.into();
488+
// Check it is the same key via the public key included in Public
489+
assert_eq!(public, new_context.read_public(new_handle)?);
490+
Ok(())
491+
}
492+
}

0 commit comments

Comments
 (0)