Use an actual FF lib
This commit is contained in:
parent
16b6a4a4b9
commit
4983e2c728
|
@ -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"
|
||||
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"] }
|
|
@ -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))`
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
106
src/lib.rs
106
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<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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue