labrador/commitments/
ajtai_commitment.rs1use crate::ring::rq_matrix::RqMatrix;
2use crate::ring::rq_vector::RqVector;
3use crate::ring::zq::Zq;
4use crate::ring::Norms;
5use thiserror::Error;
6
7#[derive(Debug, Error)]
9pub enum ParameterError {
10 #[error("parameters must be positive")]
11 ZeroParameter,
12 #[error("security bound β·m^(3/2) must be less than q")]
13 SecurityBoundViolation,
14 #[error("invalid witness bounds specified")]
15 InvalidWitnessBounds(u128),
16 #[error("commitment output length {0} is too large")]
17 TooLargeCommitmentLength(usize),
18}
19
20#[derive(Debug, Error)]
21pub enum CommitError {
22 #[error("witness coefficients exceed bound {0}")]
23 InvalidWitnessBounds(u128),
24 #[error("invalid witness vector size")]
25 InvalidWitnessSize,
26}
27
28#[derive(Debug, Error)]
29pub enum VerificationError {
30 #[error("witness coefficients exceed bound {0}")]
31 InvalidWitnessBounds(u128),
32 #[error("commitment does not match opening")]
33 CommitmentMismatch,
34 #[error("invalid opening vector size")]
35 InvalidOpeningSize,
36 #[error("invalid commitment vector size")]
37 InvalidCommitmentSize,
38}
39
40#[derive(Debug)]
42pub struct AjtaiScheme {
43 norm_bound_sq: u128,
45 random_matrix: RqMatrix,
46}
47
48impl AjtaiScheme {
49 pub fn new(norm_bound_sq: u128, random_matrix: RqMatrix) -> Result<Self, ParameterError> {
50 if norm_bound_sq == 0 {
51 return Err(ParameterError::InvalidWitnessBounds(norm_bound_sq));
52 }
53 Self::validate_parameters(
54 norm_bound_sq,
55 random_matrix.row_len(),
56 random_matrix.col_len(),
57 )?;
58
59 Ok(Self {
60 norm_bound_sq,
61 random_matrix,
62 })
63 }
64
65 pub fn commit(&self, witness: &RqVector) -> Result<RqVector, CommitError> {
67 if !self.check_bounds(witness) {
68 return Err(CommitError::InvalidWitnessBounds(self.norm_bound_sq));
69 }
70 if witness.len() != self.random_matrix.col_len() {
71 return Err(CommitError::InvalidWitnessSize);
72 }
73 let commitment = &self.random_matrix * witness;
74 Ok(commitment)
75 }
76
77 pub fn verify(
79 &self,
80 commitment: &RqVector,
81 opening: &RqVector,
82 ) -> Result<(), VerificationError> {
83 if !self.check_bounds(opening) {
84 return Err(VerificationError::InvalidWitnessBounds(self.norm_bound_sq));
85 }
86 if opening.len() != self.random_matrix.col_len() {
87 return Err(VerificationError::InvalidOpeningSize);
88 }
89 if commitment.len() != self.random_matrix.row_len() {
90 return Err(VerificationError::InvalidCommitmentSize);
91 }
92
93 let recomputed = &self.random_matrix * opening;
94 if commitment != &recomputed {
95 return Err(VerificationError::CommitmentMismatch);
96 }
97
98 Ok(())
99 }
100
101 fn validate_parameters(
114 norm_bound_sq: u128,
115 row_len: usize,
116 col_len: usize,
117 ) -> Result<(), ParameterError> {
118 if [row_len, col_len].contains(&0) {
119 return Err(ParameterError::ZeroParameter);
120 }
121 let q: u128 = Zq::NEG_ONE.to_u128() + 1;
123
124 let m_cubed: u128 = row_len
126 .checked_pow(3)
127 .ok_or(ParameterError::SecurityBoundViolation)?
128 .try_into()
129 .map_err(|_| ParameterError::TooLargeCommitmentLength(row_len))?;
130
131 let q_squared = q
133 .checked_pow(2)
134 .ok_or(ParameterError::SecurityBoundViolation)?;
135
136 if norm_bound_sq >= q_squared.checked_div(m_cubed).unwrap_or(0) {
139 return Err(ParameterError::SecurityBoundViolation);
140 }
141 Ok(())
142 }
143
144 fn check_bounds(&self, _polynomials: &RqVector) -> bool {
146 _polynomials.l2_norm_squared() <= self.norm_bound_sq()
147 }
148
149 pub fn matrix(&self) -> &RqMatrix {
151 &self.random_matrix
152 }
153
154 pub fn norm_bound_sq(&self) -> u128 {
156 self.norm_bound_sq
157 }
158
159 pub fn get_row_size(&self) -> usize {
160 self.random_matrix.elements().len()
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use crate::ring::rq::Rq;
168
169 const TEST_M: usize = 8;
170 const TEST_N: usize = 8;
171
172 mod test_utils {
174 use crate::relation::witness::Witness;
175
176 use super::*;
177
178 pub fn valid_witness(coeff: Zq) -> RqVector {
179 vec![Rq::new([coeff; Rq::DEGREE]); TEST_N].into()
180 }
181
182 pub fn random_valid_witness() -> Vec<RqVector> {
183 Witness::new(TEST_N, 1, 10000).s
184 }
185
186 pub fn setup_scheme() -> AjtaiScheme {
187 let mut rng = rand::rng();
188 let random_matrix = RqMatrix::random(&mut rng, TEST_M, TEST_N);
189 AjtaiScheme::new(10000 * 10000, random_matrix).unwrap()
190 }
191 }
192
193 #[test]
194 fn rejects_invalid_parameters() {
195 assert!(AjtaiScheme::new(
196 0,
197 RqMatrix::new(vec![RqVector::new(vec![Rq::zero()])], false)
198 )
199 .is_err());
200 let _ = test_utils::setup_scheme(); }
202
203 #[test]
204 fn initializes_with_correct_bounds() {
205 let scheme = test_utils::setup_scheme();
206 assert_eq!(scheme.norm_bound_sq(), 10000 * 10000);
207 }
208
209 #[test]
210 fn completes_commitment_cycle() {
211 let scheme = test_utils::setup_scheme();
212 let witness = test_utils::valid_witness(Zq::new(1));
213
214 let commitment = scheme.commit(&witness).unwrap();
215 assert!(scheme.verify(&commitment, &witness).is_ok());
216
217 let mut bad_opening = witness.clone();
218 let mut rng = rand::rng();
219 bad_opening.set(0, Rq::random(&mut rng));
220 assert!(scheme.verify(&commitment, &bad_opening).is_err());
221 }
222
223 #[test]
224 fn maintains_security_properties() {
225 let scheme = test_utils::setup_scheme();
226
227 let witness1 = test_utils::random_valid_witness();
229 let witness2 = test_utils::random_valid_witness();
230
231 assert_ne!(witness1, witness2, "Test requires different witnesses");
233
234 let c1 = scheme.commit(&witness1[0]).unwrap();
235 let c2 = scheme.commit(&witness2[0]).unwrap();
236 assert_ne!(
237 c1, c2,
238 "Different witnesses should produce different commitments"
239 );
240 }
241
242 #[test]
243 fn handles_edge_cases() {
244 let scheme = test_utils::setup_scheme();
245 let zero_witness = RqVector::zero(TEST_N);
246
247 assert!(scheme.commit(&zero_witness).is_ok());
248 assert!(scheme
249 .commit(&test_utils::valid_witness(Zq::new(1)))
250 .is_ok());
251 }
252
253 #[test]
254 fn stress_test() {
255 let scheme = test_utils::setup_scheme();
256
257 (0..100).for_each(|_| {
258 let witness = test_utils::valid_witness(Zq::new(1));
259 let commitment = scheme.commit(&witness).unwrap();
260 assert!(scheme.verify(&commitment, &witness).is_ok());
261 });
262 }
263}