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
[dependencies]
rand_core = "0.6.0"
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"] }

View File

@ -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))`

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 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<PF>
where
PF: PrimeField,
{
synthetic_max: u64,
threshold: u64,
key: Vec<Vec<PrimeOrderDomain<PRIME_ORDER>>>,
key: Vec<Vec<PF>>,
}
#[derive(Debug)]
struct Hash(Vec<PrimeOrderDomain<PRIME_ORDER>>);
struct Hash<PF>(Vec<PF>)
where
PF: PrimeField;
impl Generator {
pub fn generate<R: RngCore + CryptoRng>(
rng: &mut R,
impl<PF> HashKey<PF>
where
PF: PrimeField,
{
pub fn new<R: RngCore + CryptoRng + Copy>(
rng: R,
synthetic_max: u64,
threshold: u64,
) -> Generator {
) -> HashKey<PF> {
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::<PRIME_ORDER>::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<PRIME_ORDER>) -> Hash {
pub fn generate_hash(&self, value: PF) -> Hash<PF> {
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<R: RngCore + CryptoRng>(&self, rng: &mut R) -> Hash {
let mut hash = vec![PrimeOrderDomain::new(rng.gen_range(0..PRIME_ORDER))];
pub fn random<R: RngCore + CryptoRng + Copy>(&self, rng: R) -> Hash<PF> {
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<PF>
where
PF: PrimeField,
{
synthetic_max: u64,
threshold: u64,
expanded_hashes: Vec<Hash>,
expanded_hashes: Vec<Hash<PF>>,
}
impl Solver {
pub fn new(synthetic_max: u64, threshold: u64) -> Solver {
impl<PF> Solver<PF>
where
PF: PrimeField + PartialOrd + Display,
{
pub fn new(synthetic_max: u64, threshold: u64) -> Solver<PF> {
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<PF>) {
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::<Fp>::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));
}
}

View File

@ -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<PF>
where
PF: PrimeField,
{
rows: usize,
cols: usize,
vals: Vec<Vec<PrimeOrderDomain<PRIME_ORDER>>>,
vals: Vec<Vec<PF>>,
}
impl Matrix {
pub fn new(rows: usize, cols: usize) -> Matrix {
impl<PF> Matrix<PF>
where
PF: PrimeField,
{
pub fn new(rows: usize, cols: usize) -> Matrix<PF> {
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<PRIME_ORDER>) {
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<PRIME_ORDER> {
pub fn at(&self, row: usize, col: usize) -> PF {
self.vals[row][col]
}
pub fn transpose(&self) -> Matrix {
pub fn transpose(&self) -> Matrix<PF> {
let mut m = Matrix::new(self.cols, self.rows);
for i in 0..self.cols {
for j in 0..self.rows {