#[cfg(feature = "hardware-tpm")]
mod inner {
use crate::{
error::PqRascvError,
measurement::{Measurements, PcrBank, RoT, PCR_COUNT, PCR_SIZE},
};
use sha3::{Digest as _, Sha3_256};
use tss_esapi::{
constants::{CapabilityType, PropertyTag},
interface_types::algorithm::HashingAlgorithm,
structures::{CapabilityData, PcrSelectionListBuilder, PcrSlot},
tcti_ldr::TctiNameConf,
Context,
};
const TPM_PCR_SLOTS: [PcrSlot; 8] = [
PcrSlot::Slot0,
PcrSlot::Slot1,
PcrSlot::Slot2,
PcrSlot::Slot3,
PcrSlot::Slot4,
PcrSlot::Slot5,
PcrSlot::Slot6,
PcrSlot::Slot7,
];
pub struct TpmRoT<'a> {
firmware: &'a [u8],
ai_model: Option<&'a [u8]>,
event_counter_base: u64,
}
impl<'a> TpmRoT<'a> {
#[must_use]
pub fn new(firmware: &'a [u8], ai_model: Option<&'a [u8]>, event_counter_base: u64) -> Self {
Self { firmware, ai_model, event_counter_base }
}
}
impl<'a> RoT for TpmRoT<'a> {
fn measure(&self) -> Result<Measurements, PqRascvError> {
let tcti = TctiNameConf::from_environment_variable()
.map_err(|_| PqRascvError::MeasurementFailed)?;
let mut ctx = Context::new(tcti)
.map_err(|_| PqRascvError::MeasurementFailed)?;
let pcr_selection = PcrSelectionListBuilder::new()
.with_selection(HashingAlgorithm::Sha256, &TPM_PCR_SLOTS)
.build()
.map_err(|_| PqRascvError::MeasurementFailed)?;
let (_update_counter, _selection_out, digest_list) = ctx
.execute_without_session(|c| c.pcr_read(pcr_selection))
.map_err(|_| PqRascvError::MeasurementFailed)?;
let mut pcrs = PcrBank::default();
for (i, digest) in digest_list.value().iter().enumerate().take(PCR_COUNT) {
let bytes = digest.value();
if bytes.len() == PCR_SIZE {
pcrs.0[i].copy_from_slice(bytes);
} else {
let offset = PCR_SIZE - bytes.len().min(PCR_SIZE);
pcrs.0[i][offset..].copy_from_slice(&bytes[..bytes.len().min(PCR_SIZE)]);
}
}
let firmware_hash: [u8; 32] = {
let mut h = Sha3_256::new();
h.update(self.firmware);
h.finalize().into()
};
let ai_model_hash: [u8; 32] = match self.ai_model {
Some(model) => {
let mut h = Sha3_256::new();
h.update(model);
h.finalize().into()
}
None => [0u8; 32],
};
let event_counter = ctx
.execute_without_session(|c| {
c.get_capability(
CapabilityType::TpmProperties,
PropertyTag::AuditCounter0.into(),
1,
)
})
.ok()
.and_then(|(cap, _more_data)| {
if let CapabilityData::TpmProperties(props) = cap {
props.find(PropertyTag::AuditCounter0).map(|p| u64::from(p.value()))
} else {
None
}
})
.unwrap_or(0)
.wrapping_add(self.event_counter_base);
Ok(Measurements { pcrs, firmware_hash, ai_model_hash, event_counter })
}
}
}
#[cfg(feature = "hardware-tpm")]
pub use inner::TpmRoT;
#[cfg(not(feature = "hardware-tpm"))]
pub struct TpmRoT;
#[cfg(not(feature = "hardware-tpm"))]
impl crate::measurement::RoT for TpmRoT {
fn measure(&self) -> Result<crate::measurement::Measurements, crate::error::PqRascvError> {
Err(crate::error::PqRascvError::BackendUnavailable)
}
}