mimc_rs/
lib.rs

1extern crate num;
2extern crate num_bigint;
3extern crate num_traits;
4
5use tiny_keccak::Keccak;
6
7use num_bigint::{BigInt, Sign};
8use num_traits::Zero;
9
10const SEED: &str = "mimc";
11
12pub struct Constants {
13    // seed_hash: BigInt,
14    // iv: BigInt,
15    r: BigInt,
16    n_rounds: i64,
17    cts: Vec<BigInt>,
18}
19
20pub fn modulus(a: &BigInt, m: &BigInt) -> BigInt {
21    ((a % m) + m) % m
22}
23
24pub fn generate_constants() -> Constants {
25    let r: BigInt = BigInt::parse_bytes(
26        b"21888242871839275222246405745257275088548364400416034343698204186575808495617",
27        10,
28    )
29    .unwrap();
30
31    let mut keccak = Keccak::new_keccak256();
32    let mut h = [0u8; 32];
33    keccak.update(SEED.as_bytes());
34    keccak.finalize(&mut h);
35    let mut keccak = Keccak::new_keccak256();
36    let mut h_iv = [0u8; 32];
37    let seed_iv = format!("{}{}", SEED, "_iv");
38    keccak.update(seed_iv.as_bytes());
39    keccak.finalize(&mut h_iv);
40
41    // let seed_hash: BigInt = BigInt::from_bytes_be(Sign::Plus, &h);
42    // let c: BigInt = BigInt::from_bytes_be(Sign::Plus, &h_iv);
43    // let iv: BigInt = c % &r;
44    let n_rounds: i64 = 91;
45    let cts = get_constants(&r, SEED, n_rounds);
46
47    Constants {
48        // seed_hash: seed_hash,
49        // iv: iv,
50        r: r,
51        n_rounds: n_rounds,
52        cts: cts,
53    }
54}
55
56pub fn get_constants(r: &BigInt, seed: &str, n_rounds: i64) -> Vec<BigInt> {
57    let mut cts: Vec<BigInt> = Vec::new();
58    cts.push(Zero::zero());
59
60    let mut keccak = Keccak::new_keccak256();
61    let mut h = [0u8; 32];
62    keccak.update(seed.as_bytes());
63    keccak.finalize(&mut h);
64
65    let mut c = BigInt::from_bytes_be(Sign::Plus, &h);
66    for _ in 1..n_rounds {
67        let mut keccak = Keccak::new_keccak256();
68        let mut h = [0u8; 32];
69        let (_, c_bytes) = c.to_bytes_be();
70        keccak.update(&c_bytes[..]);
71        keccak.finalize(&mut h);
72        c = BigInt::from_bytes_be(Sign::Plus, &h);
73        let n = modulus(&c, &r);
74        cts.push(n);
75    }
76    cts
77}
78
79pub fn mimc7_hash_generic(r: &BigInt, x_in: &BigInt, k: &BigInt, n_rounds: i64) -> BigInt {
80    let cts = get_constants(r, SEED, n_rounds);
81    let mut h: BigInt = Zero::zero();
82    for i in 0..n_rounds as usize {
83        let mut t: BigInt;
84        if i == 0 {
85            t = x_in + k;
86        } else {
87            t = h + k + &cts[i];
88        }
89        t = modulus(&t, &r);
90        let t2 = &t * &t;
91        let t4 = &t2 * &t2;
92        h = (t4 * t2) * t;
93        h = modulus(&h, &r);
94    }
95    modulus(&(h + k), &r)
96}
97
98pub fn hash_generic(iv: BigInt, arr: Vec<BigInt>, r: BigInt, n_rounds: i64) -> BigInt {
99    let mut h: BigInt = iv;
100    for i in 0..arr.len() {
101        h = mimc7_hash_generic(&r, &h, &arr[i], n_rounds);
102    }
103    h
104}
105
106pub fn check_bigint_in_field(a: &BigInt, q: &BigInt) -> bool {
107    if a >= q {
108        return false;
109    }
110    true
111}
112
113pub fn check_bigint_array_in_field(arr: &Vec<BigInt>, q: &BigInt) -> bool {
114    for a in arr {
115        if !check_bigint_in_field(a, &q) {
116            return false;
117        }
118    }
119    true
120}
121
122pub struct Mimc7 {
123    constants: Constants,
124}
125
126impl Mimc7 {
127    pub fn new() -> Mimc7 {
128        Mimc7 {
129            constants: generate_constants(),
130        }
131    }
132
133    pub fn hash(&self, arr: Vec<BigInt>) -> Result<BigInt, String> {
134        // check if arr elements are inside the Finite Field over R
135        if !check_bigint_array_in_field(&arr, &self.constants.r) {
136            return Err("elements not inside the finite field over R".to_string());
137        }
138        let mut h: BigInt = Zero::zero();
139        for i in 0..arr.len() {
140            h = &h + &arr[i] + self.mimc7_hash(&arr[i], &h);
141            h = modulus(&h, &self.constants.r)
142        }
143        Ok(modulus(&h, &self.constants.r))
144    }
145
146    pub fn mimc7_hash(&self, x_in: &BigInt, k: &BigInt) -> BigInt {
147        let mut h: BigInt = Zero::zero();
148        for i in 0..self.constants.n_rounds as usize {
149            let t: BigInt;
150            if i == 0 {
151                t = x_in + k;
152            } else {
153                t = h + k + &self.constants.cts[i];
154            }
155            let t2 = &t * &t;
156            let t4 = &t2 * &t2;
157            h = (t4 * t2) * t;
158            h = modulus(&h, &self.constants.r);
159        }
160        modulus(&(h + k), &self.constants.r)
161    }
162
163    pub fn hash_bytes(&self, b: Vec<u8>) -> Result<BigInt, String> {
164        let n = 31;
165        let mut ints: Vec<BigInt> = Vec::new();
166        for i in 0..b.len() / n {
167            let v: BigInt = BigInt::from_bytes_le(Sign::Plus, &b[n * i..n * (i + 1)]);
168            ints.push(v);
169        }
170        if b.len() % n != 0 {
171            let v: BigInt = BigInt::from_bytes_le(Sign::Plus, &b[(b.len() / n) * n..]);
172            ints.push(v);
173        }
174        self.hash(ints)
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181    use rustc_hex::ToHex;
182
183    #[test]
184    fn test_sha3() {
185        let mut keccak = Keccak::new_keccak256();
186        let mut res = [0u8; 32];
187        keccak.update(SEED.as_bytes());
188        keccak.finalize(&mut res);
189        assert_eq!(
190            res.to_hex(),
191            "b6e489e6b37224a50bebfddbe7d89fa8fdcaa84304a70bd13f79b5d9f7951e9e"
192        );
193
194        let mut keccak = Keccak::new_keccak256();
195        let mut res = [0u8; 32];
196        keccak.update(SEED.as_bytes());
197        keccak.finalize(&mut res);
198        let c = BigInt::from_bytes_be(Sign::Plus, &res);
199        assert_eq!(
200            c.to_string(),
201            "82724731331859054037315113496710413141112897654334566532528783843265082629790"
202        );
203    }
204    #[test]
205    fn test_generate_constants() {
206        let constants = generate_constants();
207        assert_eq!(
208            "20888961410941983456478427210666206549300505294776164667214940546594746570981",
209            constants.cts[1].to_string()
210        );
211    }
212
213    #[test]
214    fn test_mimc7_generic() {
215        let b1: BigInt = BigInt::parse_bytes(b"1", 10).unwrap();
216        let b2: BigInt = BigInt::parse_bytes(b"2", 10).unwrap();
217        let constants = generate_constants();
218        let h1 = mimc7_hash_generic(&constants.r, &b1, &b2, 91);
219        assert_eq!(
220            h1.to_string(),
221            "10594780656576967754230020536574539122676596303354946869887184401991294982664"
222        );
223    }
224
225    #[test]
226    fn test_check_bigint_in_field() {
227        let r_0: BigInt = BigInt::parse_bytes(
228            b"21888242871839275222246405745257275088548364400416034343698204186575808495617",
229            10,
230        )
231        .unwrap();
232
233        let mut big_arr0: Vec<BigInt> = Vec::new();
234        big_arr0.push(r_0.clone());
235        let mimc7 = Mimc7::new();
236        let h0 = mimc7.hash(big_arr0);
237        assert_eq!(h0.is_err(), true);
238
239        let r_1: BigInt = BigInt::parse_bytes(
240            b"21888242871839275222246405745257275088548364400416034343698204186575808495616",
241            10,
242        )
243        .unwrap();
244
245        let mut big_arr1: Vec<BigInt> = Vec::new();
246        big_arr1.push(r_1.clone());
247        let mimc7 = Mimc7::new();
248        let h1 = mimc7.hash(big_arr1);
249        assert_eq!(h1.is_err(), false);
250        assert_eq!(
251            h1.unwrap().to_string(),
252            "4664475646327377862961796881776103845487084034023211145221745907673012891406"
253        );
254    }
255
256    #[test]
257    fn test_mimc7() {
258        let b12: BigInt = BigInt::parse_bytes(b"12", 10).unwrap();
259        let b45: BigInt = BigInt::parse_bytes(b"45", 10).unwrap();
260        let b78: BigInt = BigInt::parse_bytes(b"78", 10).unwrap();
261        let b41: BigInt = BigInt::parse_bytes(b"41", 10).unwrap();
262
263        let mut big_arr1: Vec<BigInt> = Vec::new();
264        big_arr1.push(b12.clone());
265        let mimc7 = Mimc7::new();
266        let h1 = mimc7.hash(big_arr1).unwrap();
267        let (_, h1_bytes) = h1.to_bytes_be();
268        assert_eq!(
269            h1_bytes.to_hex(),
270            "237c92644dbddb86d8a259e0e923aaab65a93f1ec5758b8799988894ac0958fd"
271        );
272
273        let mh2 = mimc7.mimc7_hash(&b12, &b45);
274        let (_, mh2_bytes) = mh2.to_bytes_be();
275        assert_eq!(
276            mh2_bytes.to_hex(),
277            "2ba7ebad3c6b6f5a20bdecba2333c63173ca1a5f2f49d958081d9fa7179c44e4"
278        );
279
280        let mut big_arr2: Vec<BigInt> = Vec::new();
281        big_arr2.push(b78.clone());
282        big_arr2.push(b41.clone());
283        let h2 = mimc7.hash(big_arr2).unwrap();
284        let (_, h2_bytes) = h2.to_bytes_be();
285        assert_eq!(
286            h2_bytes.to_hex(),
287            "067f3202335ea256ae6e6aadcd2d5f7f4b06a00b2d1e0de903980d5ab552dc70"
288        );
289
290        let mut big_arr2: Vec<BigInt> = Vec::new();
291        big_arr2.push(b12.clone());
292        big_arr2.push(b45.clone());
293        let h1 = mimc7.hash(big_arr2).unwrap();
294        let (_, h1_bytes) = h1.to_bytes_be();
295        assert_eq!(
296            h1_bytes.to_hex(),
297            "15ff7fe9793346a17c3150804bcb36d161c8662b110c50f55ccb7113948d8879"
298        );
299
300        let mut big_arr1: Vec<BigInt> = Vec::new();
301        big_arr1.push(b12.clone());
302        big_arr1.push(b45.clone());
303        big_arr1.push(b78.clone());
304        big_arr1.push(b41.clone());
305        let mimc7 = Mimc7::new();
306        let h1 = mimc7.hash(big_arr1).unwrap();
307        let (_, h1_bytes) = h1.to_bytes_be();
308        assert_eq!(
309            h1_bytes.to_hex(),
310            "284bc1f34f335933a23a433b6ff3ee179d682cd5e5e2fcdd2d964afa85104beb"
311        );
312    }
313    #[test]
314    fn test_hash_bytes() {
315        let msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
316        let mimc7 = Mimc7::new();
317        let h = mimc7.hash_bytes(msg.as_bytes().to_vec()).unwrap();
318        assert_eq!(
319            h.to_string(),
320            "16855787120419064316734350414336285711017110414939748784029922801367685456065"
321        );
322    }
323}