1#![allow(dead_code, unused_imports, unused_qualifications, unreachable_patterns)]
11
12use crate::internal::core::metadata;
13use crate::internal::core::traits::{EnclaveEncryptor, EnclaveKeyManager, EnclaveSigner};
14use crate::internal::core::types::{AccessPolicy, KeyType};
15use std::path::Path;
16
17#[cfg_attr(not(any(test, target_os = "windows")), allow(dead_code))]
18fn existing_policy(keys_dir: &Path, key_label: &str) -> Option<AccessPolicy> {
19 let meta_path = keys_dir.join(format!("{key_label}.meta"));
20 if !meta_path.exists() {
21 return None;
22 }
23 metadata::load_meta(keys_dir, key_label)
24 .ok()
25 .map(|meta| meta.access_policy)
26}
27
28#[cfg_attr(not(any(test, target_os = "windows")), allow(dead_code))]
29pub(crate) fn ensure_key<E>(
30 encryptor: &E,
31 keys_dir: &Path,
32 key_label: &str,
33 policy: AccessPolicy,
34) -> Result<(), String>
35where
36 E: EnclaveEncryptor + EnclaveKeyManager,
37{
38 if encryptor.public_key(key_label).is_ok() {
39 match existing_policy(keys_dir, key_label) {
40 Some(existing) if existing != policy => {
41 encryptor
42 .delete_key(key_label)
43 .map_err(|e| format!("key deletion failed: {e}"))?;
44 }
45 _ => return Ok(()),
46 }
47 }
48
49 encryptor
50 .generate(key_label, KeyType::Encryption, policy)
51 .map_err(|e| format!("key generation failed: {e}"))?;
52 Ok(())
53}
54
55#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
56pub(crate) fn ensure_signing_key<S>(
57 signer: &S,
58 keys_dir: &Path,
59 key_label: &str,
60 policy: AccessPolicy,
61) -> Result<(), String>
62where
63 S: EnclaveSigner + EnclaveKeyManager,
64{
65 if signer.public_key(key_label).is_ok() {
66 match existing_policy(keys_dir, key_label) {
67 Some(existing) if existing != policy => {
68 signer
69 .delete_key(key_label)
70 .map_err(|e| format!("key deletion failed: {e}"))?;
71 }
72 _ => return Ok(()),
73 }
74 }
75
76 signer
77 .generate(key_label, KeyType::Signing, policy)
78 .map_err(|e| format!("key generation failed: {e}"))?;
79 Ok(())
80}
81
82#[cfg(target_os = "windows")]
83mod platform {
84 use super::{ensure_key, ensure_signing_key, metadata};
85 use crate::internal::core::traits::{EnclaveEncryptor, EnclaveKeyManager, EnclaveSigner};
86 use crate::internal::core::types::AccessPolicy;
87 use crate::internal::windows::{TpmEncryptor, TpmSigner};
88
89 pub struct TpmStorage {
90 encryptor: TpmEncryptor,
91 key_label: String,
92 }
93
94 impl std::fmt::Debug for TpmStorage {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 f.debug_struct("TpmStorage")
97 .field("key_label", &self.key_label)
98 .finish_non_exhaustive()
99 }
100 }
101
102 impl TpmStorage {
103 pub fn new(
104 app_name: &str,
105 key_label: &str,
106 access_policy: AccessPolicy,
107 ) -> Result<Self, String> {
108 let encryptor = TpmEncryptor::new(app_name);
109
110 if !encryptor.is_available() {
111 return Err("TPM not available".to_string());
112 }
113
114 ensure_key(
115 &encryptor,
116 &metadata::keys_dir(app_name),
117 key_label,
118 access_policy,
119 )?;
120
121 Ok(Self {
122 encryptor,
123 key_label: key_label.to_string(),
124 })
125 }
126
127 pub fn delete(app_name: &str, key_label: &str) -> Result<(), String> {
128 let encryptor = TpmEncryptor::new(app_name);
129
130 if !encryptor.is_available() {
131 return Err("TPM not available".to_string());
132 }
133
134 encryptor
135 .delete_key(key_label)
136 .map_err(|e| format!("key delete failed: {e}"))
137 }
138
139 pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, String> {
140 self.encryptor
141 .encrypt(&self.key_label, plaintext)
142 .map_err(|e| e.to_string())
143 }
144
145 pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, String> {
146 self.encryptor
147 .decrypt(&self.key_label, ciphertext)
148 .map_err(|e| e.to_string())
149 }
150 }
151
152 pub struct TpmSigningStorage {
153 signer: TpmSigner,
154 key_label: String,
155 }
156
157 impl std::fmt::Debug for TpmSigningStorage {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 f.debug_struct("TpmSigningStorage")
160 .field("key_label", &self.key_label)
161 .finish_non_exhaustive()
162 }
163 }
164
165 impl TpmSigningStorage {
166 pub fn new(
167 app_name: &str,
168 key_label: &str,
169 access_policy: AccessPolicy,
170 ) -> Result<Self, String> {
171 let signer = TpmSigner::new(app_name);
172
173 if !signer.is_available() {
174 return Err("TPM not available".to_string());
175 }
176
177 ensure_signing_key(
178 &signer,
179 &metadata::keys_dir(app_name),
180 key_label,
181 access_policy,
182 )?;
183
184 Ok(Self {
185 signer,
186 key_label: key_label.to_string(),
187 })
188 }
189
190 pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>, String> {
191 self.signer
192 .sign(&self.key_label, data)
193 .map_err(|e| e.to_string())
194 }
195
196 pub fn public_key(&self) -> Result<Vec<u8>, String> {
197 self.signer
198 .public_key(&self.key_label)
199 .map_err(|e| e.to_string())
200 }
201
202 pub fn list_keys(&self) -> Result<Vec<String>, String> {
203 self.signer.list_keys().map_err(|e| e.to_string())
204 }
205
206 pub fn list_keys_for_app(app_name: &str) -> Result<Vec<String>, String> {
212 let signer = TpmSigner::new(app_name);
213 if !signer.is_available() {
214 return Err("TPM not available".to_string());
215 }
216 signer.list_keys().map_err(|e| e.to_string())
217 }
218
219 pub fn public_key_for_app(app_name: &str, key_label: &str) -> Result<Vec<u8>, String> {
229 let signer = TpmSigner::new(app_name);
230 if !signer.is_available() {
231 return Err("TPM not available".to_string());
232 }
233 signer.public_key(key_label).map_err(|e| e.to_string())
234 }
235
236 pub fn delete(app_name: &str, key_label: &str) -> Result<(), String> {
237 let signer = TpmSigner::new(app_name);
238
239 if !signer.is_available() {
240 return Err("TPM not available".to_string());
241 }
242
243 signer
244 .delete_key(key_label)
245 .map_err(|e| format!("key delete failed: {e}"))
246 }
247
248 pub fn key_exists(app_name: &str, key_label: &str) -> Result<bool, String> {
251 let signer = TpmSigner::new(app_name);
252
253 if !signer.is_available() {
254 return Err("TPM not available".to_string());
255 }
256
257 match signer.public_key(key_label) {
258 Ok(_) => Ok(true),
259 Err(crate::internal::core::Error::KeyNotFound { .. }) => Ok(false),
260 Err(e) => Err(e.to_string()),
261 }
262 }
263 }
264}
265
266#[cfg(not(target_os = "windows"))]
267mod platform {
268 use crate::internal::core::types::AccessPolicy;
269
270 #[derive(Debug)]
271 pub struct TpmStorage {
272 _app_name: String,
273 _key_label: String,
274 _access_policy: AccessPolicy,
275 }
276
277 impl TpmStorage {
278 #[allow(clippy::unnecessary_wraps)]
279 pub fn new(
280 app_name: &str,
281 key_label: &str,
282 access_policy: AccessPolicy,
283 ) -> Result<Self, String> {
284 Ok(Self {
285 _app_name: app_name.to_string(),
286 _key_label: key_label.to_string(),
287 _access_policy: access_policy,
288 })
289 }
290
291 #[allow(clippy::unnecessary_wraps)]
292 pub fn delete(_app_name: &str, _key_label: &str) -> Result<(), String> {
293 Ok(())
294 }
295
296 #[allow(clippy::unused_self)]
297 pub fn encrypt(&self, _plaintext: &[u8]) -> Result<Vec<u8>, String> {
298 Err("TPM bridge is only supported on Windows".to_string())
299 }
300
301 #[allow(clippy::unused_self)]
302 pub fn decrypt(&self, _ciphertext: &[u8]) -> Result<Vec<u8>, String> {
303 Err("TPM bridge is only supported on Windows".to_string())
304 }
305 }
306
307 #[derive(Debug)]
308 pub struct TpmSigningStorage {
309 _app_name: String,
310 _key_label: String,
311 _access_policy: AccessPolicy,
312 }
313
314 impl TpmSigningStorage {
315 #[allow(clippy::unnecessary_wraps)]
316 pub fn new(
317 app_name: &str,
318 key_label: &str,
319 access_policy: AccessPolicy,
320 ) -> Result<Self, String> {
321 Ok(Self {
322 _app_name: app_name.to_string(),
323 _key_label: key_label.to_string(),
324 _access_policy: access_policy,
325 })
326 }
327
328 #[allow(clippy::unused_self)]
329 pub fn sign(&self, _data: &[u8]) -> Result<Vec<u8>, String> {
330 Err("TPM signing bridge is only supported on Windows".to_string())
331 }
332
333 #[allow(clippy::unused_self)]
334 pub fn public_key(&self) -> Result<Vec<u8>, String> {
335 Err("TPM signing bridge is only supported on Windows".to_string())
336 }
337
338 #[allow(clippy::unused_self)]
339 pub fn list_keys(&self) -> Result<Vec<String>, String> {
340 Err("TPM signing bridge is only supported on Windows".to_string())
341 }
342
343 pub fn list_keys_for_app(_app_name: &str) -> Result<Vec<String>, String> {
344 Err("TPM signing bridge is only supported on Windows".to_string())
345 }
346
347 pub fn public_key_for_app(_app_name: &str, _key_label: &str) -> Result<Vec<u8>, String> {
348 Err("TPM signing bridge is only supported on Windows".to_string())
349 }
350
351 #[allow(clippy::unnecessary_wraps)]
352 pub fn delete(_app_name: &str, _key_label: &str) -> Result<(), String> {
353 Ok(())
354 }
355
356 pub fn key_exists(_app_name: &str, _key_label: &str) -> Result<bool, String> {
357 Err("TPM signing bridge is only supported on Windows".to_string())
358 }
359 }
360}
361
362pub use platform::TpmSigningStorage;
363pub use platform::TpmStorage;
364
365#[cfg(test)]
366#[allow(clippy::unwrap_used, clippy::panic)]
367mod tests {
368 use super::*;
369 use crate::internal::core::{Error, Result};
370 use std::sync::atomic::{AtomicU64, Ordering};
371 use std::sync::Mutex;
372
373 static TEST_COUNTER: AtomicU64 = AtomicU64::new(0);
374
375 fn test_dir() -> std::path::PathBuf {
376 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
377 let pid = std::process::id();
378 let dir = std::env::temp_dir().join(format!("enclaveapp-tpm-bridge-test-{pid}-{id}"));
379 std::fs::create_dir_all(&dir).unwrap();
380 dir
381 }
382
383 #[derive(Default)]
384 struct FakeState {
385 has_key: bool,
386 deleted: Vec<String>,
387 generated: Vec<(String, KeyType, AccessPolicy)>,
388 }
389
390 #[derive(Default)]
391 struct FakeEncryptor {
392 state: Mutex<FakeState>,
393 }
394
395 impl FakeEncryptor {
396 fn with_existing_key() -> Self {
397 Self {
398 state: Mutex::new(FakeState {
399 has_key: true,
400 deleted: Vec::new(),
401 generated: Vec::new(),
402 }),
403 }
404 }
405
406 fn deleted_labels(&self) -> Vec<String> {
407 self.state.lock().unwrap().deleted.clone()
408 }
409
410 fn generated_calls(&self) -> Vec<(String, KeyType, AccessPolicy)> {
411 self.state.lock().unwrap().generated.clone()
412 }
413 }
414
415 impl EnclaveKeyManager for FakeEncryptor {
416 fn generate(
417 &self,
418 label: &str,
419 key_type: KeyType,
420 policy: AccessPolicy,
421 ) -> Result<Vec<u8>> {
422 let mut state = self.state.lock().map_err(|e| Error::KeyOperation {
423 operation: "lock".to_string(),
424 detail: e.to_string(),
425 })?;
426 state.has_key = true;
427 state.generated.push((label.to_string(), key_type, policy));
428 Ok(vec![0x04; 65])
429 }
430
431 fn public_key(&self, label: &str) -> Result<Vec<u8>> {
432 let state = self.state.lock().map_err(|e| Error::KeyOperation {
433 operation: "lock".to_string(),
434 detail: e.to_string(),
435 })?;
436 if state.has_key {
437 Ok(vec![0x04; 65])
438 } else {
439 Err(Error::KeyNotFound {
440 label: label.to_string(),
441 })
442 }
443 }
444
445 fn list_keys(&self) -> Result<Vec<String>> {
446 Ok(Vec::new())
447 }
448
449 fn delete_key(&self, label: &str) -> Result<()> {
450 let mut state = self.state.lock().map_err(|e| Error::KeyOperation {
451 operation: "lock".to_string(),
452 detail: e.to_string(),
453 })?;
454 state.has_key = false;
455 state.deleted.push(label.to_string());
456 Ok(())
457 }
458
459 fn is_available(&self) -> bool {
460 true
461 }
462 }
463
464 impl EnclaveEncryptor for FakeEncryptor {
465 fn encrypt(&self, _label: &str, _plaintext: &[u8]) -> Result<Vec<u8>> {
466 Ok(Vec::new())
467 }
468
469 fn decrypt(&self, _label: &str, _ciphertext: &[u8]) -> Result<Vec<u8>> {
470 Ok(Vec::new())
471 }
472 }
473
474 #[test]
475 fn ensure_key_generates_when_missing() {
476 let dir = test_dir();
477 let encryptor = FakeEncryptor::default();
478
479 ensure_key(&encryptor, &dir, "cache-key", AccessPolicy::BiometricOnly).unwrap();
480
481 assert!(encryptor.deleted_labels().is_empty());
482 assert_eq!(
483 encryptor.generated_calls(),
484 vec![(
485 "cache-key".to_string(),
486 KeyType::Encryption,
487 AccessPolicy::BiometricOnly
488 )]
489 );
490
491 std::fs::remove_dir_all(&dir).unwrap();
492 }
493
494 #[test]
495 fn ensure_key_regenerates_when_policy_mismatches() {
496 let dir = test_dir();
497 metadata::save_meta(
498 &dir,
499 "cache-key",
500 &metadata::KeyMeta::new("cache-key", KeyType::Encryption, AccessPolicy::None),
501 )
502 .unwrap();
503 let encryptor = FakeEncryptor::with_existing_key();
504
505 ensure_key(&encryptor, &dir, "cache-key", AccessPolicy::BiometricOnly).unwrap();
506
507 assert_eq!(encryptor.deleted_labels(), vec!["cache-key".to_string()]);
508 assert_eq!(
509 encryptor.generated_calls(),
510 vec![(
511 "cache-key".to_string(),
512 KeyType::Encryption,
513 AccessPolicy::BiometricOnly
514 )]
515 );
516
517 std::fs::remove_dir_all(&dir).unwrap();
518 }
519
520 #[test]
521 fn ensure_key_keeps_existing_key_when_policy_matches() {
522 let dir = test_dir();
523 metadata::save_meta(
524 &dir,
525 "cache-key",
526 &metadata::KeyMeta::new(
527 "cache-key",
528 KeyType::Encryption,
529 AccessPolicy::BiometricOnly,
530 ),
531 )
532 .unwrap();
533 let encryptor = FakeEncryptor::with_existing_key();
534
535 ensure_key(&encryptor, &dir, "cache-key", AccessPolicy::BiometricOnly).unwrap();
536
537 assert!(encryptor.deleted_labels().is_empty());
538 assert!(encryptor.generated_calls().is_empty());
539
540 std::fs::remove_dir_all(&dir).unwrap();
541 }
542}