First cut of bulk verification function (feature gated)
This commit is contained in:
parent
5a61d9461a
commit
85dc8ed9ad
|
@ -18,6 +18,7 @@ sha3 = "0.9.1"
|
||||||
serde = {version="1.0.123", features=["derive"]}
|
serde = {version="1.0.123", features=["derive"]}
|
||||||
bit-vec = {version="0.6.3"}
|
bit-vec = {version="0.6.3"}
|
||||||
brute-force = {version="0.1.0", features=["curve25519"], optional=true}
|
brute-force = {version="0.1.0", features=["curve25519"], optional=true}
|
||||||
|
rayon = {version="1.5.0", optional=true}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = {version="0.3", features=["html_reports"]}
|
criterion = {version="0.3", features=["html_reports"]}
|
||||||
|
@ -29,4 +30,5 @@ name = "fuzzy_tags_benches"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
entangled = ["brute-force"]
|
entangled = ["brute-force"]
|
||||||
|
bulk_verify = ["rayon"]
|
104
src/lib.rs
104
src/lib.rs
|
@ -22,6 +22,15 @@ use brute_force::adaptors;
|
||||||
#[cfg(feature = "entangled")]
|
#[cfg(feature = "entangled")]
|
||||||
use brute_force::brute_force;
|
use brute_force::brute_force;
|
||||||
|
|
||||||
|
#[cfg(feature = "bulk_verify")]
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
#[cfg(feature = "bulk_verify")]
|
||||||
|
use rayon::iter::IndexedParallelIterator;
|
||||||
|
#[cfg(feature = "bulk_verify")]
|
||||||
|
use rayon::iter::IntoParallelRefIterator;
|
||||||
|
#[cfg(feature = "bulk_verify")]
|
||||||
|
use rayon::iter::ParallelIterator;
|
||||||
|
|
||||||
/// A tag is a probabilistic cryptographic structure. When constructed for a given `TaggingKey`
|
/// A tag is a probabilistic cryptographic structure. When constructed for a given `TaggingKey`
|
||||||
/// it will pass the `DetectionKey::test_tag` 100% of the time. For other tagging keys
|
/// it will pass the `DetectionKey::test_tag` 100% of the time. For other tagging keys
|
||||||
/// it will pass the test with probability `GAMMA` related to the security parameter of the system.
|
/// it will pass the test with probability `GAMMA` related to the security parameter of the system.
|
||||||
|
@ -349,11 +358,88 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
||||||
|
|
||||||
let b_i = k_i ^ c_i;
|
let b_i = k_i ^ c_i;
|
||||||
|
|
||||||
|
if b_i != 1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// assert that the plaintext is all 1's
|
// assert that the plaintext is all 1's
|
||||||
result = result & (b_i == 1);
|
result = result & (b_i == 1);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// A bulk testing function that takes in an vector of detection keys and returns a vector
|
||||||
|
/// of indexes where the tag matched.
|
||||||
|
#[cfg(feature = "bulk_verify")]
|
||||||
|
pub fn test_tag_bulk(detection_keys: &Vec<DetectionKey<{GAMMA}>>, tag: &Tag<{ GAMMA }>) -> Vec<usize> {
|
||||||
|
// A few checks to make sure the tag is well formed.
|
||||||
|
// All zeros in u or y can lead to a tag that validates against *all* tagging keys
|
||||||
|
// That doesn't seem like a great idea, so we return false to be safe.
|
||||||
|
// Zero values should never appear in well generated tags.
|
||||||
|
if tag.u.eq(&RistrettoPoint::default()) || tag.y.eq(&Scalar::zero()) {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let m = RootSecret::<GAMMA>::g(tag.u, &tag.ciphertexts);
|
||||||
|
let g = RISTRETTO_BASEPOINT_POINT;
|
||||||
|
|
||||||
|
// Re-derive w = g^z from the public tag.
|
||||||
|
// y = (1/r) * (z-m)
|
||||||
|
// u = g^r
|
||||||
|
// so w = g^m + u^y
|
||||||
|
// w = g^m + g^(r * 1/r * (z-m))
|
||||||
|
// w = g^m + g^(z-m)
|
||||||
|
// w = g^z
|
||||||
|
// See below for a full explanation as to the reason for this:
|
||||||
|
let w = RistrettoPoint::multiscalar_mul(&[m, tag.y], &[g, tag.u]);
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
|
// for each secret part...
|
||||||
|
let mut results : Vec<(usize)> = vec![];
|
||||||
|
detection_keys.par_iter().enumerate().for_each_with(tx.clone(), |tx,(index,detection_key)| {
|
||||||
|
let mut result = true;
|
||||||
|
for (i, x_i) in detection_key.0.iter().enumerate() {
|
||||||
|
// re-derive the key from the tag
|
||||||
|
let k_i = RootSecret::<GAMMA>::h(tag.u, tag.u.mul(x_i), w);
|
||||||
|
|
||||||
|
// calculate the "original" plaintext
|
||||||
|
let c_i = match tag.ciphertexts.get(i) {
|
||||||
|
Some(true) => 0x01,
|
||||||
|
Some(false) => 0x00,
|
||||||
|
_ => 0x00,
|
||||||
|
// we've run out of ciphertext, it doesn't really matter what we put here, the rest of the test will fail
|
||||||
|
// since the security of k_i is modelled as a random oracle, (k_i ^ 0) should also be random
|
||||||
|
};
|
||||||
|
|
||||||
|
let b_i = k_i ^ c_i;
|
||||||
|
|
||||||
|
if b_i != 1 {
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// assert that the plaintext is all 1's
|
||||||
|
result = result & (b_i == 1);
|
||||||
|
}
|
||||||
|
if result {
|
||||||
|
tx.send(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
std::mem::drop(tx);
|
||||||
|
loop {
|
||||||
|
let result = rx.recv();
|
||||||
|
match result {
|
||||||
|
Ok(index) => {
|
||||||
|
results.push(index)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A public identity that others can create tags for.
|
/// A public identity that others can create tags for.
|
||||||
|
@ -488,7 +574,7 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{RootSecret, Tag};
|
use crate::{RootSecret, Tag, DetectionKey};
|
||||||
use bit_vec::BitVec;
|
use bit_vec::BitVec;
|
||||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||||
use curve25519_dalek::scalar::Scalar;
|
use curve25519_dalek::scalar::Scalar;
|
||||||
|
@ -568,6 +654,22 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "bulk_verify")]
|
||||||
|
fn test_check_multiple() {
|
||||||
|
use crate::TaggingKey;
|
||||||
|
let secrets: Vec<RootSecret<24>> = (0..2).map(|_x| RootSecret::<24>::generate()).collect();
|
||||||
|
let tagging_keys: Vec<TaggingKey<24>> = secrets.iter().map(|x| x.tagging_key()).collect();
|
||||||
|
// it takes ~15 minutes on a standard desktop to find a length=24 match for 2 parties, so for testing let's keep things light
|
||||||
|
let entangled_tag = TaggingKey::generate_entangled_tag(tagging_keys, 16);
|
||||||
|
let detection_keys = secrets.iter().map(|x|x.extract_detection_key(16)).collect();
|
||||||
|
|
||||||
|
let results = DetectionKey::test_tag_bulk(&detection_keys, &entangled_tag);
|
||||||
|
for result in results.iter() {
|
||||||
|
assert_eq!(*result, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn correctness() {
|
fn correctness() {
|
||||||
let number_of_messages = 100;
|
let number_of_messages = 100;
|
||||||
|
|
Loading…
Reference in New Issue