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
|
# 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"] }
|
|
@ -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))`
|
||||||
|
|
||||||
|
|
|
@ -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 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue