Skip to main content

hyde_core/
context.rs

1use crate::{
2    backend::{TeeBackend, WrappedKey},
3    cache::SecureCache,
4    error::Result,
5    recovery::{BackupBundle, RecoveryStrategy},
6    security_level::SecurityLevel,
7};
8use serde::{Deserialize, Serialize};
9
10/// Main entry point for Hyde operations.
11pub struct HydeContext {
12    backend: Box<dyn TeeBackend>,
13    security_level: SecurityLevel,
14    cache: Option<SecureCache>,
15}
16
17#[derive(Debug, Clone)]
18pub enum FallbackPolicy {
19    /// Error if no TEE hardware is available.
20    Deny,
21    /// Warn and fall back to software backend.
22    Warn,
23    /// Silently fall back to software backend.
24    Software,
25}
26
27impl HydeContext {
28    /// Create a HydeContext with a specific backend.
29    /// Defaults to `SecurityLevel::Paranoid` (no caching).
30    pub fn with_backend(backend: Box<dyn TeeBackend>) -> Result<Self> {
31        Self::with_backend_and_security(backend, SecurityLevel::Paranoid)
32    }
33
34    /// Create a HydeContext with a specific backend and security level.
35    pub fn with_backend_and_security(
36        mut backend: Box<dyn TeeBackend>,
37        security_level: SecurityLevel,
38    ) -> Result<Self> {
39        backend.initialize_primary_key()?;
40
41        let cache = if security_level.caches_data_key() {
42            Some(SecureCache::new())
43        } else {
44            None
45        };
46
47        Ok(Self {
48            backend,
49            security_level,
50            cache,
51        })
52    }
53
54    /// Protect data by generating a Data Key, encrypting, and wrapping.
55    /// The returned `ProtectedData` can be serialized and stored anywhere.
56    pub fn protect(&mut self, data: &[u8]) -> Result<ProtectedData> {
57        let key = self.backend.generate_data_key()?;
58        let ciphertext = self.backend.seal(&key, data)?;
59        Ok(ProtectedData {
60            key,
61            ciphertext,
62            version: 1,
63        })
64    }
65
66    /// Decrypt protected data using the context's configured security level.
67    pub fn unprotect(&mut self, protected: &ProtectedData) -> Result<Vec<u8>> {
68        self.unprotect_with_level(protected, self.security_level)
69    }
70
71    /// Decrypt protected data with a specific security level override.
72    ///
73    /// Useful for one-off escalation to `Paranoid` on sensitive operations.
74    pub fn unprotect_with(
75        &mut self,
76        protected: &ProtectedData,
77        level: SecurityLevel,
78    ) -> Result<Vec<u8>> {
79        self.unprotect_with_level(protected, level)
80    }
81
82    fn unprotect_with_level(
83        &mut self,
84        protected: &ProtectedData,
85        level: SecurityLevel,
86    ) -> Result<Vec<u8>> {
87        match level {
88            SecurityLevel::Paranoid => {
89                // Always hit the TEE — no caching
90                self.backend.unseal(&protected.key, &protected.ciphertext)
91            }
92            SecurityLevel::Standard { ttl } => {
93                self.unprotect_standard(protected, ttl)
94            }
95            SecurityLevel::Performance { ttl } => {
96                self.unprotect_performance(protected, ttl)
97            }
98        }
99    }
100
101    fn unprotect_standard(
102        &mut self,
103        protected: &ProtectedData,
104        ttl: std::time::Duration,
105    ) -> Result<Vec<u8>> {
106        let cache = self.cache.get_or_insert_with(SecureCache::new);
107
108        // Check data key cache
109        if let Some(data_key) = cache.get_data_key(&protected.key.blob) {
110            // Cache hit — decrypt with cached data key (skip TPM unseal)
111            let result = crate::passphrase::aes_gcm_decrypt(&data_key, &protected.ciphertext);
112            return result;
113        }
114
115        // Cache miss — full TEE round-trip
116        let plaintext = self.backend.unseal(&protected.key, &protected.ciphertext)?;
117
118        // Extract and cache the data key for future calls.
119        // We need to unseal the data key separately to cache it.
120        // The backend.unseal() already does unseal+decrypt internally,
121        // so we need to get just the data key. We'll re-seal a known value
122        // to extract the key... Actually, the simpler approach: we call the
123        // backend's internal unseal, which gives us plaintext. We can't
124        // extract the data key without changing the TeeBackend trait.
125        //
126        // For now, cache the plaintext result keyed by (blob + ciphertext).
127        // This gives us the same performance benefit for repeated reads.
128        cache.insert_plaintext(
129            &protected.key.blob,
130            &protected.ciphertext,
131            plaintext.clone(),
132            ttl,
133        );
134
135        Ok(plaintext)
136    }
137
138    fn unprotect_performance(
139        &mut self,
140        protected: &ProtectedData,
141        ttl: std::time::Duration,
142    ) -> Result<Vec<u8>> {
143        let cache = self.cache.get_or_insert_with(SecureCache::new);
144
145        // Check plaintext cache first
146        if let Some(plaintext) = cache.get_plaintext(&protected.key.blob, &protected.ciphertext) {
147            return Ok(plaintext);
148        }
149
150        // Cache miss — full TEE round-trip
151        let plaintext = self.backend.unseal(&protected.key, &protected.ciphertext)?;
152
153        // Cache the plaintext
154        cache.insert_plaintext(
155            &protected.key.blob,
156            &protected.ciphertext,
157            plaintext.clone(),
158            ttl,
159        );
160
161        Ok(plaintext)
162    }
163
164    /// Drop all cached keys and plaintext from memory (triggers zeroize).
165    pub fn flush_cache(&mut self) {
166        if let Some(cache) = &mut self.cache {
167            cache.flush();
168        }
169    }
170
171    /// Change the security level. Flushes the cache.
172    pub fn set_security_level(&mut self, level: SecurityLevel) {
173        self.flush_cache();
174        self.security_level = level;
175
176        if level.caches_data_key() && self.cache.is_none() {
177            self.cache = Some(SecureCache::new());
178        } else if !level.caches_data_key() {
179            self.cache = None;
180        }
181    }
182
183    /// Returns the current security level.
184    pub fn security_level(&self) -> SecurityLevel {
185        self.security_level
186    }
187
188    /// Backup protected data using a chosen recovery strategy.
189    pub fn backup(
190        &self,
191        protected: &ProtectedData,
192        strategy: &dyn RecoveryStrategy,
193        secret: Option<&[u8]>,
194    ) -> Result<BackupBundle> {
195        strategy.backup(&protected.key, secret)
196    }
197
198    /// Restore protected data from a backup using the matching recovery strategy.
199    pub fn restore(
200        &self,
201        bundle: &BackupBundle,
202        ciphertext: &[u8],
203        strategy: &dyn RecoveryStrategy,
204        secret: &[u8],
205    ) -> Result<ProtectedData> {
206        let key = strategy.restore(bundle, secret)?;
207        Ok(ProtectedData {
208            key,
209            ciphertext: ciphertext.to_vec(),
210            version: 1,
211        })
212    }
213}
214
215/// TEE-protected data. Serializable for persistence.
216/// Cannot be decrypted without the corresponding TEE (or recovery).
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct ProtectedData {
219    key: WrappedKey,
220    pub ciphertext: Vec<u8>,
221    version: u8,
222}