qualia_core_db/crypto/
deontic_circuit.rs1#[cfg(feature = "zk-culling")]
5use ark_bls12_381::{Bls12_381, Fr};
6#[cfg(feature = "zk-culling")]
7use ark_groth16::{Groth16, Proof, ProvingKey, VerifyingKey};
8#[cfg(feature = "zk-culling")]
9use ark_relations::gr1cs::{
10 ConstraintSynthesizer, ConstraintSystemRef, LinearCombination, SynthesisError, Variable,
11};
12#[cfg(feature = "zk-culling")]
13use ark_serialize::CanonicalDeserialize;
14#[cfg(feature = "zk-culling")]
15use ark_snark::SNARK;
16
17#[cfg(feature = "zk-culling")]
18#[derive(Clone)]
19pub struct DeonticAccessCircuit {
20 pub user_did_commitment: Option<Fr>,
21 pub role_id: Option<Fr>,
22 pub action_permission: Option<Fr>,
23 pub policy_root: Option<Fr>,
24 pub temporal_constraint: Option<Fr>,
25}
26
27#[cfg(feature = "zk-culling")]
28impl ConstraintSynthesizer<Fr> for DeonticAccessCircuit {
29 fn generate_constraints(self, cs: ConstraintSystemRef<Fr>) -> Result<(), SynthesisError> {
30 let did_var = cs.new_witness_variable(|| {
31 self.user_did_commitment
32 .ok_or(SynthesisError::AssignmentMissing)
33 })?;
34 let role_var =
35 cs.new_witness_variable(|| self.role_id.ok_or(SynthesisError::AssignmentMissing))?;
36 let action_var = cs.new_witness_variable(|| {
37 self.action_permission
38 .ok_or(SynthesisError::AssignmentMissing)
39 })?;
40 let root_var =
41 cs.new_input_variable(|| self.policy_root.ok_or(SynthesisError::AssignmentMissing))?;
42 let time_var = cs.new_input_variable(|| {
43 self.temporal_constraint
44 .ok_or(SynthesisError::AssignmentMissing)
45 })?;
46
47 cs.enforce_r1cs_constraint(
52 || LinearCombination::from(did_var) + role_var + action_var,
53 || LinearCombination::from(Variable::One),
54 || LinearCombination::from(root_var),
55 )?;
56 let _ = time_var;
61 Ok(())
62 }
63}
64
65#[cfg(feature = "zk-culling")]
66pub struct ZkAccessVerifier {
67 verifying_key: Option<VerifyingKey<Bls12_381>>,
68}
69
70#[cfg(feature = "zk-culling")]
71impl ZkAccessVerifier {
72 pub fn new() -> Self {
73 Self {
74 verifying_key: None,
75 }
76 }
77
78 pub fn verify_access(
79 &self,
80 proof: &[u8],
81 public_inputs: &[Fr],
82 ) -> Result<bool, crate::fiduciary_crypto::MlDsaError> {
83 if let Some(ref vk) = self.verifying_key {
84 let parsed_proof =
85 Proof::<Bls12_381>::deserialize_uncompressed(&mut &*proof).map_err(|e| {
86 crate::fiduciary_crypto::MlDsaError::SignatureVerificationFailed(e.to_string())
87 })?;
88 Groth16::<Bls12_381>::verify(vk, public_inputs, &parsed_proof).map_err(|e| {
89 crate::fiduciary_crypto::MlDsaError::SignatureVerificationFailed(e.to_string())
90 })
91 } else {
92 Err(
93 crate::fiduciary_crypto::MlDsaError::SignatureVerificationFailed(
94 "Verifying key not loaded".to_string(),
95 ),
96 )
97 }
98 }
99}
100
101#[cfg(feature = "zk-culling")]
102impl Default for ZkAccessVerifier {
103 fn default() -> Self {
104 Self::new()
105 }
106}
107
108#[cfg(feature = "zk-culling")]
109pub fn generate_deontic_crs() -> Result<(ProvingKey<Bls12_381>, VerifyingKey<Bls12_381>), String> {
110 let circuit = DeonticAccessCircuit {
111 user_did_commitment: None,
112 role_id: None,
113 action_permission: None,
114 policy_root: None,
115 temporal_constraint: None,
116 };
117 let mut rng = crate::zk_proofs::zk_secure_rng();
118 Groth16::<Bls12_381>::circuit_specific_setup(circuit, &mut rng)
119 .map_err(|e| format!("Failed to generate parameters: {e}"))
120}
121
122#[cfg(test)]
123#[cfg(feature = "zk-culling")]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_circuit_setup() {
129 assert!(generate_deontic_crs().is_ok());
130 }
131
132 #[test]
133 fn test_deontic_proof_roundtrip_and_soundness() {
134 use ark_ff::UniformRand;
135 let mut rng = crate::zk_proofs::zk_secure_rng();
136 let (pk, vk) = generate_deontic_crs().unwrap();
137
138 let did = Fr::rand(&mut rng);
140 let role = Fr::rand(&mut rng);
141 let action = Fr::rand(&mut rng);
142 let policy_root = did + role + action;
143 let temporal = Fr::from(1_700_000_000u64);
144
145 let circuit = DeonticAccessCircuit {
146 user_did_commitment: Some(did),
147 role_id: Some(role),
148 action_permission: Some(action),
149 policy_root: Some(policy_root),
150 temporal_constraint: Some(temporal),
151 };
152 let proof = Groth16::<Bls12_381>::prove(&pk, circuit, &mut rng).unwrap();
153
154 assert!(
156 Groth16::<Bls12_381>::verify(&vk, &[policy_root, temporal], &proof).unwrap(),
157 "a satisfying deontic access proof must verify"
158 );
159
160 assert!(
162 !Groth16::<Bls12_381>::verify(&vk, &[policy_root + Fr::from(1u64), temporal], &proof)
163 .unwrap(),
164 "the proof must NOT verify against a falsified policy_root"
165 );
166 }
167}