Use an actual FF lib

This commit is contained in:
Sarah Jamie Lewis 2021-08-15 15:21:46 -07:00
parent 16b6a4a4b9
commit 4983e2c728
5 changed files with 88 additions and 140 deletions

View File

@ -6,5 +6,9 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
rand_core = "0.6.0" rand_core = { version = "0.6", default-features = false }
rand ="0.8.3" rand ="0.8.3"
subtle = "2.4.1"
byteorder = "1.4.3"
bitvec = "0.22.3"
ff = { version = "0.10", features = ["derive"] }

View File

@ -7,9 +7,9 @@ This package contains a toy implementation of an (s-t)Detectable Hash Function a
### How it Works ### 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))` 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))`

View File

@ -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<const ORDER: u64> {
val: u64,
}
impl<const ORDER: u64> Display for PrimeOrderDomain<{ ORDER }> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{:20}", self.val)
}
}
impl<const ORDER: u64> 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<const ORDER: u64> 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<const ORDER: u64> 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<const ORDER: u64> 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,
}
}
}

View File

@ -1,76 +1,86 @@
use crate::domain::PrimeOrderDomain; extern crate ff;
use crate::matrix::Matrix; use crate::matrix::Matrix;
use rand::Rng; use ff::PrimeField;
use rand_core::{CryptoRng, RngCore}; 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; struct HashKey<PF>
pub mod matrix; where
PF: PrimeField,
struct Generator { {
synthetic_max: u64, synthetic_max: u64,
threshold: u64, threshold: u64,
key: Vec<Vec<PrimeOrderDomain<PRIME_ORDER>>>, key: Vec<Vec<PF>>,
} }
#[derive(Debug)] #[derive(Debug)]
struct Hash(Vec<PrimeOrderDomain<PRIME_ORDER>>); struct Hash<PF>(Vec<PF>)
where
PF: PrimeField;
impl Generator { impl<PF> HashKey<PF>
pub fn generate<R: RngCore + CryptoRng>( where
rng: &mut R, PF: PrimeField,
{
pub fn new<R: RngCore + CryptoRng + Copy>(
rng: R,
synthetic_max: u64, synthetic_max: u64,
threshold: u64, threshold: u64,
) -> Generator { ) -> HashKey<PF> {
let mut k = vec![]; let mut k = vec![];
for _i in 0..synthetic_max { for _i in 0..synthetic_max {
let mut p_i = vec![]; let mut p_i = vec![];
for _j in 0..threshold { for _j in 0..threshold {
let p = rng.gen_range(0..PRIME_ORDER); p_i.push(PF::random(rng));
p_i.push(PrimeOrderDomain::<PRIME_ORDER>::new(p));
} }
k.push(p_i); k.push(p_i);
} }
Generator { HashKey {
synthetic_max, synthetic_max,
threshold, threshold,
key: k, key: k,
} }
} }
pub fn hash(&self, value: PrimeOrderDomain<PRIME_ORDER>) -> Hash { pub fn generate_hash(&self, value: PF) -> Hash<PF> {
let mut hash = vec![value]; let mut hash = vec![value];
for i in 0..self.synthetic_max as usize { 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.... // 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 { 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.push(result);
} }
Hash(hash) Hash(hash)
} }
pub fn random<R: RngCore + CryptoRng>(&self, rng: &mut R) -> Hash { pub fn random<R: RngCore + CryptoRng + Copy>(&self, rng: R) -> Hash<PF> {
let mut hash = vec![PrimeOrderDomain::new(rng.gen_range(0..PRIME_ORDER))]; let mut hash = vec![PF::random(rng)];
for _i in 0..self.synthetic_max as usize { for _i in 0..self.synthetic_max as usize {
let mut result = PrimeOrderDomain::new(rng.gen_range(0..PRIME_ORDER)); hash.push(PF::random(rng));
hash.push(result);
} }
Hash(hash) Hash(hash)
} }
} }
struct Solver { struct Solver<PF>
where
PF: PrimeField,
{
synthetic_max: u64, synthetic_max: u64,
threshold: u64, threshold: u64,
expanded_hashes: Vec<Hash>, expanded_hashes: Vec<Hash<PF>>,
} }
impl Solver { impl<PF> Solver<PF>
pub fn new(synthetic_max: u64, threshold: u64) -> Solver { where
PF: PrimeField + PartialOrd + Display,
{
pub fn new(synthetic_max: u64, threshold: u64) -> Solver<PF> {
Solver { Solver {
synthetic_max, synthetic_max,
threshold, threshold,
@ -79,13 +89,13 @@ impl Solver {
} }
/// Add a new hash to be evaluated by the 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<PF>) {
println!("Hash {}: {:?}", self.expanded_hashes.len(), 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) // 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![]; let mut expanded_hash = vec![];
for i in 0..self.threshold { 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) { for element in hash.0.iter().skip(1) {
expanded_hash.push(element.clone()) expanded_hash.push(element.clone())
@ -106,7 +116,7 @@ impl Solver {
} }
// Append the identity matrix... // Append the identity matrix...
for (i, r) in (m..m + self.expanded_hashes.len()).enumerate() { 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... // Transpose for row reduction...
@ -131,7 +141,7 @@ impl Solver {
let mut i_max = h; let mut i_max = h;
let mut i_max_v = augmented_matrix.at(h, k); let mut i_max_v = augmented_matrix.at(h, k);
for x in h + 1..rows { 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 = x;
i_max_v = augmented_matrix.at(x, k) i_max_v = augmented_matrix.at(x, k)
} }
@ -141,8 +151,8 @@ impl Solver {
} else { } else {
augmented_matrix.swap_rows(h, i_max); augmented_matrix.swap_rows(h, i_max);
for i in h + 1..rows { for i in h + 1..rows {
let f = augmented_matrix.at(i, k) * augmented_matrix.at(h, k).inverse(); let f = augmented_matrix.at(i, k) * augmented_matrix.at(h, k).invert().unwrap();
augmented_matrix.update(i, k, PrimeOrderDomain::new(0)); augmented_matrix.update(i, k, PF::zero());
for j in k + 1..cols { for j in k + 1..cols {
let val = augmented_matrix.at(i, j) - augmented_matrix.at(h, j) * f; let val = augmented_matrix.at(i, j) - augmented_matrix.at(h, j) * f;
augmented_matrix.update(i, j, val); augmented_matrix.update(i, j, val);
@ -217,17 +227,31 @@ impl Solver {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::domain::PrimeOrderDomain; use crate::ff::Field;
use crate::{Generator, Solver, PRIME_ORDER}; use crate::{HashKey, Solver};
use rand::Rng; use ff::PrimeField;
use rand_core::OsRng; 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] #[test]
fn it_works() { fn it_works() {
let mut rng = OsRng; let rng = OsRng;
let s = 3u64; let s = 3u64;
let t = 6u64; let t = 6u64;
let dhf = Generator::generate(&mut rng, s, t); let dhf = HashKey::<Fp>::new(rng, s, t);
let mut solver = Solver::new(s, t); let mut solver = Solver::new(s, t);
for i in 0..10 { for i in 0..10 {
@ -237,11 +261,11 @@ mod tests {
// If |random hashes| > s this this procedure will *not* work // 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. // There is an extended algorithm for extract assuming |true hashes| > t though - we do not implement it.
if i != 2 && i != 5 && i != 8 { if i != 2 && i != 5 && i != 8 {
let x0: u64 = rng.gen_range(0..PRIME_ORDER); let x0 = Fp::random(rng);
let hash = dhf.hash(PrimeOrderDomain::new(x0)); let hash = dhf.generate_hash(x0);
solver.add_hash(hash); solver.add_hash(hash);
} else { } else {
solver.add_hash(dhf.random(&mut rng)); solver.add_hash(dhf.random(rng));
} }
} }

View File

@ -1,21 +1,26 @@
use crate::domain::PrimeOrderDomain; use ff::PrimeField;
use crate::PRIME_ORDER;
/// A dense matrix. Apple have said t and s are going to be on the order of 30 so I feel /// 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 /// you can probably always get away with a dense representation
pub struct Matrix { pub struct Matrix<PF>
where
PF: PrimeField,
{
rows: usize, rows: usize,
cols: usize, cols: usize,
vals: Vec<Vec<PrimeOrderDomain<PRIME_ORDER>>>, vals: Vec<Vec<PF>>,
} }
impl Matrix { impl<PF> Matrix<PF>
pub fn new(rows: usize, cols: usize) -> Matrix { where
PF: PrimeField,
{
pub fn new(rows: usize, cols: usize) -> Matrix<PF> {
let mut vals = vec![]; let mut vals = vec![];
for _i in 0..rows { for _i in 0..rows {
let mut row = vec![]; let mut row = vec![];
for _j in 0..cols { for _j in 0..cols {
row.push(PrimeOrderDomain::new(0)) row.push(PF::zero())
} }
vals.push(row) vals.push(row)
} }
@ -30,15 +35,15 @@ impl Matrix {
self.cols self.cols
} }
pub fn update(&mut self, row: usize, col: usize, val: PrimeOrderDomain<PRIME_ORDER>) { pub fn update(&mut self, row: usize, col: usize, val: PF) {
self.vals[row][col] = val self.vals[row][col] = val
} }
pub fn at(&self, row: usize, col: usize) -> PrimeOrderDomain<PRIME_ORDER> { pub fn at(&self, row: usize, col: usize) -> PF {
self.vals[row][col] self.vals[row][col]
} }
pub fn transpose(&self) -> Matrix { pub fn transpose(&self) -> Matrix<PF> {
let mut m = Matrix::new(self.cols, self.rows); let mut m = Matrix::new(self.cols, self.rows);
for i in 0..self.cols { for i in 0..self.cols {
for j in 0..self.rows { for j in 0..self.rows {