use crate::constants::algorithm::{Cipher, EllipticCurve, HashingAlgorithm};
use crate::constants::tss::*;
use crate::constants::types::session::SessionType;
use crate::handles::KeyHandle;
use crate::interface_types::resource_handles::Hierarchy;
use crate::structures::{
Auth, CreateKeyResult, Data, Digest, PcrSelectionListBuilder, PublicKeyRSA, VerifiedTicket,
};
use crate::tcti::Tcti;
use crate::tss2_esys::*;
use crate::utils::{
self, create_restricted_decryption_rsa_public,
create_unrestricted_encryption_decryption_rsa_public, create_unrestricted_signing_ecc_public,
create_unrestricted_signing_rsa_public, AsymSchemeUnion, ObjectAttributes, PublicIdUnion,
PublicKey, PublicParmsUnion, Tpm2BPublicBuilder, TpmaSessionBuilder, TpmsContext,
TpmsRsaParmsBuilder, RSA_KEY_SIZES,
};
use crate::Context;
use crate::{Error, Result, WrapperErrorKind as ErrorKind};
use log::error;
use std::convert::{TryFrom, TryInto};
#[derive(Copy, Clone, Debug)]
pub struct RsaExponent {
value: u32,
}
impl Default for RsaExponent {
fn default() -> RsaExponent {
RsaExponent {
value: (1 << 16) + 1,
}
}
}
impl RsaExponent {
pub fn new(value: u32) -> RsaExponent {
RsaExponent { value }
}
pub fn value(&self) -> u32 {
self.value
}
}
#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
pub struct TransientKeyContext {
context: Context,
root_key_handle: KeyHandle,
}
impl TransientKeyContext {
pub fn create_key(
&mut self,
key_params: KeyParams,
auth_size: usize,
) -> Result<(TpmsContext, Option<Auth>)> {
if auth_size > 32 {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
let key_auth = if auth_size > 0 {
self.set_session_attrs()?;
let random_bytes = self.context.get_random(auth_size)?;
Some(Auth::try_from(random_bytes.value().to_vec())?)
} else {
None
};
let creation_pcrs = PcrSelectionListBuilder::new().build();
self.set_session_attrs()?;
let CreateKeyResult {
out_private,
out_public,
..
} = self.context.create_key(
self.root_key_handle,
&self.get_public_from_params(key_params)?,
key_auth.as_ref(),
None,
None,
creation_pcrs,
)?;
self.set_session_attrs()?;
let key_handle = self
.context
.load(self.root_key_handle, out_private, out_public)?;
self.set_session_attrs()?;
let key_context = self.context.context_save(key_handle.into()).or_else(|e| {
self.context.flush_context(key_handle.into())?;
Err(e)
})?;
self.context.flush_context(key_handle.into())?;
Ok((key_context, key_auth))
}
fn get_public_from_params(&self, params: KeyParams) -> Result<TPM2B_PUBLIC> {
match params {
KeyParams::RsaSign {
size,
scheme,
pub_exponent,
} => {
if RSA_KEY_SIZES.iter().find(|sz| **sz == size).is_none() {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
Ok(create_unrestricted_signing_rsa_public(
scheme,
size,
pub_exponent,
)?)
}
KeyParams::RsaEncrypt { size, pub_exponent } => {
if RSA_KEY_SIZES.iter().find(|sz| **sz == size).is_none() {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
create_unrestricted_encryption_decryption_rsa_public(size, pub_exponent)
}
KeyParams::Ecc { curve, scheme } => {
Ok(create_unrestricted_signing_ecc_public(scheme, curve)?)
}
}
}
pub fn load_external_rsa_public_key(&mut self, public_key: &[u8]) -> Result<TpmsContext> {
if RSA_KEY_SIZES
.iter()
.find(|sz| usize::from(**sz) == public_key.len() * 8)
.is_none()
{
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
let mut pk_buffer = [0_u8; 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 = create_unrestricted_signing_rsa_public(
AsymSchemeUnion::RSASSA(HashingAlgorithm::Sha256),
u16::try_from(public_key.len()).unwrap() * 8_u16,
0,
)?;
public.publicArea.unique = pk;
self.set_session_attrs()?;
let key_handle = self
.context
.load_external_public(&public, Hierarchy::Owner)?;
self.set_session_attrs()?;
let key_context = self.context.context_save(key_handle.into()).or_else(|e| {
self.context.flush_context(key_handle.into())?;
Err(e)
})?;
self.context.flush_context(key_handle.into())?;
Ok(key_context)
}
pub fn load_external_rsa(
&mut self,
key_prime: &[u8],
public_modulus: &[u8],
public_exponent: RsaExponent,
) -> Result<TpmsContext> {
if RSA_KEY_SIZES
.iter()
.find(|sz| usize::from(**sz) == public_modulus.len() * 8)
.is_none()
{
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
if key_prime.len() * 2 != public_modulus.len() {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
let mut public_modulus_buffer = [0_u8; 512];
public_modulus_buffer[..public_modulus.len()]
.clone_from_slice(&public_modulus[..public_modulus.len()]);
let pub_buffer = TPM2B_PUBLIC_KEY_RSA {
size: public_modulus.len().try_into().unwrap(),
buffer: public_modulus_buffer,
};
let pub_id_union = PublicIdUnion::Rsa(Box::from(pub_buffer));
let mut object_attributes = ObjectAttributes(0);
object_attributes.set_user_with_auth(true);
object_attributes.set_decrypt(false);
object_attributes.set_sign_encrypt(true);
object_attributes.set_restricted(false);
let rsa_parms = TpmsRsaParmsBuilder::new_unrestricted_signing_key(
AsymSchemeUnion::RSASSA(HashingAlgorithm::Sha256),
u16::try_from(public_modulus.len()).unwrap() * 8_u16,
public_exponent.value(),
)
.build()
.unwrap();
let public = Tpm2BPublicBuilder::new()
.with_type(TPM2_ALG_RSA)
.with_name_alg(TPM2_ALG_SHA256)
.with_object_attributes(object_attributes)
.with_parms(PublicParmsUnion::RsaDetail(rsa_parms))
.with_unique(pub_id_union)
.build()
.unwrap();
let mut key_prime_buffer = [0_u8; 256];
key_prime_buffer[..key_prime.len()].clone_from_slice(&key_prime[..key_prime.len()]);
let key_prime_len = key_prime.len().try_into().unwrap();
let private = TPM2B_SENSITIVE {
size: key_prime_len,
sensitiveArea: TPMT_SENSITIVE {
sensitiveType: TPM2_ALG_RSA,
authValue: Default::default(),
seedValue: Default::default(),
sensitive: TPMU_SENSITIVE_COMPOSITE {
rsa: TPM2B_PRIVATE_KEY_RSA {
size: key_prime_len,
buffer: key_prime_buffer,
},
},
},
};
self.set_session_attrs()?;
let key_handle = self
.context
.load_external(&private, &public, Hierarchy::Null)?;
self.set_session_attrs()?;
let key_context = self.context.context_save(key_handle.into()).or_else(|e| {
self.context.flush_context(key_handle.into())?;
Err(e)
})?;
self.context.flush_context(key_handle.into())?;
Ok(key_context)
}
pub fn read_public_key(&mut self, key_context: TpmsContext) -> Result<PublicKey> {
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.into())
.or_else(|e| {
self.context.flush_context(key_handle)?;
Err(e)
})?
.0;
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());
PublicKey::Rsa(key)
}
PublicIdUnion::Ecc(pub_key) => {
let mut x = pub_key.x.buffer.to_vec();
x.truncate(pub_key.x.size.try_into().unwrap());
let mut y = pub_key.y.buffer.to_vec();
y.truncate(pub_key.y.size.try_into().unwrap());
PublicKey::Ecc { x, y }
}
_ => return Err(Error::local_error(ErrorKind::UnsupportedParam)),
};
self.context.flush_context(key_handle)?;
Ok(key)
}
pub fn rsa_encrypt(
&mut self,
key_context: TpmsContext,
key_auth: Option<Auth>,
message: PublicKeyRSA,
scheme: AsymSchemeUnion,
label: Option<Data>,
) -> Result<PublicKeyRSA> {
self.set_session_attrs()?;
let key_handle = self
.context
.context_load(key_context)
.map(KeyHandle::from)?;
if let Some(key_auth_value) = key_auth {
self.context
.tr_set_auth(key_handle.into(), &key_auth_value)
.or_else(|e| {
self.context.flush_context(key_handle.into())?;
Err(e)
})?;
}
self.set_session_attrs()?;
let ciphertext = self
.context
.rsa_encrypt(key_handle, message, scheme, label.unwrap_or_default())
.or_else(|e| {
self.context.flush_context(key_handle.into())?;
Err(e)
})?;
self.context.flush_context(key_handle.into())?;
Ok(ciphertext)
}
pub fn rsa_decrypt(
&mut self,
key_context: TpmsContext,
key_auth: Option<Auth>,
ciphertext: PublicKeyRSA,
scheme: AsymSchemeUnion,
label: Option<Data>,
) -> Result<PublicKeyRSA> {
self.set_session_attrs()?;
let key_handle = self
.context
.context_load(key_context)
.map(KeyHandle::from)?;
if let Some(key_auth_value) = key_auth {
self.context
.tr_set_auth(key_handle.into(), &key_auth_value)
.or_else(|e| {
self.context.flush_context(key_handle.into())?;
Err(e)
})?;
}
self.set_session_attrs()?;
let plaintext = self
.context
.rsa_decrypt(key_handle, ciphertext, scheme, label.unwrap_or_default())
.or_else(|e| {
self.context.flush_context(key_handle.into())?;
Err(e)
})?;
self.context.flush_context(key_handle.into())?;
Ok(plaintext)
}
pub fn sign(
&mut self,
key_context: TpmsContext,
key_auth: Option<Auth>,
digest: Digest,
) -> Result<utils::Signature> {
self.set_session_attrs()?;
let key_handle = self
.context
.context_load(key_context)
.map(KeyHandle::from)?;
if let Some(key_auth_value) = key_auth {
self.context
.tr_set_auth(key_handle.into(), &key_auth_value)
.or_else(|e| {
self.context.flush_context(key_handle.into())?;
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.try_into()?)
.or_else(|e| {
self.context.flush_context(key_handle.into())?;
Err(e)
})?;
self.context.flush_context(key_handle.into())?;
Ok(signature)
}
pub fn verify_signature(
&mut self,
key_context: TpmsContext,
digest: Digest,
signature: utils::Signature,
) -> Result<VerifiedTicket> {
self.set_session_attrs()?;
let key_handle = self
.context
.context_load(key_context)
.map(KeyHandle::from)?;
self.set_session_attrs()?;
let verified = self
.context
.verify_signature(key_handle, &digest, signature)
.or_else(|e| {
self.context.flush_context(key_handle.into())?;
Err(e)
})?;
self.context.flush_context(key_handle.into())?;
Ok(verified)
}
fn set_session_attrs(&mut self) -> Result<()> {
if let (Some(session), _, _) = self.context.sessions() {
let session_attr = utils::TpmaSessionBuilder::new()
.with_flag(TPMA_SESSION_DECRYPT)
.with_flag(TPMA_SESSION_ENCRYPT)
.build();
self.context.tr_sess_set_attributes(session, session_attr)?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct TransientKeyContextBuilder {
tcti: Tcti,
hierarchy: Hierarchy,
root_key_size: u16,
root_key_auth_size: usize,
hierarchy_auth: Vec<u8>,
default_context_cipher: Cipher,
session_hash_alg: HashingAlgorithm,
}
impl TransientKeyContextBuilder {
pub fn new() -> Self {
TransientKeyContextBuilder {
tcti: Tcti::Device(Default::default()),
hierarchy: Hierarchy::Owner,
root_key_size: 2048,
root_key_auth_size: 32,
hierarchy_auth: Vec::new(),
default_context_cipher: Cipher::aes_256_cfb(),
session_hash_alg: HashingAlgorithm::Sha256,
}
}
pub fn with_tcti(mut self, tcti: Tcti) -> Self {
self.tcti = tcti;
self
}
pub fn with_hierarchy(mut self, hierarchy: Hierarchy) -> Self {
self.hierarchy = hierarchy;
self
}
pub fn with_root_key_size(mut self, root_key_size: u16) -> Self {
self.root_key_size = root_key_size;
self
}
pub fn with_root_key_auth_size(mut self, root_key_auth_size: usize) -> Self {
self.root_key_auth_size = root_key_auth_size;
self
}
pub fn with_hierarchy_auth(mut self, hierarchy_auth: Vec<u8>) -> Self {
self.hierarchy_auth = hierarchy_auth;
self
}
pub fn with_default_context_cipher(mut self, default_context_cipher: Cipher) -> Self {
self.default_context_cipher = default_context_cipher;
self
}
pub fn with_session_hash_alg(mut self, session_hash_alg: HashingAlgorithm) -> Self {
self.session_hash_alg = session_hash_alg;
self
}
pub unsafe fn build(self) -> Result<TransientKeyContext> {
if self.root_key_auth_size > 32 {
return Err(Error::local_error(ErrorKind::WrongParamSize));
}
if RSA_KEY_SIZES
.iter()
.find(|sz| **sz == self.root_key_size)
.is_none()
{
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(self.tcti)?;
let session = context
.start_auth_session(
None,
None,
None,
SessionType::Hmac,
self.default_context_cipher,
self.session_hash_alg,
)
.and_then(|session| {
session.ok_or_else(|| {
error!("Received unexpected NONE handle from the TPM");
Error::local_error(ErrorKind::WrongValueFromTpm)
})
})?;
let session_attr = TpmaSessionBuilder::new()
.with_flag(TPMA_SESSION_DECRYPT)
.with_flag(TPMA_SESSION_ENCRYPT)
.build();
context.tr_sess_set_attributes(session, session_attr)?;
context.set_sessions((Some(session), None, None));
let root_key_auth = if self.root_key_auth_size > 0 {
let random = context.get_random(self.root_key_auth_size)?;
Some(Auth::try_from(random.value().to_vec())?)
} else {
None
};
if !self.hierarchy_auth.is_empty() {
let auth_hierarchy = Auth::try_from(self.hierarchy_auth)?;
context.tr_set_auth(self.hierarchy.esys_rh().into(), &auth_hierarchy)?;
}
let creation_pcrs = PcrSelectionListBuilder::new().build();
let root_key_handle = context
.create_primary_key(
self.hierarchy,
&create_restricted_decryption_rsa_public(
self.default_context_cipher,
self.root_key_size,
0,
)?,
root_key_auth.as_ref(),
None,
None,
creation_pcrs,
)?
.key_handle;
let new_session = context
.start_auth_session(
Some(root_key_handle),
None,
None,
SessionType::Hmac,
self.default_context_cipher,
self.session_hash_alg,
)
.and_then(|session| {
session.ok_or_else(|| {
error!("Received unexpected NONE handle from the TPM");
Error::local_error(ErrorKind::WrongValueFromTpm)
})
})?;
if let (Some(old_session), _, _) = context.sessions() {
context.set_sessions((Some(new_session), None, None));
context.flush_context(old_session.handle().into())?;
}
Ok(TransientKeyContext {
context,
root_key_handle,
})
}
}
impl Default for TransientKeyContextBuilder {
fn default() -> Self {
TransientKeyContextBuilder::new()
}
}
#[derive(Debug, Clone, Copy)]
pub enum KeyParams {
RsaSign {
size: u16,
scheme: AsymSchemeUnion,
pub_exponent: u32,
},
RsaEncrypt {
size: u16,
pub_exponent: u32,
},
Ecc {
curve: EllipticCurve,
scheme: AsymSchemeUnion,
},
}