#![deny(
nonstandard_style,
const_err,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
private_in_public,
unconditional_recursion,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true,
missing_debug_implementations,
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
unused_results,
missing_copy_implementations
)]
#![allow(dead_code)]
#[allow(
non_snake_case,
non_camel_case_types,
non_upper_case_globals,
dead_code
)]
#[allow(clippy::all)]
#[allow(clippy::unseparated_literal_suffix)]
#[allow(improper_ctypes, missing_debug_implementations, trivial_casts)]
pub mod tss2_esys {
#[cfg(not(feature = "docs"))]
include!(concat!(env!("OUT_DIR"), "/tss2_esys_bindings.rs"));
#[cfg(feature = "docs")]
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/doc_bindings.rs"));
}
pub mod abstraction;
#[allow(
non_snake_case,
non_camel_case_types,
non_upper_case_globals,
dead_code
)]
#[allow(clippy::all)]
pub mod constants;
pub mod response_code;
pub mod utils;
pub use abstraction::transient::TransientKeyContext;
use log::{error, info};
use mbox::MBox;
use response_code::Result;
use response_code::{Error, WrapperErrorKind as ErrorKind};
use std::collections::HashSet;
use std::convert::{TryFrom, TryInto};
use std::ffi::CString;
use std::ptr::{null, null_mut};
use tss2_esys::*;
use utils::{
algorithm_specifiers::HashingAlgorithm, tickets::HashcheckTicket, Hierarchy, PcrData,
PcrSelections, PublicParmsUnion, Signature, TpmaSession, TpmaSessionBuilder, TpmsContext,
};
#[macro_use]
macro_rules! wrap_buffer {
($buf:expr, $buf_type:ty, $buf_size:expr) => {{
if $buf.len() > $buf_size {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
let mut buffer = [0_u8; $buf_size];
buffer[..$buf.len()].clone_from_slice(&$buf[..$buf.len()]);
let mut buf_struct: $buf_type = Default::default();
buf_struct.size = $buf.len().try_into().unwrap();
buf_struct.buffer = buffer;
buf_struct
}};
}
#[derive(Copy, Clone, Debug)]
pub enum Tcti {
Device,
Mssim,
Tabrmd,
}
const DEVICE: &str = "device";
const MSSIM: &str = "mssim";
const TABRMD: &str = "tabrmd";
#[derive(Debug)]
pub struct Context {
esys_context: Option<MBox<ESYS_CONTEXT>>,
sessions: (ESYS_TR, ESYS_TR, ESYS_TR),
tcti_context: Option<MBox<TSS2_TCTI_CONTEXT>>,
open_handles: HashSet<ESYS_TR>,
}
impl Context {
pub unsafe fn new(tcti: Tcti) -> Result<Self> {
let mut esys_context = null_mut();
let mut tcti_context = null_mut();
let tcti_name_conf = match tcti {
Tcti::Device => DEVICE,
Tcti::Mssim => MSSIM,
Tcti::Tabrmd => TABRMD,
};
let tcti_name_conf = CString::new(tcti_name_conf).expect("Failed conversion to CString");
let ret = Tss2_TctiLdr_Initialize(tcti_name_conf.as_ptr(), &mut tcti_context);
let ret = Error::from_tss_rc(ret);
if !ret.is_success() {
error!("Error when creating a TCTI context: {}.", ret);
return Err(ret);
}
let mut tcti_context = Some(MBox::from_raw(tcti_context));
let ret = Esys_Initialize(
&mut esys_context,
tcti_context.as_mut().unwrap().as_mut_ptr(),
null_mut(),
);
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let esys_context = Some(MBox::from_raw(esys_context));
let context = Context {
esys_context,
sessions: (ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE),
tcti_context,
open_handles: HashSet::new(),
};
Ok(context)
} else {
error!("Error when creating a new context: {}.", ret);
Err(ret)
}
}
#[allow(clippy::too_many_arguments)]
pub fn start_auth_session(
&mut self,
tpm_key: ESYS_TR,
bind: ESYS_TR,
nonce: &[u8],
session_type: TPM2_SE,
symmetric: TPMT_SYM_DEF,
auth_hash: TPMI_ALG_HASH,
) -> Result<ESYS_TR> {
let nonce_caller = wrap_buffer!(nonce, TPM2B_NONCE, 64);
let mut sess = ESYS_TR_NONE;
let ret = unsafe {
Esys_StartAuthSession(
self.mut_context(),
tpm_key,
bind,
self.sessions.0,
self.sessions.1,
self.sessions.2,
if nonce.is_empty() {
null()
} else {
&nonce_caller
},
session_type,
&symmetric,
auth_hash,
&mut sess,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let _ = self.open_handles.insert(sess);
Ok(sess)
} else {
error!("Error when creating a session: {}.", ret);
Err(ret)
}
}
pub fn set_sessions(&mut self, session_handles: (ESYS_TR, ESYS_TR, ESYS_TR)) {
self.sessions = session_handles;
}
pub fn sessions(&self) -> (ESYS_TR, ESYS_TR, ESYS_TR) {
self.sessions
}
#[allow(clippy::too_many_arguments)]
pub fn create_primary_key(
&mut self,
primary_handle: ESYS_TR,
public: &TPM2B_PUBLIC,
auth_value: &[u8],
initial_data: &[u8],
outside_info: &[u8],
creation_pcrs: &[TPMS_PCR_SELECTION],
) -> Result<ESYS_TR> {
let sensitive_create = TPM2B_SENSITIVE_CREATE {
size: std::mem::size_of::<TPMS_SENSITIVE_CREATE>()
.try_into()
.unwrap(),
sensitive: TPMS_SENSITIVE_CREATE {
userAuth: wrap_buffer!(auth_value, TPM2B_AUTH, 64),
data: wrap_buffer!(initial_data, TPM2B_SENSITIVE_DATA, 256),
},
};
let outside_info = wrap_buffer!(outside_info, TPM2B_DATA, 64);
if creation_pcrs.len() > 16 {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
let mut creation_pcrs_buffer = [Default::default(); 16];
creation_pcrs_buffer[..creation_pcrs.len()]
.clone_from_slice(&creation_pcrs[..creation_pcrs.len()]);
let creation_pcrs = TPML_PCR_SELECTION {
count: creation_pcrs.len().try_into().unwrap(),
pcrSelections: creation_pcrs_buffer,
};
let mut outpublic = null_mut();
let mut creation_data = null_mut();
let mut creation_hash = null_mut();
let mut creation_ticket = null_mut();
let mut prim_key_handle = ESYS_TR_NONE;
let ret = unsafe {
Esys_CreatePrimary(
self.mut_context(),
primary_handle,
self.sessions.0,
self.sessions.1,
self.sessions.2,
&sensitive_create,
public,
&outside_info,
&creation_pcrs,
&mut prim_key_handle,
&mut outpublic,
&mut creation_data,
&mut creation_hash,
&mut creation_ticket,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
unsafe {
let _ = MBox::from_raw(outpublic);
let _ = MBox::from_raw(creation_data);
let _ = MBox::from_raw(creation_hash);
let _ = MBox::from_raw(creation_ticket);
}
let _ = self.open_handles.insert(prim_key_handle);
Ok(prim_key_handle)
} else {
error!("Error in creating primary key: {}.", ret);
Err(ret)
}
}
#[allow(clippy::too_many_arguments)]
pub fn create_key(
&mut self,
parent_handle: ESYS_TR,
public: &TPM2B_PUBLIC,
auth_value: &[u8],
initial_data: &[u8],
outside_info: &[u8],
creation_pcrs: &[TPMS_PCR_SELECTION],
) -> Result<(TPM2B_PRIVATE, TPM2B_PUBLIC)> {
let sensitive_create = TPM2B_SENSITIVE_CREATE {
size: std::mem::size_of::<TPMS_SENSITIVE_CREATE>()
.try_into()
.unwrap(),
sensitive: TPMS_SENSITIVE_CREATE {
userAuth: wrap_buffer!(auth_value, TPM2B_AUTH, 64),
data: wrap_buffer!(initial_data, TPM2B_SENSITIVE_DATA, 256),
},
};
let outside_info = wrap_buffer!(outside_info, TPM2B_DATA, 64);
if creation_pcrs.len() > 16 {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
let mut creation_pcrs_buffer = [Default::default(); 16];
creation_pcrs_buffer[..creation_pcrs.len()]
.clone_from_slice(&creation_pcrs[..creation_pcrs.len()]);
let creation_pcrs = TPML_PCR_SELECTION {
count: creation_pcrs.len().try_into().unwrap(),
pcrSelections: creation_pcrs_buffer,
};
let mut outpublic = null_mut();
let mut outprivate = null_mut();
let mut creation_data = null_mut();
let mut digest = null_mut();
let mut creation = null_mut();
let ret = unsafe {
Esys_Create(
self.mut_context(),
parent_handle,
self.sessions.0,
self.sessions.1,
self.sessions.2,
&sensitive_create,
public,
&outside_info,
&creation_pcrs,
&mut outprivate,
&mut outpublic,
&mut creation_data,
&mut digest,
&mut creation,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let outprivate = unsafe { MBox::from_raw(outprivate) };
let outpublic = unsafe { MBox::from_raw(outpublic) };
unsafe {
let _ = MBox::from_raw(creation_data);
let _ = MBox::from_raw(digest);
let _ = MBox::from_raw(creation);
}
Ok((*outprivate, *outpublic))
} else {
error!("Error in creating derived key: {}.", ret);
Err(ret)
}
}
pub fn load(
&mut self,
parent_handle: ESYS_TR,
private: TPM2B_PRIVATE,
public: TPM2B_PUBLIC,
) -> Result<ESYS_TR> {
let mut handle = ESYS_TR_NONE;
let ret = unsafe {
Esys_Load(
self.mut_context(),
parent_handle,
self.sessions.0,
self.sessions.1,
self.sessions.2,
&private,
&public,
&mut handle,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let _ = self.open_handles.insert(handle);
Ok(handle)
} else {
error!("Error in loading: {}.", ret);
Err(ret)
}
}
pub fn sign(
&mut self,
key_handle: ESYS_TR,
digest: &[u8],
scheme: TPMT_SIG_SCHEME,
validation: &TPMT_TK_HASHCHECK,
) -> Result<Signature> {
let mut signature = null_mut();
let digest = wrap_buffer!(digest, TPM2B_DIGEST, 64);
let ret = unsafe {
Esys_Sign(
self.mut_context(),
key_handle,
self.sessions.0,
self.sessions.1,
self.sessions.2,
&digest,
&scheme,
validation,
&mut signature,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let signature = unsafe { MBox::from_raw(signature) };
Ok(unsafe { Signature::try_from(*signature)? })
} else {
error!("Error in loading: {}.", ret);
Err(ret)
}
}
pub fn verify_signature(
&mut self,
key_handle: ESYS_TR,
digest: &[u8],
signature: &TPMT_SIGNATURE,
) -> Result<TPMT_TK_VERIFIED> {
let mut validation = null_mut();
let digest = wrap_buffer!(digest, TPM2B_DIGEST, 64);
let ret = unsafe {
Esys_VerifySignature(
self.mut_context(),
key_handle,
self.sessions.0,
self.sessions.1,
self.sessions.2,
&digest,
signature,
&mut validation,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let validation = unsafe { MBox::from_raw(validation) };
Ok(*validation)
} else {
error!("Error in loading: {}.", ret);
Err(ret)
}
}
pub fn load_external(
&mut self,
private: &TPM2B_SENSITIVE,
public: &TPM2B_PUBLIC,
hierarchy: TPMI_RH_HIERARCHY,
) -> Result<ESYS_TR> {
let mut key_handle = ESYS_TR_NONE;
let ret = unsafe {
Esys_LoadExternal(
self.mut_context(),
self.sessions.0,
self.sessions.1,
self.sessions.2,
private,
public,
hierarchy,
&mut key_handle,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let _ = self.open_handles.insert(key_handle);
Ok(key_handle)
} else {
error!("Error in loading: {}.", ret);
Err(ret)
}
}
pub fn load_external_public(
&mut self,
public: &TPM2B_PUBLIC,
hierarchy: TPMI_RH_HIERARCHY,
) -> Result<ESYS_TR> {
let mut key_handle = ESYS_TR_NONE;
let ret = unsafe {
Esys_LoadExternal(
self.mut_context(),
self.sessions.0,
self.sessions.1,
self.sessions.2,
null(),
public,
hierarchy,
&mut key_handle,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let _ = self.open_handles.insert(key_handle);
Ok(key_handle)
} else {
error!("Error in loading: {}.", ret);
Err(ret)
}
}
pub fn read_public(&mut self, key_handle: ESYS_TR) -> Result<TPM2B_PUBLIC> {
let mut public = null_mut();
let mut name = null_mut();
let mut qualified_name = null_mut();
let ret = unsafe {
Esys_ReadPublic(
self.mut_context(),
key_handle,
self.sessions.0,
self.sessions.1,
self.sessions.2,
&mut public,
&mut name,
&mut qualified_name,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
unsafe {
let _ = MBox::from_raw(name);
let _ = MBox::from_raw(qualified_name);
}
let public = unsafe { MBox::<TPM2B_PUBLIC>::from_raw(public) };
Ok(*public)
} else {
error!("Error in loading: {}.", ret);
Err(ret)
}
}
pub fn flush_context(&mut self, handle: ESYS_TR) -> Result<()> {
let ret = unsafe { Esys_FlushContext(self.mut_context(), handle) };
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let _ = self.open_handles.remove(&handle);
Ok(())
} else {
error!("Error in flushing context: {}.", ret);
Err(ret)
}
}
pub fn context_save(&mut self, handle: ESYS_TR) -> Result<TpmsContext> {
let mut context = null_mut();
let ret = unsafe { Esys_ContextSave(self.mut_context(), handle, &mut context) };
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let context = unsafe { MBox::<TPMS_CONTEXT>::from_raw(context) };
Ok((*context).try_into()?)
} else {
error!("Error in saving context: {}.", ret);
Err(ret)
}
}
pub fn context_load(&mut self, context: TpmsContext) -> Result<ESYS_TR> {
let mut handle = ESYS_TR_NONE;
let ret = unsafe {
Esys_ContextLoad(
self.mut_context(),
&TPMS_CONTEXT::try_from(context)?,
&mut handle,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let _ = self.open_handles.insert(handle);
Ok(handle)
} else {
error!("Error in loading context: {}.", ret);
Err(ret)
}
}
pub fn pcr_read(
&mut self,
pcr_selections: PcrSelections,
) -> Result<(u32, PcrSelections, PcrData)> {
let mut pcr_update_counter: u32 = 0;
let mut tpml_pcr_selection_out_ptr = null_mut();
let mut tpml_digest_ptr = null_mut();
let ret = unsafe {
Esys_PCR_Read(
self.mut_context(),
self.sessions.0,
self.sessions.1,
self.sessions.2,
&pcr_selections.into(),
&mut pcr_update_counter,
&mut tpml_pcr_selection_out_ptr,
&mut tpml_digest_ptr,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let tpml_pcr_selection_out =
unsafe { MBox::<TPML_PCR_SELECTION>::from_raw(tpml_pcr_selection_out_ptr) };
let tpml_digest = unsafe { MBox::<TPML_DIGEST>::from_raw(tpml_digest_ptr) };
Ok((
pcr_update_counter,
PcrSelections::try_from(*tpml_pcr_selection_out)?,
PcrData::new(tpml_pcr_selection_out.as_ref(), tpml_digest.as_ref())?,
))
} else {
error!("Error in creating derived key: {}.", ret);
Err(ret)
}
}
pub fn quote(
&mut self,
signing_key_handle: ESYS_TR,
qualifying_data: &[u8],
signing_scheme: TPMT_SIG_SCHEME,
pcr_selection: PcrSelections,
) -> Result<(TPM2B_ATTEST, Signature)> {
let mut quoted = null_mut();
let mut signature = null_mut();
let qualifying_data = wrap_buffer!(qualifying_data, TPM2B_DATA, 64);
let ret = unsafe {
Esys_Quote(
self.mut_context(),
signing_key_handle,
self.sessions.0,
self.sessions.1,
self.sessions.2,
&qualifying_data,
&signing_scheme,
&pcr_selection.into(),
&mut quoted,
&mut signature,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let quoted = unsafe { MBox::<TPM2B_ATTEST>::from_raw(quoted) };
let signature = unsafe { MBox::from_raw(signature) };
Ok((*quoted, unsafe { Signature::try_from(*signature)? }))
} else {
error!("Error in quoting PCR: {}", ret);
Err(ret)
}
}
pub fn policy_pcr(
&mut self,
policy_session: ESYS_TR,
pcr_policy_digest: &[u8],
pcr_selections: PcrSelections,
) -> Result<()> {
let pcr_digest = wrap_buffer!(pcr_policy_digest, TPM2B_DIGEST, 64);
let ret = unsafe {
Esys_PolicyPCR(
self.mut_context(),
policy_session,
self.sessions.0,
self.sessions.1,
self.sessions.2,
&pcr_digest,
&pcr_selections.into(),
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
Ok(())
} else {
Err(ret)
}
}
pub fn get_random(&mut self, num_bytes: usize) -> Result<Vec<u8>> {
let mut buffer = null_mut();
let ret = unsafe {
Esys_GetRandom(
self.mut_context(),
self.sessions.0,
self.sessions.1,
self.sessions.2,
num_bytes
.try_into()
.or_else(|_| Err(Error::local_error(ErrorKind::WrongParamSize)))?,
&mut buffer,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let buffer = unsafe { MBox::from_raw(buffer) };
let mut random = buffer.buffer.to_vec();
random.truncate(buffer.size.try_into().unwrap());
Ok(random)
} else {
error!("Error in flushing context: {}.", ret);
Err(ret)
}
}
pub fn test_parms(&mut self, parms: PublicParmsUnion) -> Result<()> {
let public_parms = TPMT_PUBLIC_PARMS {
type_: parms.object_type(),
parameters: parms.into(),
};
let ret = unsafe {
Esys_TestParms(
self.mut_context(),
self.sessions.0,
self.sessions.1,
self.sessions.2,
&public_parms,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
Ok(())
} else {
error!("Error while testing parameters: {}.", ret);
Err(ret)
}
}
pub fn hash(
&mut self,
data: &[u8],
hashing_algorithm: HashingAlgorithm,
hierarchy: Hierarchy,
) -> Result<(Vec<u8>, HashcheckTicket)> {
let data = wrap_buffer!(data, TPM2B_MAX_BUFFER, 1024);
let mut out_hash_ptr = null_mut();
let mut validation_ptr = null_mut();
let ret = unsafe {
Esys_Hash(
self.mut_context(),
self.sessions.0,
self.sessions.1,
self.sessions.2,
&data,
hashing_algorithm.into(),
hierarchy.rh(),
&mut out_hash_ptr,
&mut validation_ptr,
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let out_hash = unsafe { MBox::<TPM2B_DIGEST>::from_raw(out_hash_ptr) };
let validation = unsafe { MBox::<TPMT_TK_HASHCHECK>::from_raw(validation_ptr) };
Ok((
out_hash.buffer[..out_hash.size as usize].to_vec(),
HashcheckTicket::try_from(*validation)?,
))
} else {
error!("Error failed to peform hash operation: {}.", ret);
Err(ret)
}
}
pub fn tr_set_auth(&mut self, handle: ESYS_TR, auth_value: &[u8]) -> Result<()> {
let auth = wrap_buffer!(auth_value, TPM2B_AUTH, 64);
let ret = unsafe { Esys_TR_SetAuth(self.mut_context(), handle, &auth) };
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
Ok(())
} else {
Err(ret)
}
}
pub fn tr_get_name(&mut self, handle: ESYS_TR) -> Result<TPM2B_NAME> {
let mut name = null_mut();
let ret = unsafe { Esys_TR_GetName(self.mut_context(), handle, &mut name) };
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
let name = unsafe { MBox::<TPM2B_NAME>::from_raw(name) };
Ok(*name)
} else {
error!("Error in getting name: {}.", ret);
Err(ret)
}
}
pub fn tr_sess_set_attributes(
&mut self,
handle: ESYS_TR,
attributes: TpmaSession,
) -> Result<()> {
let ret = unsafe {
Esys_TRSess_SetAttributes(
self.mut_context(),
handle,
attributes.flags(),
attributes.mask(),
)
};
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
Ok(())
} else {
Err(ret)
}
}
pub fn tr_sess_get_attributes(&mut self, handle: ESYS_TR) -> Result<TpmaSession> {
let mut flags: TPMA_SESSION = 0;
let ret = unsafe { Esys_TRSess_GetAttributes(self.mut_context(), handle, &mut flags) };
let ret = Error::from_tss_rc(ret);
if ret.is_success() {
Ok(TpmaSessionBuilder::new().with_flag(flags).build())
} else {
Err(ret)
}
}
fn mut_context(&mut self) -> *mut ESYS_CONTEXT {
self.esys_context.as_mut().unwrap().as_mut_ptr()
}
}
impl Drop for Context {
fn drop(&mut self) {
info!("Closing context.");
self.open_handles.clone().iter().for_each(|handle| {
info!("Flushing handle {}", *handle);
if let Err(e) = self.flush_context(*handle) {
error!("Error when dropping the context: {}.", e);
}
});
let esys_context = self.esys_context.take().unwrap();
let tcti_context = self.tcti_context.take().unwrap();
unsafe { Tss2_TctiLdr_Finalize(&mut tcti_context.into_raw()) };
unsafe { Esys_Finalize(&mut esys_context.into_raw()) };
info!("Context closed.");
}
}