1pub mod sponges;
2use crate::{
3 core::jl::Projection,
4 ring::{rq::Rq, rq_matrix::RqMatrix, rq_vector::RqVector, zq::Zq},
5};
6pub use sponges::Sponge;
7
8pub struct LabradorTranscript<S: Sponge> {
9 sponge: S,
10 pub u1: RqVector,
11 pub vector_p: Vec<Zq>,
12 pub b_ct_aggr: RqVector,
13 pub u2: RqVector,
14 pub z: RqVector,
15 pub t: RqMatrix,
16 pub g: RqMatrix,
17 pub h: RqMatrix,
18}
19
20impl<S: Sponge> LabradorTranscript<S> {
21 pub fn new(sponge: S) -> Self {
22 Self {
23 sponge,
24 u1: RqVector::new(Vec::new()),
25 vector_p: Vec::new(),
26 b_ct_aggr: RqVector::new(Vec::new()),
27 u2: RqVector::new(Vec::new()),
28 z: RqVector::new(Vec::new()),
29 t: RqMatrix::new(vec![RqVector::new(Vec::new())], false),
30 g: RqMatrix::new(vec![RqVector::new(Vec::new())], true),
31 h: RqMatrix::new(vec![RqVector::new(Vec::new())], true),
32 }
33 }
34
35 pub fn set_u1(&mut self, u1: RqVector) {
36 self.absorb_u1(&u1);
37 self.u1 = u1;
38 }
39
40 pub fn absorb_u1(&mut self, u1: &RqVector) {
41 self.sponge.absorb_rq(u1.get_elements());
42 }
43
44 pub fn set_vector_p(&mut self, p: Vec<Zq>) {
45 self.absorb_vector_p(&p);
46 self.vector_p = p;
47 }
48
49 pub fn absorb_vector_p(&mut self, p: &[Zq]) {
50 self.sponge.absorb_zq(p);
51 }
52
53 pub fn set_vector_b_ct_aggr(&mut self, input: RqVector) {
54 self.absorb_vector_b_ct_aggr(&input);
55 self.b_ct_aggr = input;
56 }
57
58 pub fn absorb_vector_b_ct_aggr(&mut self, input: &RqVector) {
59 self.sponge.absorb_rq(input.get_elements());
60 }
61
62 pub fn set_u2(&mut self, u2: RqVector) {
63 self.absorb_u2(&u2);
64 self.u2 = u2;
65 }
66
67 pub fn absorb_u2(&mut self, u2: &RqVector) {
68 self.sponge.absorb_rq(u2.get_elements());
69 }
70
71 pub fn set_recursive_part(&mut self, z: RqVector, t: RqMatrix, g: RqMatrix, h: RqMatrix) {
72 self.z = z;
73 self.t = t;
74 self.g = g;
75 self.h = h;
76 }
77
78 pub fn generate_projections(
79 &mut self,
80 security_parameter: usize,
81 rank: usize,
82 multiplicity: usize,
83 ) -> Projection {
84 let row_size = 2 * security_parameter;
86 let col_size = rank * Rq::DEGREE;
87
88 let matrices = (0..multiplicity)
89 .map(|_| {
90 let linear_projection_randomness = self.sponge.squeeze_zq(row_size * col_size);
91 linear_projection_randomness
92 .chunks_exact(col_size)
93 .map(|chunk| {
94 let coeffs = chunk
95 .iter()
96 .map(|elem| {
97 if elem.get_value() < 2_u32.pow(30) {
98 Zq::NEG_ONE
99 } else if elem.get_value() < 2_u32.pow(31) {
100 Zq::ONE
101 } else {
102 Zq::ZERO
103 }
104 })
105 .collect();
106 RqVector::new_from_zq_vector(coeffs)
107 })
108 .collect()
109 })
110 .collect();
111 Projection::new(matrices, security_parameter)
112 }
113
114 pub fn generate_vector_psi(
115 &mut self,
116 number_of_vectors: usize,
117 vector_length: usize,
118 ) -> Vec<Vec<Zq>> {
119 let elements = self.sponge.squeeze_zq(number_of_vectors * vector_length);
120 elements
121 .chunks_exact(vector_length)
122 .map(|chunk| chunk.into())
123 .collect()
124 }
125
126 pub fn generate_vector_omega(
127 &mut self,
128 number_of_vectors: usize,
129 security_parameter: usize,
130 ) -> Vec<Vec<Zq>> {
131 let elements = self
132 .sponge
133 .squeeze_zq(number_of_vectors * 2 * security_parameter);
134 elements
135 .chunks_exact(2 * security_parameter)
136 .map(|chunk| chunk.into())
137 .collect()
138 }
139
140 pub fn generate_rq_vector(&mut self, vector_length: usize) -> RqVector {
141 RqVector::new(self.sponge.squeeze_rq(vector_length))
142 }
143
144 pub fn generate_challenges(&mut self, op_norm: f64, multiplicity: usize) -> RqVector {
145 let mut result = Vec::new();
146 loop {
147 let candidate = self.sample_challenge();
148 if candidate.operator_norm() < op_norm {
149 result.push(candidate);
150 if result.len() >= multiplicity {
151 return RqVector::new(result);
152 }
153 }
154 }
155 }
156
157 #[allow(clippy::as_conversions)]
158 fn sample_challenge(&mut self) -> Rq {
159 let mut coeffs = [Zq::ZERO; Rq::DEGREE];
160 let random_bits = self.sponge.squeeze_bits(10 + 31);
161 for (item, random_bit) in coeffs.iter_mut().zip(&random_bits).take(31) {
163 *item = if *random_bit { Zq::new(1) } else { -Zq::new(1) };
164 }
165 for (item, random_bit) in coeffs.iter_mut().zip(random_bits).skip(31).take(10) {
167 *item = if random_bit { Zq::new(2) } else { -Zq::new(2) };
168 }
169
170 for i in (1..Rq::DEGREE).rev() {
172 let random_bytes = self.sponge.squeeze_bytes(4);
173 let random_index = u32::from_le_bytes(random_bytes.try_into().unwrap());
174 let random_index = (random_index as usize) % (i + 1); coeffs.swap(i, random_index);
176 }
177 Rq::new(coeffs)
178 }
179}
180
181#[cfg(test)]
182mod tests_generate_pi {
183 use super::*;
184 use crate::transcript::sponges::shake::ShakeSponge;
185
186 #[test]
187 fn test_projection_matrix_has_correct_size() {
188 let (security_parameter, rank, multiplicity) = (128, 20, 9);
189 let mut transcript = LabradorTranscript::new(ShakeSponge::default());
190 let projections = transcript.generate_projections(security_parameter, rank, multiplicity);
191 assert_eq!(projections.get_projection_matrices().len(), multiplicity); assert_eq!(
193 projections.get_projection_matrices()[0].get_row_len(),
194 2 * security_parameter
195 );
196 assert_eq!(projections.get_projection_matrices()[0].get_col_len(), rank);
197 }
198
199 #[test]
201 #[allow(clippy::as_conversions)]
202 fn test_projection_matrix_is_random() {
203 let (security_parameter, rank, multiplicity) = (128, 1000, 1);
204 let mut transcript = LabradorTranscript::new(ShakeSponge::default());
205 let projections = transcript.generate_projections(security_parameter, rank, multiplicity);
206
207 for projection_matrix in projections.get_projection_matrices() {
208 let mut counts = [0.0, 0.0, 0.0]; for row in projection_matrix.get_elements() {
210 for cell in row.concatenate_coefficients() {
211 match cell {
212 Zq::ZERO => counts[1] += 1.0,
213 Zq::ONE => counts[2] += 1.0,
214 Zq::NEG_ONE => counts[0] += 1.0,
215 _ => panic!("Should not occur"),
216 }
217 }
218 }
219 #[allow(clippy::as_conversions)]
221 let total: f64 = (256 * Rq::DEGREE * rank) as f64;
222 println!("this is the total amount of elements{}", total);
223 let expected = [0.25, 0.5, 0.25];
224 for i in 0..3 {
225 let actual = counts[i] / total;
226 println!("This is the actual value {}", actual);
227 assert!(
228 (actual - expected[i]).abs() < 0.005,
230 "Values are not within expected proportions"
231 );
232 }
233 }
234 }
235}
236
237#[cfg(test)]
238mod test_generate_psi {
239 use super::*;
240 use crate::transcript::sponges::shake::ShakeSponge;
241
242 #[test]
243 fn test_generate_vector_psi_has_correct_size() {
244 let (k_range, l_range) = (20, 12);
245 let mut transcript = LabradorTranscript::new(ShakeSponge::default());
246 let result = transcript.generate_vector_psi(k_range, l_range);
247 assert_eq!(result.len(), k_range); assert_eq!(result[0].len(), l_range);
249 }
250
251 }
253
254#[cfg(test)]
255mod test_generate_omega {
256 use super::*;
257 use crate::transcript::sponges::shake::ShakeSponge;
258
259 #[test]
260 fn test_generate_vector_omega_has_correct_size() {
261 let (security_parameter, k_range) = (128, 20);
262 let mut transcript = LabradorTranscript::new(ShakeSponge::default());
263 let result = transcript.generate_vector_omega(k_range, security_parameter);
264 assert_eq!(result.len(), k_range); assert_eq!(result[0].len(), 256);
266 }
267
268 }
270
271#[cfg(test)]
272mod test_generate_rq {
273 use super::*;
274 use crate::transcript::sponges::shake::ShakeSponge;
275
276 #[test]
277 fn test_generate_rq_has_correct_size() {
278 let k_range = 20;
279 let mut transcript = LabradorTranscript::new(ShakeSponge::default());
280 let result = transcript.generate_rq_vector(k_range);
281 assert_eq!(result.get_elements().len(), k_range); }
283
284 }
286
287#[cfg(test)]
288mod test_generate_challenges {
289 use super::*;
290 use crate::transcript::sponges::shake::ShakeSponge;
291
292 #[test]
293 fn test_generate_challenges_has_correct_size() {
294 let multiplicity = 9;
295 let mut transcript = LabradorTranscript::new(ShakeSponge::default());
296 let result = transcript.generate_challenges(15.0, multiplicity);
297 assert_eq!(result.get_elements().len(), multiplicity); }
299
300 #[test]
305 fn test_challenge_set() {
306 let op_norm = 15.0;
307 let multiplicity = 9;
308 let mut transcript = LabradorTranscript::new(ShakeSponge::default());
309
310 let challenge_set = transcript.generate_challenges(15.0, multiplicity);
311
312 for i in 0..multiplicity {
313 use crate::core::inner_product::compute_linear_combination;
315 assert_eq!(
316 compute_linear_combination(
317 challenge_set.get_elements()[i].get_coefficients(),
318 challenge_set.get_elements()[i].get_coefficients()
319 ),
320 Zq::new(71)
321 );
322 assert!(challenge_set.get_elements()[i].operator_norm() <= op_norm);
323 }
324
325 for i in 0..multiplicity {
326 for j in 0..multiplicity {
327 if i != j {
328 assert_ne!(
329 challenge_set.get_elements()[i],
330 challenge_set.get_elements()[j]
331 );
332 }
333 }
334 }
335 }
336
337 }