use crate::id::AttestAgent;
use crate::tpm::{create_identity_provider, HardwareIdentity};
use anyhow::Result;
use thiserror::Error;
use zeroize::Zeroize;
#[derive(Debug, Error)]
pub enum HardwareError {
#[error("No TPM found or initialization failed: {0}")]
NoTpmFound(String),
#[error("Hardware operation failed: {0}")]
OperationFailed(String),
}
pub struct HardwareKeystore {
provider: Box<dyn HardwareIdentity>,
}
impl std::fmt::Debug for HardwareKeystore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HardwareKeystore").finish_non_exhaustive()
}
}
impl HardwareKeystore {
pub async fn new() -> Result<Self, HardwareError> {
let dev_mode = std::env::var("VEX_DEV_MODE")
.map(|v| v == "1")
.unwrap_or(false);
let allow_fallback = std::env::var("VEX_HARDWARE_ATTESTATION")
.map(|v| v != "true")
.unwrap_or(true);
if dev_mode && allow_fallback {
return Ok(Self {
provider: Box::new(crate::tpm::StubIdentity::default()),
});
}
let provider = create_identity_provider(allow_fallback);
Ok(Self { provider })
}
pub async fn seal_identity(&self, seed: &[u8; 32]) -> Result<Vec<u8>, HardwareError> {
self.provider
.seal("identity_seed", seed)
.await
.map_err(|e| HardwareError::OperationFailed(e.to_string()))
}
pub async fn get_identity(
&self,
encrypted_blob: &[u8],
) -> Result<AgentIdentity, HardwareError> {
let seed = self
.provider
.unseal(encrypted_blob)
.await
.map_err(|e| HardwareError::OperationFailed(e.to_string()))?;
let mut seed_array: [u8; 32] = seed
.try_into()
.map_err(|_| HardwareError::OperationFailed("Unsealed seed is not 32 bytes".into()))?;
let agent = crate::id::AttestAgent::from_seed(seed_array);
seed_array.zeroize();
Ok(AgentIdentity {
agent_id: agent.to_vex_uuid().to_string(),
hardware_id: agent.id.clone(),
inner: agent,
})
}
}
#[derive(Debug, Clone)]
pub struct AgentIdentity {
pub agent_id: String,
pub hardware_id: String,
inner: AttestAgent,
}
impl Default for AgentIdentity {
fn default() -> Self {
let agent = crate::id::AttestAgent::new();
Self {
agent_id: agent.to_vex_uuid().to_string(),
hardware_id: agent.id.clone(),
inner: agent,
}
}
}
impl AgentIdentity {
pub fn new() -> Self {
Self::default()
}
pub fn public_key_hex(&self) -> String {
hex::encode(self.inner.signing_key.verifying_key().to_bytes())
}
pub fn sign(&self, message: &[u8]) -> Vec<u8> {
self.inner.sign(message).to_bytes().to_vec()
}
pub async fn get_pcrs(
&self,
indices: &[u32],
) -> Result<std::collections::HashMap<u32, String>> {
let provider = crate::tpm::create_identity_provider(true);
provider.get_pcrs(indices).await
}
}