1use rite_sdk::{
26 AttestationBackend, Backend, BackendConfig, BackendError, CertStoreBackend, KeyStoreBackend,
27 KeyTransportBackend, PivBackend, Pkcs11AdminBackend, Pkcs11Backend, RandomBackend, SignBackend,
28 TpmBackend, YubikeyBackend,
29};
30use std::collections::HashMap;
31
32pub type BackendFactory =
39 Box<dyn Fn(String, &BackendConfig) -> Result<Box<dyn Backend>, BackendError> + Send + Sync>;
40
41enum BackendState {
42 Uninitialized(BackendConfig),
45 Ready(Box<dyn Backend>),
47}
48
49pub struct BackendRegistry {
60 factory: BackendFactory,
61 backends: HashMap<String, BackendState>,
62}
63
64impl BackendRegistry {
65 pub fn with_factory(factory: BackendFactory) -> Self {
82 Self {
83 factory,
84 backends: HashMap::new(),
85 }
86 }
87
88 pub fn new() -> Self {
95 Self {
96 factory: Box::new(|name, _config| Err(BackendError::NotFound(name))),
97 backends: HashMap::new(),
98 }
99 }
100
101 pub fn declare(&mut self, name: String, config: BackendConfig) {
107 self.backends
108 .insert(name, BackendState::Uninitialized(config));
109 }
110
111 pub fn register(&mut self, backend: Box<dyn Backend>) {
115 let name = backend.name().to_string();
116 self.backends.insert(name, BackendState::Ready(backend));
117 }
118
119 pub fn get(&self, name: &str) -> Option<&dyn Backend> {
124 match self.backends.get(name) {
125 Some(BackendState::Ready(b)) => Some(b.as_ref()),
126 _ => None,
127 }
128 }
129
130 pub fn get_mut(&mut self, name: &str) -> Result<&mut dyn Backend, BackendError> {
136 self.get_ready(name)
137 }
138
139 pub fn get_keystore(&mut self, name: &str) -> Result<&mut dyn KeyStoreBackend, BackendError> {
141 self.get_ready(name)?
142 .as_keystore_mut()
143 .ok_or_else(|| unsupported(name, "KeyStoreBackend"))
144 }
145
146 pub fn get_sign(&mut self, name: &str) -> Result<&mut dyn SignBackend, BackendError> {
148 self.get_ready(name)?
149 .as_sign_mut()
150 .ok_or_else(|| unsupported(name, "SignBackend"))
151 }
152
153 pub fn get_attest_mut(
155 &mut self,
156 name: &str,
157 ) -> Result<&mut dyn AttestationBackend, BackendError> {
158 self.get_ready(name)?
159 .as_attest_mut()
160 .ok_or_else(|| unsupported(name, "AttestationBackend"))
161 }
162
163 pub fn get_piv(&mut self, name: &str) -> Result<&mut dyn PivBackend, BackendError> {
165 self.get_ready(name)?
166 .as_piv_mut()
167 .ok_or_else(|| unsupported(name, "PivBackend"))
168 }
169
170 pub fn get_yubikey(&mut self, name: &str) -> Result<&mut dyn YubikeyBackend, BackendError> {
172 self.get_ready(name)?
173 .as_yubikey_mut()
174 .ok_or_else(|| unsupported(name, "YubikeyBackend"))
175 }
176
177 pub fn get_transport(
179 &mut self,
180 name: &str,
181 ) -> Result<&mut dyn KeyTransportBackend, BackendError> {
182 self.get_ready(name)?
183 .as_transport_mut()
184 .ok_or_else(|| unsupported(name, "KeyTransportBackend"))
185 }
186
187 pub fn get_certstore(&mut self, name: &str) -> Result<&mut dyn CertStoreBackend, BackendError> {
189 self.get_ready(name)?
190 .as_certstore_mut()
191 .ok_or_else(|| unsupported(name, "CertStoreBackend"))
192 }
193
194 pub fn get_random(&mut self, name: &str) -> Result<&mut dyn RandomBackend, BackendError> {
196 self.get_ready(name)?
197 .as_random_mut()
198 .ok_or_else(|| unsupported(name, "RandomBackend"))
199 }
200
201 pub fn get_pkcs11(&mut self, name: &str) -> Result<&mut dyn Pkcs11Backend, BackendError> {
203 self.get_ready(name)?
204 .as_pkcs11_mut()
205 .ok_or_else(|| unsupported(name, "Pkcs11Backend"))
206 }
207
208 pub fn get_pkcs11_admin(
210 &mut self,
211 name: &str,
212 ) -> Result<&mut dyn Pkcs11AdminBackend, BackendError> {
213 self.get_ready(name)?
214 .as_pkcs11_admin_mut()
215 .ok_or_else(|| unsupported(name, "Pkcs11AdminBackend"))
216 }
217
218 pub fn get_tpm(&mut self, name: &str) -> Result<&mut dyn TpmBackend, BackendError> {
220 self.get_ready(name)?
221 .as_tpm_mut()
222 .ok_or_else(|| unsupported(name, "TpmBackend"))
223 }
224
225 pub fn backend_names(&self) -> impl Iterator<Item = &String> {
227 self.backends.keys()
228 }
229
230 fn get_ready(&mut self, name: &str) -> Result<&mut dyn Backend, BackendError> {
236 self.try_initialize(name)?;
237 match self.backends.get_mut(name) {
238 Some(BackendState::Ready(b)) => Ok(b.as_mut()),
239 None => Err(BackendError::NotFound(name.to_string())),
240 Some(BackendState::Uninitialized(_)) => {
241 unreachable!("backend should be Ready after try_initialize")
242 }
243 }
244 }
245
246 fn try_initialize(&mut self, name: &str) -> Result<(), BackendError> {
256 let config = match self.backends.get(name) {
258 Some(BackendState::Uninitialized(config)) => config.clone(),
259 Some(BackendState::Ready(_)) => return Ok(()),
260 None => return Err(BackendError::NotFound(name.to_string())),
261 };
262
263 let backend = (self.factory)(name.to_string(), &config)?;
265 if let Some(state) = self.backends.get_mut(name) {
267 *state = BackendState::Ready(backend);
268 }
269 Ok(())
270 }
271}
272
273impl Default for BackendRegistry {
274 fn default() -> Self {
275 Self::new()
276 }
277}
278
279fn unsupported(name: &str, trait_name: &str) -> BackendError {
280 BackendError::UnsupportedOperation(format!("Backend '{name}' does not implement {trait_name}"))
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286 use rite_sdk::{Backend, BackendError, KeyId, KeyMetadata, KeySpec, KeyStoreBackend};
287
288 struct MinimalMock {
293 name: String,
294 }
295
296 impl Backend for MinimalMock {
297 fn name(&self) -> &str {
298 &self.name
299 }
300 fn provider(&self) -> &'static str {
301 "mock"
302 }
303 fn fingerprint(&self) -> String {
304 format!("mock={}", self.name)
305 }
306 fn as_keystore_mut(&mut self) -> Option<&mut dyn KeyStoreBackend> {
307 Some(self)
308 }
309 }
310
311 impl KeyStoreBackend for MinimalMock {
312 fn generate_key(&mut self, spec: KeySpec) -> Result<KeyMetadata, BackendError> {
313 Ok(KeyMetadata {
314 key_id: KeyId::new("mock-key-1"),
315 algorithm: spec.algorithm,
316 label: spec.label,
317 public_key: None,
318 check_value: None,
319 attestation: None,
320 })
321 }
322 fn import_key(
323 &mut self,
324 spec: KeySpec,
325 _key_bytes: &[u8],
326 _passphrase: Option<&[u8]>,
327 ) -> Result<KeyMetadata, BackendError> {
328 Ok(KeyMetadata {
329 key_id: KeyId::new("mock-imported-1"),
330 algorithm: spec.algorithm,
331 label: spec.label,
332 public_key: None,
333 check_value: None,
334 attestation: None,
335 })
336 }
337 fn export_public_key(
338 &self,
339 key_id: &KeyId,
340 ) -> Result<rite_sdk::PublicKeyDer, BackendError> {
341 Err(BackendError::KeyNotFound(key_id.to_string()))
342 }
343 fn list_keys(&self) -> Result<Vec<KeyMetadata>, BackendError> {
344 Ok(vec![])
345 }
346 fn delete_key(&mut self, _key_id: &KeyId) -> Result<(), BackendError> {
347 Ok(())
348 }
349 }
350
351 #[test]
356 fn registry_not_found_returns_none() {
357 let registry = BackendRegistry::new();
358 assert!(registry.get("nonexistent").is_none());
359 }
360
361 #[test]
362 fn registry_lists_backend_names_empty() {
363 let registry = BackendRegistry::new();
364 assert_eq!(registry.backend_names().count(), 0);
365 }
366
367 #[test]
368 fn register_then_get_returns_backend() {
369 let mut registry = BackendRegistry::new();
370 registry.register(Box::new(MinimalMock {
371 name: "mock".to_string(),
372 }));
373 assert!(registry.get("mock").is_some());
374 assert_eq!(registry.get("mock").unwrap().name(), "mock");
375 }
376
377 #[test]
378 fn register_then_get_keystore_succeeds() {
379 let mut registry = BackendRegistry::new();
380 registry.register(Box::new(MinimalMock {
381 name: "mock".to_string(),
382 }));
383 assert!(registry.get_keystore("mock").is_ok());
384 }
385
386 #[test]
387 fn declare_and_lazy_init_via_factory() {
388 let mut registry = BackendRegistry::with_factory(Box::new(|name, _config| {
389 Ok(Box::new(MinimalMock { name }) as Box<dyn Backend>)
390 }));
391
392 let config = BackendConfig {
393 provider: "mock".to_string(),
394 extra: serde_json::json!({}),
395 };
396
397 registry.declare("lazy".to_string(), config);
399
400 assert!(registry.get("lazy").is_none());
402
403 let result = registry.get_keystore("lazy");
405 assert!(result.is_ok(), "Expected Ok, got: {:?}", result.err());
406
407 assert!(registry.get("lazy").is_some());
409 }
410
411 #[test]
412 fn failed_initialization_is_probed_again() {
413 use std::sync::Arc;
414 use std::sync::atomic::{AtomicU32, Ordering};
415
416 let calls = Arc::new(AtomicU32::new(0));
419 let calls_in_factory = Arc::clone(&calls);
420 let mut registry = BackendRegistry::with_factory(Box::new(move |name, _config| {
421 if calls_in_factory.fetch_add(1, Ordering::SeqCst) == 0 {
422 Err(BackendError::HardwareFailure("no device".to_string()))
423 } else {
424 Ok(Box::new(MinimalMock { name }) as Box<dyn Backend>)
425 }
426 }));
427 registry.declare(
428 "token".to_string(),
429 BackendConfig {
430 provider: "mock".to_string(),
431 extra: serde_json::json!({}),
432 },
433 );
434
435 let first = registry.get_mut("token");
436 assert!(matches!(first, Err(BackendError::HardwareFailure(_))));
437
438 let second = registry.get_mut("token");
439 assert!(second.is_ok(), "second access probes the hardware again");
440 assert_eq!(calls.load(Ordering::SeqCst), 2);
441 }
442
443 #[test]
444 fn unsupported_trait_returns_error() {
445 let mut registry = BackendRegistry::new();
447 registry.register(Box::new(MinimalMock {
448 name: "mock".to_string(),
449 }));
450
451 let result = registry.get_sign("mock");
452 assert!(result.is_err());
453 match result.err().unwrap() {
454 BackendError::UnsupportedOperation(msg) => {
455 assert!(
456 msg.contains("SignBackend"),
457 "Expected UnsupportedOperation for SignBackend, got: {msg}"
458 );
459 }
460 e => panic!("Expected UnsupportedOperation, got: {e}"),
461 }
462 }
463
464 #[test]
465 fn get_mut_undeclared_returns_not_found() {
466 let mut registry = BackendRegistry::new();
467 let result = registry.get_mut("nonexistent");
468 assert!(result.is_err());
469 match result.err().unwrap() {
470 BackendError::NotFound(name) => assert_eq!(name, "nonexistent"),
471 e => panic!("Expected NotFound, got: {e}"),
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}