use crate::constants::*;
use crate::response_code::{Error, Result, WrapperErrorKind as ErrorKind};
use crate::tss2_esys::*;
use crate::utils::{self, get_rsa_public, PublicIdUnion, TpmsContext, TpmtTkVerified};
use crate::{Context, Tcti, NO_SESSIONS};
use log::error;
use std::convert::{TryFrom, TryInto};
#[derive(Debug)]
pub struct TransientObjectContext {
context: Context,
root_key_handle: ESYS_TR,
}
impl TransientObjectContext {
pub unsafe fn new(
tcti: Tcti,
root_key_size: usize,
root_key_auth_size: usize,
owner_hierarchy_auth: &[u8],
) -> Result<Self> {
if root_key_auth_size > 32 {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
if root_key_size != 1024 && root_key_size != 2048 {
error!("The reference implementation only supports key sizes of 1,024 and 2,048 bits.");
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
let mut context = Context::new(tcti)?;
let root_key_auth: Vec<u8> = if root_key_auth_size > 0 {
context.get_random(root_key_auth_size)?
} else {
vec![]
};
if !owner_hierarchy_auth.is_empty() {
context.set_handle_auth(ESYS_TR_RH_OWNER, owner_hierarchy_auth)?;
}
let root_key_handle = context.create_primary_key(
ESYS_TR_RH_OWNER,
&get_rsa_public(true, true, false, root_key_size.try_into().unwrap()), &root_key_auth,
&[],
&[],
&[],
)?;
let new_session = context.start_auth_session(
NO_SESSIONS,
root_key_handle,
ESYS_TR_NONE,
&[],
TPM2_SE_HMAC,
utils::TpmtSymDefBuilder::aes_256_cfb(),
TPM2_ALG_SHA256,
)?;
let (old_session, _, _) = context.sessions();
context.set_sessions((new_session, ESYS_TR_NONE, ESYS_TR_NONE));
context.flush_context(old_session)?;
Ok(TransientObjectContext {
context,
root_key_handle,
})
}
pub fn create_rsa_signing_key(
&mut self,
key_size: usize,
auth_size: usize,
) -> Result<(TpmsContext, Vec<u8>)> {
if auth_size > 32 {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
if key_size != 1024 && key_size != 2048 {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
let key_auth = if auth_size > 0 {
self.set_session_attrs()?;
self.context.get_random(auth_size)?
} else {
vec![]
};
self.set_session_attrs()?;
let (key_priv, key_pub) = self.context.create_key(
self.root_key_handle,
&get_rsa_public(false, false, true, key_size.try_into().unwrap()), &key_auth,
&[],
&[],
&[],
)?;
self.set_session_attrs()?;
let key_handle = self.context.load(self.root_key_handle, key_priv, key_pub)?;
self.set_session_attrs()?;
let key_context = self.context.context_save(key_handle).or_else(|e| {
self.context.flush_context(key_handle)?;
Err(e)
})?;
self.context.flush_context(key_handle)?;
Ok((key_context, key_auth))
}
pub fn load_external_rsa_public_key(&mut self, public_key: &[u8]) -> Result<TpmsContext> {
if public_key.len() != 128 && public_key.len() != 256 {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
let mut pk_buffer = [0u8; 512];
pk_buffer[..public_key.len()].clone_from_slice(&public_key[..public_key.len()]);
let pk = TPMU_PUBLIC_ID {
rsa: TPM2B_PUBLIC_KEY_RSA {
size: public_key.len().try_into().unwrap(), buffer: pk_buffer,
},
};
let mut public = get_rsa_public(
false,
false,
true,
u16::try_from(public_key.len()).unwrap() * 8u16, );
public.publicArea.unique = pk;
self.set_session_attrs()?;
let key_handle = self.context.load_external_public(&public, TPM2_RH_OWNER)?;
self.set_session_attrs()?;
let key_context = self.context.context_save(key_handle).or_else(|e| {
self.context.flush_context(key_handle)?;
Err(e)
})?;
self.context.flush_context(key_handle)?;
Ok(key_context)
}
pub fn read_public_key(&mut self, key_context: TpmsContext) -> Result<Vec<u8>> {
self.set_session_attrs()?;
let key_handle = self.context.context_load(key_context)?;
self.set_session_attrs()?;
let key_pub_id = self.context.read_public(key_handle).or_else(|e| {
self.context.flush_context(key_handle)?;
Err(e)
})?;
let key = match unsafe { PublicIdUnion::from_public(&key_pub_id) } {
PublicIdUnion::Rsa(pub_key) => {
let mut key = pub_key.buffer.to_vec();
key.truncate(pub_key.size.try_into().unwrap()); key
}
_ => unimplemented!(),
};
self.context.flush_context(key_handle)?;
Ok(key)
}
pub fn sign(
&mut self,
key_context: TpmsContext,
key_auth: &[u8],
digest: &[u8],
) -> Result<utils::Signature> {
self.set_session_attrs()?;
let key_handle = self.context.context_load(key_context)?;
self.context
.set_handle_auth(key_handle, key_auth)
.or_else(|e| {
self.context.flush_context(key_handle)?;
Err(e)
})?;
let scheme = TPMT_SIG_SCHEME {
scheme: TPM2_ALG_NULL,
details: Default::default(),
};
let validation = TPMT_TK_HASHCHECK {
tag: TPM2_ST_HASHCHECK,
hierarchy: TPM2_RH_NULL,
digest: Default::default(),
};
self.set_session_attrs()?;
let signature = self
.context
.sign(key_handle, digest, scheme, &validation)
.or_else(|e| {
self.context.flush_context(key_handle)?;
Err(e)
})?;
self.context.flush_context(key_handle)?;
Ok(signature)
}
pub fn verify_signature(
&mut self,
key_context: TpmsContext,
digest: &[u8],
signature: utils::Signature,
) -> Result<TpmtTkVerified> {
self.set_session_attrs()?;
let key_handle = self.context.context_load(key_context)?;
let signature: TPMT_SIGNATURE = signature.try_into().or_else(|e| {
self.context.flush_context(key_handle)?;
Err(e)
})?;
self.set_session_attrs()?;
let verified = self
.context
.verify_signature(key_handle, digest, &signature)
.or_else(|e| {
self.context.flush_context(key_handle)?;
Err(e)
})?;
self.context.flush_context(key_handle)?;
Ok(verified.try_into()?)
}
fn set_session_attrs(&mut self) -> Result<()> {
let (session, _, _) = self.context.sessions();
let session_attr = utils::TpmaSession::new()
.with_flag(TPMA_SESSION_DECRYPT)
.with_flag(TPMA_SESSION_ENCRYPT);
self.context.set_session_attr(session, session_attr)?;
Ok(())
}
}