sonobe_primitives/transcripts/griffin/
mod.rs1use ark_ff::{LegendreSymbol, PrimeField, field_hashers::hash_to_field};
42use ark_r1cs_std::{
43 GR1CSVar,
44 alloc::AllocVar,
45 fields::{FieldVar, fp::FpVar},
46};
47use ark_relations::gr1cs::SynthesisError;
48use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
49use itertools::Itertools;
50use num_bigint::BigUint;
51use sha3::{
52 Shake128, Shake128Reader,
53 digest::{ExtendableOutput, Update, XofReader},
54};
55
56pub mod sponge;
57
58#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)]
63pub struct GriffinParams<F: PrimeField> {
64 round_constants: Vec<Vec<F>>,
65 t: usize,
66 d: usize,
67 d_inv: Vec<bool>,
68 rounds: usize,
69 alpha_beta: Vec<[F; 2]>,
70 mat: Vec<Vec<F>>,
71 rate: usize,
72 capacity: usize,
73}
74
75impl<F: PrimeField> GriffinParams<F> {
76 const INIT_SHAKE: &'static str = "Griffin";
77
78 pub fn new(t: usize, d: usize, rounds: usize) -> Self {
81 assert!(t == 3 || t & 3 == 0);
84 assert!(d == 3 || d == 5);
85 assert!(rounds >= 1);
86
87 let mut shake = Self::init_shake();
88
89 let d_inv = BigUint::from(d)
90 .modinv(&(-F::one()).into())
91 .unwrap()
92 .to_radix_be(2)
93 .into_iter()
94 .map(|i| i != 0)
95 .skip_while(|i| !i)
96 .collect();
97 let round_constants = Self::instantiate_rc(t, rounds, &mut shake);
98 let alpha_beta = Self::instantiate_alpha_beta(t, &mut shake);
99
100 let mat = Self::instantiate_matrix(t);
101
102 GriffinParams {
103 round_constants,
104 t,
105 d,
106 d_inv,
107 rounds,
108 alpha_beta,
109 mat,
110 rate: t - 1,
111 capacity: 1,
112 }
113 }
114
115 fn init_shake() -> Shake128Reader {
116 let mut shake = Shake128::default();
117 shake.update(Self::INIT_SHAKE.as_bytes());
118 for i in F::characteristic() {
119 shake.update(&i.to_le_bytes());
120 }
121 shake.finalize_xof()
122 }
123
124 fn instantiate_rc(t: usize, rounds: usize, shake: &mut Shake128Reader) -> Vec<Vec<F>> {
125 (0..rounds - 1)
126 .map(|_| (0..t).map(|_| hash_to_field::<_, _, 128>(shake)).collect())
127 .collect()
128 }
129
130 fn instantiate_alpha_beta(t: usize, shake: &mut Shake128Reader) -> Vec<[F; 2]> {
131 fn hash_to_non_zero_field<F: PrimeField>(reader: &mut impl XofReader) -> F {
132 loop {
133 let element = hash_to_field::<F, _, 128>(reader);
134 if !element.is_zero() {
135 return element;
136 }
137 }
138 }
139
140 let mut alpha_beta = Vec::with_capacity(t - 2);
141
142 loop {
144 let alpha = hash_to_non_zero_field::<F>(shake);
145 let mut beta = hash_to_non_zero_field::<F>(shake);
146 while alpha == beta {
148 beta = hash_to_non_zero_field::<F>(shake);
149 }
150 let mut symbol = alpha;
151 symbol.square_in_place();
152 let mut tmp = beta;
153 tmp.double_in_place();
154 tmp.double_in_place();
155 symbol.sub_assign(&tmp);
156 if symbol.legendre() == LegendreSymbol::QuadraticNonResidue {
157 alpha_beta.push([alpha, beta]);
158 break;
159 }
160 }
161
162 for i in 2..t - 1 {
164 let mut alpha = alpha_beta[0][0];
165 let mut beta = alpha_beta[0][1];
166 alpha.mul_assign(&F::from(i as u64));
167 beta.mul_assign(&F::from((i * i) as u64));
168 while alpha == beta {
170 beta = hash_to_non_zero_field::<F>(shake);
171 }
172
173 #[cfg(debug_assertions)]
174 {
175 let mut symbol = alpha;
177 symbol.square_in_place();
178 let mut tmp = beta;
179 tmp.double_in_place();
180 tmp.double_in_place();
181 symbol.sub_assign(&tmp);
182 assert_eq!(symbol.legendre(), LegendreSymbol::QuadraticNonResidue);
183 }
184
185 alpha_beta.push([alpha, beta]);
186 }
187
188 alpha_beta
189 }
190
191 fn instantiate_matrix(t: usize) -> Vec<Vec<F>> {
192 if t == 3 {
193 let row = vec![F::from(2), F::from(1), F::from(1)];
194 let t = row.len();
195 let mut mat: Vec<Vec<F>> = Vec::with_capacity(t);
196 let mut rot = row.to_owned();
197 mat.push(rot.clone());
198 for _ in 1..t {
199 rot.rotate_right(1);
200 mat.push(rot.clone());
201 }
202 mat
203 } else {
204 let row1 = vec![F::from(5), F::from(7), F::from(1), F::from(3)];
205 let row2 = vec![F::from(4), F::from(6), F::from(1), F::from(1)];
206 let row3 = vec![F::from(1), F::from(3), F::from(5), F::from(7)];
207 let row4 = vec![F::from(1), F::from(1), F::from(4), F::from(6)];
208 let c_mat = vec![row1, row2, row3, row4];
209 if t == 4 {
210 c_mat
211 } else {
212 assert_eq!(t % 4, 0);
213 let mut mat: Vec<Vec<F>> = vec![vec![F::zero(); t]; t];
214 for (row, matrow) in mat.iter_mut().enumerate().take(t) {
215 for (col, matitem) in matrow.iter_mut().enumerate().take(t) {
216 let row_mod = row % 4;
217 let col_mod = col % 4;
218 *matitem = c_mat[row_mod][col_mod];
219 if row / 4 == col / 4 {
220 matitem.add_assign(&c_mat[row_mod][col_mod]);
221 }
222 }
223 }
224 mat
225 }
226 }
227 }
228}
229
230pub struct Griffin;
232
233impl Griffin {
234 fn affine_3<F: PrimeField>(params: &GriffinParams<F>, input: &mut [F], round: usize) {
235 let mut sum = input[0];
237 input.iter().skip(1).for_each(|el| sum.add_assign(el));
238
239 if round < params.rounds - 1 {
240 for (el, rc) in input
241 .iter_mut()
242 .zip_eq(params.round_constants[round].iter())
243 {
244 el.add_assign(&sum);
245 el.add_assign(rc); }
247 } else {
248 for el in input.iter_mut() {
250 el.add_assign(&sum);
251 }
252 }
253 }
254
255 fn affine_4<F: PrimeField>(params: &GriffinParams<F>, input: &mut [F], round: usize) {
256 let mut t_0 = input[0];
257 t_0.add_assign(&input[1]);
258 let mut t_1 = input[2];
259 t_1.add_assign(&input[3]);
260 let mut t_2 = input[1];
261 t_2.double_in_place();
262 t_2.add_assign(&t_1);
263 let mut t_3 = input[3];
264 t_3.double_in_place();
265 t_3.add_assign(&t_0);
266 let mut t_4 = t_1;
267 t_4.double_in_place();
268 t_4.double_in_place();
269 t_4.add_assign(&t_3);
270 let mut t_5 = t_0;
271 t_5.double_in_place();
272 t_5.double_in_place();
273 t_5.add_assign(&t_2);
274 let mut t_6 = t_3;
275 t_6.add_assign(&t_5);
276 let mut t_7 = t_2;
277 t_7.add_assign(&t_4);
278 input[0] = t_6;
279 input[1] = t_5;
280 input[2] = t_7;
281 input[3] = t_4;
282
283 if round < params.rounds - 1 {
284 for (i, rc) in input
285 .iter_mut()
286 .zip_eq(params.round_constants[round].iter())
287 {
288 i.add_assign(rc);
289 }
290 }
291 }
292
293 fn affine<F: PrimeField>(params: &GriffinParams<F>, input: &mut [F], round: usize) {
294 if params.t == 3 {
295 Griffin::affine_3(params, input, round);
296 return;
297 }
298 if params.t == 4 {
299 Griffin::affine_4(params, input, round);
300 return;
301 }
302
303 let t4 = params.t / 4;
305 for i in 0..t4 {
306 let start_index = i * 4;
307 let mut t_0 = input[start_index];
308 t_0.add_assign(&input[start_index + 1]);
309 let mut t_1 = input[start_index + 2];
310 t_1.add_assign(&input[start_index + 3]);
311 let mut t_2 = input[start_index + 1];
312 t_2.double_in_place();
313 t_2.add_assign(&t_1);
314 let mut t_3 = input[start_index + 3];
315 t_3.double_in_place();
316 t_3.add_assign(&t_0);
317 let mut t_4: F = t_1;
318 t_4.double_in_place();
319 t_4.double_in_place();
320 t_4.add_assign(&t_3);
321 let mut t_5 = t_0;
322 t_5.double_in_place();
323 t_5.double_in_place();
324 t_5.add_assign(&t_2);
325 input[start_index] = t_3 + t_5;
326 input[start_index + 1] = t_5;
327 input[start_index + 2] = t_2 + t_4;
328 input[start_index + 3] = t_4;
329 }
330
331 let mut stored = [F::zero(); 4];
333 for l in 0..4 {
334 stored[l] = input[l];
335 for j in 1..t4 {
336 stored[l].add_assign(&input[4 * j + l]);
337 }
338 }
339
340 for i in 0..input.len() {
341 input[i].add_assign(&stored[i % 4]);
342 if round < params.rounds - 1 {
343 input[i].add_assign(¶ms.round_constants[round][i]); }
345 }
346 }
347
348 fn non_linear<F: PrimeField>(params: &GriffinParams<F>, input: &mut [F]) {
349 input[0] = {
351 let mut res = F::one();
352 for &i in ¶ms.d_inv {
353 res.square_in_place();
354 if i {
355 res *= input[0];
356 }
357 }
358 res
359 };
360
361 let mut state = input[1];
362
363 input[1].square_in_place();
364 match params.d {
365 3 => {}
366 5 => {
367 input[1].square_in_place();
368 }
369 _ => panic!(),
370 }
371 input[1].mul_assign(&state);
372
373 let mut y01_i = input[1];
374 for i in 2..input.len() {
376 y01_i += input[0];
377 let l = if i == 2 { y01_i } else { y01_i + state };
378 let ab = ¶ms.alpha_beta[i - 2];
379 state = input[i];
380 input[i] *= l.square() + l * ab[0] + ab[1];
381 }
382 }
383
384 pub fn permute<F: PrimeField>(params: &GriffinParams<F>, input: &mut [F]) {
387 Griffin::affine(params, input, params.rounds); for r in 0..params.rounds {
390 Griffin::non_linear(params, input);
391 Griffin::affine(params, input, r);
392 }
393 }
394
395 pub fn hash<F: PrimeField>(params: &GriffinParams<F>, message: &[F]) -> F {
399 let mut state = vec![F::zero(); params.t];
400 for chunk in message.chunks(params.rate) {
401 for i in 0..chunk.len() {
402 state[i] += &chunk[i];
403 }
404 Griffin::permute(params, &mut state)
405 }
406 state[0]
407 }
408}
409
410pub struct GriffinGadget;
413
414impl GriffinGadget {
415 fn non_linear<F: PrimeField>(
416 params: &GriffinParams<F>,
417 state: &[FpVar<F>],
418 ) -> Result<Vec<FpVar<F>>, SynthesisError> {
419 let cs = state.cs();
420 let mut result = state.to_owned();
421 result[0] = FpVar::new_variable_with_inferred_mode(cs, || {
423 Ok({
424 {
425 let v = result[0].value().unwrap_or_default();
426 let mut res = F::one();
427 for &i in ¶ms.d_inv {
428 res.square_in_place();
429 if i {
430 res *= v;
431 }
432 }
433 res
434 }
435 })
436 })?;
437
438 let mut sq = result[0].square()?;
439 if params.d == 5 {
440 sq = sq.square()?;
441 }
442 result[0].mul_equals(&sq, &state[0])?;
443
444 let mut sq = result[1].square()?;
446 if params.d == 5 {
447 sq = sq.square()?;
448 }
449 result[1] *= sq;
450
451 let mut y01_i = result[1].clone();
452
453 for i in 2..result.len() {
455 y01_i += &result[0];
456 let l = if i == 2 {
457 y01_i.clone()
458 } else {
459 &y01_i + &state[i - 1]
460 };
461 let ab = ¶ms.alpha_beta[i - 2];
462 result[i] *= l.square()? + l * ab[0] + ab[1];
463 }
464
465 Ok(result)
466 }
467
468 pub fn permute<F: PrimeField>(
471 params: &GriffinParams<F>,
472 state: &[FpVar<F>],
473 ) -> Result<Vec<FpVar<F>>, SynthesisError> {
474 let mut current_state = state.to_owned();
475 current_state = params
476 .mat
477 .iter()
478 .map(|row| current_state.iter().zip_eq(row).map(|(a, b)| a * *b).sum())
479 .collect();
480
481 for r in 0..params.rounds {
482 current_state = GriffinGadget::non_linear(params, ¤t_state)?;
483 current_state = params
484 .mat
485 .iter()
486 .map(|row| current_state.iter().zip_eq(row).map(|(a, b)| a * *b).sum())
487 .collect();
488 if r < params.rounds - 1 {
489 current_state = current_state
490 .iter()
491 .zip_eq(¶ms.round_constants[r])
492 .map(|(c, rc)| c + *rc)
493 .collect();
494 }
495 }
496 Ok(current_state)
497 }
498
499 pub fn hash<F: PrimeField>(
503 params: &GriffinParams<F>,
504 message: &[FpVar<F>],
505 ) -> Result<FpVar<F>, SynthesisError> {
506 let mut state = vec![FpVar::zero(); params.t];
507 for chunk in message.chunks(params.rate) {
508 for i in 0..chunk.len() {
509 state[i] += &chunk[i];
510 }
511 state = GriffinGadget::permute(params, &state)?;
512 }
513 Ok(state[0].clone())
514 }
515}
516
517#[cfg(test)]
518mod tests {
519 use ark_bn254::Fr;
520 use ark_ff::UniformRand;
521 use ark_relations::gr1cs::ConstraintSystem;
522 use ark_std::{error::Error, rand::thread_rng};
523 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
524 use wasm_bindgen_test::wasm_bindgen_test as test;
525
526 use super::*;
527
528 #[test]
529 fn test() -> Result<(), Box<dyn Error>> {
530 let rng = &mut thread_rng();
531 let params = GriffinParams::new(24, 5, 9);
532 let t = params.t;
533 let x: Vec<Fr> = (0..t).map(|_| Fr::rand(rng)).collect();
534
535 let y = Griffin::hash(¶ms, &x);
536
537 let cs = ConstraintSystem::new_ref();
538 let x_var = Vec::new_witness(cs.clone(), || Ok(x.clone()))?;
539 let y_var = GriffinGadget::hash(¶ms, &x_var)?;
540 assert_eq!(y, y_var.value()?);
541 println!("{}", cs.num_constraints());
542 assert!(cs.is_satisfied()?);
543
544 Ok(())
545 }
546
547 #[test]
548 fn test_consistent_perm() {
549 let rng = &mut thread_rng();
550 let params = GriffinParams::new(3, 5, 12);
551 let t = params.t;
552 for _ in 0..5 {
553 let input1: Vec<_> = (0..t).map(|_| Fr::rand(rng)).collect();
554
555 let mut input2: Vec<_>;
556 loop {
557 input2 = (0..t).map(|_| Fr::rand(rng)).collect();
558 if input1 != input2 {
559 break;
560 }
561 }
562
563 let mut perm1 = input1.clone();
564 let mut perm2 = input1.clone();
565 let mut perm3 = input2.clone();
566 Griffin::permute(¶ms, &mut perm1);
567 Griffin::permute(¶ms, &mut perm2);
568 Griffin::permute(¶ms, &mut perm3);
569 assert_eq!(perm1, perm2);
570 assert_ne!(perm1, perm3);
571 }
572 }
573
574 fn matmul<F: PrimeField>(input: &[F], mat: &[Vec<F>]) -> Vec<F> {
575 let t = mat.len();
576 debug_assert!(t == input.len());
577 let mut out = vec![F::zero(); t];
578 for row in 0..t {
579 for (col, inp) in input.iter().enumerate() {
580 let mut tmp = mat[row][col];
581 tmp *= inp;
582 out[row] += &tmp;
583 }
584 }
585 out
586 }
587
588 fn test_affine_opt<F: PrimeField>(t: usize) {
589 let rng = &mut thread_rng();
590 let params = GriffinParams::<F>::new(t, 5, 1);
591
592 let mat = ¶ms.mat;
593
594 for _ in 0..5 {
595 let input: Vec<F> = (0..t).map(|_| F::rand(rng)).collect();
596
597 let output1 = matmul(&input, mat);
599 let mut output2 = input.to_owned();
600 Griffin::affine(¶ms, &mut output2, 1);
601 assert_eq!(output1, output2);
602 }
603 }
604
605 #[test]
606 fn test_affine_3() {
607 test_affine_opt::<Fr>(3);
608 }
609
610 #[test]
611 fn test_affine_4() {
612 test_affine_opt::<Fr>(4);
613 }
614
615 #[test]
616 fn test_affine_8() {
617 test_affine_opt::<Fr>(8);
618 }
619
620 #[test]
621 fn test_affine_60() {
622 test_affine_opt::<Fr>(60);
623 }
624}