1use async_trait::async_trait;
40use scirs2_core::random::{Rng, SeedableRng};
41use serde::{Deserialize, Serialize};
42use std::collections::HashMap;
43use std::sync::Arc;
44use std::time::{SystemTime, UNIX_EPOCH};
45use thiserror::Error;
46use tokio::sync::RwLock;
47use tracing::{debug, info, warn};
48use voirs_sdk::{AudioBuffer, VoirsError};
49
50use crate::quality::QualityEvaluator;
51use crate::traits::{QualityEvaluationConfig, QualityEvaluator as QualityEvaluatorTrait};
52
53#[derive(Error, Debug)]
55pub enum PrivacyError {
56 #[error("Privacy budget exhausted: used {used:.3}, available {available:.3}")]
58 BudgetExhausted {
59 used: f64,
61 available: f64,
63 },
64
65 #[error("Invalid privacy parameters: {message}")]
67 InvalidParameters {
68 message: String,
70 },
71
72 #[error("Encryption error: {message}")]
74 EncryptionError {
75 message: String,
77 },
78
79 #[error("Access denied: {reason}")]
81 AccessDenied {
82 reason: String,
84 },
85
86 #[error("Audit log error: {message}")]
88 AuditError {
89 message: String,
91 },
92
93 #[error("VoiRS error: {0}")]
95 VoirsError(#[from] VoirsError),
96
97 #[error("Evaluation error: {0}")]
99 EvaluationError(#[from] crate::EvaluationError),
100
101 #[error("Serialization error: {0}")]
103 SerializationError(#[from] serde_json::Error),
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
108pub enum PrivacyMechanism {
109 Laplace,
111 Gaussian,
113 Exponential,
115 SecureAggregation,
117 HomomorphicEncryption,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct PrivacyBudget {
124 pub total_epsilon: f64,
126 pub used_epsilon: f64,
128 pub delta: f64,
130 pub reset_interval_seconds: Option<u64>,
132 pub last_reset: u64,
134}
135
136impl PrivacyBudget {
137 pub fn new(total_epsilon: f64, delta: f64) -> Self {
139 let now = SystemTime::now()
140 .duration_since(UNIX_EPOCH)
141 .unwrap()
142 .as_secs();
143
144 Self {
145 total_epsilon,
146 used_epsilon: 0.0,
147 delta,
148 reset_interval_seconds: None,
149 last_reset: now,
150 }
151 }
152
153 pub fn is_available(&self, epsilon: f64) -> bool {
155 self.used_epsilon + epsilon <= self.total_epsilon
156 }
157
158 pub fn consume(&mut self, epsilon: f64) -> Result<(), PrivacyError> {
160 if !self.is_available(epsilon) {
161 return Err(PrivacyError::BudgetExhausted {
162 used: self.used_epsilon,
163 available: self.total_epsilon - self.used_epsilon,
164 });
165 }
166
167 self.used_epsilon += epsilon;
168 Ok(())
169 }
170
171 pub fn remaining(&self) -> f64 {
173 (self.total_epsilon - self.used_epsilon).max(0.0)
174 }
175
176 pub fn maybe_reset(&mut self) {
178 if let Some(interval) = self.reset_interval_seconds {
179 let now = SystemTime::now()
180 .duration_since(UNIX_EPOCH)
181 .unwrap()
182 .as_secs();
183
184 if now - self.last_reset >= interval {
185 self.used_epsilon = 0.0;
186 self.last_reset = now;
187 info!("Privacy budget reset");
188 }
189 }
190 }
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct PrivacyConfig {
196 pub mechanism: PrivacyMechanism,
198 pub epsilon: f64,
200 pub delta: f64,
202 pub sensitivity: f64,
204 pub clipping_bound: f64,
206 pub enable_secure_aggregation: bool,
208 pub enable_anonymization: bool,
210 pub enable_audit_log: bool,
212 pub access_policies: HashMap<String, AccessLevel>,
214}
215
216impl PrivacyConfig {
217 pub fn new() -> Self {
219 Self {
220 mechanism: PrivacyMechanism::Laplace,
221 epsilon: 1.0,
222 delta: 1e-5,
223 sensitivity: 1.0,
224 clipping_bound: 1.0,
225 enable_secure_aggregation: false,
226 enable_anonymization: true,
227 enable_audit_log: true,
228 access_policies: HashMap::new(),
229 }
230 }
231
232 pub fn with_epsilon(mut self, epsilon: f64) -> Self {
234 self.epsilon = epsilon;
235 self
236 }
237
238 pub fn with_delta(mut self, delta: f64) -> Self {
240 self.delta = delta;
241 self
242 }
243
244 pub fn with_sensitivity(mut self, sensitivity: f64) -> Self {
246 self.sensitivity = sensitivity;
247 self
248 }
249
250 pub fn with_clipping_bound(mut self, bound: f64) -> Self {
252 self.clipping_bound = bound;
253 self
254 }
255
256 pub fn with_mechanism(mut self, mechanism: PrivacyMechanism) -> Self {
258 self.mechanism = mechanism;
259 self
260 }
261
262 pub fn with_secure_aggregation(mut self, enable: bool) -> Self {
264 self.enable_secure_aggregation = enable;
265 self
266 }
267
268 pub fn validate(&self) -> Result<(), PrivacyError> {
270 if self.epsilon <= 0.0 {
271 return Err(PrivacyError::InvalidParameters {
272 message: "Epsilon must be positive".to_string(),
273 });
274 }
275
276 if self.delta < 0.0 || self.delta > 1.0 {
277 return Err(PrivacyError::InvalidParameters {
278 message: "Delta must be between 0 and 1".to_string(),
279 });
280 }
281
282 if self.sensitivity <= 0.0 {
283 return Err(PrivacyError::InvalidParameters {
284 message: "Sensitivity must be positive".to_string(),
285 });
286 }
287
288 Ok(())
289 }
290}
291
292impl Default for PrivacyConfig {
293 fn default() -> Self {
294 Self::new()
295 }
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
300pub enum AccessLevel {
301 None,
303 ReadOnly,
305 ReadEvaluate,
307 Full,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct PrivateEvaluationResult {
314 pub score: f64,
316 pub epsilon_used: f64,
318 pub noise_scale: f64,
320 pub confidence_interval: (f64, f64),
322 pub privacy_guarantees: Vec<String>,
324 pub timestamp: u64,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct AuditLogEntry {
331 pub id: String,
333 pub timestamp: u64,
335 pub actor: String,
337 pub action: String,
339 pub epsilon_consumed: f64,
341 pub result_summary: HashMap<String, serde_json::Value>,
343 pub access_level: AccessLevel,
345}
346
347pub struct PrivacyPreservingEvaluator {
349 config: PrivacyConfig,
350 budget: Arc<RwLock<PrivacyBudget>>,
351 evaluator: Arc<RwLock<QualityEvaluator>>,
352 audit_log: Arc<RwLock<Vec<AuditLogEntry>>>,
353 rng: Arc<RwLock<scirs2_core::random::StdRng>>,
354}
355
356impl PrivacyPreservingEvaluator {
357 pub async fn new(config: PrivacyConfig) -> Result<Self, PrivacyError> {
359 config.validate()?;
360
361 let budget = PrivacyBudget::new(config.epsilon * 10.0, config.delta);
362 let evaluator = QualityEvaluator::new().await?;
363 let rng = scirs2_core::random::StdRng::seed_from_u64(42);
364
365 Ok(Self {
366 config,
367 budget: Arc::new(RwLock::new(budget)),
368 evaluator: Arc::new(RwLock::new(evaluator)),
369 audit_log: Arc::new(RwLock::new(Vec::new())),
370 rng: Arc::new(RwLock::new(rng)),
371 })
372 }
373
374 pub async fn evaluate_with_privacy(
376 &self,
377 audio: &AudioBuffer,
378 reference: Option<&AudioBuffer>,
379 ) -> Result<PrivateEvaluationResult, PrivacyError> {
380 let mut budget = self.budget.write().await;
382 budget.maybe_reset();
383
384 if !budget.is_available(self.config.epsilon) {
385 return Err(PrivacyError::BudgetExhausted {
386 used: budget.used_epsilon,
387 available: budget.remaining(),
388 });
389 }
390
391 let evaluator = self.evaluator.read().await;
393 let eval_config = QualityEvaluationConfig::default();
394 let quality = evaluator
395 .evaluate_quality(audio, reference, Some(&eval_config))
396 .await?;
397
398 let (private_score, noise_scale) = self
400 .apply_differential_privacy(quality.overall_score as f64)
401 .await;
402
403 let clipped_score = private_score.clamp(0.0, 5.0);
405
406 let confidence_interval = self.calculate_confidence_interval(clipped_score, noise_scale);
408
409 budget.consume(self.config.epsilon)?;
411
412 if self.config.enable_audit_log {
414 self.log_evaluation("evaluate_with_privacy", self.config.epsilon, clipped_score)
415 .await;
416 }
417
418 let timestamp = SystemTime::now()
419 .duration_since(UNIX_EPOCH)
420 .unwrap()
421 .as_secs();
422
423 Ok(PrivateEvaluationResult {
424 score: clipped_score,
425 epsilon_used: self.config.epsilon,
426 noise_scale,
427 confidence_interval,
428 privacy_guarantees: vec![
429 format!(
430 "(ε={:.3}, δ={:.6})-differential privacy",
431 self.config.epsilon, self.config.delta
432 ),
433 format!("Mechanism: {:?}", self.config.mechanism),
434 ],
435 timestamp,
436 })
437 }
438
439 async fn apply_differential_privacy(&self, true_value: f64) -> (f64, f64) {
441 match self.config.mechanism {
442 PrivacyMechanism::Laplace => self.laplace_mechanism(true_value).await,
443 PrivacyMechanism::Gaussian => self.gaussian_mechanism(true_value).await,
444 _ => {
445 warn!(
446 "Privacy mechanism {:?} not fully implemented, using Laplace",
447 self.config.mechanism
448 );
449 self.laplace_mechanism(true_value).await
450 }
451 }
452 }
453
454 async fn laplace_mechanism(&self, true_value: f64) -> (f64, f64) {
456 let scale = self.config.sensitivity / self.config.epsilon;
457 let noise = self.sample_laplace(scale).await;
458 (true_value + noise, scale)
459 }
460
461 async fn gaussian_mechanism(&self, true_value: f64) -> (f64, f64) {
463 let c2 = 2.0 * (1.25 / self.config.delta).ln();
465 let sigma = (self.config.sensitivity * c2.sqrt()) / self.config.epsilon;
466 let noise = self.sample_gaussian(0.0, sigma).await;
467 (true_value + noise, sigma)
468 }
469
470 async fn sample_laplace(&self, scale: f64) -> f64 {
472 let mut rng = self.rng.write().await;
473 let u: f64 = rng.gen_range(-0.5..0.5);
474 -scale * u.signum() * (1.0 - 2.0 * u.abs()).ln()
475 }
476
477 async fn sample_gaussian(&self, mean: f64, std_dev: f64) -> f64 {
479 let mut rng = self.rng.write().await;
480 let u1: f64 = rng.gen();
481 let u2: f64 = rng.gen();
482 let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos();
483 mean + std_dev * z
484 }
485
486 fn calculate_confidence_interval(&self, value: f64, noise_scale: f64) -> (f64, f64) {
488 let margin = 1.96 * noise_scale; ((value - margin).max(0.0), (value + margin).min(5.0))
491 }
492
493 pub async fn aggregate_private_results(
495 &self,
496 results: Vec<PrivateEvaluationResult>,
497 ) -> Result<PrivateEvaluationResult, PrivacyError> {
498 if results.is_empty() {
499 return Err(PrivacyError::InvalidParameters {
500 message: "Cannot aggregate empty results".to_string(),
501 });
502 }
503
504 let avg_score = results.iter().map(|r| r.score).sum::<f64>() / results.len() as f64;
506 let total_epsilon = results.iter().map(|r| r.epsilon_used).sum::<f64>();
507 let avg_noise_scale =
508 results.iter().map(|r| r.noise_scale).sum::<f64>() / results.len() as f64;
509
510 let confidence_interval = self.calculate_confidence_interval(avg_score, avg_noise_scale);
511
512 let timestamp = SystemTime::now()
513 .duration_since(UNIX_EPOCH)
514 .unwrap()
515 .as_secs();
516
517 Ok(PrivateEvaluationResult {
518 score: avg_score,
519 epsilon_used: total_epsilon,
520 noise_scale: avg_noise_scale,
521 confidence_interval,
522 privacy_guarantees: vec![
523 format!("Aggregated from {} evaluations", results.len()),
524 format!("Total privacy loss: ε={:.3}", total_epsilon),
525 ],
526 timestamp,
527 })
528 }
529
530 pub async fn get_budget_status(&self) -> PrivacyBudget {
532 let budget = self.budget.read().await;
533 budget.clone()
534 }
535
536 pub async fn reset_budget(&self, _authorization: &str) -> Result<(), PrivacyError> {
538 let mut budget = self.budget.write().await;
540 budget.used_epsilon = 0.0;
541 budget.last_reset = SystemTime::now()
542 .duration_since(UNIX_EPOCH)
543 .unwrap()
544 .as_secs();
545 info!("Privacy budget manually reset");
546 Ok(())
547 }
548
549 pub async fn get_audit_log(&self) -> Vec<AuditLogEntry> {
551 let log = self.audit_log.read().await;
552 log.clone()
553 }
554
555 async fn log_evaluation(&self, action: &str, epsilon_consumed: f64, score: f64) {
557 let mut log = self.audit_log.write().await;
558
559 let entry = AuditLogEntry {
560 id: uuid::Uuid::new_v4().to_string(),
561 timestamp: SystemTime::now()
562 .duration_since(UNIX_EPOCH)
563 .unwrap()
564 .as_secs(),
565 actor: "system".to_string(),
566 action: action.to_string(),
567 epsilon_consumed,
568 result_summary: {
569 let mut map = HashMap::new();
570 map.insert("score".to_string(), serde_json::json!(score));
571 map
572 },
573 access_level: AccessLevel::ReadEvaluate,
574 };
575
576 log.push(entry);
577
578 if log.len() > 10000 {
580 let len = log.len();
581 log.drain(0..len - 10000);
582 }
583 }
584
585 pub fn check_access(
587 &self,
588 user: &str,
589 required_level: AccessLevel,
590 ) -> Result<(), PrivacyError> {
591 if let Some(&level) = self.config.access_policies.get(user) {
592 if level as u8 >= required_level as u8 {
593 return Ok(());
594 }
595 }
596
597 Err(PrivacyError::AccessDenied {
598 reason: format!("User '{}' does not have required access level", user),
599 })
600 }
601
602 pub fn anonymize_metadata(&self, metadata: &mut HashMap<String, String>) {
604 if !self.config.enable_anonymization {
605 return;
606 }
607
608 let pii_keys = vec!["speaker_id", "user_id", "name", "email", "phone", "address"];
610 let pii_count = pii_keys.len();
611 for key in pii_keys {
612 metadata.remove(key);
613 }
614
615 if let Some(value) = metadata.get_mut("session_id") {
617 *value = format!("hashed_{}", self.hash_value(value));
618 }
619
620 debug!("Anonymized metadata, removed {} PII fields", pii_count);
621 }
622
623 fn hash_value(&self, value: &str) -> String {
625 format!(
627 "{:x}",
628 value
629 .bytes()
630 .fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64))
631 )
632 }
633}
634
635pub struct FederatedPrivacyCoordinator {
637 participants: Vec<String>,
638 global_budget: Arc<RwLock<PrivacyBudget>>,
639 aggregated_results: Arc<RwLock<Vec<PrivateEvaluationResult>>>,
640}
641
642impl FederatedPrivacyCoordinator {
643 pub fn new(participants: Vec<String>, total_epsilon: f64, delta: f64) -> Self {
645 let budget = PrivacyBudget::new(total_epsilon, delta);
646 Self {
647 participants,
648 global_budget: Arc::new(RwLock::new(budget)),
649 aggregated_results: Arc::new(RwLock::new(Vec::new())),
650 }
651 }
652
653 pub async fn add_participant_result(
655 &self,
656 _participant_id: &str,
657 result: PrivateEvaluationResult,
658 ) -> Result<(), PrivacyError> {
659 let mut results = self.aggregated_results.write().await;
660 results.push(result);
661 Ok(())
662 }
663
664 pub async fn compute_federated_aggregate(
666 &self,
667 ) -> Result<PrivateEvaluationResult, PrivacyError> {
668 let results = self.aggregated_results.read().await;
669 if results.is_empty() {
670 return Err(PrivacyError::InvalidParameters {
671 message: "No participant results available".to_string(),
672 });
673 }
674
675 let evaluator = PrivacyPreservingEvaluator::new(PrivacyConfig::default()).await?;
676 evaluator.aggregate_private_results(results.clone()).await
677 }
678
679 pub fn participant_count(&self) -> usize {
681 self.participants.len()
682 }
683}
684
685#[cfg(test)]
686mod tests {
687 use super::*;
688
689 #[test]
690 fn test_privacy_budget_creation() {
691 let budget = PrivacyBudget::new(1.0, 1e-5);
692 assert_eq!(budget.total_epsilon, 1.0);
693 assert_eq!(budget.used_epsilon, 0.0);
694 assert_eq!(budget.delta, 1e-5);
695 }
696
697 #[test]
698 fn test_privacy_budget_consumption() {
699 let mut budget = PrivacyBudget::new(1.0, 1e-5);
700 assert!(budget.is_available(0.5));
701 budget.consume(0.5).unwrap();
702 assert_eq!(budget.remaining(), 0.5);
703 assert!(budget.is_available(0.5));
704 budget.consume(0.5).unwrap();
705 assert_eq!(budget.remaining(), 0.0);
706 assert!(!budget.is_available(0.1));
707 }
708
709 #[test]
710 fn test_privacy_budget_exhausted() {
711 let mut budget = PrivacyBudget::new(1.0, 1e-5);
712 budget.consume(1.0).unwrap();
713 let result = budget.consume(0.1);
714 assert!(result.is_err());
715 }
716
717 #[test]
718 fn test_privacy_config_validation() {
719 let config = PrivacyConfig::new().with_epsilon(1.0).with_delta(1e-5);
720 assert!(config.validate().is_ok());
721
722 let invalid_config = PrivacyConfig::new().with_epsilon(-1.0);
723 assert!(invalid_config.validate().is_err());
724
725 let invalid_delta = PrivacyConfig::new().with_delta(1.5);
726 assert!(invalid_delta.validate().is_err());
727 }
728
729 #[test]
730 fn test_privacy_config_builder() {
731 let config = PrivacyConfig::new()
732 .with_epsilon(0.5)
733 .with_delta(1e-6)
734 .with_sensitivity(2.0)
735 .with_clipping_bound(1.5)
736 .with_mechanism(PrivacyMechanism::Gaussian);
737
738 assert_eq!(config.epsilon, 0.5);
739 assert_eq!(config.delta, 1e-6);
740 assert_eq!(config.sensitivity, 2.0);
741 assert_eq!(config.clipping_bound, 1.5);
742 assert_eq!(config.mechanism, PrivacyMechanism::Gaussian);
743 }
744
745 #[test]
746 fn test_access_level_comparison() {
747 assert!(AccessLevel::Full as u8 > AccessLevel::ReadEvaluate as u8);
748 assert!(AccessLevel::ReadEvaluate as u8 > AccessLevel::ReadOnly as u8);
749 assert!(AccessLevel::ReadOnly as u8 > AccessLevel::None as u8);
750 }
751
752 #[test]
753 fn test_federated_coordinator_creation() {
754 let participants = vec!["node1".to_string(), "node2".to_string()];
755 let coordinator = FederatedPrivacyCoordinator::new(participants, 2.0, 1e-5);
756 assert_eq!(coordinator.participant_count(), 2);
757 }
758
759 #[tokio::test]
760 async fn test_privacy_preserving_evaluator_creation() {
761 let config = PrivacyConfig::new();
762 let evaluator = PrivacyPreservingEvaluator::new(config).await;
763 assert!(evaluator.is_ok());
764 }
765
766 #[tokio::test]
767 async fn test_privacy_budget_status() {
768 let config = PrivacyConfig::new();
769 let evaluator = PrivacyPreservingEvaluator::new(config).await.unwrap();
770 let status = evaluator.get_budget_status().await;
771 assert_eq!(status.total_epsilon, 10.0); assert_eq!(status.used_epsilon, 0.0);
773 }
774
775 #[tokio::test]
776 async fn test_audit_log() {
777 let config = PrivacyConfig::new();
778 let evaluator = PrivacyPreservingEvaluator::new(config).await.unwrap();
779 evaluator.log_evaluation("test_action", 0.5, 4.2).await;
780 let log = evaluator.get_audit_log().await;
781 assert_eq!(log.len(), 1);
782 assert_eq!(log[0].action, "test_action");
783 assert_eq!(log[0].epsilon_consumed, 0.5);
784 }
785
786 #[tokio::test]
787 async fn test_access_control() {
788 let mut config = PrivacyConfig::new();
789 config
790 .access_policies
791 .insert("user1".to_string(), AccessLevel::ReadEvaluate);
792 config
793 .access_policies
794 .insert("user2".to_string(), AccessLevel::ReadOnly);
795
796 let evaluator = PrivacyPreservingEvaluator::new(config).await.unwrap();
797
798 assert!(evaluator
799 .check_access("user1", AccessLevel::ReadEvaluate)
800 .is_ok());
801 assert!(evaluator.check_access("user1", AccessLevel::Full).is_err());
802 assert!(evaluator
803 .check_access("user2", AccessLevel::ReadEvaluate)
804 .is_err());
805 assert!(evaluator
806 .check_access("user3", AccessLevel::ReadOnly)
807 .is_err());
808 }
809
810 #[test]
811 fn test_metadata_anonymization() {
812 let config = PrivacyConfig::new();
813 let evaluator_result = PrivacyPreservingEvaluator::new(config);
814 let mut metadata = HashMap::new();
816 metadata.insert("speaker_id".to_string(), "john_doe".to_string());
817 metadata.insert("user_id".to_string(), "user123".to_string());
818 metadata.insert("session_id".to_string(), "session456".to_string());
819 metadata.insert("language".to_string(), "en-US".to_string());
820
821 assert_eq!(metadata.len(), 4);
823 assert!(metadata.contains_key("speaker_id"));
824 }
825}