1use crate::NonceCredential;
2use crate::nonce::error::NonceError;
3use crate::nonce::storage::NonceStorage;
4use crate::nonce::time_utils::{current_timestamp, is_outside_window};
5use base64::Engine;
6use hmac::{Hmac, Mac};
7use sha2::Sha256;
8use std::future::Future;
9use std::pin::Pin;
10use std::sync::Arc;
11use std::time::Duration;
12
13type HmacSha256 = Hmac<Sha256>;
14
15type SecretProviderFn = Box<
17 dyn for<'a> Fn(
18 Option<&'a str>,
19 )
20 -> Pin<Box<dyn Future<Output = Result<Vec<u8>, NonceError>> + Send + 'a>>
21 + Send
22 + Sync,
23>;
24
25pub struct CredentialVerifier {
35 storage: Arc<dyn NonceStorage>,
36 secret: Option<Vec<u8>>,
37 secret_provider: Option<SecretProviderFn>,
38 context: Option<String>,
39 storage_ttl: Duration,
40 time_window: Duration,
41}
42
43impl CredentialVerifier {
44 pub fn new(storage: Arc<dyn NonceStorage>) -> Self {
69 Self {
70 storage,
71 secret: None,
72 secret_provider: None,
73 context: None,
74 storage_ttl: Duration::from_secs(300), time_window: Duration::from_secs(60), }
77 }
78
79 pub fn with_secret(mut self, secret: &[u8]) -> Self {
81 self.secret = Some(secret.to_vec());
82 self
83 }
84
85 pub fn with_context(mut self, context: Option<&str>) -> Self {
90 self.context = context.map(|s| s.to_string());
91 self
92 }
93
94 pub fn with_storage_ttl(mut self, storage_ttl: Duration) -> Self {
119 self.storage_ttl = storage_ttl;
120 self
121 }
122
123 pub fn with_time_window(mut self, time_window: Duration) -> Self {
125 self.time_window = time_window;
126 self
127 }
128
129 pub fn with_secret_provider<P, F>(mut self, provider: P) -> Self
162 where
163 P: for<'a> Fn(Option<&'a str>) -> F + Send + Sync + 'static,
164 F: Future<Output = Result<Vec<u8>, NonceError>> + Send + 'static,
165 {
166 self.secret_provider = Some(Box::new(move |context| Box::pin(provider(context))));
167 self
168 }
169
170 pub async fn verify(
203 self,
204 credential: &NonceCredential,
205 payload: &[u8],
206 ) -> Result<(), NonceError> {
207 let secret = if let Some(ref provider) = self.secret_provider {
208 provider(self.context.as_deref()).await?
209 } else if let Some(ref secret) = self.secret {
210 secret.clone()
211 } else {
212 return Err(NonceError::CryptoError(
213 "Either secret or secret_provider must be set before verification".to_string(),
214 ));
215 };
216
217 self.verify_internal(credential, &secret, |secret| {
218 self.verify_signature(
219 secret,
220 credential.timestamp,
221 &credential.nonce,
222 payload,
223 &credential.signature,
224 )
225 })
226 .await
227 }
228
229 pub async fn verify_structured(
256 self,
257 credential: &NonceCredential,
258 components: &[&[u8]],
259 ) -> Result<(), NonceError> {
260 let secret = if let Some(ref provider) = self.secret_provider {
261 provider(self.context.as_deref()).await?
262 } else if let Some(ref secret) = self.secret {
263 secret.clone()
264 } else {
265 return Err(NonceError::CryptoError(
266 "Either secret or secret_provider must be set before verification".to_string(),
267 ));
268 };
269
270 self.verify_internal(credential, &secret, |secret| {
271 self.verify_structured_signature(
272 secret,
273 credential.timestamp,
274 &credential.nonce,
275 components,
276 &credential.signature,
277 )
278 })
279 .await
280 }
281
282 pub async fn verify_with<F>(
322 self,
323 credential: &NonceCredential,
324 mac_fn: F,
325 ) -> Result<(), NonceError>
326 where
327 F: FnOnce(&mut HmacSha256),
328 {
329 let secret = if let Some(ref provider) = self.secret_provider {
330 provider(self.context.as_deref()).await?
331 } else if let Some(ref secret) = self.secret {
332 secret.clone()
333 } else {
334 return Err(NonceError::CryptoError(
335 "Either secret or secret_provider must be set before verification".to_string(),
336 ));
337 };
338
339 self.verify_internal(credential, &secret, |secret| {
340 self.verify_custom_signature(
341 secret,
342 credential.timestamp,
343 &credential.nonce,
344 &credential.signature,
345 mac_fn,
346 )
347 })
348 .await
349 }
350
351 fn verify_signature(
353 &self,
354 secret: &[u8],
355 timestamp: u64,
356 nonce: &str,
357 payload: &[u8],
358 signature: &str,
359 ) -> Result<bool, NonceError> {
360 let mut mac = HmacSha256::new_from_slice(secret)
361 .map_err(|e| NonceError::CryptoError(format!("Invalid secret key: {e}")))?;
362
363 mac.update(timestamp.to_string().as_bytes());
364 mac.update(nonce.as_bytes());
365 mac.update(payload);
366
367 let expected_signature =
368 base64::engine::general_purpose::STANDARD.encode(mac.finalize().into_bytes());
369 Ok(expected_signature == signature)
370 }
371
372 fn verify_structured_signature(
374 &self,
375 secret: &[u8],
376 timestamp: u64,
377 nonce: &str,
378 components: &[&[u8]],
379 signature: &str,
380 ) -> Result<bool, NonceError> {
381 let mut mac = HmacSha256::new_from_slice(secret)
382 .map_err(|e| NonceError::CryptoError(format!("Invalid secret key: {e}")))?;
383
384 mac.update(timestamp.to_string().as_bytes());
385 mac.update(nonce.as_bytes());
386 for component in components {
387 mac.update(component);
388 }
389
390 let expected_signature =
391 base64::engine::general_purpose::STANDARD.encode(mac.finalize().into_bytes());
392 Ok(expected_signature == signature)
393 }
394
395 fn verify_custom_signature<F>(
397 &self,
398 secret: &[u8],
399 _timestamp: u64,
400 _nonce: &str,
401 signature: &str,
402 mac_fn: F,
403 ) -> Result<bool, NonceError>
404 where
405 F: FnOnce(&mut HmacSha256),
406 {
407 let mut mac = HmacSha256::new_from_slice(secret)
408 .map_err(|e| NonceError::CryptoError(format!("Invalid secret key: {e}")))?;
409
410 mac_fn(&mut mac);
411
412 let expected_signature =
413 base64::engine::general_purpose::STANDARD.encode(mac.finalize().into_bytes());
414 Ok(expected_signature == signature)
415 }
416
417 async fn verify_internal<F>(
419 &self,
420 credential: &NonceCredential,
421 secret: &[u8],
422 verify_signature: F,
423 ) -> Result<(), NonceError>
424 where
425 F: FnOnce(&[u8]) -> Result<bool, NonceError>,
426 {
427 let current_time = current_timestamp()?;
429 if is_outside_window(credential.timestamp, current_time, self.time_window) {
430 return Err(NonceError::TimestampOutOfWindow);
431 }
432
433 let signature_valid = verify_signature(secret)?;
435 if !signature_valid {
436 return Err(NonceError::InvalidSignature);
437 }
438
439 let context_str = self.context.as_deref();
441
442 if self.storage.exists(&credential.nonce, context_str).await? {
444 return Err(NonceError::DuplicateNonce);
445 }
446
447 self.storage
449 .set(&credential.nonce, context_str, self.storage_ttl)
450 .await?;
451
452 Ok(())
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use super::*;
459 use crate::CredentialBuilder;
460 use crate::nonce::storage::MemoryStorage;
461 use std::time::Duration;
462
463 #[tokio::test]
464 async fn test_basic_verification() {
465 let storage = Arc::new(MemoryStorage::new());
466 let secret = b"test_secret";
467 let payload = b"test_payload";
468
469 let credential = CredentialBuilder::new(secret).sign(payload).unwrap();
470
471 let result = CredentialVerifier::new(storage)
472 .with_secret(secret)
473 .verify(&credential, payload)
474 .await;
475
476 assert!(result.is_ok());
477 }
478
479 #[tokio::test]
480 async fn test_duplicate_nonce_rejection() {
481 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
482 let secret = b"test_secret";
483 let payload = b"test_payload";
484
485 let credential = CredentialBuilder::new(secret).sign(payload).unwrap();
486
487 CredentialVerifier::new(Arc::clone(&storage))
489 .with_secret(secret)
490 .verify(&credential, payload)
491 .await
492 .unwrap();
493
494 let result = CredentialVerifier::new(storage)
496 .with_secret(secret)
497 .verify(&credential, payload)
498 .await;
499
500 assert!(matches!(result, Err(NonceError::DuplicateNonce)));
501 }
502
503 #[tokio::test]
504 async fn test_invalid_signature() {
505 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
506 let payload = b"test_payload";
507
508 let credential = CredentialBuilder::new(b"secret1").sign(payload).unwrap();
509
510 let result = CredentialVerifier::new(storage)
511 .with_secret(b"secret2") .verify(&credential, payload)
513 .await;
514
515 assert!(matches!(result, Err(NonceError::InvalidSignature)));
516 }
517
518 #[tokio::test]
519 async fn test_timestamp_out_of_window() {
520 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
521 let secret = b"test_secret";
522 let payload = b"test_payload";
523
524 let old_timestamp = (current_timestamp().unwrap() - 3600) as u64; let credential = CredentialBuilder::new(secret)
527 .with_time_provider(move || Ok(old_timestamp))
528 .sign(payload)
529 .unwrap();
530
531 let result = CredentialVerifier::new(storage)
532 .with_secret(secret)
533 .with_time_window(Duration::from_secs(60)) .verify(&credential, payload)
535 .await;
536
537 assert!(matches!(result, Err(NonceError::TimestampOutOfWindow)));
538 }
539
540 #[tokio::test]
541 async fn test_context_isolation() {
542 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
543 let secret = b"test_secret";
544 let payload = b"test_payload";
545
546 let credential = CredentialBuilder::new(secret).sign(payload).unwrap();
547
548 CredentialVerifier::new(Arc::clone(&storage))
550 .with_secret(secret)
551 .with_context(Some("context1"))
552 .verify(&credential, payload)
553 .await
554 .unwrap();
555
556 let result = CredentialVerifier::new(Arc::clone(&storage))
558 .with_secret(secret)
559 .with_context(Some("context2"))
560 .verify(&credential, payload)
561 .await;
562
563 assert!(result.is_ok());
564
565 let result = CredentialVerifier::new(storage)
567 .with_secret(secret)
568 .with_context(Some("context1"))
569 .verify(&credential, payload)
570 .await;
571
572 assert!(matches!(result, Err(NonceError::DuplicateNonce)));
573 }
574
575 #[tokio::test]
576 async fn test_structured_verification() {
577 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
578 let secret = b"test_secret";
579 let components = [b"part1".as_slice(), b"part2", b"part3"];
580
581 let credential = CredentialBuilder::new(secret)
582 .sign_structured(&components)
583 .unwrap();
584
585 let result = CredentialVerifier::new(storage)
586 .with_secret(secret)
587 .verify_structured(&credential, &components)
588 .await;
589
590 assert!(result.is_ok());
591 }
592
593 #[tokio::test]
594 async fn test_custom_verification() {
595 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
596 let secret = b"test_secret";
597
598 let credential = CredentialBuilder::new(secret)
599 .sign_with(|mac, timestamp, nonce| {
600 mac.update(b"prefix:");
601 mac.update(timestamp.as_bytes());
602 mac.update(b":nonce:");
603 mac.update(nonce.as_bytes());
604 mac.update(b":custom");
605 })
606 .unwrap();
607
608 let result = CredentialVerifier::new(storage)
609 .with_secret(secret)
610 .verify_with(&credential, |mac| {
611 mac.update(b"prefix:");
612 mac.update(credential.timestamp.to_string().as_bytes());
613 mac.update(b":nonce:");
614 mac.update(credential.nonce.as_bytes());
615 mac.update(b":custom");
616 })
617 .await;
618
619 assert!(result.is_ok());
620 }
621
622 #[tokio::test]
623 async fn test_secret_provider() {
624 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
625 let payload = b"test_payload";
626
627 let credential = CredentialBuilder::new(b"user123_secret")
628 .sign(payload)
629 .unwrap();
630
631 let result = CredentialVerifier::new(storage)
632 .with_context(Some("user123"))
633 .with_secret_provider(|context| {
634 let owned_context = context.map(|s| s.to_owned());
635 async move {
636 match owned_context.as_deref() {
637 Some("user123") => Ok(b"user123_secret".to_vec()),
638 Some("user456") => Ok(b"user456_secret".to_vec()),
639 _ => Err(NonceError::CryptoError("Unknown user".to_string())),
640 }
641 }
642 })
643 .verify(&credential, payload)
644 .await;
645
646 assert!(result.is_ok());
647 }
648
649 #[tokio::test]
650 async fn test_secret_provider_error() {
651 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
652 let payload = b"test_payload";
653
654 let credential = CredentialBuilder::new(b"secret").sign(payload).unwrap();
655
656 let result = CredentialVerifier::new(storage)
657 .with_context(Some("unknown_user"))
658 .with_secret_provider(|_context| async {
659 Err(NonceError::from_storage_message("User not found"))
660 })
661 .verify(&credential, payload)
662 .await;
663
664 assert!(matches!(result, Err(NonceError::StorageError(_))));
665 }
666
667 #[tokio::test]
668 async fn test_verification_without_secret() {
669 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
670 let payload = b"test_payload";
671
672 let credential = CredentialBuilder::new(b"secret").sign(payload).unwrap();
673
674 let result = CredentialVerifier::new(storage)
675 .verify(&credential, payload) .await;
677
678 assert!(matches!(result, Err(NonceError::CryptoError(_))));
679 }
680
681 #[test]
682 fn test_credential_verifier_implements_sync() {
683 fn assert_sync<T: Sync>() {}
685 assert_sync::<CredentialVerifier>();
686
687 let storage: Arc<dyn NonceStorage> = Arc::new(MemoryStorage::new());
689 let verifier = Arc::new(CredentialVerifier::new(storage).with_secret(b"test"));
690
691 let verifier_clone = Arc::clone(&verifier);
692 let handle = std::thread::spawn(move || {
693 drop(verifier_clone);
695 });
696
697 handle.join().unwrap();
698 println!("CredentialVerifier successfully implements Sync!");
699 }
700}