use crate::error::Error;
pub struct Device {
dev: Box<wolftpm_sys::WOLFTPM2_DEV>,
}
unsafe impl Send for Device {}
impl Device {
pub fn open() -> Result<Self, Error> {
let mut dev = Box::new(unsafe { std::mem::zeroed::<wolftpm_sys::WOLFTPM2_DEV>() });
let rc = unsafe {
wolftpm_sys::wolfTPM2_Init(dev.as_mut() as *mut _, None, std::ptr::null_mut())
};
Error::check(rc)?;
Ok(Self { dev })
}
#[cfg(feature = "swtpm")]
pub fn open_swtpm(host: &str, port: u16) -> Result<Self, Error> {
use std::ffi::CString;
let host_c =
CString::new(host).map_err(|_| Error::InvalidInput("host contains null byte"))?;
let port_c = CString::new(port.to_string())
.map_err(|_| Error::InvalidInput("port string invalid"))?;
let dev = wolftpm_sys::swtpm::init_swtpm(&host_c, &port_c)
.map_err(|e| match e {
wolftpm_sys::swtpm::InitError::Env => {
Error::InvalidInput("setenv failed (ENOMEM?); cannot set swtpm connection vars")
}
wolftpm_sys::swtpm::InitError::WolfTpm(rc) => {
Error::Tpm { rc: crate::error::TpmRc::from_raw(rc as u32) }
}
})?;
Ok(Self { dev })
}
pub(crate) fn dev_ptr_mut(&mut self) -> *mut wolftpm_sys::WOLFTPM2_DEV {
self.dev.as_mut() as *mut _
}
pub fn with_ecc_key<F, T, E>(&mut self, f: F) -> Result<T, E>
where
F: FnOnce(&mut crate::key::EccKey<'_>) -> Result<T, E>,
E: From<Error>,
{
let mut key = crate::key::EccKey::create(self).map_err(E::from)?;
let result = f(&mut key);
result
}
pub fn get_random(&mut self, n: usize) -> Result<Vec<u8>, Error> {
let len =
u32::try_from(n).map_err(|_| Error::InvalidInput("requested length exceeds u32"))?;
let mut buf = vec![0u8; n];
let rc =
unsafe { wolftpm_sys::wolfTPM2_GetRandom(self.dev_ptr_mut(), buf.as_mut_ptr(), len) };
Error::check(rc)?;
Ok(buf)
}
pub fn pcr_read(&mut self, index: u8) -> Result<[u8; 32], Error> {
if index > 23 {
return Err(Error::InvalidPcrIndex(index));
}
let mut digest = [0u8; 64];
let mut digest_len: std::ffi::c_int = 64;
let rc = unsafe {
wolftpm_sys::wolfTPM2_ReadPCR(
self.dev_ptr_mut(),
std::ffi::c_int::from(index),
wolftpm_sys::TPM_ALG_ID_T_TPM_ALG_SHA256 as std::ffi::c_int,
digest.as_mut_ptr(),
&mut digest_len as *mut _,
)
};
Error::check(rc)?;
if digest_len != 32 {
return Err(Error::UnexpectedResponse);
}
let mut out = [0u8; 32];
out.copy_from_slice(&digest[..32]);
Ok(out)
}
}
impl core::fmt::Debug for Device {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Device")
.field("dev", &format_args!("{:p}", self.dev.as_ref()))
.finish()
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe {
wolftpm_sys::wolfTPM2_Cleanup(self.dev_ptr_mut());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore = "requires /dev/tpm0"]
fn open_and_get_random() {
let mut dev = Device::open().expect("open");
let rand = dev.get_random(32).expect("get_random");
assert_eq!(rand.len(), 32);
}
#[test]
#[ignore = "requires /dev/tpm0"]
fn pcr_read_bank0() {
let mut dev = Device::open().expect("open");
let pcr = dev.pcr_read(0).expect("pcr_read");
assert_eq!(pcr.len(), 32);
}
}