1use auths_verifier::core::Ed25519PublicKey;
4
5use crate::crypto::provider_bridge;
6use crate::crypto::signer::{decrypt_keypair, extract_seed_from_key_bytes};
7use crate::error::AgentError;
8use crate::storage::keychain::{IdentityDID, KeyAlias, KeyStorage};
9
10use std::collections::HashMap;
11use std::sync::{Arc, Mutex};
12use std::time::{Duration, Instant};
13use zeroize::Zeroizing;
14
15type PassphraseCallback = dyn Fn(&str) -> Result<Zeroizing<String>, AgentError> + Send + Sync;
17
18#[derive(Debug, thiserror::Error)]
31pub enum DidResolverError {
32 #[error("Unsupported DID method: {0}")]
34 UnsupportedMethod(String),
35
36 #[error("Invalid did:key format: {0}")]
38 InvalidDidKey(String),
39
40 #[error("Invalid did:key format: {0}")]
42 InvalidDidKeyFormat(String),
43
44 #[error("did:key decoding failed: {0}")]
46 DidKeyDecodingFailed(String),
47
48 #[error("Invalid did:key multicodec prefix")]
50 InvalidDidKeyMulticodec,
51
52 #[error("Resolution error: {0}")]
54 Resolution(String),
55
56 #[error("Repository error: {0}")]
58 Repository(String),
59}
60
61#[derive(Debug, Clone)]
75pub enum ResolvedDid {
76 Key {
78 did: String,
80 public_key: Ed25519PublicKey,
82 },
83 Keri {
85 did: String,
87 public_key: Ed25519PublicKey,
89 sequence: u64,
91 can_rotate: bool,
93 },
94}
95
96impl ResolvedDid {
97 pub fn did(&self) -> &str {
99 match self {
100 ResolvedDid::Key { did, .. } | ResolvedDid::Keri { did, .. } => did,
101 }
102 }
103
104 pub fn public_key(&self) -> &Ed25519PublicKey {
106 match self {
107 ResolvedDid::Key { public_key, .. } | ResolvedDid::Keri { public_key, .. } => {
108 public_key
109 }
110 }
111 }
112
113 pub fn is_key(&self) -> bool {
115 matches!(self, ResolvedDid::Key { .. })
116 }
117
118 pub fn is_keri(&self) -> bool {
120 matches!(self, ResolvedDid::Keri { .. })
121 }
122}
123
124pub trait DidResolver: Send + Sync {
149 fn resolve(&self, did: &str) -> Result<ResolvedDid, DidResolverError>;
151}
152
153pub trait PassphraseProvider: Send + Sync {
159 fn get_passphrase(&self, prompt_message: &str) -> Result<Zeroizing<String>, AgentError>;
169
170 fn on_incorrect_passphrase(&self, _prompt_message: &str) {}
179}
180
181pub trait SecureSigner: Send + Sync {
184 fn sign_with_alias(
199 &self,
200 alias: &KeyAlias,
201 passphrase_provider: &dyn PassphraseProvider,
202 message: &[u8],
203 ) -> Result<Vec<u8>, AgentError>;
204
205 fn sign_for_identity(
224 &self,
225 identity_did: &IdentityDID,
226 passphrase_provider: &dyn PassphraseProvider,
227 message: &[u8],
228 ) -> Result<Vec<u8>, AgentError>;
229}
230
231pub struct StorageSigner<S: KeyStorage> {
236 storage: S,
238}
239
240impl<S: KeyStorage> StorageSigner<S> {
241 pub fn new(storage: S) -> Self {
243 Self { storage }
244 }
245
246 pub fn inner(&self) -> &S {
248 &self.storage
249 }
250}
251
252impl<S: KeyStorage + Send + Sync + 'static> SecureSigner for StorageSigner<S> {
253 fn sign_with_alias(
254 &self,
255 alias: &KeyAlias,
256 passphrase_provider: &dyn PassphraseProvider,
257 message: &[u8],
258 ) -> Result<Vec<u8>, AgentError> {
259 let (_identity_did, encrypted_data) = self.storage.load_key(alias)?;
260
261 const MAX_ATTEMPTS: u8 = 3;
262 let mut attempt = 0u8;
263 let key_bytes = loop {
264 let prompt = if attempt == 0 {
265 format!("Enter passphrase for key '{}' to sign:", alias)
266 } else {
267 format!(
268 "Incorrect passphrase, try again ({}/{}):",
269 attempt + 1,
270 MAX_ATTEMPTS
271 )
272 };
273
274 let passphrase = passphrase_provider.get_passphrase(&prompt)?;
275
276 match decrypt_keypair(&encrypted_data, &passphrase) {
277 Ok(kb) => break kb,
278 Err(AgentError::IncorrectPassphrase) if attempt + 1 < MAX_ATTEMPTS => {
279 passphrase_provider.on_incorrect_passphrase(&prompt);
280 attempt += 1;
281 }
282 Err(e) => return Err(e),
283 }
284 };
285
286 let seed = extract_seed_from_key_bytes(&key_bytes)?;
287
288 provider_bridge::sign_ed25519_sync(&seed, message)
289 .map_err(|e| AgentError::CryptoError(format!("Ed25519 signing failed: {}", e)))
290 }
291
292 fn sign_for_identity(
293 &self,
294 identity_did: &IdentityDID,
295 passphrase_provider: &dyn PassphraseProvider,
296 message: &[u8],
297 ) -> Result<Vec<u8>, AgentError> {
298 let aliases = self.storage.list_aliases_for_identity(identity_did)?;
300
301 let alias = aliases.first().ok_or(AgentError::KeyNotFound)?;
303
304 self.sign_with_alias(alias, passphrase_provider, message)
306 }
307}
308
309pub struct CallbackPassphraseProvider {
325 callback: Box<PassphraseCallback>,
326}
327
328impl CallbackPassphraseProvider {
329 pub fn new<F>(callback: F) -> Self
334 where
335 F: Fn(&str) -> Result<Zeroizing<String>, AgentError> + Send + Sync + 'static,
336 {
337 Self {
338 callback: Box::new(callback),
339 }
340 }
341}
342
343impl PassphraseProvider for CallbackPassphraseProvider {
344 fn get_passphrase(&self, prompt_message: &str) -> Result<Zeroizing<String>, AgentError> {
345 (self.callback)(prompt_message)
346 }
347}
348
349pub struct CachedPassphraseProvider {
362 inner: Arc<dyn PassphraseProvider + Send + Sync>,
363 cache: Mutex<HashMap<String, (Zeroizing<String>, Instant)>>,
364 ttl: Duration,
365}
366
367impl CachedPassphraseProvider {
368 pub fn new(inner: Arc<dyn PassphraseProvider + Send + Sync>, ttl: Duration) -> Self {
374 Self {
375 inner,
376 cache: Mutex::new(HashMap::new()),
377 ttl,
378 }
379 }
380
381 pub fn clear_cache(&self) {
386 self.cache.lock().unwrap().clear();
387 }
388}
389
390impl PassphraseProvider for CachedPassphraseProvider {
391 fn get_passphrase(&self, prompt_message: &str) -> Result<Zeroizing<String>, AgentError> {
392 let mut cache = self.cache.lock().unwrap();
393
394 if let Some((passphrase, cached_at)) = cache.get(prompt_message) {
396 if cached_at.elapsed() < self.ttl {
397 return Ok(passphrase.clone());
399 }
400 cache.remove(prompt_message);
402 }
403
404 drop(cache); let passphrase = self.inner.get_passphrase(prompt_message)?;
407
408 let mut cache = self.cache.lock().unwrap();
410 cache.insert(
411 prompt_message.to_string(),
412 (passphrase.clone(), Instant::now()),
413 );
414 Ok(passphrase)
415 }
416
417 fn on_incorrect_passphrase(&self, prompt_message: &str) {
418 self.cache.lock().unwrap().remove(prompt_message);
419 }
420}
421
422pub struct PrefilledPassphraseProvider {
440 passphrase: Zeroizing<String>,
441}
442
443impl PrefilledPassphraseProvider {
444 pub fn new(passphrase: &str) -> Self {
454 Self {
455 passphrase: Zeroizing::new(passphrase.to_string()),
456 }
457 }
458}
459
460impl PassphraseProvider for PrefilledPassphraseProvider {
461 fn get_passphrase(&self, _prompt_message: &str) -> Result<Zeroizing<String>, AgentError> {
462 Ok(self.passphrase.clone())
463 }
464}
465
466pub struct UnifiedPassphraseProvider {
471 inner: Arc<dyn PassphraseProvider + Send + Sync>,
472 cached: Mutex<Option<Zeroizing<String>>>,
473}
474
475impl UnifiedPassphraseProvider {
476 pub fn new(inner: Arc<dyn PassphraseProvider + Send + Sync>) -> Self {
478 Self {
479 inner,
480 cached: Mutex::new(None),
481 }
482 }
483}
484
485impl PassphraseProvider for UnifiedPassphraseProvider {
486 fn get_passphrase(&self, prompt_message: &str) -> Result<Zeroizing<String>, AgentError> {
487 let mut guard = self.cached.lock().unwrap();
488 if let Some(ref cached) = *guard {
489 return Ok(Zeroizing::new(cached.as_str().to_string()));
490 }
491 let passphrase = self.inner.get_passphrase(prompt_message)?;
492 *guard = Some(Zeroizing::new(passphrase.as_str().to_string()));
493 Ok(passphrase)
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500 use crate::crypto::signer::encrypt_keypair;
501 use ring::rand::SystemRandom;
502 use ring::signature::{ED25519, Ed25519KeyPair, KeyPair, UnparsedPublicKey};
503 use std::collections::HashMap;
504 use std::sync::Mutex;
505
506 struct MockKeyStorage {
508 keys: Mutex<HashMap<String, (IdentityDID, Vec<u8>)>>,
509 }
510
511 impl MockKeyStorage {
512 fn new() -> Self {
513 Self {
514 keys: Mutex::new(HashMap::new()),
515 }
516 }
517 }
518
519 impl KeyStorage for MockKeyStorage {
520 fn store_key(
521 &self,
522 alias: &KeyAlias,
523 identity_did: &IdentityDID,
524 encrypted_key_data: &[u8],
525 ) -> Result<(), AgentError> {
526 self.keys.lock().unwrap().insert(
527 alias.as_str().to_string(),
528 (identity_did.clone(), encrypted_key_data.to_vec()),
529 );
530 Ok(())
531 }
532
533 fn load_key(&self, alias: &KeyAlias) -> Result<(IdentityDID, Vec<u8>), AgentError> {
534 self.keys
535 .lock()
536 .unwrap()
537 .get(alias.as_str())
538 .cloned()
539 .ok_or(AgentError::KeyNotFound)
540 }
541
542 fn delete_key(&self, alias: &KeyAlias) -> Result<(), AgentError> {
543 self.keys
544 .lock()
545 .unwrap()
546 .remove(alias.as_str())
547 .map(|_| ())
548 .ok_or(AgentError::KeyNotFound)
549 }
550
551 fn list_aliases(&self) -> Result<Vec<KeyAlias>, AgentError> {
552 Ok(self
553 .keys
554 .lock()
555 .unwrap()
556 .keys()
557 .map(|s| KeyAlias::new_unchecked(s.clone()))
558 .collect())
559 }
560
561 fn list_aliases_for_identity(
562 &self,
563 identity_did: &IdentityDID,
564 ) -> Result<Vec<KeyAlias>, AgentError> {
565 Ok(self
566 .keys
567 .lock()
568 .unwrap()
569 .iter()
570 .filter(|(_, (did, _))| did == identity_did)
571 .map(|(alias, _)| KeyAlias::new_unchecked(alias.clone()))
572 .collect())
573 }
574
575 fn get_identity_for_alias(&self, alias: &KeyAlias) -> Result<IdentityDID, AgentError> {
576 self.keys
577 .lock()
578 .unwrap()
579 .get(alias.as_str())
580 .map(|(did, _)| did.clone())
581 .ok_or(AgentError::KeyNotFound)
582 }
583
584 fn backend_name(&self) -> &'static str {
585 "MockKeyStorage"
586 }
587 }
588
589 struct MockPassphraseProvider {
591 passphrase: String,
592 }
593
594 impl MockPassphraseProvider {
595 fn new(passphrase: &str) -> Self {
596 Self {
597 passphrase: passphrase.to_string(),
598 }
599 }
600 }
601
602 impl PassphraseProvider for MockPassphraseProvider {
603 fn get_passphrase(&self, _prompt_message: &str) -> Result<Zeroizing<String>, AgentError> {
604 Ok(Zeroizing::new(self.passphrase.clone()))
605 }
606 }
607
608 fn generate_test_keypair() -> (Vec<u8>, Vec<u8>) {
609 let rng = SystemRandom::new();
610 let pkcs8_doc = Ed25519KeyPair::generate_pkcs8(&rng).expect("Failed to generate PKCS#8");
611 let pkcs8_bytes = pkcs8_doc.as_ref().to_vec();
612 let keypair = Ed25519KeyPair::from_pkcs8(&pkcs8_bytes).expect("Failed to parse PKCS#8");
613 let pubkey_bytes = keypair.public_key().as_ref().to_vec();
614 (pkcs8_bytes, pubkey_bytes)
615 }
616
617 #[test]
618 fn test_sign_for_identity_success() {
619 let (pkcs8_bytes, pubkey_bytes) = generate_test_keypair();
620 let passphrase = "Test-P@ss12345";
621 let identity_did = IdentityDID::new("did:keri:ABC123");
622 let alias = KeyAlias::new_unchecked("test-key-alias");
623
624 let encrypted = encrypt_keypair(&pkcs8_bytes, passphrase).expect("Failed to encrypt");
626
627 let storage = MockKeyStorage::new();
629 storage
630 .store_key(&alias, &identity_did, &encrypted)
631 .expect("Failed to store key");
632
633 let signer = StorageSigner::new(storage);
635 let passphrase_provider = MockPassphraseProvider::new(passphrase);
636
637 let message = b"test message for sign_for_identity";
639 let signature = signer
640 .sign_for_identity(&identity_did, &passphrase_provider, message)
641 .expect("Signing failed");
642
643 let public_key = UnparsedPublicKey::new(&ED25519, &pubkey_bytes);
645 assert!(public_key.verify(message, &signature).is_ok());
646 }
647
648 #[test]
649 fn test_sign_for_identity_no_key_for_identity() {
650 let storage = MockKeyStorage::new();
651 let signer = StorageSigner::new(storage);
652 let passphrase_provider = MockPassphraseProvider::new("any-passphrase");
653
654 let identity_did = IdentityDID::new("did:keri:NONEXISTENT");
655 let message = b"test message";
656
657 let result = signer.sign_for_identity(&identity_did, &passphrase_provider, message);
658 assert!(matches!(result, Err(AgentError::KeyNotFound)));
659 }
660
661 #[test]
662 fn test_sign_for_identity_multiple_aliases() {
663 let (pkcs8_bytes, pubkey_bytes) = generate_test_keypair();
665 let passphrase = "Test-P@ss12345";
666 let identity_did = IdentityDID::new("did:keri:MULTI123");
667
668 let encrypted = encrypt_keypair(&pkcs8_bytes, passphrase).expect("Failed to encrypt");
669
670 let storage = MockKeyStorage::new();
671 let alias = KeyAlias::new_unchecked("primary-alias");
673 storage
674 .store_key(&alias, &identity_did, &encrypted)
675 .expect("Failed to store key");
676
677 let signer = StorageSigner::new(storage);
678 let passphrase_provider = MockPassphraseProvider::new(passphrase);
679
680 let message = b"test message with multiple aliases";
681 let signature = signer
682 .sign_for_identity(&identity_did, &passphrase_provider, message)
683 .expect("Signing should succeed");
684
685 let public_key = UnparsedPublicKey::new(&ED25519, &pubkey_bytes);
687 assert!(public_key.verify(message, &signature).is_ok());
688 }
689
690 #[test]
691 fn test_callback_passphrase_provider() {
692 use std::sync::Arc;
693 use std::sync::atomic::{AtomicUsize, Ordering};
694
695 let call_count = Arc::new(AtomicUsize::new(0));
697 let call_count_clone = Arc::clone(&call_count);
698
699 let provider = CallbackPassphraseProvider::new(move |prompt| {
700 call_count_clone.fetch_add(1, Ordering::SeqCst);
701 assert!(prompt.contains("test-alias"));
702 Ok(Zeroizing::new("callback-passphrase".to_string()))
703 });
704
705 let result = provider.get_passphrase("Enter passphrase for test-alias:");
707 assert!(result.is_ok());
708 assert_eq!(*result.unwrap(), "callback-passphrase");
709 assert_eq!(call_count.load(Ordering::SeqCst), 1);
710
711 let result2 = provider.get_passphrase("Another prompt for test-alias");
713 assert!(result2.is_ok());
714 assert_eq!(call_count.load(Ordering::SeqCst), 2);
715 }
716
717 #[test]
718 fn test_callback_passphrase_provider_error() {
719 let provider =
720 CallbackPassphraseProvider::new(|_prompt| Err(AgentError::UserInputCancelled));
721
722 let result = provider.get_passphrase("Enter passphrase:");
723 assert!(matches!(result, Err(AgentError::UserInputCancelled)));
724 }
725
726 #[test]
727 fn test_cached_passphrase_provider_cache_hit() {
728 use std::sync::Arc;
729 use std::sync::atomic::{AtomicUsize, Ordering};
730 use std::time::Duration;
731
732 let call_count = Arc::new(AtomicUsize::new(0));
733 let call_count_clone = Arc::clone(&call_count);
734
735 let inner = Arc::new(CallbackPassphraseProvider::new(move |_prompt| {
736 call_count_clone.fetch_add(1, Ordering::SeqCst);
737 Ok(Zeroizing::new("cached-pass".to_string()))
738 }));
739
740 let cached = CachedPassphraseProvider::new(inner, Duration::from_secs(60));
741
742 let result1 = cached.get_passphrase("prompt1");
744 assert!(result1.is_ok());
745 assert_eq!(*result1.unwrap(), "cached-pass");
746 assert_eq!(call_count.load(Ordering::SeqCst), 1);
747
748 let result2 = cached.get_passphrase("prompt1");
750 assert!(result2.is_ok());
751 assert_eq!(*result2.unwrap(), "cached-pass");
752 assert_eq!(call_count.load(Ordering::SeqCst), 1); }
754
755 #[test]
756 fn test_cached_passphrase_provider_cache_miss() {
757 use std::sync::Arc;
758 use std::sync::atomic::{AtomicUsize, Ordering};
759 use std::time::Duration;
760
761 let call_count = Arc::new(AtomicUsize::new(0));
762 let call_count_clone = Arc::clone(&call_count);
763
764 let inner = Arc::new(CallbackPassphraseProvider::new(move |_prompt| {
765 call_count_clone.fetch_add(1, Ordering::SeqCst);
766 Ok(Zeroizing::new("pass".to_string()))
767 }));
768
769 let cached = CachedPassphraseProvider::new(inner, Duration::from_secs(60));
770
771 let _ = cached.get_passphrase("prompt1");
773 assert_eq!(call_count.load(Ordering::SeqCst), 1);
774
775 let _ = cached.get_passphrase("prompt2");
776 assert_eq!(call_count.load(Ordering::SeqCst), 2);
777
778 let _ = cached.get_passphrase("prompt3");
779 assert_eq!(call_count.load(Ordering::SeqCst), 3);
780 }
781
782 #[test]
783 fn test_cached_passphrase_provider_expiry() {
784 use std::sync::Arc;
785 use std::sync::atomic::{AtomicUsize, Ordering};
786 use std::time::Duration;
787
788 let call_count = Arc::new(AtomicUsize::new(0));
789 let call_count_clone = Arc::clone(&call_count);
790
791 let inner = Arc::new(CallbackPassphraseProvider::new(move |_prompt| {
792 call_count_clone.fetch_add(1, Ordering::SeqCst);
793 Ok(Zeroizing::new("pass".to_string()))
794 }));
795
796 let cached = CachedPassphraseProvider::new(inner, Duration::from_millis(10));
798
799 let _ = cached.get_passphrase("prompt");
801 assert_eq!(call_count.load(Ordering::SeqCst), 1);
802
803 std::thread::sleep(Duration::from_millis(20));
805
806 let _ = cached.get_passphrase("prompt");
808 assert_eq!(call_count.load(Ordering::SeqCst), 2);
809 }
810
811 #[test]
812 fn test_cached_passphrase_provider_clear_cache() {
813 use std::sync::Arc;
814 use std::sync::atomic::{AtomicUsize, Ordering};
815 use std::time::Duration;
816
817 let call_count = Arc::new(AtomicUsize::new(0));
818 let call_count_clone = Arc::clone(&call_count);
819
820 let inner = Arc::new(CallbackPassphraseProvider::new(move |_prompt| {
821 call_count_clone.fetch_add(1, Ordering::SeqCst);
822 Ok(Zeroizing::new("pass".to_string()))
823 }));
824
825 let cached = CachedPassphraseProvider::new(inner, Duration::from_secs(60));
826
827 let _ = cached.get_passphrase("prompt");
829 assert_eq!(call_count.load(Ordering::SeqCst), 1);
830
831 let _ = cached.get_passphrase("prompt");
833 assert_eq!(call_count.load(Ordering::SeqCst), 1);
834
835 cached.clear_cache();
837
838 let _ = cached.get_passphrase("prompt");
840 assert_eq!(call_count.load(Ordering::SeqCst), 2);
841 }
842
843 #[test]
844 fn test_prefilled_passphrase_provider_returns_stored_value() {
845 let provider = PrefilledPassphraseProvider::new("my-secret");
846 let result = provider.get_passphrase("any prompt").unwrap();
847 assert_eq!(*result, "my-secret");
848
849 let result2 = provider.get_passphrase("different prompt").unwrap();
850 assert_eq!(*result2, "my-secret");
851 }
852
853 #[test]
854 fn test_prefilled_passphrase_provider_empty_passphrase() {
855 let provider = PrefilledPassphraseProvider::new("");
856 let result = provider.get_passphrase("prompt").unwrap();
857 assert_eq!(*result, "");
858 }
859
860 #[test]
861 fn test_unified_passphrase_provider_prompts_once_for_multiple_keys() {
862 use std::sync::atomic::{AtomicUsize, Ordering};
863
864 let call_count = Arc::new(AtomicUsize::new(0));
865 let count_clone = call_count.clone();
866 let inner = CallbackPassphraseProvider::new(move |_prompt: &str| {
867 count_clone.fetch_add(1, Ordering::SeqCst);
868 Ok(Zeroizing::new("secret".to_string()))
869 });
870
871 let provider = UnifiedPassphraseProvider::new(Arc::new(inner));
872
873 let p1 = provider
875 .get_passphrase("Enter passphrase for DEVICE key 'dev':")
876 .unwrap();
877 let p2 = provider
878 .get_passphrase("Enter passphrase for IDENTITY key 'id':")
879 .unwrap();
880
881 assert_eq!(*p1, "secret");
882 assert_eq!(*p2, "secret");
883 assert_eq!(call_count.load(Ordering::SeqCst), 1); }
885}