From 8e81dc980eb73e430e211498bc7d046340632ab1 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Sat, 3 Aug 2019 20:44:34 +0200 Subject: [PATCH] add mimc7 --- .gitignore | 3 + Cargo.toml | 12 +++ README.md | 8 ++ src/lib.rs | 248 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 271 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a9f744d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "mimc-rs" +version = "0.1.0" +authors = ["arnaucube "] +edition = "2018" + +[dependencies] +num = "0.2.0" +num-bigint = "0.2.2" +num-traits = "0.2.8" +tiny-keccak = "1.5" +rustc-hex = "1.0.0" diff --git a/README.md b/README.md new file mode 100644 index 0000000..125948e --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# mimc-rs +MIMC7 hash implementation in Rust + +https://eprint.iacr.org/2016/492.pdf + + +## Warning +Do not use in production diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6c346fa --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,248 @@ +extern crate num; +extern crate num_bigint; +extern crate num_traits; + +use tiny_keccak::Keccak; + +use num_bigint::{BigInt, Sign}; +use num_traits::Zero; + +const SEED: &str = "mimc"; + +pub struct Constants { + seed_hash: BigInt, + iv: BigInt, + r: BigInt, + n_rounds: i64, + cts: Vec, +} + +pub fn modulus(a: &BigInt, m: &BigInt) -> BigInt { + ((a % m) + m) % m +} + +pub fn generate_constants() -> Constants { + let r: BigInt = BigInt::parse_bytes( + b"21888242871839275222246405745257275088548364400416034343698204186575808495617", + 10, + ) + .unwrap(); + + let mut keccak = Keccak::new_keccak256(); + let mut h = [0u8; 32]; + keccak.update(SEED.as_bytes()); + keccak.finalize(&mut h); + let mut keccak = Keccak::new_keccak256(); + let mut h_iv = [0u8; 32]; + let seed_iv = format!("{}{}", SEED, "_iv"); + keccak.update(seed_iv.as_bytes()); + keccak.finalize(&mut h_iv); + + let seed_hash: BigInt = BigInt::from_bytes_be(Sign::Plus, &h); + let c: BigInt = BigInt::from_bytes_be(Sign::Plus, &h_iv); + let iv: BigInt = c % &r; + let n_rounds: i64 = 91; + let cts = get_constants(&r, SEED, n_rounds); + + Constants { + seed_hash: seed_hash, + iv: iv, + r: r, + n_rounds: n_rounds, + cts: cts, + } +} + +pub fn get_constants(r: &BigInt, seed: &str, n_rounds: i64) -> Vec { + let mut cts: Vec = Vec::new(); + cts.push(Zero::zero()); + + let mut keccak = Keccak::new_keccak256(); + let mut h = [0u8; 32]; + keccak.update(seed.as_bytes()); + keccak.finalize(&mut h); + + let mut c = BigInt::from_bytes_be(Sign::Plus, &h); + for _ in 1..n_rounds { + let mut keccak = Keccak::new_keccak256(); + let mut h = [0u8; 32]; + let (_, c_bytes) = c.to_bytes_be(); + keccak.update(&c_bytes[..]); + keccak.finalize(&mut h); + c = BigInt::from_bytes_be(Sign::Plus, &h); + let n = modulus(&c, &r); + cts.push(n); + } + cts +} + +pub fn mimc7_hash_generic(r: &BigInt, x_in: &BigInt, k: &BigInt, n_rounds: i64) -> BigInt { + let cts = get_constants(r, SEED, n_rounds); + let mut h: BigInt = Zero::zero(); + for i in 0..n_rounds as usize { + let mut t: BigInt; + if i == 0 { + t = x_in + k; + } else { + t = h + k + &cts[i]; + } + t = modulus(&t, &r); + let t2 = &t * &t; + let t4 = &t2 * &t2; + h = (t4 * t2) * t; + h = modulus(&h, &r); + } + modulus(&(h + k), &r) +} + +pub fn hash_generic(iv: BigInt, arr: Vec, r: BigInt, n_rounds: i64) -> BigInt { + let mut h: BigInt = iv; + for i in 0..arr.len() { + h = mimc7_hash_generic(&r, &h, &arr[i], n_rounds); + } + h +} + +pub struct Mimc7 { + constants: Constants, +} + +impl Mimc7 { + pub fn new() -> Mimc7 { + Mimc7 { + constants: generate_constants(), + } + } + + pub fn hash(&self, arr: Vec) -> BigInt { + // TODO check if arr elements are inside the Finite Field over R + let mut h: BigInt = Zero::zero(); + for i in 0..arr.len() { + h = &h + &arr[i] + self.mimc7_hash(&arr[i], &h); + h = modulus(&h, &self.constants.r) + } + modulus(&h, &self.constants.r) + } + + pub fn mimc7_hash(&self, x_in: &BigInt, k: &BigInt) -> BigInt { + let mut h: BigInt = Zero::zero(); + for i in 0..self.constants.n_rounds as usize { + let t: BigInt; + if i == 0 { + t = x_in + k; + } else { + t = h + k + &self.constants.cts[i]; + } + let t2 = &t * &t; + let t4 = &t2 * &t2; + h = (t4 * t2) * t; + h = modulus(&h, &self.constants.r); + } + modulus(&(h + k), &self.constants.r) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rustc_hex::ToHex; + + #[test] + fn test_sha3() { + let mut keccak = Keccak::new_keccak256(); + let mut res = [0u8; 32]; + keccak.update(SEED.as_bytes()); + keccak.finalize(&mut res); + assert_eq!( + res.to_hex(), + "b6e489e6b37224a50bebfddbe7d89fa8fdcaa84304a70bd13f79b5d9f7951e9e" + ); + + let mut keccak = Keccak::new_keccak256(); + let mut res = [0u8; 32]; + keccak.update(SEED.as_bytes()); + keccak.finalize(&mut res); + let c = BigInt::from_bytes_be(Sign::Plus, &res); + assert_eq!( + c.to_string(), + "82724731331859054037315113496710413141112897654334566532528783843265082629790" + ); + } + #[test] + fn test_generate_constants() { + let constants = generate_constants(); + assert_eq!( + "20888961410941983456478427210666206549300505294776164667214940546594746570981", + constants.cts[1].to_string() + ); + } + + #[test] + fn test_mimc7_generic() { + let b1: BigInt = BigInt::parse_bytes(b"1", 10).unwrap(); + let b2: BigInt = BigInt::parse_bytes(b"2", 10).unwrap(); + let constants = generate_constants(); + let h1 = mimc7_hash_generic(&constants.r, &b1, &b2, 91); + assert_eq!( + h1.to_string(), + "10594780656576967754230020536574539122676596303354946869887184401991294982664" + ); + } + #[test] + fn test_mimc7() { + let b12: BigInt = BigInt::parse_bytes(b"12", 10).unwrap(); + let b45: BigInt = BigInt::parse_bytes(b"45", 10).unwrap(); + let b78: BigInt = BigInt::parse_bytes(b"78", 10).unwrap(); + let b41: BigInt = BigInt::parse_bytes(b"41", 10).unwrap(); + + let mut big_arr1: Vec = Vec::new(); + big_arr1.push(b12.clone()); + let mimc7 = Mimc7::new(); + let h1 = mimc7.hash(big_arr1); + let (_, h1_bytes) = h1.to_bytes_be(); + assert_eq!( + h1_bytes.to_hex(), + "237c92644dbddb86d8a259e0e923aaab65a93f1ec5758b8799988894ac0958fd" + ); + + let mh2 = mimc7.mimc7_hash(&b12, &b45); + let (_, mh2_bytes) = mh2.to_bytes_be(); + assert_eq!( + mh2_bytes.to_hex(), + "2ba7ebad3c6b6f5a20bdecba2333c63173ca1a5f2f49d958081d9fa7179c44e4" + ); + + let mut big_arr2: Vec = Vec::new(); + big_arr2.push(b78.clone()); + big_arr2.push(b41.clone()); + let h2 = mimc7.hash(big_arr2); + let (_, h2_bytes) = h2.to_bytes_be(); + assert_eq!( + h2_bytes.to_hex(), + "067f3202335ea256ae6e6aadcd2d5f7f4b06a00b2d1e0de903980d5ab552dc70" + ); + + let mut big_arr2: Vec = Vec::new(); + big_arr2.push(b12.clone()); + big_arr2.push(b45.clone()); + let h1 = mimc7.hash(big_arr2); + let (_, h1_bytes) = h1.to_bytes_be(); + assert_eq!( + h1_bytes.to_hex(), + "15ff7fe9793346a17c3150804bcb36d161c8662b110c50f55ccb7113948d8879" + ); + + let mut big_arr1: Vec = Vec::new(); + big_arr1.push(b12.clone()); + big_arr1.push(b45.clone()); + big_arr1.push(b78.clone()); + big_arr1.push(b41.clone()); + let mimc7 = Mimc7::new(); + let h1 = mimc7.hash(big_arr1); + let (_, h1_bytes) = h1.to_bytes_be(); + assert_eq!( + h1_bytes.to_hex(), + "284bc1f34f335933a23a433b6ff3ee179d682cd5e5e2fcdd2d964afa85104beb" + ); + } +}