Prototype entangled tags
This commit is contained in:
parent
c5bb0e2c88
commit
d6b90fcbc8
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "fuzzytags"
|
||||
description = "a probabilistic cryptographic structure for metadata resistant tagging"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
repository = "https://git.openprivacy.ca/openprivacy/fuzzytags"
|
||||
authors = ["Sarah Jamie Lewis <sarah@openprivacy.ca>"]
|
||||
edition = "2018"
|
||||
|
@ -16,6 +16,7 @@ curve25519-dalek = {version="3.0.0", features=["serde"]}
|
|||
sha3 = "0.9.1"
|
||||
bit-vec = {version="0.6.3", features=["serde"]}
|
||||
serde = {version="1.0.123", features=["derive"]}
|
||||
rayon = "1.5.0"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = {version="0.3", features=["html_reports"]}
|
||||
|
|
114
src/lib.rs
114
src/lib.rs
|
@ -10,12 +10,15 @@ use curve25519_dalek::ristretto::RistrettoPoint;
|
|||
use curve25519_dalek::scalar::Scalar;
|
||||
use curve25519_dalek::traits::MultiscalarMul;
|
||||
use rand::rngs::OsRng;
|
||||
use rayon::iter::ParallelIterator;
|
||||
use rayon::prelude::IntoParallelIterator;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use sha3::Sha3_512;
|
||||
use std::fmt;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::{Mul, Sub};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A tag is a probabilistic cryptographic structure. When constructed for a given `FuzzyPublicKey`
|
||||
/// it will pass the `FuzzyDetectionKey::test` 100% of the time. For other public keys
|
||||
|
@ -281,11 +284,107 @@ impl FuzzyPublicKey {
|
|||
|
||||
return FuzzyTag { u, y, ciphertexts };
|
||||
}
|
||||
|
||||
/// WARNING: if you pass in a large length into this function it will take a long time!
|
||||
/// This begins a very slow, but ,parallel search for a tag that will validate of the given
|
||||
/// public keys up to a given false positive rate 2^-1
|
||||
pub fn generate_entangled_tag(public_keys: Vec<FuzzyPublicKey>, length: usize) -> FuzzyTag {
|
||||
let arc_public_keys = Arc::new(public_keys);
|
||||
loop {
|
||||
let results: Vec<FuzzyTag> = (0..8)
|
||||
.into_par_iter()
|
||||
.map(|_x| FuzzyPublicKey::try_entangled_tag(arc_public_keys.clone(), length))
|
||||
.filter(|x| x.is_ok())
|
||||
.map(|x| x.unwrap())
|
||||
.collect();
|
||||
if results.is_empty() == false {
|
||||
return results[0].clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_entangled_tag(public_keys: Arc<Vec<FuzzyPublicKey>>, length: usize) -> Result<FuzzyTag, ()> {
|
||||
let mut rng = OsRng::default();
|
||||
let g = RISTRETTO_BASEPOINT_POINT;
|
||||
|
||||
// generate some random points...
|
||||
let r = Scalar::random(&mut rng);
|
||||
let u = g.mul(r);
|
||||
|
||||
let mut entangled = false;
|
||||
let mut z = Scalar::zero();
|
||||
|
||||
// construct the ciphertext portion of the tag
|
||||
let mut ciphertexts = BitVec::new();
|
||||
let mut attempts = 0;
|
||||
|
||||
let mut public_key_precomputes = vec![];
|
||||
for public_key in public_keys.iter() {
|
||||
let mut precompute = vec![];
|
||||
for i in public_key.0.iter() {
|
||||
precompute.push(i.mul(r));
|
||||
}
|
||||
public_key_precomputes.push(precompute);
|
||||
}
|
||||
|
||||
while !entangled && attempts < 1000 {
|
||||
attempts += 1;
|
||||
ciphertexts = BitVec::new();
|
||||
z = Scalar::random(&mut rng);
|
||||
let w = g.mul(z);
|
||||
entangled = true;
|
||||
for (i, precompute) in public_key_precomputes[0].iter().enumerate() {
|
||||
let mut same = true;
|
||||
let k_i = FuzzySecretKey::h(u, *precompute, w);
|
||||
|
||||
if i < length {
|
||||
for precompute in public_key_precomputes.iter().skip(1) {
|
||||
let n_k_i = FuzzySecretKey::h(u, precompute[i], w);
|
||||
if k_i != n_k_i {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !same {
|
||||
entangled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// encrypt a plaintext of all 1's
|
||||
let c_i = k_i ^ 0x01;
|
||||
ciphertexts.push(c_i == 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
if entangled == false {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Without this next part, this scheme would not be CCA-secure. Consider a scheme with just
|
||||
// u = ^r and and h_i^r = g^(x_i*r)
|
||||
// An adversarial server with access to a Test oracle (i.e. the decryption key) may be able
|
||||
// to maul a challenge ciphertext by e.g. replacing the order of the ciphertexts.
|
||||
|
||||
// From the paper:
|
||||
// "The value w corresponds to a chameleon hash [KR00] computed on the message (0,z), where z is chosen at random.
|
||||
// Once the ciphertext has been computed, we use a master trapdoor for the chameleon hash (which is part of the scheme’s secret key) in order to compute a collision (y,m) where m
|
||||
// is a hash of the remaining components of the ciphertext"
|
||||
|
||||
// Translated m is a challenge over the random element u and the ordered ciphertexts
|
||||
// It is then used to construct a response y which can be used to recover w the random element
|
||||
// used to derive the key.
|
||||
|
||||
// finally calculate a `y` = 1/r * (z-m) which will be used to re-derive `w`
|
||||
let m = FuzzySecretKey::g(u, &ciphertexts);
|
||||
let y = r.invert().mul(z.sub(m));
|
||||
|
||||
return Ok(FuzzyTag { u, y, ciphertexts });
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::FuzzySecretKey;
|
||||
use crate::{FuzzyPublicKey, FuzzySecretKey};
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
|
@ -296,6 +395,19 @@ mod tests {
|
|||
println!("{}", serde_json::to_string(&detection_key).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple() {
|
||||
let secret_keys: Vec<FuzzySecretKey> = (0..3).map(|_x| FuzzySecretKey::generate(24)).collect();
|
||||
let public_keys: Vec<FuzzyPublicKey> = secret_keys.iter().map(|x| x.public_key()).collect();
|
||||
let entangled_tag = FuzzyPublicKey::generate_entangled_tag(public_keys, 8);
|
||||
println!("{}", entangled_tag);
|
||||
for secret_key in secret_keys.iter() {
|
||||
let detection_key = secret_key.extract(8);
|
||||
assert!(detection_key.test_tag(&entangled_tag));
|
||||
println!("{}", detection_key);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn correctness() {
|
||||
let number_of_messages = 100;
|
||||
|
|
Loading…
Reference in New Issue