1use crate::fiduciary_crypto::{CryptoContext, FiduciaryCrypto, MlDsaSignature};
9use crate::zk_proofs::{
10 FieldElement, MathematicalStatement, SemanticProof, StatementType, ZkProofSystem,
11};
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14
15pub struct SemanticCuller {
17 zk_system: ZkProofSystem,
18 fiduciary_crypto: FiduciaryCrypto,
19 agency_policies: HashMap<String, AgencyPolicy>,
20 culling_stats: CullingStats,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq)]
25pub struct CullingVerdict {
26 pub quin_hash: u64,
27 pub semantic_id: u64,
28 pub allowed: bool,
29 pub reason: CullingReason,
30 pub proof_valid: u8,
31 pub signature_valid: u8,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum CullingError {
36 OutputBufferFull,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct AgencyPolicy {
42 pub agency_id: String,
43 pub access_level: AccessLevel,
44 pub semantic_filters: Vec<SemanticFilter>,
45 pub temporal_constraints: TemporalConstraints,
46 pub deontic_rules: Vec<DeonticRule>,
47}
48
49#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
51pub enum AccessLevel {
52 Read,
53 Write,
54 Admin,
55 System,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct SemanticFilter {
61 pub filter_id: String,
62 pub filter_type: FilterType,
63 pub criteria: String,
64 pub required: bool,
65}
66
67#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
69pub enum FilterType {
70 Category,
71 IntensityThreshold,
72 EpistemicLevel,
73 TemporalRange,
74 Custom,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct TemporalConstraints {
80 pub valid_from: u64,
81 pub valid_until: u64,
82 pub max_age_seconds: Option<u64>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct DeonticRule {
88 pub rule_id: String,
89 pub rule_type: DeonticType,
90 pub condition: String,
91 pub action: DeonticAction,
92}
93
94#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub enum DeonticType {
97 Obligation,
98 Permission,
99 Prohibition,
100}
101
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
104pub enum DeonticAction {
105 Allow,
106 Deny,
107 RequireProof,
108 RequireSignature,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct Quin {
114 pub quin_id: String,
115 pub semantic_id: u64,
116 pub intensity: f64,
117 pub epistemic_level: f64,
118 pub timestamp: u64,
119 pub category: String,
120 pub agency_id: Option<String>,
121 pub proof: Option<SemanticProof>,
122 pub signature: Option<MlDsaSignature>,
123}
124
125#[derive(Debug, Clone)]
127pub struct CullingResult {
128 pub quin_id: String,
129 pub allowed: bool,
130 pub reason: CullingReason,
131 pub verification_data: Option<VerificationData>,
132}
133
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub enum CullingReason {
137 SemanticFilterMatch,
138 TemporalConstraintViolation,
139 DeonticRuleViolation,
140 ProofVerificationFailed,
141 SignatureVerificationFailed,
142 MissingPermission,
143 Allowed,
144}
145
146#[derive(Debug, Clone)]
148pub struct VerificationData {
149 pub proof_valid: Option<bool>,
150 pub signature_valid: Option<bool>,
151 pub verification_time_ms: u64,
152}
153
154#[derive(Debug, Clone)]
156pub struct CullingStats {
157 pub total_processed: u64,
158 pub total_allowed: u64,
159 pub total_denied: u64,
160 pub semantic_filtered: u64,
161 pub temporal_filtered: u64,
162 pub deontic_filtered: u64,
163 pub crypto_filtered: u64,
164}
165
166impl SemanticCuller {
167 pub fn new() -> Self {
169 Self {
170 zk_system: ZkProofSystem::new(),
171 fiduciary_crypto: FiduciaryCrypto::new(),
172 agency_policies: HashMap::new(),
173 culling_stats: CullingStats::default(),
174 }
175 }
176
177 pub fn add_policy(&mut self, policy: AgencyPolicy) {
179 self.agency_policies
180 .insert(policy.agency_id.clone(), policy);
181 }
182
183 pub fn cull_quins(&mut self, agency_id: &str, quins: Vec<Quin>) -> Vec<CullingResult> {
185 let policy_ptr = self
186 .agency_policies
187 .get(agency_id)
188 .map(|policy| policy as *const AgencyPolicy);
189
190 let results: Vec<CullingResult> = quins
191 .iter()
192 .map(|quin| {
193 let policy = policy_ptr.map(|ptr| unsafe { &*ptr });
194 self.cull_single_quin(agency_id, quin, policy)
195 })
196 .collect();
197
198 self.update_stats(&results);
200
201 results
202 }
203
204 pub fn cull_quins_into(
206 &mut self,
207 agency_id: &str,
208 quins: &[Quin],
209 out: &mut [CullingVerdict],
210 ) -> Result<usize, CullingError> {
211 let policy_ptr = self
212 .agency_policies
213 .get(agency_id)
214 .map(|policy| policy as *const AgencyPolicy);
215 if out.len() < quins.len() {
216 return Err(CullingError::OutputBufferFull);
217 }
218
219 let mut written = 0usize;
220 for quin in quins {
221 let policy = policy_ptr.map(|ptr| unsafe { &*ptr });
222 let result = self.cull_single_quin(agency_id, quin, policy);
223 out[written] = CullingVerdict {
224 quin_hash: crate::q_hash(&quin.quin_id),
225 semantic_id: quin.semantic_id,
226 allowed: result.allowed,
227 reason: result.reason.clone(),
228 proof_valid: result
229 .verification_data
230 .as_ref()
231 .and_then(|data| data.proof_valid)
232 .map(bool_to_flag)
233 .unwrap_or(0xFF),
234 signature_valid: result
235 .verification_data
236 .as_ref()
237 .and_then(|data| data.signature_valid)
238 .map(bool_to_flag)
239 .unwrap_or(0xFF),
240 };
241 written += 1;
242 }
243
244 self.update_stats_from_verdicts(&out[..written]);
245 Ok(written)
246 }
247
248 fn cull_single_quin(
250 &mut self,
251 _agency_id: &str,
252 quin: &Quin,
253 policy: Option<&AgencyPolicy>,
254 ) -> CullingResult {
255 let policy = match policy {
257 Some(p) => p,
258 None => {
259 return CullingResult {
260 quin_id: quin.quin_id.clone(),
261 allowed: false,
262 reason: CullingReason::MissingPermission,
263 verification_data: None,
264 }
265 }
266 };
267
268 if let Some(reason) = self.check_semantic_filters(quin, policy) {
270 return CullingResult {
271 quin_id: quin.quin_id.clone(),
272 allowed: false,
273 reason,
274 verification_data: None,
275 };
276 }
277
278 if let Some(reason) = self.check_temporal_constraints(quin, policy) {
280 return CullingResult {
281 quin_id: quin.quin_id.clone(),
282 allowed: false,
283 reason,
284 verification_data: None,
285 };
286 }
287
288 if let Some(reason) = self.check_deontic_rules(quin, policy) {
290 return CullingResult {
291 quin_id: quin.quin_id.clone(),
292 allowed: false,
293 reason,
294 verification_data: None,
295 };
296 }
297
298 if let Some(verification_data) = self.check_cryptographic_verification(quin, policy) {
300 if !verification_data.is_allowed() {
301 let reason = if verification_data.proof_valid == Some(false) {
302 CullingReason::ProofVerificationFailed
303 } else if verification_data.signature_valid == Some(false) {
304 CullingReason::SignatureVerificationFailed
305 } else {
306 CullingReason::DeonticRuleViolation
307 };
308
309 return CullingResult {
310 quin_id: quin.quin_id.clone(),
311 allowed: false,
312 reason,
313 verification_data: Some(verification_data),
314 };
315 }
316 }
317
318 CullingResult {
320 quin_id: quin.quin_id.clone(),
321 allowed: true,
322 reason: CullingReason::Allowed,
323 verification_data: None,
324 }
325 }
326
327 fn check_semantic_filters(&self, quin: &Quin, policy: &AgencyPolicy) -> Option<CullingReason> {
329 for filter in &policy.semantic_filters {
330 if !filter.required {
331 continue;
332 }
333
334 let matches = match filter.filter_type {
335 FilterType::Category => quin.category == filter.criteria,
336 FilterType::IntensityThreshold => {
337 let threshold: f64 = filter.criteria.parse().unwrap_or(0.0);
338 quin.intensity < threshold
339 }
340 FilterType::EpistemicLevel => {
341 let threshold: f64 = filter.criteria.parse().unwrap_or(0.0);
342 quin.epistemic_level < threshold
343 }
344 FilterType::TemporalRange => {
345 true
347 }
348 FilterType::Custom => {
349 false
351 }
352 };
353
354 if matches {
355 return Some(CullingReason::SemanticFilterMatch);
356 }
357 }
358
359 None
360 }
361
362 fn check_temporal_constraints(
364 &self,
365 quin: &Quin,
366 policy: &AgencyPolicy,
367 ) -> Option<CullingReason> {
368 let now = std::time::SystemTime::now()
369 .duration_since(std::time::UNIX_EPOCH)
370 .unwrap()
371 .as_secs();
372
373 if quin.timestamp < policy.temporal_constraints.valid_from {
375 return Some(CullingReason::TemporalConstraintViolation);
376 }
377
378 if quin.timestamp > policy.temporal_constraints.valid_until {
380 return Some(CullingReason::TemporalConstraintViolation);
381 }
382
383 if let Some(max_age) = policy.temporal_constraints.max_age_seconds {
385 if now - quin.timestamp > max_age {
386 return Some(CullingReason::TemporalConstraintViolation);
387 }
388 }
389
390 None
391 }
392
393 fn check_deontic_rules(&self, quin: &Quin, policy: &AgencyPolicy) -> Option<CullingReason> {
395 for rule in &policy.deontic_rules {
396 match rule.rule_type {
397 DeonticType::Prohibition => {
398 if rule.action == DeonticAction::Deny
399 && self.evaluate_condition(&rule.condition, quin)
400 {
401 return Some(CullingReason::DeonticRuleViolation);
402 }
403 }
404 DeonticType::Obligation => {
405 if rule.action == DeonticAction::RequireProof && quin.proof.is_none() {
406 return Some(CullingReason::DeonticRuleViolation);
407 }
408 if rule.action == DeonticAction::RequireSignature && quin.signature.is_none() {
409 return Some(CullingReason::DeonticRuleViolation);
410 }
411 }
412 DeonticType::Permission => {
413 }
415 }
416 }
417
418 None
419 }
420
421 fn evaluate_condition(&self, condition: &str, quin: &Quin) -> bool {
423 condition.contains(&quin.category) || condition.contains(&quin.semantic_id.to_string())
426 }
427
428 fn check_cryptographic_verification(
430 &mut self,
431 quin: &Quin,
432 policy: &AgencyPolicy,
433 ) -> Option<VerificationData> {
434 let mut proof_valid = None;
435 let mut signature_valid = None;
436 let start_time = std::time::Instant::now();
437
438 let requires_proof = policy.deontic_rules.iter().any(|r| {
440 r.rule_type == DeonticType::Obligation && r.action == DeonticAction::RequireProof
441 });
442 let requires_signature = policy.deontic_rules.iter().any(|r| {
443 r.rule_type == DeonticType::Obligation && r.action == DeonticAction::RequireSignature
444 });
445
446 if requires_proof {
447 if let Some(ref proof) = quin.proof {
448 let mut proof_copy = proof.clone();
449 match self.zk_system.verify_semantic_proof(&mut proof_copy) {
450 Ok(_) => proof_valid = Some(true),
451 Err(_) => proof_valid = Some(false),
452 }
453 } else {
454 proof_valid = Some(false);
455 }
456 }
457
458 if requires_signature {
459 if let Some(ref signature) = quin.signature {
460 let message = format!("{}:{}:{}", quin.quin_id, quin.semantic_id, quin.timestamp);
461 let context = CryptoContext {
462 domain: "webizen_culling".to_string(),
463 purpose: "agency_verification".to_string(),
464 timestamp: 0,
465 nonce: [0u8; 32],
466 };
467
468 match self.fiduciary_crypto.verify(
470 message.as_bytes(),
471 signature,
472 None,
473 context.domain,
474 context.purpose,
475 ) {
476 Ok(valid) => signature_valid = Some(valid),
477 Err(_) => signature_valid = Some(false),
478 }
479 } else {
480 signature_valid = Some(false);
481 }
482 }
483
484 let verification_time = start_time.elapsed().as_millis() as u64;
485
486 if proof_valid.is_some() || signature_valid.is_some() {
487 Some(VerificationData {
488 proof_valid,
489 signature_valid,
490 verification_time_ms: verification_time,
491 })
492 } else {
493 None
494 }
495 }
496
497 fn update_stats(&mut self, results: &[CullingResult]) {
499 self.culling_stats.total_processed += results.len() as u64;
500
501 for result in results {
502 if result.allowed {
503 self.culling_stats.total_allowed += 1;
504 } else {
505 self.culling_stats.total_denied += 1;
506
507 match result.reason {
508 CullingReason::SemanticFilterMatch => {
509 self.culling_stats.semantic_filtered += 1;
510 }
511 CullingReason::TemporalConstraintViolation => {
512 self.culling_stats.temporal_filtered += 1;
513 }
514 CullingReason::DeonticRuleViolation => {
515 self.culling_stats.deontic_filtered += 1;
516 }
517 CullingReason::ProofVerificationFailed
518 | CullingReason::SignatureVerificationFailed => {
519 self.culling_stats.crypto_filtered += 1;
520 }
521 _ => {}
522 }
523 }
524 }
525 }
526
527 fn update_stats_from_verdicts(&mut self, results: &[CullingVerdict]) {
528 self.culling_stats.total_processed += results.len() as u64;
529
530 for result in results {
531 if result.allowed {
532 self.culling_stats.total_allowed += 1;
533 } else {
534 self.culling_stats.total_denied += 1;
535
536 match result.reason {
537 CullingReason::SemanticFilterMatch => self.culling_stats.semantic_filtered += 1,
538 CullingReason::TemporalConstraintViolation => {
539 self.culling_stats.temporal_filtered += 1
540 }
541 CullingReason::DeonticRuleViolation => self.culling_stats.deontic_filtered += 1,
542 CullingReason::ProofVerificationFailed
543 | CullingReason::SignatureVerificationFailed => {
544 self.culling_stats.crypto_filtered += 1;
545 }
546 _ => {}
547 }
548 }
549 }
550 }
551
552 pub fn get_stats(&self) -> &CullingStats {
554 &self.culling_stats
555 }
556
557 pub fn reset_stats(&mut self) {
559 self.culling_stats = CullingStats::default();
560 }
561
562 pub fn generate_proof_for_quin(&mut self, quin: &Quin) -> Result<SemanticProof, String> {
564 let statement = MathematicalStatement {
565 statement_id: quin.quin_id.clone(),
566 statement_type: StatementType::Equality,
567 expression: format!("intensity == {}", quin.intensity),
568 variables: vec!["intensity".to_string()],
569 constraints: vec![],
570 };
571
572 let mut witness = HashMap::new();
573 witness.insert("intensity".to_string(), FieldElement { value: [0u8; 32] });
574
575 self.zk_system
576 .generate_semantic_proof(statement, witness)
577 .map_err(|e| format!("Proof generation failed: {:?}", e))
578 }
579
580 pub fn sign_quin(
582 &mut self,
583 quin: &Quin,
584 key_id: Option<&str>,
585 ) -> Result<MlDsaSignature, String> {
586 let message = format!("{}:{}:{}", quin.quin_id, quin.semantic_id, quin.timestamp);
587
588 self.fiduciary_crypto
589 .sign(
590 message.as_bytes(),
591 key_id,
592 "webizen_quin".to_string(),
593 "agency_signature".to_string(),
594 )
595 .map_err(|e| format!("Signing failed: {:?}", e))
596 }
597}
598
599impl VerificationData {
600 fn is_allowed(&self) -> bool {
602 match (self.proof_valid, self.signature_valid) {
603 (Some(true), Some(true)) => true,
604 (Some(true), None) => true,
605 (None, Some(true)) => true,
606 (Some(false), _) => false,
607 (_, Some(false)) => false,
608 (None, None) => true,
609 }
610 }
611}
612
613fn bool_to_flag(value: bool) -> u8 {
614 if value {
615 1
616 } else {
617 0
618 }
619}
620
621impl Default for CullingStats {
622 fn default() -> Self {
623 Self {
624 total_processed: 0,
625 total_allowed: 0,
626 total_denied: 0,
627 semantic_filtered: 0,
628 temporal_filtered: 0,
629 deontic_filtered: 0,
630 crypto_filtered: 0,
631 }
632 }
633}
634
635#[cfg(test)]
636mod tests {
637 use super::*;
638
639 #[test]
640 fn test_semantic_culler_creation() {
641 let culler = SemanticCuller::new();
642 assert_eq!(culler.get_stats().total_processed, 0);
643 }
644
645 #[test]
646 fn test_agency_policy() {
647 let policy = AgencyPolicy {
648 agency_id: "test_agency".to_string(),
649 access_level: AccessLevel::Read,
650 semantic_filters: vec![],
651 temporal_constraints: TemporalConstraints {
652 valid_from: 0,
653 valid_until: u64::MAX,
654 max_age_seconds: None,
655 },
656 deontic_rules: vec![],
657 };
658
659 let mut culler = SemanticCuller::new();
660 culler.add_policy(policy);
661
662 assert_eq!(culler.agency_policies.len(), 1);
663 }
664
665 #[test]
666 fn test_quin_culling() {
667 let mut culler = SemanticCuller::new();
668
669 let policy = AgencyPolicy {
670 agency_id: "test_agency".to_string(),
671 access_level: AccessLevel::Read,
672 semantic_filters: vec![],
673 temporal_constraints: TemporalConstraints {
674 valid_from: 0,
675 valid_until: u64::MAX,
676 max_age_seconds: None,
677 },
678 deontic_rules: vec![],
679 };
680 culler.add_policy(policy);
681
682 let quin = Quin {
683 quin_id: "test_quin".to_string(),
684 semantic_id: 123,
685 intensity: 0.5,
686 epistemic_level: 0.9,
687 timestamp: std::time::SystemTime::now()
688 .duration_since(std::time::UNIX_EPOCH)
689 .unwrap()
690 .as_secs(),
691 category: "test".to_string(),
692 agency_id: None,
693 proof: None,
694 signature: None,
695 };
696
697 let results = culler.cull_quins("test_agency", vec![quin]);
698 assert_eq!(results.len(), 1);
699 assert!(results[0].allowed);
700 }
701}