2021-08-15 22:21:46 +00:00
|
|
|
extern crate ff;
|
2021-08-14 17:57:08 +00:00
|
|
|
use crate::matrix::Matrix;
|
2021-08-15 22:21:46 +00:00
|
|
|
use ff::PrimeField;
|
2021-08-14 17:57:08 +00:00
|
|
|
use rand_core::{CryptoRng, RngCore};
|
2021-08-15 22:21:46 +00:00
|
|
|
use std::fmt::Display;
|
2021-08-14 17:57:08 +00:00
|
|
|
|
2021-08-15 22:21:46 +00:00
|
|
|
mod matrix;
|
2021-08-14 18:07:58 +00:00
|
|
|
|
2021-08-15 22:21:46 +00:00
|
|
|
struct HashKey<PF>
|
|
|
|
where
|
|
|
|
PF: PrimeField,
|
|
|
|
{
|
2021-08-14 17:57:08 +00:00
|
|
|
synthetic_max: u64,
|
|
|
|
threshold: u64,
|
2021-08-15 22:21:46 +00:00
|
|
|
key: Vec<Vec<PF>>,
|
2021-08-14 17:57:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2021-08-15 22:21:46 +00:00
|
|
|
struct Hash<PF>(Vec<PF>)
|
|
|
|
where
|
|
|
|
PF: PrimeField;
|
2021-08-14 17:57:08 +00:00
|
|
|
|
2021-08-15 22:21:46 +00:00
|
|
|
impl<PF> HashKey<PF>
|
|
|
|
where
|
|
|
|
PF: PrimeField,
|
|
|
|
{
|
|
|
|
pub fn new<R: RngCore + CryptoRng + Copy>(
|
|
|
|
rng: R,
|
2021-08-14 17:57:08 +00:00
|
|
|
synthetic_max: u64,
|
|
|
|
threshold: u64,
|
2021-08-15 22:21:46 +00:00
|
|
|
) -> HashKey<PF> {
|
2021-08-14 17:57:08 +00:00
|
|
|
let mut k = vec![];
|
|
|
|
for _i in 0..synthetic_max {
|
|
|
|
let mut p_i = vec![];
|
|
|
|
for _j in 0..threshold {
|
2021-08-15 22:21:46 +00:00
|
|
|
p_i.push(PF::random(rng));
|
2021-08-14 17:57:08 +00:00
|
|
|
}
|
|
|
|
k.push(p_i);
|
|
|
|
}
|
2021-08-15 22:21:46 +00:00
|
|
|
HashKey {
|
2021-08-14 17:57:08 +00:00
|
|
|
synthetic_max,
|
|
|
|
threshold,
|
|
|
|
key: k,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-15 22:21:46 +00:00
|
|
|
pub fn generate_hash(&self, value: PF) -> Hash<PF> {
|
2021-08-14 17:57:08 +00:00
|
|
|
let mut hash = vec![value];
|
2021-08-15 21:25:44 +00:00
|
|
|
|
2021-08-14 17:57:08 +00:00
|
|
|
for i in 0..self.synthetic_max as usize {
|
2021-08-15 21:25:44 +00:00
|
|
|
// Here evaluate our polynomial key[i]*X key[i]*X^2 key[i]*X^3 etc....
|
2021-08-15 22:21:46 +00:00
|
|
|
let mut result = PF::zero();
|
2021-08-14 17:57:08 +00:00
|
|
|
for j in 0..self.threshold as usize {
|
2021-08-15 22:21:46 +00:00
|
|
|
result = result + (self.key[i][j].clone() * value.pow_vartime(&[j as u64]));
|
2021-08-14 17:57:08 +00:00
|
|
|
}
|
|
|
|
hash.push(result);
|
|
|
|
}
|
|
|
|
Hash(hash)
|
|
|
|
}
|
|
|
|
|
2021-08-15 22:21:46 +00:00
|
|
|
pub fn random<R: RngCore + CryptoRng + Copy>(&self, rng: R) -> Hash<PF> {
|
|
|
|
let mut hash = vec![PF::random(rng)];
|
2021-08-14 17:57:08 +00:00
|
|
|
for _i in 0..self.synthetic_max as usize {
|
2021-08-15 22:21:46 +00:00
|
|
|
hash.push(PF::random(rng));
|
2021-08-14 17:57:08 +00:00
|
|
|
}
|
|
|
|
Hash(hash)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-15 22:21:46 +00:00
|
|
|
struct Solver<PF>
|
|
|
|
where
|
|
|
|
PF: PrimeField,
|
|
|
|
{
|
2021-08-14 17:57:08 +00:00
|
|
|
synthetic_max: u64,
|
|
|
|
threshold: u64,
|
2021-08-15 22:21:46 +00:00
|
|
|
expanded_hashes: Vec<Hash<PF>>,
|
2021-08-14 17:57:08 +00:00
|
|
|
}
|
|
|
|
|
2021-08-15 22:21:46 +00:00
|
|
|
impl<PF> Solver<PF>
|
|
|
|
where
|
|
|
|
PF: PrimeField + PartialOrd + Display,
|
|
|
|
{
|
|
|
|
pub fn new(synthetic_max: u64, threshold: u64) -> Solver<PF> {
|
2021-08-14 17:57:08 +00:00
|
|
|
Solver {
|
|
|
|
synthetic_max,
|
|
|
|
threshold,
|
|
|
|
expanded_hashes: vec![],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add a new hash to be evaluated by the solver...
|
2021-08-15 22:21:46 +00:00
|
|
|
pub fn add_hash(&mut self, hash: Hash<PF>) {
|
2021-08-14 17:57:08 +00:00
|
|
|
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 {
|
2021-08-15 22:21:46 +00:00
|
|
|
expanded_hash.push(hash.0[0].pow_vartime(&[i]))
|
2021-08-14 17:57:08 +00:00
|
|
|
}
|
|
|
|
for element in hash.0.iter().skip(1) {
|
|
|
|
expanded_hash.push(element.clone())
|
|
|
|
}
|
|
|
|
self.expanded_hashes.push(Hash(expanded_hash))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Solve the system and return the indexes of the true hashes (or error if the system is unsolvable)
|
|
|
|
pub fn attempt_solve(&self) -> Result<Vec<usize>, ()> {
|
|
|
|
// Arrange the hashes into an augmented for matrix...
|
2021-08-14 18:11:32 +00:00
|
|
|
let m = (self.synthetic_max + self.threshold) as usize;
|
2021-08-15 14:35:26 +00:00
|
|
|
let mut augmented_matrix =
|
|
|
|
Matrix::new(m + self.expanded_hashes.len(), self.expanded_hashes.len());
|
2021-08-14 17:57:08 +00:00
|
|
|
for j in 0..self.expanded_hashes.len() {
|
|
|
|
for i in 0..m {
|
|
|
|
augmented_matrix.update(i, j, self.expanded_hashes[j as usize].0[i as usize]);
|
|
|
|
}
|
|
|
|
}
|
2021-08-14 18:11:32 +00:00
|
|
|
// Append the identity matrix...
|
2021-08-14 17:57:08 +00:00
|
|
|
for (i, r) in (m..m + self.expanded_hashes.len()).enumerate() {
|
2021-08-15 22:21:46 +00:00
|
|
|
augmented_matrix.update(r as usize, i, PF::one());
|
2021-08-14 17:57:08 +00:00
|
|
|
}
|
2021-08-14 18:11:32 +00:00
|
|
|
|
|
|
|
// Transpose for row reduction...
|
2021-08-14 17:57:08 +00:00
|
|
|
augmented_matrix = augmented_matrix.transpose();
|
|
|
|
|
2021-08-14 18:11:32 +00:00
|
|
|
// Pretty Print...
|
2021-08-14 17:57:08 +00:00
|
|
|
for i in 0..augmented_matrix.rows() {
|
|
|
|
for j in 0..augmented_matrix.cols() {
|
|
|
|
print!("{} ", augmented_matrix.at(i, j as usize));
|
|
|
|
}
|
|
|
|
println!(";")
|
|
|
|
}
|
|
|
|
|
2021-08-14 18:11:32 +00:00
|
|
|
// Do the actual row reduction...
|
2021-08-14 17:57:08 +00:00
|
|
|
let cols = augmented_matrix.cols();
|
|
|
|
let rows = augmented_matrix.rows();
|
|
|
|
|
|
|
|
let mut h = 0;
|
|
|
|
let mut k = 0;
|
|
|
|
|
|
|
|
while h < rows && k < cols {
|
|
|
|
let mut i_max = h;
|
|
|
|
let mut i_max_v = augmented_matrix.at(h, k);
|
|
|
|
for x in h + 1..rows {
|
2021-08-15 22:21:46 +00:00
|
|
|
if augmented_matrix.at(x, k) > i_max_v {
|
2021-08-14 17:57:08 +00:00
|
|
|
i_max = x;
|
|
|
|
i_max_v = augmented_matrix.at(x, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if augmented_matrix.at(i_max, k).is_zero() {
|
|
|
|
k += 1;
|
|
|
|
} else {
|
|
|
|
augmented_matrix.swap_rows(h, i_max);
|
|
|
|
for i in h + 1..rows {
|
2021-08-15 22:21:46 +00:00
|
|
|
let f = augmented_matrix.at(i, k) * augmented_matrix.at(h, k).invert().unwrap();
|
|
|
|
augmented_matrix.update(i, k, PF::zero());
|
2021-08-14 17:57:08 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
h += 1;
|
|
|
|
k += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-14 18:11:32 +00:00
|
|
|
println!();
|
|
|
|
println!();
|
|
|
|
println!();
|
|
|
|
|
|
|
|
// Transpose back to column form...
|
2021-08-14 17:57:08 +00:00
|
|
|
augmented_matrix = augmented_matrix.transpose();
|
|
|
|
|
2021-08-14 18:11:32 +00:00
|
|
|
// Pretty Print for checking...
|
2021-08-14 17:57:08 +00:00
|
|
|
for i in 0..augmented_matrix.rows() {
|
|
|
|
for j in 0..augmented_matrix.cols() {
|
|
|
|
print!("{} ", augmented_matrix.at(i, j as usize));
|
|
|
|
}
|
|
|
|
println!(";");
|
|
|
|
if i + 1 == m {
|
|
|
|
println!("----------------------------------------");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-14 18:11:32 +00:00
|
|
|
// Calculate the Kernel Basis Vectors...these are the columns where the
|
|
|
|
// Original matrix is now all 0s
|
2021-08-14 17:57:08 +00:00
|
|
|
let mut count = 0;
|
|
|
|
for col in (0..self.expanded_hashes.len()).rev() {
|
|
|
|
let mut is_null = true;
|
|
|
|
for row in 0..m {
|
|
|
|
if !augmented_matrix.at(row, col).is_zero() {
|
|
|
|
is_null = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if is_null == false {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
count += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if count == 0 {
|
2021-08-14 18:11:32 +00:00
|
|
|
// Unsolvable...
|
2021-08-14 17:57:08 +00:00
|
|
|
return Err(());
|
|
|
|
}
|
|
|
|
|
2021-08-14 18:11:32 +00:00
|
|
|
// We found some vectors...now we can derive a solution...
|
2021-08-14 17:57:08 +00:00
|
|
|
println!("Found {} Kernel Basis Vectors...", count);
|
|
|
|
let mut solution = vec![];
|
|
|
|
|
2021-08-14 18:16:55 +00:00
|
|
|
// Check all the basis vectors...
|
2021-08-14 17:57:08 +00:00
|
|
|
let basis_state = self.expanded_hashes.len() - count;
|
|
|
|
for i in 0..self.expanded_hashes.len() {
|
|
|
|
let mut is_zero = true;
|
|
|
|
for b in 0..count {
|
|
|
|
is_zero = is_zero & augmented_matrix.at(i + m, basis_state + b).is_zero()
|
|
|
|
}
|
|
|
|
if !is_zero {
|
|
|
|
solution.push(i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
println!("Solution: {:?}", solution);
|
|
|
|
Ok(solution)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-08-15 22:21:46 +00:00
|
|
|
use crate::ff::Field;
|
|
|
|
use crate::{HashKey, Solver};
|
|
|
|
use ff::PrimeField;
|
2021-08-14 17:57:08 +00:00
|
|
|
use rand_core::OsRng;
|
2021-08-15 22:21:46 +00:00
|
|
|
use std::fmt::{Display, Formatter};
|
|
|
|
|
|
|
|
// Generate a Prime Field to Test with...
|
2021-08-15 22:27:19 +00:00
|
|
|
// This is way larger than the 64 bit prime the paper specifies, but wth
|
2021-08-15 22:21:46 +00:00
|
|
|
#[derive(PrimeField)]
|
2021-08-15 22:27:19 +00:00
|
|
|
#[PrimeFieldModulus = "52435875175126190479447740508185965837690552500527637822603658699938581184513"]
|
|
|
|
#[PrimeFieldGenerator = "7"]
|
2021-08-15 22:21:46 +00:00
|
|
|
#[PrimeFieldReprEndianness = "little"]
|
2021-08-15 22:27:19 +00:00
|
|
|
struct Fp([u64; 4]);
|
2021-08-15 22:21:46 +00:00
|
|
|
|
|
|
|
impl Display for Fp {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{:10}", self.0[0])
|
|
|
|
}
|
|
|
|
}
|
2021-08-14 17:57:08 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn it_works() {
|
2021-08-15 22:21:46 +00:00
|
|
|
let rng = OsRng;
|
2021-08-15 22:27:19 +00:00
|
|
|
let s = 10u64;
|
|
|
|
let t = 30u64;
|
2021-08-15 22:21:46 +00:00
|
|
|
let dhf = HashKey::<Fp>::new(rng, s, t);
|
2021-08-15 14:35:26 +00:00
|
|
|
|
2021-08-15 14:39:04 +00:00
|
|
|
let mut solver = Solver::new(s, t);
|
2021-08-15 22:27:19 +00:00
|
|
|
for i in 0..40 {
|
2021-08-15 14:39:04 +00:00
|
|
|
// These are the indexes which are to be random...you can try swapping them around..
|
2021-08-15 21:38:21 +00:00
|
|
|
// REMEMBER: the number of true hash needs to be AT LEAST `t+1` AND the number of fake hashes
|
|
|
|
// must be AT-MOST `s`.
|
|
|
|
// 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.
|
2021-08-15 22:27:19 +00:00
|
|
|
if i != 2 && i != 5 && i != 8 && i < 34 {
|
2021-08-15 22:21:46 +00:00
|
|
|
let x0 = Fp::random(rng);
|
|
|
|
let hash = dhf.generate_hash(x0);
|
2021-08-14 17:57:08 +00:00
|
|
|
solver.add_hash(hash);
|
2021-08-15 14:39:04 +00:00
|
|
|
} else {
|
2021-08-15 22:21:46 +00:00
|
|
|
solver.add_hash(dhf.random(rng));
|
2021-08-14 17:57:08 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-15 14:39:04 +00:00
|
|
|
|
2021-08-15 22:27:19 +00:00
|
|
|
assert_eq!(
|
|
|
|
solver.attempt_solve().unwrap(),
|
|
|
|
vec![
|
|
|
|
0, 1, 3, 4, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
|
|
|
25, 26, 27, 28, 29, 30, 31, 32, 33
|
|
|
|
]
|
|
|
|
);
|
2021-08-14 17:57:08 +00:00
|
|
|
}
|
|
|
|
}
|