diff --git a/Cargo.toml b/Cargo.toml index 754e310..0a7c98b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand_core = "0.6.0" -rand ="0.8.3" \ No newline at end of file +rand_core = { version = "0.6", default-features = false } +rand ="0.8.3" +subtle = "2.4.1" +byteorder = "1.4.3" +bitvec = "0.22.3" +ff = { version = "0.10", features = ["derive"] } \ No newline at end of file diff --git a/README.md b/README.md index f46fcd6..7cb0818 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ This package contains a toy implementation of an (s-t)Detectable Hash Function a ### How it Works -A client creates new `Generator` which instantiates the secret key behind the scenes. +A client creates new `HashKey` which instantiates the secret key behind the scenes. -A client can then use this `Generator` to produce two kinds of `Hash`. +A client can then use this `HashKey` to produce two kinds of `Hash`. The first kind of hash (`Hash(x)`) "a true hash" multiplies the given `x` by the secret key (treating the secret key as a `s x t` matrix of polynomials (`p_1..p_s`)). `DHF(k,x):=(x,p_1(x),...,p_s(x))` diff --git a/src/domain.rs b/src/domain.rs deleted file mode 100644 index 0d5e17c..0000000 --- a/src/domain.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::fmt::{Display, Formatter, Result}; -use std::ops::{Add, Mul, Sub}; - -/// Did I really implement an awfully hacky prime order field to play with this -/// Yes...yes I did... -#[derive(Clone, Copy, Debug)] -pub struct PrimeOrderDomain { - val: u64, -} - -impl Display for PrimeOrderDomain<{ ORDER }> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{:20}", self.val) - } -} - -impl PrimeOrderDomain<{ ORDER }> { - pub fn new(val: u64) -> PrimeOrderDomain<{ ORDER }> { - PrimeOrderDomain { val } - } - - pub fn is_zero(&self) -> bool { - self.val == 0 - } - - pub fn gt(&self, rhs: &Self) -> bool { - self.val > rhs.val - } - - pub fn inverse(&self) -> Self { - self.pow(ORDER - 2) // at least it is faster an iterating... - } - - // exponentiation by squaring - pub fn pow(&self, exp: u64) -> Self { - if exp == 0 { - return PrimeOrderDomain::new(1); - } - if exp == 1 { - return self.clone(); - } - - if exp % 2 == 0 { - (*self * *self).pow(exp / 2) - } else { - *self * self.pow(2).pow((exp - 1) / 2) - } - } -} - -impl Add for PrimeOrderDomain<{ ORDER }> { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - PrimeOrderDomain { - val: ((self.val as u128 + rhs.val as u128) % ORDER as u128) as u64, - } - } -} - -impl Sub for PrimeOrderDomain<{ ORDER }> { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - if rhs.val > self.val { - return PrimeOrderDomain { - val: ((ORDER - rhs.val) + self.val) % ORDER, - }; - } - - PrimeOrderDomain { - val: (self.val - rhs.val) % ORDER, - } - } -} - -impl Mul for PrimeOrderDomain<{ ORDER }> { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - PrimeOrderDomain { - val: ((self.val as u128 * rhs.val as u128) % ORDER as u128) as u64, - } - } -} diff --git a/src/lib.rs b/src/lib.rs index c02c20f..1a152a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,76 +1,86 @@ -use crate::domain::PrimeOrderDomain; +extern crate ff; use crate::matrix::Matrix; -use rand::Rng; +use ff::PrimeField; use rand_core::{CryptoRng, RngCore}; +use std::fmt::Display; -pub const PRIME_ORDER: u64 = 18446744073709551557; // picked because largest 64 bit prime... +mod matrix; -pub mod domain; -pub mod matrix; - -struct Generator { +struct HashKey +where + PF: PrimeField, +{ synthetic_max: u64, threshold: u64, - key: Vec>>, + key: Vec>, } #[derive(Debug)] -struct Hash(Vec>); +struct Hash(Vec) +where + PF: PrimeField; -impl Generator { - pub fn generate( - rng: &mut R, +impl HashKey +where + PF: PrimeField, +{ + pub fn new( + rng: R, synthetic_max: u64, threshold: u64, - ) -> Generator { + ) -> HashKey { let mut k = vec![]; for _i in 0..synthetic_max { let mut p_i = vec![]; for _j in 0..threshold { - let p = rng.gen_range(0..PRIME_ORDER); - p_i.push(PrimeOrderDomain::::new(p)); + p_i.push(PF::random(rng)); } k.push(p_i); } - Generator { + HashKey { synthetic_max, threshold, key: k, } } - pub fn hash(&self, value: PrimeOrderDomain) -> Hash { + pub fn generate_hash(&self, value: PF) -> Hash { let mut hash = vec![value]; for i in 0..self.synthetic_max as usize { // Here evaluate our polynomial key[i]*X key[i]*X^2 key[i]*X^3 etc.... - let mut result = PrimeOrderDomain::new(0); + let mut result = PF::zero(); for j in 0..self.threshold as usize { - result = result + (self.key[i][j].clone() * value.pow(j as u64)); + result = result + (self.key[i][j].clone() * value.pow_vartime(&[j as u64])); } hash.push(result); } Hash(hash) } - pub fn random(&self, rng: &mut R) -> Hash { - let mut hash = vec![PrimeOrderDomain::new(rng.gen_range(0..PRIME_ORDER))]; + pub fn random(&self, rng: R) -> Hash { + let mut hash = vec![PF::random(rng)]; for _i in 0..self.synthetic_max as usize { - let mut result = PrimeOrderDomain::new(rng.gen_range(0..PRIME_ORDER)); - hash.push(result); + hash.push(PF::random(rng)); } Hash(hash) } } -struct Solver { +struct Solver +where + PF: PrimeField, +{ synthetic_max: u64, threshold: u64, - expanded_hashes: Vec, + expanded_hashes: Vec>, } -impl Solver { - pub fn new(synthetic_max: u64, threshold: u64) -> Solver { +impl Solver +where + PF: PrimeField + PartialOrd + Display, +{ + pub fn new(synthetic_max: u64, threshold: u64) -> Solver { Solver { synthetic_max, threshold, @@ -79,13 +89,13 @@ impl Solver { } /// Add a new hash to be evaluated by the solver... - pub fn add_hash(&mut self, hash: Hash) { + pub fn add_hash(&mut self, hash: Hash) { println!("Hash {}: {:?}", self.expanded_hashes.len(), hash); // To expand a Hash we take DHF(x0,p1..ps) and produce DHF(1,x0,x0^1..x0^t-1,p1..ps) let mut expanded_hash = vec![]; for i in 0..self.threshold { - expanded_hash.push(hash.0[0].pow(i)) + expanded_hash.push(hash.0[0].pow_vartime(&[i])) } for element in hash.0.iter().skip(1) { expanded_hash.push(element.clone()) @@ -106,7 +116,7 @@ impl Solver { } // Append the identity matrix... for (i, r) in (m..m + self.expanded_hashes.len()).enumerate() { - augmented_matrix.update(r as usize, i, PrimeOrderDomain::new(1)); + augmented_matrix.update(r as usize, i, PF::one()); } // Transpose for row reduction... @@ -131,7 +141,7 @@ impl Solver { let mut i_max = h; let mut i_max_v = augmented_matrix.at(h, k); for x in h + 1..rows { - if augmented_matrix.at(x, k).gt(&i_max_v) { + if augmented_matrix.at(x, k) > i_max_v { i_max = x; i_max_v = augmented_matrix.at(x, k) } @@ -141,8 +151,8 @@ impl Solver { } else { augmented_matrix.swap_rows(h, i_max); for i in h + 1..rows { - let f = augmented_matrix.at(i, k) * augmented_matrix.at(h, k).inverse(); - augmented_matrix.update(i, k, PrimeOrderDomain::new(0)); + let f = augmented_matrix.at(i, k) * augmented_matrix.at(h, k).invert().unwrap(); + augmented_matrix.update(i, k, PF::zero()); for j in k + 1..cols { let val = augmented_matrix.at(i, j) - augmented_matrix.at(h, j) * f; augmented_matrix.update(i, j, val); @@ -217,17 +227,31 @@ impl Solver { #[cfg(test)] mod tests { - use crate::domain::PrimeOrderDomain; - use crate::{Generator, Solver, PRIME_ORDER}; - use rand::Rng; + use crate::ff::Field; + use crate::{HashKey, Solver}; + use ff::PrimeField; use rand_core::OsRng; + use std::fmt::{Display, Formatter}; + + // Generate a Prime Field to Test with... + #[derive(PrimeField)] + #[PrimeFieldModulus = "65537"] + #[PrimeFieldGenerator = "3"] + #[PrimeFieldReprEndianness = "little"] + struct Fp([u64; 1]); + + impl Display for Fp { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:10}", self.0[0]) + } + } #[test] fn it_works() { - let mut rng = OsRng; + let rng = OsRng; let s = 3u64; let t = 6u64; - let dhf = Generator::generate(&mut rng, s, t); + let dhf = HashKey::::new(rng, s, t); let mut solver = Solver::new(s, t); for i in 0..10 { @@ -237,11 +261,11 @@ mod tests { // If |random hashes| > s this this procedure will *not* work // There is an extended algorithm for extract assuming |true hashes| > t though - we do not implement it. if i != 2 && i != 5 && i != 8 { - let x0: u64 = rng.gen_range(0..PRIME_ORDER); - let hash = dhf.hash(PrimeOrderDomain::new(x0)); + let x0 = Fp::random(rng); + let hash = dhf.generate_hash(x0); solver.add_hash(hash); } else { - solver.add_hash(dhf.random(&mut rng)); + solver.add_hash(dhf.random(rng)); } } diff --git a/src/matrix.rs b/src/matrix.rs index e187732..fb8d14a 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1,21 +1,26 @@ -use crate::domain::PrimeOrderDomain; -use crate::PRIME_ORDER; +use ff::PrimeField; /// A dense matrix. Apple have said t and s are going to be on the order of 30 so I feel /// you can probably always get away with a dense representation -pub struct Matrix { +pub struct Matrix +where + PF: PrimeField, +{ rows: usize, cols: usize, - vals: Vec>>, + vals: Vec>, } -impl Matrix { - pub fn new(rows: usize, cols: usize) -> Matrix { +impl Matrix +where + PF: PrimeField, +{ + pub fn new(rows: usize, cols: usize) -> Matrix { let mut vals = vec![]; for _i in 0..rows { let mut row = vec![]; for _j in 0..cols { - row.push(PrimeOrderDomain::new(0)) + row.push(PF::zero()) } vals.push(row) } @@ -30,15 +35,15 @@ impl Matrix { self.cols } - pub fn update(&mut self, row: usize, col: usize, val: PrimeOrderDomain) { + pub fn update(&mut self, row: usize, col: usize, val: PF) { self.vals[row][col] = val } - pub fn at(&self, row: usize, col: usize) -> PrimeOrderDomain { + pub fn at(&self, row: usize, col: usize) -> PF { self.vals[row][col] } - pub fn transpose(&self) -> Matrix { + pub fn transpose(&self) -> Matrix { let mut m = Matrix::new(self.cols, self.rows); for i in 0..self.cols { for j in 0..self.rows {