1use super::*;
2
3#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
5pub enum NoiseMechanism {
6 Laplace,
7 Gaussian,
8 Exponential,
9 Geometric,
10 Custom(String),
11}
12
13pub struct PrivacyAccountant {
15 pub total_epsilon_spent: f64,
16 pub total_delta_spent: f64,
17 pub composition_method: CompositionMethod,
18 pub remaining_budget: PrivacyBudget,
19}
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub enum CompositionMethod {
24 BasicComposition,
25 AdvancedComposition,
26 RDPComposition,
27 GaussianDP,
28 Custom(String),
29}
30
31#[derive(Debug, Clone)]
33pub struct SensitivityFunction {
34 pub function_id: String,
35 pub sensitivity: f64,
36 pub computation_method: SensitivityMethod,
37}
38
39#[derive(Debug, Clone, PartialEq)]
41pub enum SensitivityMethod {
42 Global,
43 Local,
44 Smooth,
45 Approximate,
46}
47
48pub struct SecureAggregation {
50 aggregation_protocols: Vec<AggregationProtocol>,
51 encryption_schemes: Vec<EncryptionScheme>,
52 integrity_checks: Vec<IntegrityCheck>,
53}
54
55#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
57pub enum AggregationProtocol {
58 SecureSum,
59 SecureMean,
60 SecureMin,
61 SecureMax,
62 SecureMedian,
63 Custom(String),
64}
65
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub enum EncryptionScheme {
69 Homomorphic,
70 SecretSharing,
71 Threshold,
72 Oblivious,
73 Custom(String),
74}
75
76#[derive(Debug, Clone)]
78pub struct IntegrityCheck {
79 pub check_id: String,
80 pub check_type: IntegrityCheckType,
81 pub verification_method: VerificationMethod,
82}
83
84#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
86pub enum IntegrityCheckType {
87 Hash,
88 MAC,
89 DigitalSignature,
90 ZeroKnowledge,
91}
92
93#[derive(Debug, Clone, PartialEq)]
95pub enum VerificationMethod {
96 Deterministic,
97 Probabilistic,
98 Interactive,
99 NonInteractive,
100}
101
102pub struct PrivacyBudget {
104 pub epsilon: f64,
105 pub delta: f64,
106 pub remaining_epsilon: f64,
107 pub remaining_delta: f64,
108 pub budget_period: u64,
109 pub last_reset: u64,
110}
111
112impl StatisticalPrivacyEngine {
113 pub fn new() -> Self {
114 Self {
115 fiduciary_crypto: Arc::new(Mutex::new(FiduciaryCrypto::new())),
116 zk_proofs: Arc::new(Mutex::new(ZkProofSystem::new())),
117 differential_privacy: DifferentialPrivacy::new(),
118 secure_aggregation: SecureAggregation::new(),
119 privacy_budget: PrivacyBudget {
120 epsilon: 1.0,
121 delta: 1e-6,
122 remaining_epsilon: 1.0,
123 remaining_delta: 1e-6,
124 budget_period: 86400, last_reset: 0,
126 },
127 }
128 }
129
130 pub fn initialize(&mut self) -> Result<(), StatisticalError> {
131 self.differential_privacy.initialize()?;
132 self.secure_aggregation.initialize()?;
133 Ok(())
134 }
135
136 pub fn add_laplace_noise(
137 &mut self,
138 value: f64,
139 sensitivity: f64,
140 ) -> Result<(f64, f64), StatisticalError> {
141 let epsilon = 1.0;
142 let scale = sensitivity / epsilon;
143
144 let noise = self.generate_laplace_noise(scale)?;
145 let noisy_value = value + noise;
146
147 self.privacy_budget.remaining_epsilon -= epsilon;
149
150 Ok((noisy_value, epsilon))
151 }
152
153 pub fn add_histogram_noise(
154 &mut self,
155 counts: &[u32],
156 ) -> Result<(Vec<u32>, f64), StatisticalError> {
157 let epsilon = 1.0;
158 let sensitivity = 1.0;
159 let scale = sensitivity / epsilon;
160
161 let mut noisy_counts = Vec::with_capacity(counts.len());
162 for &count in counts {
163 let noise = self.generate_laplace_noise(scale)?;
164 let noisy_count = (count as f64 + noise).max(0.0) as u32;
165 noisy_counts.push(noisy_count);
166 }
167
168 self.privacy_budget.remaining_epsilon -= epsilon;
170
171 Ok((noisy_counts, epsilon))
172 }
173
174 fn generate_laplace_noise(&self, scale: f64) -> Result<f64, StatisticalError> {
191 let mut bytes = [0u8; 8];
192 getrandom::fill(&mut bytes).map_err(|e| {
193 StatisticalError::PrivacyError(format!(
194 "no OS entropy for differential-privacy noise: {e}"
195 ))
196 })?;
197 let x = u64::from_le_bytes(bytes) as f64;
200 let r = (x + 0.5) / (u64::MAX as f64 + 1.0);
201 let u = r - 0.5;
204 Ok(-scale * u.signum() * (1.0 - 2.0 * u.abs()).ln())
205 }
206
207 pub fn encrypt_result(&self, data: &[u8]) -> Result<Vec<u8>, StatisticalError> {
216 let mut crypto = self
217 .fiduciary_crypto
218 .lock()
219 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
220
221 const STAT_KEY_ID: &str = "statistical_results";
222 if !crypto.list_keys().iter().any(|k| k == STAT_KEY_ID) {
223 crypto
224 .generate_key(STAT_KEY_ID.to_string())
225 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
226 }
227
228 let signature = crypto
229 .sign(
230 data,
231 Some(STAT_KEY_ID),
232 "statistical_computing".to_string(),
233 "result_encryption".to_string(),
234 )
235 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
236
237 Ok(signature.sig_bytes)
238 }
239
240 pub fn verify_result(&self, data: &[u8], signature: &[u8]) -> Result<bool, StatisticalError> {
245 let crypto = self
246 .fiduciary_crypto
247 .lock()
248 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
249
250 const STAT_KEY_ID: &str = "statistical_results";
251 let sig = MlDsaSignature {
252 sig_bytes: signature.to_vec(),
253 };
254 crypto
255 .verify(
256 data,
257 &sig,
258 Some(STAT_KEY_ID),
259 "statistical_computing".to_string(),
260 "result_encryption".to_string(),
261 )
262 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))
263 }
264
265 pub fn prove_computation(
277 &self,
278 computation_id: &str,
279 inputs: &[Vec<u8>],
280 outputs: &[Vec<u8>],
281 ) -> Result<Vec<u8>, StatisticalError> {
282 let mut zk = self
283 .zk_proofs
284 .lock()
285 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
286
287 let mut hasher = Sha256::new();
289 for chunk in inputs {
290 hasher.update(chunk);
291 }
292 for chunk in outputs {
293 hasher.update(chunk);
294 }
295 let digest = hasher.finalize();
296 let mut commitment = [0u8; 32];
297 commitment.copy_from_slice(&digest);
298
299 let circuit_id = format!("stat_comp_{}", computation_id);
300 zk.create_circuit(circuit_id.clone())
301 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
302
303 zk.add_variable(&circuit_id, "commitment".to_string(), VariableType::Public)
305 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
306 zk.add_variable(&circuit_id, "one".to_string(), VariableType::Private)
308 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
309 zk.add_variable(&circuit_id, "in_commit".to_string(), VariableType::Private)
310 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
311
312 zk.add_constraint(
314 &circuit_id,
315 CircuitExpression::Variable("one".to_string()),
316 CircuitExpression::Variable("in_commit".to_string()),
317 CircuitExpression::Variable("commitment".to_string()),
318 )
319 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
320
321 zk.generate_keys(&circuit_id)
322 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
323
324 let mut one_val = [0u8; 32];
326 one_val[0] = 1;
327
328 let mut witness = HashMap::new();
329 witness.insert("one".to_string(), FieldElement { value: one_val });
330 witness.insert("in_commit".to_string(), FieldElement { value: commitment });
331 witness.insert("commitment".to_string(), FieldElement { value: commitment });
332
333 let public_inputs = vec![FieldElement { value: commitment }];
334
335 let proof = zk
336 .generate_proof(&circuit_id, witness, public_inputs)
337 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
338
339 serde_json::to_vec(&proof).map_err(|e| StatisticalError::PrivacyError(e.to_string()))
340 }
341
342 pub fn verify_computation(
349 &self,
350 proof: &[u8],
351 public_inputs: &[Vec<u8>],
352 ) -> Result<bool, StatisticalError> {
353 let zk_proof: ZkProof = serde_json::from_slice(proof)
354 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
355
356 if !public_inputs.is_empty() {
359 if public_inputs.len() != zk_proof.public_inputs.len() {
360 return Ok(false);
361 }
362 for (expected, actual) in public_inputs.iter().zip(&zk_proof.public_inputs) {
363 let mut expected_arr = [0u8; 32];
364 let len = expected.len().min(32);
365 expected_arr[..len].copy_from_slice(&expected[..len]);
366 if expected_arr != actual.value {
367 return Ok(false);
368 }
369 }
370 }
371
372 let mut zk = self
373 .zk_proofs
374 .lock()
375 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
376
377 let result = zk
378 .verify_proof(&zk_proof)
379 .map_err(|e| StatisticalError::PrivacyError(e.to_string()))?;
380
381 Ok(result.is_valid)
382 }
383}
384
385impl DifferentialPrivacy {
386 pub fn new() -> Self {
387 Self {
388 noise_mechanisms: vec![NoiseMechanism::Laplace, NoiseMechanism::Gaussian],
389 privacy_accountant: PrivacyAccountant {
390 total_epsilon_spent: 0.0,
391 total_delta_spent: 0.0,
392 composition_method: CompositionMethod::AdvancedComposition,
393 remaining_budget: PrivacyBudget {
394 epsilon: 1.0,
395 delta: 1e-6,
396 remaining_epsilon: 1.0,
397 remaining_delta: 1e-6,
398 budget_period: 86400,
399 last_reset: 0,
400 },
401 },
402 sensitivity_analyzer: SensitivityAnalyzer::new(),
403 }
404 }
405
406 pub fn initialize(&mut self) -> Result<(), StatisticalError> {
407 self.sensitivity_analyzer.initialize()?;
408 Ok(())
409 }
410
411 pub fn noise_mechanisms(&self) -> &[NoiseMechanism] {
413 &self.noise_mechanisms
414 }
415
416 pub fn add_noise_mechanism(&mut self, mechanism: NoiseMechanism) {
418 if !self.noise_mechanisms.contains(&mechanism) {
419 self.noise_mechanisms.push(mechanism);
420 }
421 }
422
423 pub fn supports_noise_mechanism(&self, mechanism: &NoiseMechanism) -> bool {
425 self.noise_mechanisms.contains(mechanism)
426 }
427
428 pub fn privacy_accountant(&self) -> &PrivacyAccountant {
430 &self.privacy_accountant
431 }
432
433 pub fn privacy_accountant_mut(&mut self) -> &mut PrivacyAccountant {
435 &mut self.privacy_accountant
436 }
437
438 pub fn spend_budget(&mut self, epsilon: f64, delta: f64) -> Result<(), StatisticalError> {
442 let remaining = &self.privacy_accountant.remaining_budget;
443 if remaining.remaining_epsilon < epsilon || remaining.remaining_delta < delta {
444 return Err(StatisticalError::PrivacyError(
445 "Insufficient privacy budget".to_string(),
446 ));
447 }
448 self.privacy_accountant.remaining_budget.remaining_epsilon -= epsilon;
449 self.privacy_accountant.remaining_budget.remaining_delta -= delta;
450 self.privacy_accountant.total_epsilon_spent += epsilon;
451 self.privacy_accountant.total_delta_spent += delta;
452 Ok(())
453 }
454}
455
456impl SensitivityAnalyzer {
457 pub fn new() -> Self {
458 Self {
459 sensitivity_functions: HashMap::new(),
460 sensitivity_cache: HashMap::new(),
461 }
462 }
463
464 pub fn initialize(&mut self) -> Result<(), StatisticalError> {
465 Ok(())
466 }
467
468 pub fn register_function(&mut self, name: &str, func: SensitivityFunction) {
471 self.sensitivity_functions.insert(name.to_string(), func);
472 }
473
474 pub fn compute_sensitivity(
491 &mut self,
492 operation: &str,
493 data: &[f64],
494 ) -> Result<f64, StatisticalError> {
495 if let Some(func) = self.sensitivity_functions.get(operation) {
497 self.sensitivity_cache
498 .insert(operation.to_string(), func.sensitivity);
499 return Ok(func.sensitivity);
500 }
501
502 if data.is_empty() {
503 return Err(StatisticalError::InvalidData(
504 "Cannot compute sensitivity over empty data".to_string(),
505 ));
506 }
507
508 let n = data.len() as f64;
509 let sensitivity = match operation {
510 "mean" => 1.0 / n,
511 "sum" => 1.0,
512 "count" => 1.0,
513 "histogram" => 1.0,
514 "median" => {
515 let min = data.iter().cloned().fold(f64::INFINITY, f64::min);
516 let max = data.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
517 (max - min) / n
518 }
519 "variance" => {
520 let min = data.iter().cloned().fold(f64::INFINITY, f64::min);
521 let max = data.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
522 let range = max - min;
523 (range * range) / n
524 }
525 other => {
526 return Err(StatisticalError::InvalidOperation(format!(
527 "Unknown sensitivity operation '{}'",
528 other
529 )))
530 }
531 };
532
533 self.sensitivity_cache
534 .insert(operation.to_string(), sensitivity);
535 Ok(sensitivity)
536 }
537
538 pub fn get_sensitivity(
541 &mut self,
542 operation: &str,
543 data: &[f64],
544 ) -> Result<f64, StatisticalError> {
545 if let Some(cached) = self.sensitivity_cache.get(operation) {
546 return Ok(*cached);
547 }
548 self.compute_sensitivity(operation, data)
549 }
550}
551
552impl SecureAggregation {
553 pub fn new() -> Self {
554 Self {
555 aggregation_protocols: vec![
556 AggregationProtocol::SecureSum,
557 AggregationProtocol::SecureMean,
558 ],
559 encryption_schemes: vec![
560 EncryptionScheme::Homomorphic,
561 EncryptionScheme::SecretSharing,
562 ],
563 integrity_checks: Vec::new(),
564 }
565 }
566
567 pub fn initialize(&mut self) -> Result<(), StatisticalError> {
568 Ok(())
569 }
570
571 pub fn aggregation_protocols(&self) -> &[AggregationProtocol] {
573 &self.aggregation_protocols
574 }
575
576 pub fn add_aggregation_protocol(&mut self, protocol: AggregationProtocol) {
578 if !self.aggregation_protocols.contains(&protocol) {
579 self.aggregation_protocols.push(protocol);
580 }
581 }
582
583 pub fn encryption_schemes(&self) -> &[EncryptionScheme] {
585 &self.encryption_schemes
586 }
587
588 pub fn add_encryption_scheme(&mut self, scheme: EncryptionScheme) {
590 if !self.encryption_schemes.contains(&scheme) {
591 self.encryption_schemes.push(scheme);
592 }
593 }
594
595 pub fn add_integrity_check(&mut self, check: IntegrityCheck) {
597 self.integrity_checks.push(check);
598 }
599
600 pub fn integrity_checks(&self) -> &[IntegrityCheck] {
602 &self.integrity_checks
603 }
604
605 pub fn get_integrity_check(&self, check_id: &str) -> Option<&IntegrityCheck> {
607 self.integrity_checks
608 .iter()
609 .find(|c| c.check_id == check_id)
610 }
611
612 pub fn integrity_check_count(&self) -> usize {
614 self.integrity_checks.len()
615 }
616}