use crate::attributes::{ObjectAttributes, ObjectAttributesBuilder};
use crate::constants::{tss::*, PropertyTag};
use crate::interface_types::{algorithm::HashingAlgorithm, ecc::EccCurve};
use crate::structures::{
Digest, KeyedHashParameters, PcrSlot, SymmetricCipherParameters, SymmetricDefinitionObject,
};
use crate::tss2_esys::*;
use crate::{Context, Error, Result, WrapperErrorKind};
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,
},
})
}
Some(TPM2_ALG_KEYEDHASH) => {
let parameters;
let unique;
if let Some(PublicParmsUnion::KeyedHashDetail(parms)) = self.parameters {
parameters = TPMU_PUBLIC_PARMS {
keyedHashDetail: parms.into(),
};
} 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::KeyedHash(hash_unique)) = self.unique {
unique = TPMU_PUBLIC_ID {
keyedHash: hash_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(|| TPMT_SYM_DEF_OBJECT {
algorithm: TPM2_ALG_NULL,
..Default::default()
});
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<crate::abstraction::cipher::Cipher>,
pub scheme: AsymSchemeUnion,
pub curve: EccCurve,
pub for_signing: bool,
pub for_decryption: bool,
pub restricted: bool,
}
impl TpmsEccParmsBuilder {
pub fn new_restricted_decryption_key(
symmetric: crate::abstraction::cipher::Cipher,
curve: EccCurve,
) -> 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: EccCurve) -> 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 == EccCurve::BnP256 || self.curve == EccCurve::BnP638)
&& scheme.scheme != TPM2_ALG_ECDAA
{
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
}
let symmetric = match self.symmetric {
Some(symmetric) => symmetric.try_into()?,
None => SymmetricDefinitionObject::Null,
};
Ok(TPMS_ECC_PARMS {
symmetric: symmetric.into(),
scheme,
curveID: self.curve.into(),
kdf: TPMT_KDF_SCHEME {
scheme: TPM2_ALG_NULL,
details: Default::default(),
},
})
}
}
#[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(KeyedHashParameters),
SymDetail(crate::abstraction::cipher::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 TryFrom<PublicParmsUnion> for TPMU_PUBLIC_PARMS {
type Error = Error;
fn try_from(parms: PublicParmsUnion) -> Result<Self> {
match parms {
PublicParmsUnion::AsymDetail(tss_parms) => Ok(TPMU_PUBLIC_PARMS {
asymDetail: tss_parms,
}),
PublicParmsUnion::EccDetail(tss_parms) => Ok(TPMU_PUBLIC_PARMS {
eccDetail: tss_parms,
}),
PublicParmsUnion::RsaDetail(tss_parms) => Ok(TPMU_PUBLIC_PARMS {
rsaDetail: tss_parms,
}),
PublicParmsUnion::SymDetail(cipher) => Ok(TPMU_PUBLIC_PARMS {
symDetail: SymmetricCipherParameters::try_from(cipher)?.into(),
}),
PublicParmsUnion::KeyedHashDetail(tss_parms) => Ok(TPMU_PUBLIC_PARMS {
keyedHashDetail: tss_parms.into(),
}),
}
}
}
#[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(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()
})
}
}
pub fn create_restricted_decryption_rsa_public(
symmetric: crate::abstraction::cipher::Cipher,
key_bits: u16,
pub_exponent: u32,
) -> Result<TPM2B_PUBLIC> {
let rsa_parms = TpmsRsaParmsBuilder::new_restricted_decryption_key(
SymmetricDefinitionObject::try_from(symmetric)?.into(),
key_bits,
pub_exponent,
)
.build()?;
let object_attributes = ObjectAttributesBuilder::new()
.with_fixed_tpm(true)
.with_fixed_parent(true)
.with_sensitive_data_origin(true)
.with_user_with_auth(true)
.with_decrypt(true)
.with_sign_encrypt(false)
.with_restricted(true)
.build()?;
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 object_attributes = ObjectAttributesBuilder::new()
.with_fixed_tpm(true)
.with_fixed_parent(true)
.with_sensitive_data_origin(true)
.with_user_with_auth(true)
.with_decrypt(true)
.with_sign_encrypt(true)
.with_restricted(false)
.build()?;
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 object_attributes = ObjectAttributesBuilder::new()
.with_fixed_tpm(true)
.with_fixed_parent(true)
.with_sensitive_data_origin(true)
.with_user_with_auth(true)
.with_decrypt(false)
.with_sign_encrypt(true)
.with_restricted(false)
.build()?;
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: EccCurve,
) -> Result<TPM2B_PUBLIC> {
let ecc_parms = TpmsEccParmsBuilder::new_unrestricted_signing_key(scheme, curve).build()?;
let object_attributes = ObjectAttributesBuilder::new()
.with_fixed_tpm(true)
.with_fixed_parent(true)
.with_sensitive_data_origin(true)
.with_user_with_auth(true)
.with_decrypt(false)
.with_sign_encrypt(true)
.with_restricted(false)
.build()?;
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
}
}
fn tpm_int_to_string(num: u32) -> String {
num.to_be_bytes()
.iter()
.filter(|x| **x != 0)
.map(|x| char::from(*x))
.collect()
}
pub fn get_tpm_vendor(context: &mut Context) -> Result<String> {
Ok([
PropertyTag::VendorString1,
PropertyTag::VendorString2,
PropertyTag::VendorString3,
PropertyTag::VendorString4,
]
.iter()
.map(|propid| context.get_tpm_property(*propid))
.collect::<Result<Vec<Option<u32>>>>()?
.iter()
.filter_map(|x| *x)
.filter(|x| *x != 0)
.map(tpm_int_to_string)
.collect())
}