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 {
81 Self {
82 factory,
83 backends: HashMap::new(),
84 }
85 }
86
87 pub fn new() -> Self {
94 Self {
95 factory: Box::new(|name, _config| Err(BackendError::NotFound(name))),
96 backends: HashMap::new(),
97 }
98 }
99
100 pub fn declare(&mut self, name: String, config: BackendConfig) {
106 self.backends
107 .insert(name, BackendState::Uninitialized(config));
108 }
109
110 pub fn register(&mut self, backend: Box<dyn Backend>) {
114 let name = backend.name().to_string();
115 self.backends.insert(name, BackendState::Ready(backend));
116 }
117
118 pub fn get(&self, name: &str) -> Option<&dyn Backend> {
123 match self.backends.get(name) {
124 Some(BackendState::Ready(b)) => Some(b.as_ref()),
125 _ => None,
126 }
127 }
128
129 pub fn get_mut(&mut self, name: &str) -> Result<&mut dyn Backend, BackendError> {
134 self.get_ready(name)
135 }
136
137 pub fn get_keystore(&mut self, name: &str) -> Result<&mut dyn KeyStoreBackend, BackendError> {
139 self.get_ready(name)?
140 .as_keystore_mut()
141 .ok_or_else(|| unsupported(name, "KeyStoreBackend"))
142 }
143
144 pub fn get_sign(&mut self, name: &str) -> Result<&mut dyn SignBackend, BackendError> {
146 self.get_ready(name)?
147 .as_sign_mut()
148 .ok_or_else(|| unsupported(name, "SignBackend"))
149 }
150
151 pub fn get_attest_mut(
153 &mut self,
154 name: &str,
155 ) -> Result<&mut dyn AttestationBackend, BackendError> {
156 self.get_ready(name)?
157 .as_attest_mut()
158 .ok_or_else(|| unsupported(name, "AttestationBackend"))
159 }
160
161 pub fn get_piv(&mut self, name: &str) -> Result<&mut dyn PivBackend, BackendError> {
163 self.get_ready(name)?
164 .as_piv_mut()
165 .ok_or_else(|| unsupported(name, "PivBackend"))
166 }
167
168 pub fn get_yubikey(&mut self, name: &str) -> Result<&mut dyn YubikeyBackend, BackendError> {
170 self.get_ready(name)?
171 .as_yubikey_mut()
172 .ok_or_else(|| unsupported(name, "YubikeyBackend"))
173 }
174
175 pub fn get_transport(
177 &mut self,
178 name: &str,
179 ) -> Result<&mut dyn KeyTransportBackend, BackendError> {
180 self.get_ready(name)?
181 .as_transport_mut()
182 .ok_or_else(|| unsupported(name, "KeyTransportBackend"))
183 }
184
185 pub fn get_certstore(&mut self, name: &str) -> Result<&mut dyn CertStoreBackend, BackendError> {
187 self.get_ready(name)?
188 .as_certstore_mut()
189 .ok_or_else(|| unsupported(name, "CertStoreBackend"))
190 }
191
192 pub fn get_random(&mut self, name: &str) -> Result<&mut dyn RandomBackend, BackendError> {
194 self.get_ready(name)?
195 .as_random_mut()
196 .ok_or_else(|| unsupported(name, "RandomBackend"))
197 }
198
199 pub fn get_pkcs11(&mut self, name: &str) -> Result<&mut dyn Pkcs11Backend, BackendError> {
201 self.get_ready(name)?
202 .as_pkcs11_mut()
203 .ok_or_else(|| unsupported(name, "Pkcs11Backend"))
204 }
205
206 pub fn get_pkcs11_admin(
208 &mut self,
209 name: &str,
210 ) -> Result<&mut dyn Pkcs11AdminBackend, BackendError> {
211 self.get_ready(name)?
212 .as_pkcs11_admin_mut()
213 .ok_or_else(|| unsupported(name, "Pkcs11AdminBackend"))
214 }
215
216 pub fn get_tpm(&mut self, name: &str) -> Result<&mut dyn TpmBackend, BackendError> {
218 self.get_ready(name)?
219 .as_tpm_mut()
220 .ok_or_else(|| unsupported(name, "TpmBackend"))
221 }
222
223 pub fn backend_names(&self) -> impl Iterator<Item = &String> {
225 self.backends.keys()
226 }
227
228 fn get_ready(&mut self, name: &str) -> Result<&mut dyn Backend, BackendError> {
234 self.try_initialize(name)?;
235 match self.backends.get_mut(name) {
236 Some(BackendState::Ready(b)) => Ok(b.as_mut()),
237 Some(BackendState::Failed(msg)) => Err(BackendError::HardwareFailure(msg.clone())),
238 None => Err(BackendError::NotFound(name.to_string())),
239 Some(BackendState::Uninitialized(_)) => {
240 unreachable!("backend should be Ready or Failed after try_initialize")
241 }
242 }
243 }
244
245 fn try_initialize(&mut self, name: &str) -> Result<(), BackendError> {
252 let config = match self.backends.get(name) {
254 Some(BackendState::Uninitialized(config)) => config.clone(),
255 Some(BackendState::Ready(_)) => return Ok(()),
256 Some(BackendState::Failed(msg)) => {
257 return Err(BackendError::HardwareFailure(msg.clone()));
258 }
259 None => return Err(BackendError::NotFound(name.to_string())),
260 };
261
262 match (self.factory)(name.to_string(), &config) {
264 Ok(backend) => {
265 if let Some(state) = self.backends.get_mut(name) {
267 *state = BackendState::Ready(backend);
268 }
269 Ok(())
270 }
271 Err(e) => {
272 let msg = e.to_string();
273 if let Some(state) = self.backends.get_mut(name) {
274 *state = BackendState::Failed(msg);
275 }
276 Err(e)
277 }
278 }
279 }
280}
281
282impl Default for BackendRegistry {
283 fn default() -> Self {
284 Self::new()
285 }
286}
287
288fn unsupported(name: &str, trait_name: &str) -> BackendError {
289 BackendError::UnsupportedOperation(format!("Backend '{name}' does not implement {trait_name}"))
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295 use rite_sdk::{Backend, BackendError, KeyId, KeyMetadata, KeySpec, KeyStoreBackend};
296
297 struct MinimalMock {
302 name: String,
303 }
304
305 impl Backend for MinimalMock {
306 fn name(&self) -> &str {
307 &self.name
308 }
309 fn provider(&self) -> &'static str {
310 "mock"
311 }
312 fn fingerprint(&self) -> String {
313 format!("mock={}", self.name)
314 }
315 fn as_keystore_mut(&mut self) -> Option<&mut dyn KeyStoreBackend> {
316 Some(self)
317 }
318 }
319
320 impl KeyStoreBackend for MinimalMock {
321 fn generate_key(&mut self, spec: KeySpec) -> Result<KeyMetadata, BackendError> {
322 Ok(KeyMetadata {
323 key_id: KeyId::new("mock-key-1"),
324 algorithm: spec.algorithm,
325 label: spec.label,
326 public_key: None,
327 attestation: None,
328 })
329 }
330 fn import_private_key(
331 &mut self,
332 spec: KeySpec,
333 _key_bytes: &[u8],
334 ) -> Result<KeyMetadata, BackendError> {
335 Ok(KeyMetadata {
336 key_id: KeyId::new("mock-imported-1"),
337 algorithm: spec.algorithm,
338 label: spec.label,
339 public_key: None,
340 attestation: None,
341 })
342 }
343 fn export_public_key(&self, _key_id: &KeyId) -> Result<Vec<u8>, BackendError> {
344 Ok(vec![])
345 }
346 fn list_keys(&self) -> Result<Vec<KeyMetadata>, BackendError> {
347 Ok(vec![])
348 }
349 fn delete_key(&mut self, _key_id: &KeyId) -> Result<(), BackendError> {
350 Ok(())
351 }
352 }
353
354 #[test]
359 fn registry_not_found_returns_none() {
360 let registry = BackendRegistry::new();
361 assert!(registry.get("nonexistent").is_none());
362 }
363
364 #[test]
365 fn registry_lists_backend_names_empty() {
366 let registry = BackendRegistry::new();
367 assert_eq!(registry.backend_names().count(), 0);
368 }
369
370 #[test]
371 fn register_then_get_returns_backend() {
372 let mut registry = BackendRegistry::new();
373 registry.register(Box::new(MinimalMock {
374 name: "mock".to_string(),
375 }));
376 assert!(registry.get("mock").is_some());
377 assert_eq!(registry.get("mock").unwrap().name(), "mock");
378 }
379
380 #[test]
381 fn register_then_get_keystore_succeeds() {
382 let mut registry = BackendRegistry::new();
383 registry.register(Box::new(MinimalMock {
384 name: "mock".to_string(),
385 }));
386 assert!(registry.get_keystore("mock").is_ok());
387 }
388
389 #[test]
390 fn declare_and_lazy_init_via_factory() {
391 let mut registry = BackendRegistry::with_factory(Box::new(|name, _config| {
392 Ok(Box::new(MinimalMock { name }) as Box<dyn Backend>)
393 }));
394
395 let config = BackendConfig {
396 provider: "mock".to_string(),
397 extra: serde_json::json!({}),
398 };
399
400 registry.declare("lazy".to_string(), config);
402
403 assert!(registry.get("lazy").is_none());
405
406 let result = registry.get_keystore("lazy");
408 assert!(result.is_ok(), "Expected Ok, got: {:?}", result.err());
409
410 assert!(registry.get("lazy").is_some());
412 }
413
414 #[test]
415 fn unsupported_trait_returns_error() {
416 let mut registry = BackendRegistry::new();
418 registry.register(Box::new(MinimalMock {
419 name: "mock".to_string(),
420 }));
421
422 let result = registry.get_sign("mock");
423 assert!(result.is_err());
424 match result.err().unwrap() {
425 BackendError::UnsupportedOperation(msg) => {
426 assert!(
427 msg.contains("SignBackend"),
428 "Expected UnsupportedOperation for SignBackend, got: {msg}"
429 );
430 }
431 e => panic!("Expected UnsupportedOperation, got: {e}"),
432 }
433 }
434
435 #[test]
436 fn get_mut_undeclared_returns_not_found() {
437 let mut registry = BackendRegistry::new();
438 let result = registry.get_mut("nonexistent");
439 assert!(result.is_err());
440 match result.err().unwrap() {
441 BackendError::NotFound(name) => assert_eq!(name, "nonexistent"),
442 e => panic!("Expected NotFound, got: {e}"),
443 }
444 }
445
446 #[test]
447 fn factory_failure_cached_no_retry() {
448 use std::sync::Arc;
449 use std::sync::atomic::{AtomicUsize, Ordering};
450
451 let call_count = Arc::new(AtomicUsize::new(0));
452 let call_count_clone = Arc::clone(&call_count);
453
454 let mut registry = BackendRegistry::with_factory(Box::new(move |_name, _config| {
455 call_count_clone.fetch_add(1, Ordering::SeqCst);
456 Err(BackendError::HardwareFailure(
457 "device not found".to_string(),
458 ))
459 }));
460
461 let config = BackendConfig {
462 provider: "mock".to_string(),
463 extra: serde_json::json!({}),
464 };
465 registry.declare("failing".to_string(), config);
466
467 let _ = registry.get_keystore("failing");
469 assert_eq!(call_count.load(Ordering::SeqCst), 1);
470
471 let _ = registry.get_keystore("failing");
473 assert_eq!(
474 call_count.load(Ordering::SeqCst),
475 1,
476 "Factory called more than once"
477 );
478 }
479
480 #[test]
481 fn lazy_init_unsupported_trait_returns_error() {
482 let mut registry = BackendRegistry::with_factory(Box::new(|name, _config| {
485 Ok(Box::new(MinimalMock { name }) as Box<dyn Backend>)
486 }));
487
488 let config = BackendConfig {
489 provider: "yubikey".to_string(),
490 extra: serde_json::json!({}),
491 };
492 registry.declare("yk".to_string(), config);
493
494 let result = registry.get_attest_mut("yk");
495 match result {
496 Err(BackendError::UnsupportedOperation(msg)) => {
497 assert!(
498 msg.contains("AttestationBackend"),
499 "Expected AttestationBackend error, got: {msg}"
500 );
501 }
502 Ok(_) => panic!("Expected UnsupportedOperation, got Ok"),
503 Err(e) => panic!("Expected UnsupportedOperation, got: {e:?}"),
504 }
505 }
506}