1use rite_sdk::{
24 AttestationBackend, Backend, BackendConfig, BackendError, CertStoreBackend, KeyStoreBackend,
25 KeyTransportBackend, PivBackend, Pkcs11AdminBackend, Pkcs11Backend, RandomBackend, SignBackend,
26 TpmBackend, YubikeyBackend,
27};
28use std::collections::HashMap;
29
30pub type BackendFactory =
37 Box<dyn Fn(String, &BackendConfig) -> Result<Box<dyn Backend>, BackendError> + Send + Sync>;
38
39enum BackendState {
40 Uninitialized(BackendConfig),
42 Ready(Box<dyn Backend>),
44 Failed(String),
46}
47
48pub struct BackendRegistry {
59 factory: BackendFactory,
60 backends: HashMap<String, BackendState>,
61}
62
63impl BackendRegistry {
64 pub fn with_factory(factory: BackendFactory) -> Self {
76 Self {
77 factory,
78 backends: HashMap::new(),
79 }
80 }
81
82 pub fn new() -> Self {
89 Self {
90 factory: Box::new(|name, _config| Err(BackendError::NotFound(name))),
91 backends: HashMap::new(),
92 }
93 }
94
95 pub fn declare(&mut self, name: String, config: BackendConfig) {
101 self.backends
102 .insert(name, BackendState::Uninitialized(config));
103 }
104
105 pub fn register(&mut self, backend: Box<dyn Backend>) {
109 let name = backend.name().to_string();
110 self.backends.insert(name, BackendState::Ready(backend));
111 }
112
113 pub fn get(&self, name: &str) -> Option<&dyn Backend> {
118 match self.backends.get(name) {
119 Some(BackendState::Ready(b)) => Some(b.as_ref()),
120 _ => None,
121 }
122 }
123
124 pub fn get_mut(&mut self, name: &str) -> Result<&mut dyn Backend, BackendError> {
129 self.get_ready(name)
130 }
131
132 pub fn get_keystore(&mut self, name: &str) -> Result<&mut dyn KeyStoreBackend, BackendError> {
134 self.get_ready(name)?
135 .as_keystore_mut()
136 .ok_or_else(|| unsupported(name, "KeyStoreBackend"))
137 }
138
139 pub fn get_sign(&mut self, name: &str) -> Result<&mut dyn SignBackend, BackendError> {
141 self.get_ready(name)?
142 .as_sign_mut()
143 .ok_or_else(|| unsupported(name, "SignBackend"))
144 }
145
146 pub fn get_attest_mut(
148 &mut self,
149 name: &str,
150 ) -> Result<&mut dyn AttestationBackend, BackendError> {
151 self.get_ready(name)?
152 .as_attest_mut()
153 .ok_or_else(|| unsupported(name, "AttestationBackend"))
154 }
155
156 pub fn get_piv(&mut self, name: &str) -> Result<&mut dyn PivBackend, BackendError> {
158 self.get_ready(name)?
159 .as_piv_mut()
160 .ok_or_else(|| unsupported(name, "PivBackend"))
161 }
162
163 pub fn get_yubikey(&mut self, name: &str) -> Result<&mut dyn YubikeyBackend, BackendError> {
165 self.get_ready(name)?
166 .as_yubikey_mut()
167 .ok_or_else(|| unsupported(name, "YubikeyBackend"))
168 }
169
170 pub fn get_transport(
172 &mut self,
173 name: &str,
174 ) -> Result<&mut dyn KeyTransportBackend, BackendError> {
175 self.get_ready(name)?
176 .as_transport_mut()
177 .ok_or_else(|| unsupported(name, "KeyTransportBackend"))
178 }
179
180 pub fn get_certstore(&mut self, name: &str) -> Result<&mut dyn CertStoreBackend, BackendError> {
182 self.get_ready(name)?
183 .as_certstore_mut()
184 .ok_or_else(|| unsupported(name, "CertStoreBackend"))
185 }
186
187 pub fn get_random(&mut self, name: &str) -> Result<&mut dyn RandomBackend, BackendError> {
189 self.get_ready(name)?
190 .as_random_mut()
191 .ok_or_else(|| unsupported(name, "RandomBackend"))
192 }
193
194 pub fn get_pkcs11(&mut self, name: &str) -> Result<&mut dyn Pkcs11Backend, BackendError> {
196 self.get_ready(name)?
197 .as_pkcs11_mut()
198 .ok_or_else(|| unsupported(name, "Pkcs11Backend"))
199 }
200
201 pub fn get_pkcs11_admin(
203 &mut self,
204 name: &str,
205 ) -> Result<&mut dyn Pkcs11AdminBackend, BackendError> {
206 self.get_ready(name)?
207 .as_pkcs11_admin_mut()
208 .ok_or_else(|| unsupported(name, "Pkcs11AdminBackend"))
209 }
210
211 pub fn get_tpm(&mut self, name: &str) -> Result<&mut dyn TpmBackend, BackendError> {
213 self.get_ready(name)?
214 .as_tpm_mut()
215 .ok_or_else(|| unsupported(name, "TpmBackend"))
216 }
217
218 pub fn backend_names(&self) -> impl Iterator<Item = &String> {
220 self.backends.keys()
221 }
222
223 fn get_ready(&mut self, name: &str) -> Result<&mut dyn Backend, BackendError> {
229 self.try_initialize(name)?;
230 match self.backends.get_mut(name) {
231 Some(BackendState::Ready(b)) => Ok(b.as_mut()),
232 Some(BackendState::Failed(msg)) => Err(BackendError::HardwareFailure(msg.clone())),
233 None => Err(BackendError::NotFound(name.to_string())),
234 Some(BackendState::Uninitialized(_)) => {
235 unreachable!("backend should be Ready or Failed after try_initialize")
236 }
237 }
238 }
239
240 fn try_initialize(&mut self, name: &str) -> Result<(), BackendError> {
247 let config = match self.backends.get(name) {
249 Some(BackendState::Uninitialized(config)) => config.clone(),
250 Some(BackendState::Ready(_)) => return Ok(()),
251 Some(BackendState::Failed(msg)) => {
252 return Err(BackendError::HardwareFailure(msg.clone()));
253 }
254 None => return Err(BackendError::NotFound(name.to_string())),
255 };
256
257 match (self.factory)(name.to_string(), &config) {
259 Ok(backend) => {
260 if let Some(state) = self.backends.get_mut(name) {
262 *state = BackendState::Ready(backend);
263 }
264 Ok(())
265 }
266 Err(e) => {
267 let msg = e.to_string();
268 if let Some(state) = self.backends.get_mut(name) {
269 *state = BackendState::Failed(msg);
270 }
271 Err(e)
272 }
273 }
274 }
275}
276
277impl Default for BackendRegistry {
278 fn default() -> Self {
279 Self::new()
280 }
281}
282
283fn unsupported(name: &str, trait_name: &str) -> BackendError {
284 BackendError::UnsupportedOperation(format!("Backend '{name}' does not implement {trait_name}"))
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290 use rite_sdk::{Backend, BackendError, KeyId, KeyMetadata, KeySpec, KeyStoreBackend};
291
292 struct MinimalMock {
297 name: String,
298 }
299
300 impl Backend for MinimalMock {
301 fn name(&self) -> &str {
302 &self.name
303 }
304 fn provider(&self) -> &'static str {
305 "mock"
306 }
307 fn fingerprint(&self) -> String {
308 format!("mock={}", self.name)
309 }
310 fn as_keystore_mut(&mut self) -> Option<&mut dyn KeyStoreBackend> {
311 Some(self)
312 }
313 }
314
315 impl KeyStoreBackend for MinimalMock {
316 fn generate_key(&mut self, spec: KeySpec) -> Result<KeyMetadata, BackendError> {
317 Ok(KeyMetadata {
318 key_id: KeyId::new("mock-key-1"),
319 algorithm: spec.algorithm,
320 label: spec.label,
321 public_key: None,
322 attestation: None,
323 })
324 }
325 fn import_private_key(
326 &mut self,
327 spec: KeySpec,
328 _key_bytes: &[u8],
329 ) -> Result<KeyMetadata, BackendError> {
330 Ok(KeyMetadata {
331 key_id: KeyId::new("mock-imported-1"),
332 algorithm: spec.algorithm,
333 label: spec.label,
334 public_key: None,
335 attestation: None,
336 })
337 }
338 fn export_public_key(&self, _key_id: &KeyId) -> Result<Vec<u8>, BackendError> {
339 Ok(vec![])
340 }
341 fn list_keys(&self) -> Result<Vec<KeyMetadata>, BackendError> {
342 Ok(vec![])
343 }
344 fn delete_key(&mut self, _key_id: &KeyId) -> Result<(), BackendError> {
345 Ok(())
346 }
347 }
348
349 #[test]
354 fn registry_not_found_returns_none() {
355 let registry = BackendRegistry::new();
356 assert!(registry.get("nonexistent").is_none());
357 }
358
359 #[test]
360 fn registry_lists_backend_names_empty() {
361 let registry = BackendRegistry::new();
362 assert_eq!(registry.backend_names().count(), 0);
363 }
364
365 #[test]
366 fn register_then_get_returns_backend() {
367 let mut registry = BackendRegistry::new();
368 registry.register(Box::new(MinimalMock {
369 name: "mock".to_string(),
370 }));
371 assert!(registry.get("mock").is_some());
372 assert_eq!(registry.get("mock").unwrap().name(), "mock");
373 }
374
375 #[test]
376 fn register_then_get_keystore_succeeds() {
377 let mut registry = BackendRegistry::new();
378 registry.register(Box::new(MinimalMock {
379 name: "mock".to_string(),
380 }));
381 assert!(registry.get_keystore("mock").is_ok());
382 }
383
384 #[test]
385 fn declare_and_lazy_init_via_factory() {
386 let mut registry = BackendRegistry::with_factory(Box::new(|name, _config| {
387 Ok(Box::new(MinimalMock { name }) as Box<dyn Backend>)
388 }));
389
390 let config = BackendConfig {
391 provider: "mock".to_string(),
392 extra: serde_json::json!({}),
393 };
394
395 registry.declare("lazy".to_string(), config);
397
398 assert!(registry.get("lazy").is_none());
400
401 let result = registry.get_keystore("lazy");
403 assert!(result.is_ok(), "Expected Ok, got: {:?}", result.err());
404
405 assert!(registry.get("lazy").is_some());
407 }
408
409 #[test]
410 fn unsupported_trait_returns_error() {
411 let mut registry = BackendRegistry::new();
413 registry.register(Box::new(MinimalMock {
414 name: "mock".to_string(),
415 }));
416
417 let result = registry.get_sign("mock");
418 assert!(result.is_err());
419 match result.err().unwrap() {
420 BackendError::UnsupportedOperation(msg) => {
421 assert!(
422 msg.contains("SignBackend"),
423 "Expected UnsupportedOperation for SignBackend, got: {msg}"
424 );
425 }
426 e => panic!("Expected UnsupportedOperation, got: {e}"),
427 }
428 }
429
430 #[test]
431 fn get_mut_undeclared_returns_not_found() {
432 let mut registry = BackendRegistry::new();
433 let result = registry.get_mut("nonexistent");
434 assert!(result.is_err());
435 match result.err().unwrap() {
436 BackendError::NotFound(name) => assert_eq!(name, "nonexistent"),
437 e => panic!("Expected NotFound, got: {e}"),
438 }
439 }
440
441 #[test]
442 fn factory_failure_cached_no_retry() {
443 use std::sync::Arc;
444 use std::sync::atomic::{AtomicUsize, Ordering};
445
446 let call_count = Arc::new(AtomicUsize::new(0));
447 let call_count_clone = Arc::clone(&call_count);
448
449 let mut registry = BackendRegistry::with_factory(Box::new(move |_name, _config| {
450 call_count_clone.fetch_add(1, Ordering::SeqCst);
451 Err(BackendError::HardwareFailure(
452 "device not found".to_string(),
453 ))
454 }));
455
456 let config = BackendConfig {
457 provider: "mock".to_string(),
458 extra: serde_json::json!({}),
459 };
460 registry.declare("failing".to_string(), config);
461
462 let _ = registry.get_keystore("failing");
464 assert_eq!(call_count.load(Ordering::SeqCst), 1);
465
466 let _ = registry.get_keystore("failing");
468 assert_eq!(
469 call_count.load(Ordering::SeqCst),
470 1,
471 "Factory called more than once"
472 );
473 }
474
475 #[test]
476 fn lazy_init_unsupported_trait_returns_error() {
477 let mut registry = BackendRegistry::with_factory(Box::new(|name, _config| {
480 Ok(Box::new(MinimalMock { name }) as Box<dyn Backend>)
481 }));
482
483 let config = BackendConfig {
484 provider: "yubikey".to_string(),
485 extra: serde_json::json!({}),
486 };
487 registry.declare("yk".to_string(), config);
488
489 let result = registry.get_attest_mut("yk");
490 match result {
491 Err(BackendError::UnsupportedOperation(msg)) => {
492 assert!(
493 msg.contains("AttestationBackend"),
494 "Expected AttestationBackend error, got: {msg}"
495 );
496 }
497 Ok(_) => panic!("Expected UnsupportedOperation, got Ok"),
498 Err(e) => panic!("Expected UnsupportedOperation, got: {e:?}"),
499 }
500 }
501}