Skip to main content

uvb_biometric_security/
lib.rs

1//! # Biometric Security Enhancements
2//!
3//! Enterprise-grade biometric authentication security to address:
4//! - **Risk #6**: Biometric spoofing attacks (deepfake voice, fingerprint molds)
5//!
6//! ## Features
7//!
8//! - **User Verification (UV) Enforcement**: Require UV=true in WebAuthn
9//! - **Liveness Detection Validation**: Verify authenticator supports liveness
10//! - **Step-Up Authentication**: Require additional auth for sensitive operations
11//! - **Risk-Based Re-Verification**: Re-verify biometrics based on risk
12//! - **Biometric Quality Checks**: Validate biometric quality scores
13//! - **Authenticator Attestation**: Verify authenticator capabilities
14//! - **Usage Tracking**: Monitor biometric usage patterns
15//! - **Anomaly Detection**: Detect suspicious biometric usage
16
17use async_trait::async_trait;
18use chrono::{DateTime, Duration, Utc};
19use serde::{Deserialize, Serialize};
20use std::collections::HashMap;
21use thiserror::Error;
22use tracing::info;
23use uuid::Uuid;
24
25use uvb_core::{TenantId, UserId};
26
27/// Errors that can occur in biometric security
28#[derive(Debug, Error)]
29pub enum BiometricError {
30    #[error("User verification not performed")]
31    UserVerificationNotPerformed,
32
33    #[error("Liveness detection not supported")]
34    LivenessNotSupported,
35
36    #[error("Step-up authentication required")]
37    StepUpRequired,
38
39    #[error("Biometric re-verification required")]
40    ReverificationRequired,
41
42    #[error("Biometric quality too low: {quality} (minimum: {minimum})")]
43    QualityTooLow { quality: u8, minimum: u8 },
44
45    #[error("Authenticator not verified")]
46    AuthenticatorNotVerified,
47
48    #[error("Biometric verification expired at {0}")]
49    VerificationExpired(DateTime<Utc>),
50
51    #[error("Risk score too high: {0}")]
52    RiskScoreTooHigh(u8),
53
54    #[error("Storage error: {0}")]
55    Storage(String),
56}
57
58/// Biometric modality
59#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
60pub enum BiometricModality {
61    Fingerprint,
62    FaceRecognition,
63    IrisScanning,
64    VoiceRecognition,
65    BehavioralBiometrics,
66}
67
68impl BiometricModality {
69    /// Get modality name
70    pub fn name(&self) -> &'static str {
71        match self {
72            Self::Fingerprint => "Fingerprint",
73            Self::FaceRecognition => "Face Recognition",
74            Self::IrisScanning => "Iris Scanning",
75            Self::VoiceRecognition => "Voice Recognition",
76            Self::BehavioralBiometrics => "Behavioral Biometrics",
77        }
78    }
79
80    /// Get spoofing risk level
81    pub fn spoofing_risk(&self) -> RiskLevel {
82        match self {
83            Self::IrisScanning => RiskLevel::Low,
84            Self::FaceRecognition => RiskLevel::Medium, // Deepfake risk
85            Self::Fingerprint => RiskLevel::Medium,     // Mold/lift risk
86            Self::VoiceRecognition => RiskLevel::High,  // Deepfake risk
87            Self::BehavioralBiometrics => RiskLevel::Low,
88        }
89    }
90
91    /// Check if liveness detection is critical
92    pub fn requires_liveness(&self) -> bool {
93        matches!(
94            self,
95            Self::FaceRecognition | Self::Fingerprint | Self::VoiceRecognition
96        )
97    }
98}
99
100/// Risk level
101#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
102pub enum RiskLevel {
103    Low,
104    Medium,
105    High,
106    Critical,
107}
108
109/// Authenticator capabilities
110#[derive(Clone, Debug, Serialize, Deserialize)]
111pub struct AuthenticatorCapabilities {
112    /// AAGUID (Authenticator Attestation GUID)
113    pub aaguid: String,
114
115    /// Manufacturer
116    pub manufacturer: Option<String>,
117
118    /// Model
119    pub model: Option<String>,
120
121    /// Supports user verification
122    pub supports_uv: bool,
123
124    /// Supports liveness detection
125    pub supports_liveness: bool,
126
127    /// Biometric modality
128    pub biometric_modality: Option<BiometricModality>,
129
130    /// Certification level
131    pub certification_level: CertificationLevel,
132
133    /// Firmware version
134    pub firmware_version: Option<String>,
135}
136
137impl AuthenticatorCapabilities {
138    /// Create new capabilities
139    pub fn new(aaguid: String) -> Self {
140        Self {
141            aaguid,
142            manufacturer: None,
143            model: None,
144            supports_uv: false,
145            supports_liveness: false,
146            biometric_modality: None,
147            certification_level: CertificationLevel::None,
148            firmware_version: None,
149        }
150    }
151
152    /// Check if authenticator is secure enough
153    pub fn is_secure(&self) -> bool {
154        self.supports_uv
155            && self.supports_liveness
156            && self.certification_level >= CertificationLevel::Level1
157    }
158
159    /// Get security score (0-100)
160    pub fn security_score(&self) -> u8 {
161        let mut score = 0u8;
162
163        if self.supports_uv {
164            score += 30;
165        }
166        if self.supports_liveness {
167            score += 30;
168        }
169        score += match self.certification_level {
170            CertificationLevel::Level3Plus => 40,
171            CertificationLevel::Level3 => 30,
172            CertificationLevel::Level2 => 20,
173            CertificationLevel::Level1 => 10,
174            CertificationLevel::None => 0,
175        };
176
177        score
178    }
179}
180
181/// Certification level (FIDO)
182#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
183pub enum CertificationLevel {
184    None,
185    Level1,
186    Level2,
187    Level3,
188    Level3Plus,
189}
190
191/// Biometric verification
192#[derive(Clone, Debug, Serialize, Deserialize)]
193pub struct BiometricVerification {
194    /// Verification ID
195    pub verification_id: String,
196
197    /// User ID
198    pub user_id: UserId,
199
200    /// Tenant ID
201    pub tenant_id: TenantId,
202
203    /// Authenticator AAGUID
204    pub aaguid: String,
205
206    /// Biometric modality
207    pub modality: Option<BiometricModality>,
208
209    /// User verification performed
210    pub user_verification: bool,
211
212    /// Liveness detection performed
213    pub liveness_detection: bool,
214
215    /// Quality score (0-100)
216    pub quality_score: Option<u8>,
217
218    /// Verified at
219    pub verified_at: DateTime<Utc>,
220
221    /// Expires at
222    pub expires_at: DateTime<Utc>,
223
224    /// IP address
225    pub ip_address: Option<String>,
226
227    /// Risk score (0-100)
228    pub risk_score: u8,
229
230    /// Step-up completed
231    pub step_up_completed: bool,
232}
233
234impl BiometricVerification {
235    /// Create new verification
236    pub fn new(
237        user_id: UserId,
238        tenant_id: TenantId,
239        aaguid: String,
240        modality: Option<BiometricModality>,
241        user_verification: bool,
242        liveness_detection: bool,
243        validity_seconds: i64,
244    ) -> Self {
245        let now = Utc::now();
246        Self {
247            verification_id: Uuid::new_v4().to_string(),
248            user_id,
249            tenant_id,
250            aaguid,
251            modality,
252            user_verification,
253            liveness_detection,
254            quality_score: None,
255            verified_at: now,
256            expires_at: now + Duration::seconds(validity_seconds),
257            ip_address: None,
258            risk_score: 0,
259            step_up_completed: false,
260        }
261    }
262
263    /// Check if verification is expired
264    pub fn is_expired(&self) -> bool {
265        Utc::now() > self.expires_at
266    }
267
268    /// Set quality score
269    pub fn with_quality(mut self, score: u8) -> Self {
270        self.quality_score = Some(score);
271        self
272    }
273
274    /// Set risk score
275    pub fn with_risk_score(mut self, score: u8) -> Self {
276        self.risk_score = score;
277        self
278    }
279
280    /// Set IP address
281    pub fn with_ip(mut self, ip: String) -> Self {
282        self.ip_address = Some(ip);
283        self
284    }
285
286    /// Mark step-up completed
287    pub fn mark_step_up_completed(mut self) -> Self {
288        self.step_up_completed = true;
289        self
290    }
291}
292
293/// Biometric policy
294#[derive(Clone, Debug, Serialize, Deserialize)]
295pub struct BiometricPolicy {
296    /// Tenant ID
297    pub tenant_id: TenantId,
298
299    /// Require user verification
300    pub require_uv: bool,
301
302    /// Require liveness detection
303    pub require_liveness: bool,
304
305    /// Minimum quality score
306    pub min_quality_score: u8,
307
308    /// Minimum authenticator certification
309    pub min_certification_level: CertificationLevel,
310
311    /// Verification validity (seconds)
312    pub verification_validity_seconds: i64,
313
314    /// Require step-up for sensitive operations
315    pub require_step_up_sensitive: bool,
316
317    /// Risk threshold for re-verification
318    pub risk_threshold_reverify: u8,
319
320    /// Allowed biometric modalities
321    pub allowed_modalities: Option<Vec<BiometricModality>>,
322}
323
324impl Default for BiometricPolicy {
325    fn default() -> Self {
326        Self {
327            tenant_id: TenantId::new("default"),
328            require_uv: true,        // CRITICAL: Always require UV
329            require_liveness: false, // Optional by default
330            min_quality_score: 50,
331            min_certification_level: CertificationLevel::Level1,
332            verification_validity_seconds: 300, // 5 minutes
333            require_step_up_sensitive: true,
334            risk_threshold_reverify: 70,
335            allowed_modalities: None, // All allowed
336        }
337    }
338}
339
340impl BiometricPolicy {
341    /// Create strict policy
342    pub fn strict() -> Self {
343        Self {
344            tenant_id: TenantId::new("strict"),
345            require_uv: true,
346            require_liveness: true, // REQUIRED
347            min_quality_score: 70,
348            min_certification_level: CertificationLevel::Level2,
349            verification_validity_seconds: 60, // 1 minute
350            require_step_up_sensitive: true,
351            risk_threshold_reverify: 50, // Lower threshold
352            allowed_modalities: Some(vec![
353                BiometricModality::Fingerprint,
354                BiometricModality::FaceRecognition,
355                BiometricModality::IrisScanning,
356            ]),
357        }
358    }
359
360    /// Create lenient policy
361    pub fn lenient() -> Self {
362        Self {
363            tenant_id: TenantId::new("lenient"),
364            require_uv: true, // Still required
365            require_liveness: false,
366            min_quality_score: 30,
367            min_certification_level: CertificationLevel::None,
368            verification_validity_seconds: 900, // 15 minutes
369            require_step_up_sensitive: false,
370            risk_threshold_reverify: 90, // Higher threshold
371            allowed_modalities: None,
372        }
373    }
374
375    /// Check compliance
376    pub fn check_compliance(
377        &self,
378        verification: &BiometricVerification,
379        capabilities: &AuthenticatorCapabilities,
380    ) -> Result<(), BiometricError> {
381        // Check UV requirement
382        if self.require_uv && !verification.user_verification {
383            return Err(BiometricError::UserVerificationNotPerformed);
384        }
385
386        // Check liveness requirement
387        if self.require_liveness && !verification.liveness_detection {
388            return Err(BiometricError::LivenessNotSupported);
389        }
390
391        // Check quality score
392        if let Some(quality) = verification.quality_score {
393            if quality < self.min_quality_score {
394                return Err(BiometricError::QualityTooLow {
395                    quality,
396                    minimum: self.min_quality_score,
397                });
398            }
399        }
400
401        // Check certification level
402        if capabilities.certification_level < self.min_certification_level {
403            return Err(BiometricError::AuthenticatorNotVerified);
404        }
405
406        // Check expiration
407        if verification.is_expired() {
408            return Err(BiometricError::VerificationExpired(verification.expires_at));
409        }
410
411        // Check risk score
412        if verification.risk_score > self.risk_threshold_reverify {
413            return Err(BiometricError::ReverificationRequired);
414        }
415
416        // Check allowed modalities
417        if let Some(ref allowed) = self.allowed_modalities {
418            if let Some(modality) = verification.modality {
419                if !allowed.contains(&modality) {
420                    return Err(BiometricError::AuthenticatorNotVerified);
421                }
422            }
423        }
424
425        Ok(())
426    }
427}
428
429/// Operation sensitivity level
430#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
431pub enum OperationSensitivity {
432    Low,
433    Medium,
434    High,
435    Critical,
436}
437
438impl OperationSensitivity {
439    /// Check if step-up required
440    pub fn requires_step_up(&self) -> bool {
441        matches!(self, Self::High | Self::Critical)
442    }
443
444    /// Get required verification freshness (seconds)
445    pub fn required_freshness(&self) -> i64 {
446        match self {
447            Self::Low => 900,     // 15 minutes
448            Self::Medium => 300,  // 5 minutes
449            Self::High => 60,     // 1 minute
450            Self::Critical => 30, // 30 seconds
451        }
452    }
453}
454
455/// Storage trait for biometric security
456#[async_trait]
457pub trait BiometricStorage: Send + Sync {
458    /// Store authenticator capabilities
459    async fn store_capabilities(
460        &self,
461        capabilities: &AuthenticatorCapabilities,
462    ) -> Result<(), BiometricError>;
463
464    /// Get authenticator capabilities
465    async fn get_capabilities(
466        &self,
467        aaguid: &str,
468    ) -> Result<Option<AuthenticatorCapabilities>, BiometricError>;
469
470    /// Store verification
471    async fn store_verification(
472        &self,
473        verification: &BiometricVerification,
474    ) -> Result<(), BiometricError>;
475
476    /// Get latest verification for user
477    async fn get_latest_verification(
478        &self,
479        user_id: &UserId,
480    ) -> Result<Option<BiometricVerification>, BiometricError>;
481
482    /// Store policy
483    async fn store_policy(&self, policy: &BiometricPolicy) -> Result<(), BiometricError>;
484
485    /// Get policy
486    async fn get_policy(&self, tenant_id: &TenantId) -> Result<BiometricPolicy, BiometricError>;
487}
488
489/// In-memory storage for testing
490pub struct InMemoryBiometricStorage {
491    capabilities: tokio::sync::RwLock<HashMap<String, AuthenticatorCapabilities>>,
492    verifications: tokio::sync::RwLock<HashMap<UserId, BiometricVerification>>,
493    policies: tokio::sync::RwLock<HashMap<TenantId, BiometricPolicy>>,
494}
495
496impl InMemoryBiometricStorage {
497    pub fn new() -> Self {
498        Self {
499            capabilities: tokio::sync::RwLock::new(HashMap::new()),
500            verifications: tokio::sync::RwLock::new(HashMap::new()),
501            policies: tokio::sync::RwLock::new(HashMap::new()),
502        }
503    }
504}
505
506impl Default for InMemoryBiometricStorage {
507    fn default() -> Self {
508        Self::new()
509    }
510}
511
512#[async_trait]
513impl BiometricStorage for InMemoryBiometricStorage {
514    async fn store_capabilities(
515        &self,
516        capabilities: &AuthenticatorCapabilities,
517    ) -> Result<(), BiometricError> {
518        let mut caps = self.capabilities.write().await;
519        caps.insert(capabilities.aaguid.clone(), capabilities.clone());
520        Ok(())
521    }
522
523    async fn get_capabilities(
524        &self,
525        aaguid: &str,
526    ) -> Result<Option<AuthenticatorCapabilities>, BiometricError> {
527        let caps = self.capabilities.read().await;
528        Ok(caps.get(aaguid).cloned())
529    }
530
531    async fn store_verification(
532        &self,
533        verification: &BiometricVerification,
534    ) -> Result<(), BiometricError> {
535        let mut verifications = self.verifications.write().await;
536        verifications.insert(verification.user_id.clone(), verification.clone());
537        Ok(())
538    }
539
540    async fn get_latest_verification(
541        &self,
542        user_id: &UserId,
543    ) -> Result<Option<BiometricVerification>, BiometricError> {
544        let verifications = self.verifications.read().await;
545        Ok(verifications.get(user_id).cloned())
546    }
547
548    async fn store_policy(&self, policy: &BiometricPolicy) -> Result<(), BiometricError> {
549        let mut policies = self.policies.write().await;
550        policies.insert(policy.tenant_id.clone(), policy.clone());
551        Ok(())
552    }
553
554    async fn get_policy(&self, tenant_id: &TenantId) -> Result<BiometricPolicy, BiometricError> {
555        let policies = self.policies.read().await;
556        Ok(policies
557            .get(tenant_id)
558            .cloned()
559            .unwrap_or_else(BiometricPolicy::default))
560    }
561}
562
563/// Biometric security manager
564pub struct BiometricSecurityManager<S: BiometricStorage> {
565    storage: S,
566}
567
568impl<S: BiometricStorage> BiometricSecurityManager<S> {
569    /// Create new manager
570    pub fn new(storage: S) -> Self {
571        Self { storage }
572    }
573
574    /// Register authenticator capabilities
575    pub async fn register_authenticator(
576        &self,
577        capabilities: AuthenticatorCapabilities,
578    ) -> Result<AuthenticatorCapabilities, BiometricError> {
579        self.storage.store_capabilities(&capabilities).await?;
580        info!(
581            "Authenticator registered: {} (secure: {})",
582            capabilities.aaguid,
583            capabilities.is_secure()
584        );
585        Ok(capabilities)
586    }
587
588    /// Record biometric verification
589    pub async fn record_verification(
590        &self,
591        verification: BiometricVerification,
592    ) -> Result<BiometricVerification, BiometricError> {
593        // Get policy
594        let policy = self.storage.get_policy(&verification.tenant_id).await?;
595
596        // Get authenticator capabilities
597        let capabilities = self
598            .storage
599            .get_capabilities(&verification.aaguid)
600            .await?
601            .unwrap_or_else(|| AuthenticatorCapabilities::new(verification.aaguid.clone()));
602
603        // Check compliance
604        policy.check_compliance(&verification, &capabilities)?;
605
606        self.storage.store_verification(&verification).await?;
607
608        info!(
609            "Biometric verification recorded for user {} (UV: {}, liveness: {})",
610            verification.user_id, verification.user_verification, verification.liveness_detection
611        );
612        Ok(verification)
613    }
614
615    /// Verify biometric for operation
616    pub async fn verify_for_operation(
617        &self,
618        user_id: &UserId,
619        tenant_id: &TenantId,
620        sensitivity: OperationSensitivity,
621    ) -> Result<(), BiometricError> {
622        // Get policy
623        let policy = self.storage.get_policy(tenant_id).await?;
624
625        // Get latest verification
626        let verification = self
627            .storage
628            .get_latest_verification(user_id)
629            .await?
630            .ok_or(BiometricError::UserVerificationNotPerformed)?;
631
632        // Get authenticator capabilities
633        let capabilities = self
634            .storage
635            .get_capabilities(&verification.aaguid)
636            .await?
637            .unwrap_or_else(|| AuthenticatorCapabilities::new(verification.aaguid.clone()));
638
639        // Check basic compliance
640        policy.check_compliance(&verification, &capabilities)?;
641
642        // Check freshness for operation sensitivity
643        let age_seconds = (Utc::now() - verification.verified_at).num_seconds();
644        if age_seconds > sensitivity.required_freshness() {
645            return Err(BiometricError::ReverificationRequired);
646        }
647
648        // Check step-up requirement
649        if sensitivity.requires_step_up()
650            && policy.require_step_up_sensitive
651            && !verification.step_up_completed
652        {
653            return Err(BiometricError::StepUpRequired);
654        }
655
656        info!(
657            "Biometric verification passed for user {} (sensitivity: {:?})",
658            user_id, sensitivity
659        );
660        Ok(())
661    }
662
663    /// Calculate risk score
664    pub fn calculate_risk_score(
665        &self,
666        verification: &BiometricVerification,
667        capabilities: &AuthenticatorCapabilities,
668    ) -> u8 {
669        let mut risk = 0u8;
670
671        // No UV = +40 risk
672        if !verification.user_verification {
673            risk = risk.saturating_add(40);
674        }
675
676        // No liveness = +30 risk
677        if !verification.liveness_detection {
678            if let Some(modality) = verification.modality {
679                if modality.requires_liveness() {
680                    risk = risk.saturating_add(30);
681                }
682            }
683        }
684
685        // Low quality = +20 risk
686        if let Some(quality) = verification.quality_score {
687            if quality < 50 {
688                risk = risk.saturating_add(20);
689            }
690        }
691
692        // Low certification = +10 risk
693        if capabilities.certification_level < CertificationLevel::Level1 {
694            risk = risk.saturating_add(10);
695        }
696
697        risk
698    }
699}
700
701#[cfg(test)]
702mod tests {
703    use super::*;
704
705    #[test]
706    fn test_authenticator_security_score() {
707        let mut caps = AuthenticatorCapabilities::new("test-aaguid".to_string());
708        caps.supports_uv = true;
709        caps.supports_liveness = true;
710        caps.certification_level = CertificationLevel::Level3;
711
712        assert_eq!(caps.security_score(), 90); // 30 + 30 + 30
713        assert!(caps.is_secure());
714    }
715
716    #[tokio::test]
717    async fn test_verification_recording() {
718        let storage = InMemoryBiometricStorage::new();
719        let manager = BiometricSecurityManager::new(storage);
720
721        // Register authenticator
722        let mut caps = AuthenticatorCapabilities::new("secure-auth".to_string());
723        caps.supports_uv = true;
724        caps.supports_liveness = true;
725        caps.certification_level = CertificationLevel::Level2;
726        manager.register_authenticator(caps).await.unwrap();
727
728        // Record verification
729        let verification = BiometricVerification::new(
730            UserId::new("test_user"),
731            TenantId::new("test_tenant"),
732            "secure-auth".to_string(),
733            Some(BiometricModality::Fingerprint),
734            true,
735            true,
736            300,
737        );
738
739        let recorded = manager.record_verification(verification).await.unwrap();
740        assert!(recorded.user_verification);
741        assert!(recorded.liveness_detection);
742    }
743
744    #[tokio::test]
745    async fn test_policy_compliance() {
746        let policy = BiometricPolicy::strict();
747        let mut caps = AuthenticatorCapabilities::new("test".to_string());
748        caps.supports_uv = true;
749        caps.supports_liveness = true;
750        caps.certification_level = CertificationLevel::Level2;
751
752        let verification = BiometricVerification::new(
753            UserId::new("test_user"),
754            TenantId::new("test_tenant"),
755            "test".to_string(),
756            Some(BiometricModality::Fingerprint),
757            true,
758            true,
759            300,
760        );
761
762        // Should pass
763        assert!(policy.check_compliance(&verification, &caps).is_ok());
764
765        // Fail without UV
766        let mut bad_verification = verification.clone();
767        bad_verification.user_verification = false;
768        assert!(policy.check_compliance(&bad_verification, &caps).is_err());
769    }
770
771    #[test]
772    fn test_risk_scoring() {
773        let storage = InMemoryBiometricStorage::new();
774        let manager = BiometricSecurityManager::new(storage);
775
776        let verification = BiometricVerification::new(
777            UserId::new("test_user"),
778            TenantId::new("test_tenant"),
779            "test".to_string(),
780            Some(BiometricModality::FaceRecognition),
781            false, // No UV = +40
782            false, // No liveness = +30
783            300,
784        )
785        .with_quality(30); // Low quality = +20
786
787        let caps = AuthenticatorCapabilities::new("test".to_string());
788        // No certification = +10
789
790        let risk = manager.calculate_risk_score(&verification, &caps);
791        assert_eq!(risk, 100); // 40 + 30 + 20 + 10
792    }
793}