arnaucube
arnaucube/mimc-rsPublic Self-hosted repository
mimc-rs/src/lib.rs
src/lib.rsRaw
  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 struct Mimc7 {
107    constants: Constants,
108}
109
110impl Mimc7 {
111    pub fn new() -> Mimc7 {
112        Mimc7 {
113            constants: generate_constants(),
114        }
115    }
116
117    pub fn hash(&self, arr: Vec<BigInt>) -> BigInt {
118        // TODO check if arr elements are inside the Finite Field over R
119        let mut h: BigInt = Zero::zero();
120        for i in 0..arr.len() {
121            h = &h + &arr[i] + self.mimc7_hash(&arr[i], &h);
122            h = modulus(&h, &self.constants.r)
123        }
124        modulus(&h, &self.constants.r)
125    }
126
127    pub fn mimc7_hash(&self, x_in: &BigInt, k: &BigInt) -> BigInt {
128        let mut h: BigInt = Zero::zero();
129        for i in 0..self.constants.n_rounds as usize {
130            let t: BigInt;
131            if i == 0 {
132                t = x_in + k;
133            } else {
134                t = h + k + &self.constants.cts[i];
135            }
136            let t2 = &t * &t;
137            let t4 = &t2 * &t2;
138            h = (t4 * t2) * t;
139            h = modulus(&h, &self.constants.r);
140        }
141        modulus(&(h + k), &self.constants.r)
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use rustc_hex::ToHex;
149
150    #[test]
151    fn test_sha3() {
152        let mut keccak = Keccak::new_keccak256();
153        let mut res = [0u8; 32];
154        keccak.update(SEED.as_bytes());
155        keccak.finalize(&mut res);
156        assert_eq!(
157            res.to_hex(),
158            "b6e489e6b37224a50bebfddbe7d89fa8fdcaa84304a70bd13f79b5d9f7951e9e"
159        );
160
161        let mut keccak = Keccak::new_keccak256();
162        let mut res = [0u8; 32];
163        keccak.update(SEED.as_bytes());
164        keccak.finalize(&mut res);
165        let c = BigInt::from_bytes_be(Sign::Plus, &res);
166        assert_eq!(
167            c.to_string(),
168            "82724731331859054037315113496710413141112897654334566532528783843265082629790"
169        );
170    }
171    #[test]
172    fn test_generate_constants() {
173        let constants = generate_constants();
174        assert_eq!(
175            "20888961410941983456478427210666206549300505294776164667214940546594746570981",
176            constants.cts[1].to_string()
177        );
178    }
179
180    #[test]
181    fn test_mimc7_generic() {
182        let b1: BigInt = BigInt::parse_bytes(b"1", 10).unwrap();
183        let b2: BigInt = BigInt::parse_bytes(b"2", 10).unwrap();
184        let constants = generate_constants();
185        let h1 = mimc7_hash_generic(&constants.r, &b1, &b2, 91);
186        assert_eq!(
187            h1.to_string(),
188            "10594780656576967754230020536574539122676596303354946869887184401991294982664"
189        );
190    }
191    #[test]
192    fn test_mimc7() {
193        let b12: BigInt = BigInt::parse_bytes(b"12", 10).unwrap();
194        let b45: BigInt = BigInt::parse_bytes(b"45", 10).unwrap();
195        let b78: BigInt = BigInt::parse_bytes(b"78", 10).unwrap();
196        let b41: BigInt = BigInt::parse_bytes(b"41", 10).unwrap();
197
198        let mut big_arr1: Vec<BigInt> = Vec::new();
199        big_arr1.push(b12.clone());
200        let mimc7 = Mimc7::new();
201        let h1 = mimc7.hash(big_arr1);
202        let (_, h1_bytes) = h1.to_bytes_be();
203        assert_eq!(
204            h1_bytes.to_hex(),
205            "237c92644dbddb86d8a259e0e923aaab65a93f1ec5758b8799988894ac0958fd"
206        );
207
208        let mh2 = mimc7.mimc7_hash(&b12, &b45);
209        let (_, mh2_bytes) = mh2.to_bytes_be();
210        assert_eq!(
211            mh2_bytes.to_hex(),
212            "2ba7ebad3c6b6f5a20bdecba2333c63173ca1a5f2f49d958081d9fa7179c44e4"
213        );
214
215        let mut big_arr2: Vec<BigInt> = Vec::new();
216        big_arr2.push(b78.clone());
217        big_arr2.push(b41.clone());
218        let h2 = mimc7.hash(big_arr2);
219        let (_, h2_bytes) = h2.to_bytes_be();
220        assert_eq!(
221            h2_bytes.to_hex(),
222            "067f3202335ea256ae6e6aadcd2d5f7f4b06a00b2d1e0de903980d5ab552dc70"
223        );
224
225        let mut big_arr2: Vec<BigInt> = Vec::new();
226        big_arr2.push(b12.clone());
227        big_arr2.push(b45.clone());
228        let h1 = mimc7.hash(big_arr2);
229        let (_, h1_bytes) = h1.to_bytes_be();
230        assert_eq!(
231            h1_bytes.to_hex(),
232            "15ff7fe9793346a17c3150804bcb36d161c8662b110c50f55ccb7113948d8879"
233        );
234
235        let mut big_arr1: Vec<BigInt> = Vec::new();
236        big_arr1.push(b12.clone());
237        big_arr1.push(b45.clone());
238        big_arr1.push(b78.clone());
239        big_arr1.push(b41.clone());
240        let mimc7 = Mimc7::new();
241        let h1 = mimc7.hash(big_arr1);
242        let (_, h1_bytes) = h1.to_bytes_be();
243        assert_eq!(
244            h1_bytes.to_hex(),
245            "284bc1f34f335933a23a433b6ff3ee179d682cd5e5e2fcdd2d964afa85104beb"
246        );
247    }
248}