1use std::marker::PhantomData;
2
3use ark_ff::PrimeField;
4
5#[cfg(feature = "r1cs")]
6pub mod constraints;
7pub mod params;
8pub mod utils;
9
10#[cfg(feature = "paramgen")]
11pub mod paramgen;
12
13pub use traits::*;
14mod traits;
15
16pub trait MiMCParameters: Clone + Default {
17 const ROUNDS: usize;
18 const EXPONENT: usize;
19}
20
21#[derive(Default, Clone)]
22pub struct MiMC<F: PrimeField, P: MiMCParameters> {
23 pub num_outputs: usize,
24 pub k: F,
25 pub round_keys: Vec<F>,
26 params: PhantomData<P>,
27}
28
29impl<F: PrimeField, P: MiMCParameters> MiMC<F, P> {
30 pub fn new(num_outputs: usize, k: F, round_keys: Vec<F>) -> Self {
31 assert_eq!(round_keys.len(), P::ROUNDS, "Invalid round keys length");
32 Self {
33 num_outputs,
34 params: PhantomData,
35 k,
36 round_keys,
37 }
38 }
39}
40
41impl<F: PrimeField, P: MiMCParameters> MiMC<F, P> {
42 pub fn permute_feistel(&self, state: Vec<F>) -> Vec<F> {
44 let mut r = F::zero();
45 let mut c = F::zero();
46 for s in state.into_iter() {
47 r += s;
48 (r, c) = self.feistel(r, c);
49 }
50 let mut outputs = vec![r];
51 match self.num_outputs {
52 0 | 1 => outputs,
53 _ => {
54 for _ in 1..self.num_outputs {
55 (r, c) = self.feistel(r, c);
56 outputs.push(r);
57 }
58 outputs
59 }
60 }
61 }
62
63 fn feistel(&self, left: F, right: F) -> (F, F) {
64 let mut x_l = left;
65 let mut x_r = right;
66 for i in 0..P::ROUNDS {
67 let t = match i == 0 {
68 true => self.k + x_l,
69 false => self.k + x_l + self.round_keys[i],
70 };
71 let mut tn = F::one();
72 (0..P::EXPONENT).for_each(|_| tn *= t);
73 (x_l, x_r) = match i < P::ROUNDS - 1 {
74 true => (x_r + tn, x_l),
75 false => (x_l, x_r + tn),
76 };
77 }
78 (x_l, x_r)
79 }
80
81 pub fn permute_non_feistel(&self, state: Vec<F>) -> Vec<F> {
83 let mut r = self.k;
84 for s in state.into_iter() {
85 r += s + self.non_feistel(s, r);
86 }
87 let mut outputs = vec![r];
88 match self.num_outputs {
89 0 | 1 => outputs,
90 _ => {
91 for _ in 1..self.num_outputs {
92 r += self.non_feistel(r, r);
93 outputs.push(r);
94 }
95 outputs
96 }
97 }
98 }
99
100 fn non_feistel(&self, x: F, k: F) -> F {
101 let mut r = F::zero();
102 for i in 0..P::ROUNDS {
103 let t = match i == 0 {
104 true => k + x,
105 false => k + r + self.round_keys[i],
106 };
107 let mut tn = F::one();
108 (0..P::EXPONENT).for_each(|_| tn *= t);
109 r = tn;
110 }
111 r + k
112 }
113}