use crate::constants::algorithm::{Cipher, EllipticCurve, HashingAlgorithm};
use crate::constants::tss::*;
use crate::structures::{Digest, PcrSlot};
use crate::tss2_esys::*;
use crate::{Error, Result, WrapperErrorKind};
use bitfield::bitfield;
use enumflags2::BitFlags;
use log::error;
use zeroize::Zeroize;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::convert::{TryFrom, TryInto};
#[allow(missing_debug_implementations)]
pub struct Tpm2BPublicBuilder {
type_: Option<TPMI_ALG_PUBLIC>,
name_alg: TPMI_ALG_HASH,
object_attributes: ObjectAttributes,
auth_policy: TPM2B_DIGEST,
parameters: Option<PublicParmsUnion>,
unique: Option<PublicIdUnion>,
}
impl Tpm2BPublicBuilder {
pub fn new() -> Self {
Tpm2BPublicBuilder {
type_: None,
name_alg: TPM2_ALG_NULL,
object_attributes: ObjectAttributes(0),
auth_policy: Default::default(),
parameters: None,
unique: None,
}
}
pub fn with_type(mut self, type_: TPMI_ALG_PUBLIC) -> Self {
self.type_ = Some(type_);
self
}
pub fn with_name_alg(mut self, name_alg: TPMI_ALG_HASH) -> Self {
self.name_alg = name_alg;
self
}
pub fn with_object_attributes(mut self, obj_attr: ObjectAttributes) -> Self {
self.object_attributes = obj_attr;
self
}
pub fn with_auth_policy(mut self, size: u16, buffer: [u8; 64]) -> Self {
self.auth_policy = TPM2B_DIGEST { size, buffer };
self
}
pub fn with_parms(mut self, parameters: PublicParmsUnion) -> Self {
self.parameters = Some(parameters);
self
}
pub fn with_unique(mut self, unique: PublicIdUnion) -> Self {
self.unique = Some(unique);
self
}
pub fn build(self) -> Result<TPM2B_PUBLIC> {
match self.type_ {
Some(TPM2_ALG_RSA) => {
let parameters;
let unique;
if let Some(PublicParmsUnion::RsaDetail(parms)) = self.parameters {
parameters = TPMU_PUBLIC_PARMS { rsaDetail: parms };
} else if self.parameters.is_none() {
return Err(Error::local_error(WrapperErrorKind::ParamsMissing));
} else {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
if let Some(PublicIdUnion::Rsa(rsa_unique)) = self.unique {
unique = TPMU_PUBLIC_ID { rsa: *rsa_unique };
} else if self.unique.is_none() {
unique = Default::default();
} else {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
Ok(TPM2B_PUBLIC {
size: std::mem::size_of::<TPMT_PUBLIC>()
.try_into()
.expect("Failed to convert usize to u16"),
publicArea: TPMT_PUBLIC {
type_: self.type_.unwrap(),
nameAlg: self.name_alg,
objectAttributes: self.object_attributes.0,
authPolicy: self.auth_policy,
parameters,
unique,
},
})
}
Some(TPM2_ALG_ECC) => {
let parameters;
let unique;
if let Some(PublicParmsUnion::EccDetail(parms)) = self.parameters {
parameters = TPMU_PUBLIC_PARMS { eccDetail: parms };
} else if self.parameters.is_none() {
return Err(Error::local_error(WrapperErrorKind::ParamsMissing));
} else {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
if let Some(PublicIdUnion::Ecc(rsa_unique)) = self.unique {
unique = TPMU_PUBLIC_ID { ecc: *rsa_unique };
} else if self.unique.is_none() {
unique = Default::default();
} else {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
Ok(TPM2B_PUBLIC {
size: std::mem::size_of::<TPMT_PUBLIC>()
.try_into()
.expect("Failed to convert usize to u16"),
publicArea: TPMT_PUBLIC {
type_: self.type_.unwrap(),
nameAlg: self.name_alg,
objectAttributes: self.object_attributes.0,
authPolicy: self.auth_policy,
parameters,
unique,
},
})
}
_ => Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
}
}
}
impl Default for Tpm2BPublicBuilder {
fn default() -> Self {
Tpm2BPublicBuilder::new()
}
}
#[allow(missing_debug_implementations)]
#[derive(Copy, Clone, Default)]
pub struct TpmsRsaParmsBuilder {
pub symmetric: Option<TPMT_SYM_DEF_OBJECT>,
pub scheme: Option<AsymSchemeUnion>,
pub key_bits: TPMI_RSA_KEY_BITS,
pub exponent: u32,
pub for_signing: bool,
pub for_decryption: bool,
pub restricted: bool,
}
impl TpmsRsaParmsBuilder {
pub fn new_restricted_decryption_key(
symmetric: TPMT_SYM_DEF_OBJECT,
key_bits: TPMI_RSA_KEY_BITS,
exponent: u32,
) -> Self {
TpmsRsaParmsBuilder {
symmetric: Some(symmetric),
scheme: Some(AsymSchemeUnion::AnySig(None)),
key_bits,
exponent,
for_signing: false,
for_decryption: true,
restricted: true,
}
}
pub fn new_unrestricted_signing_key(
scheme: AsymSchemeUnion,
key_bits: TPMI_RSA_KEY_BITS,
exponent: u32,
) -> Self {
TpmsRsaParmsBuilder {
symmetric: None,
scheme: Some(scheme),
key_bits,
exponent,
for_signing: true,
for_decryption: false,
restricted: false,
}
}
pub fn build(self) -> Result<TPMS_RSA_PARMS> {
if self.restricted && self.for_decryption {
if self.symmetric.is_none() {
return Err(Error::local_error(WrapperErrorKind::ParamsMissing));
}
} else if self.symmetric.is_some() {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
let symmetric = self.symmetric.unwrap_or_else(|| {
let mut def: TPMT_SYM_DEF_OBJECT = Default::default();
def.algorithm = TPM2_ALG_NULL;
def
});
let scheme = self
.scheme
.ok_or_else(|| Error::local_error(WrapperErrorKind::ParamsMissing))?
.get_rsa_scheme_struct();
if self.restricted {
if self.for_signing
&& scheme.scheme != TPM2_ALG_RSAPSS
&& scheme.scheme != TPM2_ALG_RSASSA
{
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
if self.for_decryption && scheme.scheme != TPM2_ALG_NULL {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
} else {
if self.for_decryption && self.for_signing && scheme.scheme != TPM2_ALG_NULL {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
if self.for_signing
&& scheme.scheme != TPM2_ALG_RSAPSS
&& scheme.scheme != TPM2_ALG_RSASSA
&& scheme.scheme != TPM2_ALG_NULL
{
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
if self.for_decryption
&& scheme.scheme != TPM2_ALG_RSAES
&& scheme.scheme != TPM2_ALG_OAEP
&& scheme.scheme != TPM2_ALG_NULL
{
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
}
Ok(TPMS_RSA_PARMS {
symmetric,
scheme,
keyBits: self.key_bits,
exponent: self.exponent,
})
}
}
pub const RSA_KEY_SIZES: [u16; 4] = [1024, 2048, 3072, 4096];
#[derive(Copy, Clone, Debug)]
pub struct TpmsEccParmsBuilder {
pub symmetric: Option<Cipher>,
pub scheme: AsymSchemeUnion,
pub curve: EllipticCurve,
pub for_signing: bool,
pub for_decryption: bool,
pub restricted: bool,
}
impl TpmsEccParmsBuilder {
pub fn new_restricted_decryption_key(symmetric: Cipher, curve: EllipticCurve) -> Self {
TpmsEccParmsBuilder {
symmetric: Some(symmetric),
scheme: AsymSchemeUnion::AnySig(None),
curve,
for_signing: false,
for_decryption: true,
restricted: true,
}
}
pub fn new_unrestricted_signing_key(scheme: AsymSchemeUnion, curve: EllipticCurve) -> Self {
TpmsEccParmsBuilder {
symmetric: None,
scheme,
curve,
for_signing: true,
for_decryption: false,
restricted: false,
}
}
pub fn build(self) -> Result<TPMS_ECC_PARMS> {
if self.restricted && self.for_decryption {
if self.symmetric.is_none() {
return Err(Error::local_error(WrapperErrorKind::ParamsMissing));
}
} else if self.symmetric.is_some() {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
if self.for_decryption && self.for_signing {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
let scheme = self.scheme.get_ecc_scheme_struct();
if self.for_signing
&& scheme.scheme != TPM2_ALG_ECDSA
&& scheme.scheme != TPM2_ALG_ECDAA
&& scheme.scheme != TPM2_ALG_SM2
&& scheme.scheme != TPM2_ALG_ECSCHNORR
{
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
if self.for_decryption
&& scheme.scheme != TPM2_ALG_SM2
&& scheme.scheme != TPM2_ALG_ECDH
&& scheme.scheme != TPM2_ALG_ECMQV
&& scheme.scheme != TPM2_ALG_NULL
{
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
if (self.curve == EllipticCurve::BnP256 || self.curve == EllipticCurve::BnP638)
&& scheme.scheme != TPM2_ALG_ECDAA
{
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
let symmetric = match self.symmetric {
Some(symmetric) => symmetric.into(),
None => {
let mut def: TPMT_SYM_DEF_OBJECT = Default::default();
def.algorithm = TPM2_ALG_NULL;
def
}
};
Ok(TPMS_ECC_PARMS {
symmetric,
scheme,
curveID: self.curve.into(),
kdf: TPMT_KDF_SCHEME {
scheme: TPM2_ALG_NULL,
details: Default::default(),
},
})
}
}
#[derive(Copy, Clone, Debug)]
pub struct TpmtSymDefBuilder {
algorithm: Option<TPM2_ALG_ID>,
key_bits: u16,
mode: TPM2_ALG_ID,
}
impl TpmtSymDefBuilder {
pub fn new() -> Self {
TpmtSymDefBuilder {
algorithm: None,
key_bits: 0,
mode: TPM2_ALG_NULL,
}
}
pub fn with_algorithm(mut self, algorithm: TPM2_ALG_ID) -> Self {
self.algorithm = Some(algorithm);
self
}
pub fn with_key_bits(mut self, key_bits: TPM2_KEY_BITS) -> Self {
self.key_bits = key_bits;
self
}
pub fn with_hash(mut self, hash: TPM2_ALG_ID) -> Self {
self.key_bits = hash;
self
}
pub fn with_mode(mut self, mode: TPM2_ALG_ID) -> Self {
self.mode = mode;
self
}
pub fn build(self) -> Result<TPMT_SYM_DEF> {
let (key_bits, mode) = self.bits_and_mode()?;
Ok(TPMT_SYM_DEF {
algorithm: self.algorithm.unwrap(),
keyBits: key_bits,
mode,
})
}
pub fn build_object(self) -> Result<TPMT_SYM_DEF_OBJECT> {
let (key_bits, mode) = self.bits_and_mode()?;
Ok(TPMT_SYM_DEF_OBJECT {
algorithm: self.algorithm.unwrap(),
keyBits: key_bits,
mode,
})
}
fn bits_and_mode(self) -> Result<(TPMU_SYM_KEY_BITS, TPMU_SYM_MODE)> {
let key_bits;
let mode;
match self.algorithm {
Some(TPM2_ALG_XOR) => {
key_bits = TPMU_SYM_KEY_BITS {
exclusiveOr: self.key_bits,
};
mode = Default::default();
}
Some(TPM2_ALG_AES) => {
key_bits = TPMU_SYM_KEY_BITS { aes: self.key_bits };
mode = TPMU_SYM_MODE { aes: self.mode };
}
Some(TPM2_ALG_SM4) => {
key_bits = TPMU_SYM_KEY_BITS { sm4: self.key_bits };
mode = TPMU_SYM_MODE { sm4: self.mode };
}
Some(TPM2_ALG_CAMELLIA) => {
key_bits = TPMU_SYM_KEY_BITS {
camellia: self.key_bits,
};
mode = TPMU_SYM_MODE {
camellia: self.mode,
};
}
Some(TPM2_ALG_NULL) => {
key_bits = Default::default();
mode = Default::default();
}
None => return Err(Error::local_error(WrapperErrorKind::ParamsMissing)),
_ => return Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
}
Ok((key_bits, mode))
}
}
impl Default for TpmtSymDefBuilder {
fn default() -> Self {
TpmtSymDefBuilder::new()
}
}
bitfield! {
pub struct ObjectAttributes(TPMA_OBJECT);
impl Debug;
pub fixed_tpm, set_fixed_tpm: 1;
pub st_clear, set_st_clear: 2;
pub fixed_parent, set_fixed_parent: 4;
pub sensitive_data_origin, set_sensitive_data_origin: 5;
pub user_with_auth, set_user_with_auth: 6;
pub admin_with_policy, set_admin_with_policy: 7;
pub no_da, set_no_da: 10;
pub encrypted_duplication, set_encrypted_duplication: 11;
pub restricted, set_restricted: 16;
pub decrypt, set_decrypt: 17;
pub sign_encrypt, set_sign_encrypt: 18;
}
impl ObjectAttributes {
pub fn new_fixed_parent_key() -> Self {
let mut attrs = ObjectAttributes(0);
attrs.set_fixed_tpm(true);
attrs.set_fixed_parent(true);
attrs.set_sensitive_data_origin(true);
attrs.set_user_with_auth(true);
attrs.set_decrypt(true);
attrs.set_restricted(true);
attrs
}
pub fn new_fixed_signing_key() -> Self {
let mut attrs = ObjectAttributes(0);
attrs.set_fixed_tpm(true);
attrs.set_fixed_parent(true);
attrs.set_sensitive_data_origin(true);
attrs.set_user_with_auth(true);
attrs.set_sign_encrypt(true);
attrs
}
}
#[allow(missing_debug_implementations)]
pub enum PublicIdUnion {
KeyedHash(TPM2B_DIGEST),
Sym(TPM2B_DIGEST),
Rsa(Box<TPM2B_PUBLIC_KEY_RSA>),
Ecc(Box<TPMS_ECC_POINT>),
}
impl PublicIdUnion {
pub unsafe fn from_public(public: &TPM2B_PUBLIC) -> Result<Self> {
match public.publicArea.type_ {
TPM2_ALG_RSA => Ok(PublicIdUnion::Rsa(Box::from(public.publicArea.unique.rsa))),
TPM2_ALG_ECC => Ok(PublicIdUnion::Ecc(Box::from(public.publicArea.unique.ecc))),
TPM2_ALG_SYMCIPHER => Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
TPM2_ALG_KEYEDHASH => Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
_ => Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
}
}
}
#[allow(missing_debug_implementations)]
#[allow(clippy::pub_enum_variant_names)]
#[derive(Copy, Clone)]
pub enum PublicParmsUnion {
KeyedHashDetail(TPMS_KEYEDHASH_PARMS),
SymDetail(Cipher),
RsaDetail(TPMS_RSA_PARMS),
EccDetail(TPMS_ECC_PARMS),
AsymDetail(TPMS_ASYM_PARMS),
}
impl PublicParmsUnion {
pub fn object_type(&self) -> TPMI_ALG_PUBLIC {
match self {
PublicParmsUnion::AsymDetail(..) => TPM2_ALG_NULL,
PublicParmsUnion::EccDetail(..) => TPM2_ALG_ECC,
PublicParmsUnion::RsaDetail(..) => TPM2_ALG_RSA,
PublicParmsUnion::SymDetail(..) => TPM2_ALG_SYMCIPHER,
PublicParmsUnion::KeyedHashDetail(..) => TPM2_ALG_KEYEDHASH,
}
}
}
impl From<PublicParmsUnion> for TPMU_PUBLIC_PARMS {
fn from(parms: PublicParmsUnion) -> Self {
match parms {
PublicParmsUnion::AsymDetail(tss_parms) => TPMU_PUBLIC_PARMS {
asymDetail: tss_parms,
},
PublicParmsUnion::EccDetail(tss_parms) => TPMU_PUBLIC_PARMS {
eccDetail: tss_parms,
},
PublicParmsUnion::RsaDetail(tss_parms) => TPMU_PUBLIC_PARMS {
rsaDetail: tss_parms,
},
PublicParmsUnion::SymDetail(cipher) => TPMU_PUBLIC_PARMS {
symDetail: cipher.into(),
},
PublicParmsUnion::KeyedHashDetail(tss_parms) => TPMU_PUBLIC_PARMS {
keyedHashDetail: tss_parms,
},
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum AsymSchemeUnion {
ECDH(HashingAlgorithm),
ECMQV(HashingAlgorithm),
RSASSA(HashingAlgorithm),
RSAPSS(HashingAlgorithm),
ECDSA(HashingAlgorithm),
ECDAA(HashingAlgorithm, u16),
SM2(HashingAlgorithm),
ECSchnorr(HashingAlgorithm),
RSAES,
RSAOAEP(HashingAlgorithm),
AnySig(Option<HashingAlgorithm>),
}
impl AsymSchemeUnion {
pub fn scheme_id(self) -> TPM2_ALG_ID {
match self {
AsymSchemeUnion::ECDH(_) => TPM2_ALG_ECDH,
AsymSchemeUnion::ECMQV(_) => TPM2_ALG_ECMQV,
AsymSchemeUnion::RSASSA(_) => TPM2_ALG_RSASSA,
AsymSchemeUnion::RSAPSS(_) => TPM2_ALG_RSAPSS,
AsymSchemeUnion::ECDSA(_) => TPM2_ALG_ECDSA,
AsymSchemeUnion::ECDAA(_, _) => TPM2_ALG_ECDAA,
AsymSchemeUnion::SM2(_) => TPM2_ALG_SM2,
AsymSchemeUnion::ECSchnorr(_) => TPM2_ALG_ECSCHNORR,
AsymSchemeUnion::RSAES => TPM2_ALG_RSAES,
AsymSchemeUnion::RSAOAEP(_) => TPM2_ALG_OAEP,
AsymSchemeUnion::AnySig(_) => TPM2_ALG_NULL,
}
}
fn get_details(self) -> TPMU_ASYM_SCHEME {
match self {
AsymSchemeUnion::ECDH(hash_alg) => TPMU_ASYM_SCHEME {
ecdh: TPMS_SCHEME_HASH {
hashAlg: hash_alg.into(),
},
},
AsymSchemeUnion::ECMQV(hash_alg) => TPMU_ASYM_SCHEME {
ecmqv: TPMS_SCHEME_HASH {
hashAlg: hash_alg.into(),
},
},
AsymSchemeUnion::RSASSA(hash_alg) => TPMU_ASYM_SCHEME {
rsassa: TPMS_SCHEME_HASH {
hashAlg: hash_alg.into(),
},
},
AsymSchemeUnion::RSAPSS(hash_alg) => TPMU_ASYM_SCHEME {
rsapss: TPMS_SCHEME_HASH {
hashAlg: hash_alg.into(),
},
},
AsymSchemeUnion::ECDSA(hash_alg) => TPMU_ASYM_SCHEME {
ecdsa: TPMS_SCHEME_HASH {
hashAlg: hash_alg.into(),
},
},
AsymSchemeUnion::ECDAA(hash_alg, count) => TPMU_ASYM_SCHEME {
ecdaa: TPMS_SCHEME_ECDAA {
hashAlg: hash_alg.into(),
count,
},
},
AsymSchemeUnion::SM2(hash_alg) => TPMU_ASYM_SCHEME {
sm2: TPMS_SCHEME_HASH {
hashAlg: hash_alg.into(),
},
},
AsymSchemeUnion::ECSchnorr(hash_alg) => TPMU_ASYM_SCHEME {
ecschnorr: TPMS_SCHEME_HASH {
hashAlg: hash_alg.into(),
},
},
AsymSchemeUnion::RSAES => TPMU_ASYM_SCHEME {
rsaes: Default::default(),
},
AsymSchemeUnion::RSAOAEP(hash_alg) => TPMU_ASYM_SCHEME {
oaep: TPMS_SCHEME_HASH {
hashAlg: hash_alg.into(),
},
},
AsymSchemeUnion::AnySig(hash_alg) => TPMU_ASYM_SCHEME {
anySig: TPMS_SCHEME_HASH {
hashAlg: hash_alg.map(u16::from).or(Some(TPM2_ALG_NULL)).unwrap(),
},
},
}
}
fn get_rsa_scheme_struct(self) -> TPMT_RSA_SCHEME {
let scheme = self.scheme_id();
let details = self.get_details();
TPMT_RSA_SCHEME { scheme, details }
}
pub fn get_rsa_decrypt_struct(self) -> TPMT_RSA_DECRYPT {
let scheme = self.scheme_id();
let details = self.get_details();
TPMT_RSA_DECRYPT { scheme, details }
}
fn get_ecc_scheme_struct(self) -> TPMT_ECC_SCHEME {
let scheme = self.scheme_id();
let details = self.get_details();
TPMT_ECC_SCHEME { scheme, details }
}
pub fn is_signing(self) -> bool {
match self {
AsymSchemeUnion::ECDH(_)
| AsymSchemeUnion::ECMQV(_)
| AsymSchemeUnion::RSAOAEP(_)
| AsymSchemeUnion::RSAES => false,
AsymSchemeUnion::RSASSA(_)
| AsymSchemeUnion::RSAPSS(_)
| AsymSchemeUnion::ECDSA(_)
| AsymSchemeUnion::ECDAA(_, _)
| AsymSchemeUnion::SM2(_)
| AsymSchemeUnion::ECSchnorr(_)
| AsymSchemeUnion::AnySig(_) => true,
}
}
pub fn is_decryption(self) -> bool {
match self {
AsymSchemeUnion::ECDH(_)
| AsymSchemeUnion::ECMQV(_)
| AsymSchemeUnion::RSAOAEP(_)
| AsymSchemeUnion::RSAES => true,
AsymSchemeUnion::RSASSA(_)
| AsymSchemeUnion::RSAPSS(_)
| AsymSchemeUnion::ECDSA(_)
| AsymSchemeUnion::ECDAA(_, _)
| AsymSchemeUnion::SM2(_)
| AsymSchemeUnion::ECSchnorr(_)
| AsymSchemeUnion::AnySig(_) => false,
}
}
pub fn is_rsa(self) -> bool {
match self {
AsymSchemeUnion::RSASSA(_)
| AsymSchemeUnion::RSAOAEP(_)
| AsymSchemeUnion::RSAPSS(_)
| AsymSchemeUnion::AnySig(_)
| AsymSchemeUnion::RSAES => true,
AsymSchemeUnion::ECDH(_)
| AsymSchemeUnion::ECMQV(_)
| AsymSchemeUnion::ECDSA(_)
| AsymSchemeUnion::ECDAA(_, _)
| AsymSchemeUnion::SM2(_)
| AsymSchemeUnion::ECSchnorr(_) => false,
}
}
pub fn is_ecc(self) -> bool {
match self {
AsymSchemeUnion::RSASSA(_)
| AsymSchemeUnion::RSAOAEP(_)
| AsymSchemeUnion::RSAPSS(_)
| AsymSchemeUnion::AnySig(_)
| AsymSchemeUnion::RSAES => false,
AsymSchemeUnion::ECDH(_)
| AsymSchemeUnion::ECMQV(_)
| AsymSchemeUnion::ECDSA(_)
| AsymSchemeUnion::ECDAA(_, _)
| AsymSchemeUnion::SM2(_)
| AsymSchemeUnion::ECSchnorr(_) => true,
}
}
}
#[derive(Debug)]
pub struct Signature {
pub scheme: AsymSchemeUnion,
pub signature: SignatureData,
}
#[derive(Debug, PartialEq, Zeroize)]
#[zeroize(drop)]
pub enum SignatureData {
RsaSignature(Vec<u8>),
EcdsaSignature { r: Vec<u8>, s: Vec<u8> },
}
impl Signature {
pub unsafe fn try_from(tss_signature: TPMT_SIGNATURE) -> Result<Self> {
match tss_signature.sigAlg {
TPM2_ALG_RSASSA => {
let hash_alg = tss_signature.signature.rsassa.hash;
let scheme = AsymSchemeUnion::RSASSA(hash_alg.try_into()?);
let signature_buf = tss_signature.signature.rsassa.sig;
let mut signature = signature_buf.buffer.to_vec();
let buf_size = signature_buf.size.into();
if buf_size > signature.len() {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
signature.truncate(buf_size);
Ok(Signature {
scheme,
signature: SignatureData::RsaSignature(signature),
})
}
TPM2_ALG_RSAPSS => {
let hash_alg = tss_signature.signature.rsapss.hash;
let scheme = AsymSchemeUnion::RSAPSS(hash_alg.try_into()?);
let signature_buf = tss_signature.signature.rsassa.sig;
let mut signature = signature_buf.buffer.to_vec();
let buf_size = signature_buf.size.into();
if buf_size > signature.len() {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
signature.truncate(buf_size);
Ok(Signature {
scheme,
signature: SignatureData::RsaSignature(signature),
})
}
TPM2_ALG_ECDSA => {
let hash_alg = tss_signature.signature.ecdsa.hash;
let scheme = AsymSchemeUnion::ECDSA(hash_alg.try_into()?);
let buf = tss_signature.signature.ecdsa.signatureR;
let mut r = buf.buffer.to_vec();
let buf_size = buf.size.into();
if buf_size > r.len() {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
r.truncate(buf_size);
let buf = tss_signature.signature.ecdsa.signatureS;
let mut s = buf.buffer.to_vec();
let buf_size = buf.size.into();
if buf_size > s.len() {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
s.truncate(buf_size);
Ok(Signature {
scheme,
signature: SignatureData::EcdsaSignature { r, s },
})
}
TPM2_ALG_SM2 | TPM2_ALG_ECSCHNORR | TPM2_ALG_ECDAA => {
Err(Error::local_error(WrapperErrorKind::UnsupportedParam))
}
_ => Err(Error::local_error(WrapperErrorKind::InconsistentParams)),
}
}
}
impl TryFrom<Signature> for TPMT_SIGNATURE {
type Error = Error;
fn try_from(sig: Signature) -> Result<Self> {
if sig.scheme.is_decryption() {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
match sig.scheme {
AsymSchemeUnion::RSASSA(hash_alg) => {
let signature = match sig.signature {
SignatureData::RsaSignature(signature) => signature,
SignatureData::EcdsaSignature { .. } => {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams))
}
};
let len = signature.len();
if len > 512 {
return Err(Error::local_error(WrapperErrorKind::WrongParamSize));
}
let mut buffer = [0_u8; 512];
buffer[..len].clone_from_slice(&signature[..len]);
Ok(TPMT_SIGNATURE {
sigAlg: TPM2_ALG_RSASSA,
signature: TPMU_SIGNATURE {
rsassa: TPMS_SIGNATURE_RSA {
hash: hash_alg.into(),
sig: TPM2B_PUBLIC_KEY_RSA {
size: len.try_into().expect("Failed to convert length to u16"),
buffer,
},
},
},
})
}
AsymSchemeUnion::RSAPSS(hash_alg) => {
let signature = match sig.signature {
SignatureData::RsaSignature(signature) => signature,
SignatureData::EcdsaSignature { .. } => {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams))
}
};
let len = signature.len();
if len > 512 {
return Err(Error::local_error(WrapperErrorKind::WrongParamSize));
}
let mut buffer = [0_u8; 512];
buffer[..len].clone_from_slice(&signature[..len]);
Ok(TPMT_SIGNATURE {
sigAlg: TPM2_ALG_RSAPSS,
signature: TPMU_SIGNATURE {
rsapss: TPMS_SIGNATURE_RSA {
hash: hash_alg.into(),
sig: TPM2B_PUBLIC_KEY_RSA {
size: len.try_into().expect("Failed to convert length to u16"),
buffer,
},
},
},
})
}
AsymSchemeUnion::ECDSA(hash_alg) => {
let signature = match sig.signature {
SignatureData::EcdsaSignature { r, s } => (r, s),
SignatureData::RsaSignature(_) => {
return Err(Error::local_error(WrapperErrorKind::InconsistentParams))
}
};
let r_len = signature.0.len();
if r_len > 128 {
return Err(Error::local_error(WrapperErrorKind::WrongParamSize));
}
let mut r_buffer = [0_u8; 128];
r_buffer[..r_len].clone_from_slice(&signature.0[..r_len]);
let s_len = signature.1.len();
if s_len > 128 {
return Err(Error::local_error(WrapperErrorKind::WrongParamSize));
}
let mut s_buffer = [0_u8; 128];
s_buffer[..s_len].clone_from_slice(&signature.1[..s_len]);
Ok(TPMT_SIGNATURE {
sigAlg: TPM2_ALG_ECDSA,
signature: TPMU_SIGNATURE {
ecdsa: TPMS_SIGNATURE_ECDSA {
hash: hash_alg.into(),
signatureR: TPM2B_ECC_PARAMETER {
size: r_len.try_into().expect("Failed to convert length to u16"),
buffer: r_buffer,
},
signatureS: TPM2B_ECC_PARAMETER {
size: s_len.try_into().expect("Failed to convert length to u16"),
buffer: s_buffer,
},
},
},
})
}
_ => Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
}
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct TpmaSession {
flags: TPMA_SESSION,
mask: TPMA_SESSION,
}
impl TpmaSession {
pub fn clone_with_new_mask(self, new_mask: TPMA_SESSION) -> TpmaSession {
TpmaSession {
flags: self.flags,
mask: new_mask,
}
}
pub fn mask(self) -> TPMA_SESSION {
self.mask
}
pub fn flags(self) -> TPMA_SESSION {
self.flags
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct TpmaSessionBuilder {
flags: TPMA_SESSION,
mask: Option<TPMA_SESSION>,
}
impl TpmaSessionBuilder {
pub fn new() -> TpmaSessionBuilder {
TpmaSessionBuilder {
flags: 0,
mask: None,
}
}
pub fn with_flag(mut self, flag: TPMA_SESSION) -> Self {
self.flags |= flag;
self
}
pub fn with_mask(mut self, mask: TPMA_SESSION) -> Self {
self.mask = Some(self.mask.unwrap_or(0) | mask);
self
}
pub fn build(self) -> TpmaSession {
TpmaSession {
flags: self.flags,
mask: self.mask.unwrap_or(self.flags),
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Zeroize)]
#[zeroize(drop)]
pub struct TpmsContext {
sequence: u64,
saved_handle: TPMI_DH_CONTEXT,
hierarchy: TPMI_RH_HIERARCHY,
context_blob: Vec<u8>,
}
impl TryFrom<TPMS_CONTEXT> for TpmsContext {
type Error = Error;
fn try_from(tss2_context: TPMS_CONTEXT) -> Result<Self> {
let mut context = TpmsContext {
sequence: tss2_context.sequence,
saved_handle: tss2_context.savedHandle,
hierarchy: tss2_context.hierarchy,
context_blob: tss2_context.contextBlob.buffer.to_vec(),
};
context.context_blob.truncate(
tss2_context
.contextBlob
.size
.try_into()
.map_err(|_| Error::local_error(WrapperErrorKind::WrongParamSize))?,
);
Ok(context)
}
}
#[allow(clippy::needless_update)]
impl TryFrom<TpmsContext> for TPMS_CONTEXT {
type Error = Error;
fn try_from(context: TpmsContext) -> Result<Self> {
let buffer_size = context.context_blob.len();
if buffer_size > 5188 {
return Err(Error::local_error(WrapperErrorKind::WrongParamSize));
}
let mut buffer = [0_u8; 5188];
for (i, val) in context.context_blob.iter().enumerate() {
buffer[i] = *val;
}
Ok(TPMS_CONTEXT {
sequence: context.sequence,
savedHandle: context.saved_handle,
hierarchy: context.hierarchy,
contextBlob: TPM2B_CONTEXT_DATA {
size: buffer_size.try_into().unwrap(),
buffer,
},
..Default::default()
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Hierarchy {
Null,
Owner,
Platform,
Endorsement,
}
impl Hierarchy {
pub fn esys_rh(self) -> TPMI_RH_HIERARCHY {
match self {
Hierarchy::Null => ESYS_TR_RH_NULL,
Hierarchy::Owner => ESYS_TR_RH_OWNER,
Hierarchy::Platform => ESYS_TR_RH_PLATFORM,
Hierarchy::Endorsement => ESYS_TR_RH_ENDORSEMENT,
}
}
pub fn rh(self) -> TPM2_RH {
match self {
Hierarchy::Null => TPM2_RH_NULL,
Hierarchy::Owner => TPM2_RH_OWNER,
Hierarchy::Platform => TPM2_RH_PLATFORM,
Hierarchy::Endorsement => TPM2_RH_ENDORSEMENT,
}
}
}
impl TryFrom<TPM2_HANDLE> for Hierarchy {
type Error = Error;
fn try_from(handle: TPM2_HANDLE) -> Result<Self> {
match handle {
TPM2_RH_NULL | ESYS_TR_RH_NULL => Ok(Hierarchy::Null),
TPM2_RH_OWNER | ESYS_TR_RH_OWNER => Ok(Hierarchy::Owner),
TPM2_RH_PLATFORM | ESYS_TR_RH_PLATFORM => Ok(Hierarchy::Platform),
TPM2_RH_ENDORSEMENT | ESYS_TR_RH_ENDORSEMENT => Ok(Hierarchy::Endorsement),
_ => Err(Error::local_error(WrapperErrorKind::InconsistentParams)),
}
}
}
pub fn create_restricted_decryption_rsa_public(
symmetric: Cipher,
key_bits: u16,
pub_exponent: u32,
) -> Result<TPM2B_PUBLIC> {
let rsa_parms = TpmsRsaParmsBuilder::new_restricted_decryption_key(
symmetric.into(),
key_bits,
pub_exponent,
)
.build()?;
let mut object_attributes = ObjectAttributes(0);
object_attributes.set_fixed_tpm(true);
object_attributes.set_fixed_parent(true);
object_attributes.set_sensitive_data_origin(true);
object_attributes.set_user_with_auth(true);
object_attributes.set_decrypt(true);
object_attributes.set_sign_encrypt(false);
object_attributes.set_restricted(true);
Tpm2BPublicBuilder::new()
.with_type(TPM2_ALG_RSA)
.with_name_alg(TPM2_ALG_SHA256)
.with_object_attributes(object_attributes)
.with_parms(PublicParmsUnion::RsaDetail(rsa_parms))
.build()
}
pub fn create_unrestricted_encryption_decryption_rsa_public(
key_bits: u16,
pub_exponent: u32,
) -> Result<TPM2B_PUBLIC> {
let rsa_parms = TpmsRsaParmsBuilder {
symmetric: None,
scheme: Some(AsymSchemeUnion::AnySig(None)),
key_bits,
exponent: pub_exponent,
for_signing: true,
for_decryption: true,
restricted: false,
}
.build()
.unwrap();
let mut object_attributes = ObjectAttributes(0);
object_attributes.set_fixed_tpm(true);
object_attributes.set_fixed_parent(true);
object_attributes.set_sensitive_data_origin(true);
object_attributes.set_user_with_auth(true);
object_attributes.set_decrypt(true);
object_attributes.set_sign_encrypt(true);
object_attributes.set_restricted(false);
Tpm2BPublicBuilder::new()
.with_type(TPM2_ALG_RSA)
.with_name_alg(TPM2_ALG_SHA256)
.with_object_attributes(object_attributes)
.with_parms(PublicParmsUnion::RsaDetail(rsa_parms))
.build()
}
pub fn create_unrestricted_signing_rsa_public(
scheme: AsymSchemeUnion,
key_bits: u16,
pub_exponent: u32,
) -> Result<TPM2B_PUBLIC> {
let rsa_parms =
TpmsRsaParmsBuilder::new_unrestricted_signing_key(scheme, key_bits, pub_exponent)
.build()?;
let mut object_attributes = ObjectAttributes(0);
object_attributes.set_fixed_tpm(true);
object_attributes.set_fixed_parent(true);
object_attributes.set_sensitive_data_origin(true);
object_attributes.set_user_with_auth(true);
object_attributes.set_decrypt(false);
object_attributes.set_sign_encrypt(true);
object_attributes.set_restricted(false);
Tpm2BPublicBuilder::new()
.with_type(TPM2_ALG_RSA)
.with_name_alg(TPM2_ALG_SHA256)
.with_object_attributes(object_attributes)
.with_parms(PublicParmsUnion::RsaDetail(rsa_parms))
.build()
}
pub fn create_unrestricted_signing_ecc_public(
scheme: AsymSchemeUnion,
curve: EllipticCurve,
) -> Result<TPM2B_PUBLIC> {
let ecc_parms = TpmsEccParmsBuilder::new_unrestricted_signing_key(scheme, curve).build()?;
let mut object_attributes = ObjectAttributes(0);
object_attributes.set_fixed_tpm(true);
object_attributes.set_fixed_parent(true);
object_attributes.set_sensitive_data_origin(true);
object_attributes.set_user_with_auth(true);
object_attributes.set_decrypt(false);
object_attributes.set_sign_encrypt(true);
object_attributes.set_restricted(false);
Tpm2BPublicBuilder::new()
.with_type(TPM2_ALG_ECC)
.with_name_alg(TPM2_ALG_SHA256)
.with_object_attributes(object_attributes)
.with_parms(PublicParmsUnion::EccDetail(ecc_parms))
.build()
}
#[derive(Debug, Clone)]
pub enum PublicKey {
Rsa(Vec<u8>),
Ecc { x: Vec<u8>, y: Vec<u8> },
}
type PcrValue = Digest;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PcrBank {
bank: BTreeMap<PcrSlot, PcrValue>,
}
impl PcrBank {
pub fn pcr_value(&self, pcr_slot: PcrSlot) -> Option<&PcrValue> {
self.bank.get(&pcr_slot)
}
pub fn len(&self) -> usize {
self.bank.len()
}
pub fn is_empty(&self) -> bool {
self.bank.is_empty()
}
}
impl<'a> IntoIterator for &'a PcrBank {
type Item = (&'a PcrSlot, &'a PcrValue);
type IntoIter = ::std::collections::btree_map::Iter<'a, PcrSlot, PcrValue>;
fn into_iter(self) -> Self::IntoIter {
self.bank.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PcrData {
data: HashMap<HashingAlgorithm, PcrBank>,
}
impl PcrData {
pub fn new(
tpml_pcr_selections: &TPML_PCR_SELECTION,
tpml_digests: &TPML_DIGEST,
) -> Result<Self> {
let digests_count = tpml_digests.count as usize;
if digests_count > 8 {
error!("Error: Invalid TPML_DIGEST count(> 8)");
return Err(Error::local_error(WrapperErrorKind::InvalidParam));
}
let digests = &tpml_digests.digests[..digests_count];
let selections_count = tpml_pcr_selections.count as usize;
if selections_count > 16 {
error!("Error: Invalid TPML_SELECTIONS count(> 16)");
return Err(Error::local_error(WrapperErrorKind::InvalidParam));
}
let pcr_selections = &tpml_pcr_selections.pcrSelections[..selections_count];
let mut digest_iter = digests.iter();
let mut parsed_pcr_data = PcrData {
data: Default::default(),
};
for &pcr_selection in pcr_selections {
let parsed_hash_algorithm =
HashingAlgorithm::try_from(pcr_selection.hash).map_err(|e| {
error!("Error converting hash to a HashingAlgorithm: {}.", e);
Error::local_error(WrapperErrorKind::InvalidParam)
})?;
let parsed_pcr_slots: BitFlags<PcrSlot> =
BitFlags::<PcrSlot>::try_from(u32::from_le_bytes(pcr_selection.pcrSelect))
.map_err(|e| {
error!("Error parsing pcrSelect to a BitFlags<PcrSlot>: {}.", e);
Error::local_error(WrapperErrorKind::UnsupportedParam)
})?;
let mut parsed_pcr_bank = PcrBank {
bank: Default::default(),
};
for pcr_slot in parsed_pcr_slots.iter() {
let digest = match digest_iter.next() {
Some(val) => val,
None => {
error!("Error number of items in selection does not match number of items in data.");
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
};
if parsed_pcr_bank
.bank
.insert(pcr_slot, PcrValue::try_from(*digest)?)
.is_some()
{
error!("Error trying to insert data into PcrSlot where data have already been inserted.");
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
}
if parsed_pcr_data
.data
.insert(parsed_hash_algorithm, parsed_pcr_bank)
.is_some()
{
error!("Error trying to insert data into a PcrBank where data have already been inserted.");
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
}
if digest_iter.next().is_some() {
error!("Error not all values in the digest have been handled.");
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
Ok(parsed_pcr_data)
}
pub fn pcr_bank(&self, hashing_algorithm: HashingAlgorithm) -> Option<&PcrBank> {
self.data.get(&hashing_algorithm)
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
impl<'a> IntoIterator for &'a PcrData {
type Item = (&'a HashingAlgorithm, &'a PcrBank);
type IntoIter = ::std::collections::hash_map::Iter<'a, HashingAlgorithm, PcrBank>;
fn into_iter(self) -> Self::IntoIter {
self.data.iter()
}
}
impl From<PcrData> for TPML_DIGEST {
fn from(pcr_data: PcrData) -> Self {
let mut tpml_digest: TPML_DIGEST = Default::default();
for (_hash_algo, pcr_bank) in pcr_data.into_iter() {
for (_pcr_slot, pcr_value) in pcr_bank.into_iter() {
let i = tpml_digest.count as usize;
let size = pcr_value.value().len() as u16;
tpml_digest.digests[i].size = size;
tpml_digest.digests[i].buffer[..size as usize].copy_from_slice(pcr_value.value());
tpml_digest.count += 1;
}
}
tpml_digest
}
}