Skip to main content

vex_hardware/
api.rs

1use crate::id::AttestAgent;
2use crate::tpm::{create_identity_provider, HardwareIdentity};
3use anyhow::Result;
4use thiserror::Error;
5use zeroize::Zeroize;
6
7#[derive(Debug, Error)]
8pub enum HardwareError {
9    #[error("No TPM found or initialization failed: {0}")]
10    NoTpmFound(String),
11    #[error("Hardware operation failed: {0}")]
12    OperationFailed(String),
13}
14
15pub struct HardwareKeystore {
16    provider: Box<dyn HardwareIdentity>,
17}
18
19impl std::fmt::Debug for HardwareKeystore {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        f.debug_struct("HardwareKeystore").finish_non_exhaustive()
22    }
23}
24
25impl HardwareKeystore {
26    /// Initialize connection to the TPM (or CNG on Windows)
27    pub async fn new() -> Result<Self, HardwareError> {
28        let dev_mode = std::env::var("VEX_DEV_MODE")
29            .map(|v| v == "1")
30            .unwrap_or(false);
31        let allow_fallback = std::env::var("VEX_HARDWARE_ATTESTATION")
32            .map(|v| v != "true")
33            .unwrap_or(true);
34
35        // Force stub identity in dev mode if fallback is allowed, bypassing potential TPM malfunctions
36        if dev_mode && allow_fallback {
37            return Ok(Self {
38                provider: Box::new(crate::tpm::StubIdentity::default()),
39            });
40        }
41
42        let provider = create_identity_provider(allow_fallback);
43        Ok(Self { provider })
44    }
45
46    /// Helper to seed identity for external persistence (VEX-persist)
47    pub async fn seal_identity(&self, seed: &[u8; 32]) -> Result<Vec<u8>, HardwareError> {
48        self.provider
49            .seal("identity_seed", seed)
50            .await
51            .map_err(|e| HardwareError::OperationFailed(e.to_string()))
52    }
53
54    /// Get the Unsealed Identity for real-time signing from a persisted hardware blob
55    pub async fn get_identity(
56        &self,
57        encrypted_blob: &[u8],
58    ) -> Result<AgentIdentity, HardwareError> {
59        let seed = self
60            .provider
61            .unseal(encrypted_blob)
62            .await
63            .map_err(|e| HardwareError::OperationFailed(e.to_string()))?;
64
65        let mut seed_array: [u8; 32] = seed
66            .try_into()
67            .map_err(|_| HardwareError::OperationFailed("Unsealed seed is not 32 bytes".into()))?;
68
69        let agent = crate::id::AttestAgent::from_seed(seed_array);
70        seed_array.zeroize();
71
72        Ok(AgentIdentity {
73            agent_id: agent.to_vex_uuid().to_string(),
74            hardware_id: agent.id.clone(),
75            inner: agent,
76        })
77    }
78}
79
80#[derive(Debug, Clone)]
81pub struct AgentIdentity {
82    pub agent_id: String,
83    pub hardware_id: String,
84    inner: AttestAgent,
85}
86
87impl Default for AgentIdentity {
88    fn default() -> Self {
89        let agent = crate::id::AttestAgent::new();
90        Self {
91            agent_id: agent.to_vex_uuid().to_string(),
92            hardware_id: agent.id.clone(),
93            inner: agent,
94        }
95    }
96}
97
98impl AgentIdentity {
99    /// Create a fresh hardware identity (for simulation or new agents)
100    pub fn new() -> Self {
101        Self::default()
102    }
103
104    /// Returns the raw hex of the public key (32 bytes)
105    pub fn public_key_hex(&self) -> String {
106        hex::encode(self.inner.signing_key.verifying_key().to_bytes())
107    }
108
109    /// Generate an Ed25519 signature over the provided deterministic bytes
110    pub fn sign(&self, message: &[u8]) -> Vec<u8> {
111        self.inner.sign(message).to_bytes().to_vec()
112    }
113
114    /// Retrieve hardware PCR measurements (async)
115    pub async fn get_pcrs(
116        &self,
117        indices: &[u32],
118    ) -> Result<std::collections::HashMap<u32, String>> {
119        let provider = crate::tpm::create_identity_provider(true);
120        provider.get_pcrs(indices).await
121    }
122}