use alloc::vec::Vec;
#[cfg(feature = "pem")]
use alloc::string::String;
use hex::ToHex;
use rasn::prelude::*;
pub const OID_TPMKEY_LOADABLE: &Oid = Oid::const_new(&[2, 23, 133, 10, 1, 3]);
pub const OID_TPMKEY_IMPORTABLE: &Oid = Oid::const_new(&[2, 23, 133, 10, 1, 4]);
pub const OID_TPMKEY_SEALED: &Oid = Oid::const_new(&[2, 23, 133, 10, 1, 5]);
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct TPMKeyType(ObjectIdentifier);
impl TPMKeyType {
pub fn new_loadable() -> Self {
Self(ObjectIdentifier::from(OID_TPMKEY_LOADABLE))
}
pub fn new_importable() -> Self {
Self(ObjectIdentifier::from(OID_TPMKEY_IMPORTABLE))
}
pub fn new_sealed() -> Self {
Self(ObjectIdentifier::from(OID_TPMKEY_SEALED))
}
pub fn validate(&self) -> bool {
self.0 == OID_TPMKEY_LOADABLE
|| self.0 == OID_TPMKEY_IMPORTABLE
|| self.0 == OID_TPMKEY_SEALED
}
}
impl AsRef<ObjectIdentifier> for TPMKeyType {
fn as_ref(&self) -> &ObjectIdentifier {
&self.0
}
}
impl core::fmt::Display for TPMKeyType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let typestr = if self.0 == OID_TPMKEY_LOADABLE {
"Loadable Key"
} else if self.0 == OID_TPMKEY_IMPORTABLE {
"Importable Key"
} else if self.0 == OID_TPMKEY_SEALED {
"Sealed Data"
} else {
"Unknown"
};
write!(f, "{typestr} (OID:{})", &self.0)
}
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
pub struct TPMPolicy {
#[rasn(tag(explicit(0)), default)]
pub command_code: u32,
#[rasn(tag(explicit(1)), default)]
pub command_policy: OctetString,
}
impl core::fmt::Display for TPMPolicy {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"TPMPolicy {{ command_code: 0x{:08X}, command_policy: {} bytes }}",
self.command_code,
self.command_policy.len()
)
}
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
pub struct TPMAuthPolicy {
#[rasn(tag(explicit(0)), default)]
pub name: Option<Utf8String>,
#[rasn(tag(explicit(1)), default)]
pub policy: SequenceOf<TPMPolicy>,
}
impl core::fmt::Display for TPMAuthPolicy {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "TPMAuthPolicy {{")?;
if let Some(name) = &self.name {
write!(f, " name: \"{}\",", name)?;
}
write!(f, " policy: {} entries }}", self.policy.len())
}
}
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, Hash)]
pub struct TPMKey {
pub r#type: TPMKeyType,
#[rasn(tag(explicit(0)), default)]
pub empty_auth: Option<bool>,
#[rasn(tag(explicit(1)), default)]
pub policy: Option<SequenceOf<TPMPolicy>>,
#[rasn(tag(explicit(2)), default)]
pub secret: Option<OctetString>,
#[rasn(tag(explicit(3)), default)]
pub auth_policy: Option<SequenceOf<TPMAuthPolicy>>,
#[rasn(tag(explicit(4)), default)]
pub description: Option<Utf8String>,
#[rasn(tag(explicit(5)), default)]
pub rsa_parent: Option<bool>,
pub parent: u32,
pub pubkey: OctetString,
pub privkey: OctetString,
}
impl core::fmt::Display for TPMKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f, "TPMKey {{")?;
writeln!(f, " type: \"{}\"", self.r#type)?;
if let Some(empty_auth) = &self.empty_auth {
writeln!(f, " empty_auth: \"{}\"", empty_auth)?;
}
if let Some(policy) = &self.policy {
writeln!(f, " policy: {} entries", policy.len())?;
}
if let Some(secret) = &self.secret {
writeln!(f, " secret: {} bytes", secret.len())?;
}
if let Some(auth_policy) = &self.auth_policy {
writeln!(f, " auth_policy: {} entries", auth_policy.len())?;
}
if let Some(description) = &self.description {
writeln!(f, " description: \"{}\"", description)?;
}
if let Some(rsa_parent) = &self.rsa_parent {
writeln!(f, " rsa_parent: \"{}\"", rsa_parent)?;
}
writeln!(f, " parent: \"0x{:08X}\"", self.parent)?;
write!(f, " pubkey: \"")?;
for b in self.pubkey.iter() {
write!(f, "{:02X}", b)?;
}
writeln!(f, "\"")?;
write!(f, " privkey: \"")?;
for b in self.privkey.iter() {
write!(f, "{:02X}", b)?;
}
writeln!(f, "\"")?;
write!(f, "}}")
}
}
#[cfg(feature = "pem")]
impl TPMKey {
pub fn try_from_pem(pem: &[u8]) -> Result<TPMKey, crate::Error> {
let der = crate::checked_pem_to_der(pem)?;
TPMKey::try_from_der(&der)
}
pub fn try_to_pem(&self) -> Result<String, crate::Error> {
let der = self.try_to_der()?;
let pem =
pem_rfc7468::encode_string("TSS2 PRIVATE KEY", pem_rfc7468::LineEnding::LF, &der)?;
Ok(pem)
}
}
impl TPMKey {
pub fn try_from_der(der: &[u8]) -> Result<TPMKey, crate::Error> {
let tpmkey: TPMKey = rasn::der::decode(der)?;
tpmkey.validate()?;
Ok(tpmkey)
}
pub fn try_to_der(&self) -> Result<Vec<u8>, crate::Error> {
Ok(rasn::der::encode(self)?)
}
pub fn new_loadable(parent: u32, pubkey: &[u8], privkey: &[u8]) -> Self {
Self {
r#type: TPMKeyType::new_loadable(),
parent,
pubkey: pubkey.into(),
privkey: privkey.into(),
empty_auth: None,
policy: None,
secret: None,
auth_policy: None,
description: None,
rsa_parent: None,
}
}
pub fn new_importable(parent: u32, pubkey: &[u8], privkey: &[u8]) -> Self {
Self {
r#type: TPMKeyType::new_importable(),
parent,
pubkey: pubkey.into(),
privkey: privkey.into(),
empty_auth: None,
policy: None,
secret: None,
auth_policy: None,
description: None,
rsa_parent: None,
}
}
pub fn new_sealed(parent: u32, pubkey: &[u8], privkey: &[u8]) -> Self {
Self {
r#type: TPMKeyType::new_sealed(),
parent,
pubkey: pubkey.into(),
privkey: privkey.into(),
empty_auth: None,
policy: None,
secret: None,
auth_policy: None,
description: None,
rsa_parent: None,
}
}
pub fn is_loadable(&self) -> bool {
self.r#type.0 == OID_TPMKEY_LOADABLE
}
pub fn is_importable(&self) -> bool {
self.r#type.0 == OID_TPMKEY_IMPORTABLE
}
pub fn is_sealed(&self) -> bool {
self.r#type.0 == OID_TPMKEY_SEALED
}
pub fn requires_auth(&self) -> bool {
!self.empty_auth.unwrap_or(false)
}
pub fn validate(&self) -> Result<(), crate::Error> {
if !self.r#type.validate() {
return Err(crate::Error::NoTss2PrivateKey);
};
let mso = (self.parent >> 24) & 0xFF;
if mso != 0x40 && mso != 0x81 && mso != 0x80 {
return Err(crate::Error::InvalidParentHandle(
self.parent.to_be_bytes().encode_hex(),
));
};
if self.is_loadable() && self.secret.is_some() {
return Err(crate::Error::UnexpectedSecret);
}
if self.is_importable() && self.secret.is_none() {
return Err(crate::Error::MissingSecret);
}
Ok(())
}
}