Merge pull request 'Performance Improvements' (#2) from perf into trunk
Reviewed-on: #2
This commit is contained in:
commit
3d8e285b51
14
Cargo.toml
14
Cargo.toml
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "fuzzytags"
|
name = "fuzzytags"
|
||||||
description = "a probabilistic cryptographic structure for metadata resistant tagging"
|
description = "a probabilistic cryptographic structure for metadata resistant tagging"
|
||||||
version = "0.4.2"
|
version = "0.5.0"
|
||||||
repository = "https://git.openprivacy.ca/openprivacy/fuzzytags"
|
repository = "https://git.openprivacy.ca/openprivacy/fuzzytags"
|
||||||
authors = ["Sarah Jamie Lewis <sarah@openprivacy.ca>"]
|
authors = ["Sarah Jamie Lewis <sarah@openprivacy.ca>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
@ -12,12 +12,13 @@ keywords = ["fuzzytags","privacy","metadata-resistance","ristretto","cryptograph
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hex = "0.4.2"
|
hex = "0.4.2"
|
||||||
rand = "0.7.3"
|
rand_core = "0.6.0"
|
||||||
curve25519-dalek = {version="3.0.0", features=["serde"]}
|
rand ="0.8.3"
|
||||||
|
curve25519-dalek = { package = "curve25519-dalek-ng", version="4.0.1", features=["serde"]}
|
||||||
sha3 = "0.9.1"
|
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 = {git="https://git.openprivacy.ca/sarah/brute-force.git", version="0.2.0", features=["curve25519"], optional=true}
|
||||||
rayon = {version="1.5.0", optional=true}
|
rayon = {version="1.5.0", optional=true}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -29,6 +30,11 @@ bincode = "1.3.1"
|
||||||
name = "fuzzy_tags_benches"
|
name = "fuzzy_tags_benches"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "entangled"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
entangled = ["brute-force"]
|
entangled = ["brute-force"]
|
||||||
bulk_verify = ["rayon"]
|
bulk_verify = ["rayon"]
|
||||||
|
simd = ["curve25519-dalek/simd_backend"]
|
38
README.md
38
README.md
|
@ -88,7 +88,9 @@ provided them (depending on the efficiency of the decryption method).
|
||||||
A party first needs to generate `RootSecret`
|
A party first needs to generate `RootSecret`
|
||||||
|
|
||||||
use fuzzytags::RootSecret;
|
use fuzzytags::RootSecret;
|
||||||
let secret = RootSecret::<24>::generate();
|
use rand::rngs::OsRng;
|
||||||
|
let mut rng = OsRng::default();
|
||||||
|
let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
|
|
||||||
From the secret detection key a party can derive a `DetectionKey` which can be given to adversarial server to
|
From the secret detection key a party can derive a `DetectionKey` which can be given to adversarial server to
|
||||||
fuzzily detect tags on behalf of the party.
|
fuzzily detect tags on behalf of the party.
|
||||||
|
@ -104,12 +106,14 @@ validate against a random public key with a maximum probability of _2^-gamma_.
|
||||||
Once in possession of a tagging key, a party in a metadata resistant app can use it to generate tags:
|
Once in possession of a tagging key, a party in a metadata resistant app can use it to generate tags:
|
||||||
|
|
||||||
use fuzzytags::RootSecret;
|
use fuzzytags::RootSecret;
|
||||||
let secret = RootSecret::<24>::generate();
|
use rand::rngs::OsRng;
|
||||||
|
let mut rng = OsRng::default();
|
||||||
|
let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
let tagging_key = secret.tagging_key();
|
let tagging_key = secret.tagging_key();
|
||||||
|
|
||||||
// Give public key to a another party...
|
// Give public key to a another party...
|
||||||
// and then they can do...
|
// and then they can do...
|
||||||
let tag = tagging_key.generate_tag();
|
let tag = tagging_key.generate_tag(&mut rng);
|
||||||
|
|
||||||
These tags can then be attached to a message in a metadata resistant system.
|
These tags can then be attached to a message in a metadata resistant system.
|
||||||
|
|
||||||
|
@ -120,14 +124,16 @@ First it is necessary to extract a detection key for a given false positive prob
|
||||||
This extracted key can then be given to an adversarial server. The server can then test a given tag against the detection key e.g.:
|
This extracted key can then be given to an adversarial server. The server can then test a given tag against the detection key e.g.:
|
||||||
|
|
||||||
use fuzzytags::RootSecret;
|
use fuzzytags::RootSecret;
|
||||||
let secret = RootSecret::<24>::generate();
|
use rand::rngs::OsRng;
|
||||||
|
let mut rng = OsRng::default();
|
||||||
|
let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
let tagging_key = secret.tagging_key();
|
let tagging_key = secret.tagging_key();
|
||||||
// extract a detection key
|
// extract a detection key
|
||||||
let detection_key = secret.extract_detection_key(5);
|
let detection_key = secret.extract_detection_key(5);
|
||||||
|
|
||||||
// Give the tagging key to a another party...
|
// Give the tagging key to a another party...
|
||||||
// and then they can do...
|
// and then they can do...
|
||||||
let tag = tagging_key.generate_tag();
|
let tag = tagging_key.generate_tag(&mut rng);
|
||||||
|
|
||||||
// The server can now do this:
|
// The server can now do this:
|
||||||
if detection_key.test_tag(&tag) {
|
if detection_key.test_tag(&tag) {
|
||||||
|
@ -144,14 +150,16 @@ opens up applications like **multiple broadcast** and **deniable sending**.
|
||||||
|
|
||||||
|
|
||||||
use fuzzytags::{RootSecret, TaggingKey};
|
use fuzzytags::{RootSecret, TaggingKey};
|
||||||
let secret_1 = RootSecret::<24>::generate();
|
use rand::rngs::OsRng;
|
||||||
let secret_2 = RootSecret::<24>::generate();
|
let mut rng = OsRng::default();
|
||||||
|
let secret_1 = RootSecret::<24>::generate(&mut rng);
|
||||||
|
let secret_2 = RootSecret::<24>::generate(&mut rng);
|
||||||
let tagging_key_1 = secret_1.tagging_key(); // give this to a sender
|
let tagging_key_1 = secret_1.tagging_key(); // give this to a sender
|
||||||
let tagging_key_2 = secret_2.tagging_key(); // give this to a sender
|
let tagging_key_2 = secret_2.tagging_key(); // give this to a sender
|
||||||
// Will validate for detection keys derived from both secret_1 and secret_2 up
|
// Will validate for detection keys derived from both secret_1 and secret_2 up
|
||||||
// to n=8
|
// to n=8
|
||||||
#[cfg(feature = "entangled")]
|
#[cfg(feature = "entangled")]
|
||||||
let tag = TaggingKey::generate_entangled_tag(vec![tagging_key_1,tagging_key_2], 8);
|
let tag = TaggingKey::generate_entangled_tag(vec![tagging_key_1,tagging_key_2], &mut rng, 8);
|
||||||
|
|
||||||
## Serialization
|
## Serialization
|
||||||
|
|
||||||
|
@ -161,13 +169,15 @@ of different approaches e.g.:
|
||||||
|
|
||||||
use fuzzytags::RootSecret;
|
use fuzzytags::RootSecret;
|
||||||
use fuzzytags::Tag;
|
use fuzzytags::Tag;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
let secret = RootSecret::<24>::generate();
|
let mut rng = OsRng::default();
|
||||||
|
let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
let tagging_key = secret.tagging_key();
|
let tagging_key = secret.tagging_key();
|
||||||
|
|
||||||
// Give public key to a another party...
|
// Give public key to a another party...
|
||||||
// and then they can do...
|
// and then they can do...
|
||||||
let tag = tagging_key.generate_tag();
|
let tag = tagging_key.generate_tag(&mut rng);
|
||||||
|
|
||||||
// An example using JSON serialization...see serde doc for other formats:
|
// An example using JSON serialization...see serde doc for other formats:
|
||||||
let serialized_tag = serde_json::to_string(&tag).unwrap();
|
let serialized_tag = serde_json::to_string(&tag).unwrap();
|
||||||
|
@ -183,6 +193,14 @@ We use [criterion](https://crates.io/crates/criterion) for benchmarking, and ben
|
||||||
|
|
||||||
Results will be in `target/criterion/report/index.html`.
|
Results will be in `target/criterion/report/index.html`.
|
||||||
|
|
||||||
|
### AVX2
|
||||||
|
|
||||||
|
This crate has support for the avx2 under the feature `simd`, to take advantage of this feature it is
|
||||||
|
necessary to build with `RUSTFLAGS="-C target_feature=+avx2"` e.g.
|
||||||
|
|
||||||
|
`env RUSTFLAGS="-C target_feature=+avx2" cargo test --release --features "bulk_verify,entangled,simd"`
|
||||||
|
|
||||||
|
This results in a 40%+ performance improvements on the provided benchmarks.
|
||||||
|
|
||||||
## Credits and Contributions
|
## Credits and Contributions
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
|
use fuzzytags::{RootSecret, TaggingKey};
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
fn benchmark_entangled(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("entangling");
|
||||||
|
group.measurement_time(Duration::new(10, 0));
|
||||||
|
group.sample_size(10);
|
||||||
|
let mut rng = OsRng::default();
|
||||||
|
for p in [24].iter() {
|
||||||
|
let secret_key_1 = RootSecret::<24>::generate(&mut rng);
|
||||||
|
let secret_key_2 = RootSecret::<24>::generate(&mut rng);
|
||||||
|
let public_key_1 = secret_key_1.tagging_key();
|
||||||
|
let public_key_2 = secret_key_2.tagging_key();
|
||||||
|
group.bench_with_input(BenchmarkId::from_parameter(p), p, |b, _gamma| {
|
||||||
|
b.iter(|| {
|
||||||
|
TaggingKey::generate_entangled_tag(
|
||||||
|
vec![public_key_1.clone(), public_key_2.clone()],
|
||||||
|
&mut rng,
|
||||||
|
*p,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, benchmark_entangled);
|
||||||
|
criterion_main!(benches);
|
|
@ -1,15 +1,19 @@
|
||||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
use fuzzytags::RootSecret;
|
use fuzzytags::{RootSecret, TaggingKey};
|
||||||
|
use rand::rngs::OsRng;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
fn benchmark_generate_tag(c: &mut Criterion) {
|
fn benchmark_generate_tag(c: &mut Criterion) {
|
||||||
let mut group = c.benchmark_group("generate_tags");
|
let mut group = c.benchmark_group("generate_tags");
|
||||||
group.measurement_time(Duration::new(10, 0));
|
group.measurement_time(Duration::new(10, 0));
|
||||||
group.sample_size(1000);
|
group.sample_size(1000);
|
||||||
let secret_key = RootSecret::<24>::generate();
|
let mut rng = OsRng::default();
|
||||||
|
let secret_key = RootSecret::<24>::generate(&mut rng);
|
||||||
|
let public_key = secret_key.tagging_key();
|
||||||
for p in [5, 10, 15].iter() {
|
for p in [5, 10, 15].iter() {
|
||||||
let public_key = secret_key.tagging_key();
|
group.bench_with_input(BenchmarkId::from_parameter(p), p, |b, _gamma| {
|
||||||
group.bench_with_input(BenchmarkId::from_parameter(p), p, |b, _gamma| b.iter(|| public_key.generate_tag()));
|
b.iter(|| public_key.generate_tag(&mut rng))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,12 +21,16 @@ fn benchmark_test_tag(c: &mut Criterion) {
|
||||||
let mut group = c.benchmark_group("test_tags");
|
let mut group = c.benchmark_group("test_tags");
|
||||||
group.measurement_time(Duration::new(10, 0));
|
group.measurement_time(Duration::new(10, 0));
|
||||||
group.sample_size(1000);
|
group.sample_size(1000);
|
||||||
let secret_key = RootSecret::<24>::generate();
|
let mut rng = OsRng::default();
|
||||||
|
|
||||||
for p in [5, 10, 15].iter() {
|
let secret_key = RootSecret::<24>::generate(&mut rng);
|
||||||
let tag = secret_key.tagging_key().generate_tag();
|
|
||||||
|
for p in [5, 10, 15, 24].iter() {
|
||||||
let detection_key = secret_key.extract_detection_key(*p);
|
let detection_key = secret_key.extract_detection_key(*p);
|
||||||
group.bench_with_input(BenchmarkId::from_parameter(p), p, |b, _gamma| b.iter(|| detection_key.test_tag(&tag)));
|
group.bench_with_input(BenchmarkId::from_parameter(p), p, |b, _gamma| {
|
||||||
|
let tag = secret_key.tagging_key().generate_tag(&mut rng);
|
||||||
|
b.iter(|| detection_key.test_tag(&tag))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
rustfmt.toml
11
rustfmt.toml
|
@ -2,8 +2,15 @@ max_width = 200
|
||||||
hard_tabs = false
|
hard_tabs = false
|
||||||
tab_spaces = 4
|
tab_spaces = 4
|
||||||
newline_style = "Auto"
|
newline_style = "Auto"
|
||||||
use_small_heuristics = "Default"
|
|
||||||
indent_style = "Block"
|
indent_style = "Block"
|
||||||
|
use_small_heuristics = "Default"
|
||||||
|
fn_call_width = 60
|
||||||
|
attr_fn_like_width = 70
|
||||||
|
struct_lit_width = 18
|
||||||
|
struct_variant_width = 35
|
||||||
|
array_width = 60
|
||||||
|
chain_width = 60
|
||||||
|
single_line_if_else_max_width = 50
|
||||||
wrap_comments = false
|
wrap_comments = false
|
||||||
format_code_in_doc_comments = false
|
format_code_in_doc_comments = false
|
||||||
comment_width = 80
|
comment_width = 80
|
||||||
|
@ -54,7 +61,7 @@ use_field_init_shorthand = false
|
||||||
force_explicit_abi = true
|
force_explicit_abi = true
|
||||||
condense_wildcard_suffixes = false
|
condense_wildcard_suffixes = false
|
||||||
color = "Auto"
|
color = "Auto"
|
||||||
required_version = "1.4.34"
|
required_version = "1.4.37"
|
||||||
unstable_features = false
|
unstable_features = false
|
||||||
disable_all_formatting = false
|
disable_all_formatting = false
|
||||||
skip_children = false
|
skip_children = false
|
||||||
|
|
292
src/lib.rs
292
src/lib.rs
|
@ -1,6 +1,5 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![feature(external_doc)]
|
#![feature(external_doc)]
|
||||||
#![feature(const_generics)]
|
|
||||||
#![doc(include = "../README.md")]
|
#![doc(include = "../README.md")]
|
||||||
#![doc(include = "../ANONYMITY.md")]
|
#![doc(include = "../ANONYMITY.md")]
|
||||||
#![doc(html_logo_url = "https://git.openprivacy.ca/openprivacy/fuzzytags/media/branch/trunk/FuzzyTags_Logo.png")]
|
#![doc(html_logo_url = "https://git.openprivacy.ca/openprivacy/fuzzytags/media/branch/trunk/FuzzyTags_Logo.png")]
|
||||||
|
@ -10,9 +9,8 @@ use curve25519_dalek::digest::Digest;
|
||||||
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
||||||
use curve25519_dalek::scalar::Scalar;
|
use curve25519_dalek::scalar::Scalar;
|
||||||
use curve25519_dalek::traits::MultiscalarMul;
|
use curve25519_dalek::traits::MultiscalarMul;
|
||||||
use rand::rngs::OsRng;
|
|
||||||
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use sha3::Sha3_512;
|
use sha3::{Sha3_256, Sha3_512};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
@ -23,6 +21,7 @@ use brute_force::adaptors;
|
||||||
#[cfg(feature = "entangled")]
|
#[cfg(feature = "entangled")]
|
||||||
use brute_force::brute_force;
|
use brute_force::brute_force;
|
||||||
|
|
||||||
|
use rand_core::{CryptoRng, RngCore};
|
||||||
#[cfg(feature = "bulk_verify")]
|
#[cfg(feature = "bulk_verify")]
|
||||||
use rayon::iter::IndexedParallelIterator;
|
use rayon::iter::IndexedParallelIterator;
|
||||||
#[cfg(feature = "bulk_verify")]
|
#[cfg(feature = "bulk_verify")]
|
||||||
|
@ -81,7 +80,10 @@ impl<'de, const GAMMA: u8> Deserialize<'de> for Tag<{ GAMMA }> {
|
||||||
{
|
{
|
||||||
let mut bytes = vec![];
|
let mut bytes = vec![];
|
||||||
for i in 0..64 {
|
for i in 0..64 {
|
||||||
bytes.push(seq.next_element()?.ok_or(serde::de::Error::invalid_length(i, &"expected at least 64 bytes"))?);
|
bytes.push(seq.next_element()?.ok_or(serde::de::Error::invalid_length(
|
||||||
|
i,
|
||||||
|
&"expected at least 64 bytes",
|
||||||
|
))?);
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
match seq.next_element().unwrap_or(None) {
|
match seq.next_element().unwrap_or(None) {
|
||||||
|
@ -104,15 +106,17 @@ impl<const GAMMA: u8> Tag<{ GAMMA }> {
|
||||||
/// Ciphertext is right-padded with zeros to the nearest byte
|
/// Ciphertext is right-padded with zeros to the nearest byte
|
||||||
/// You probably want to use one of the many serde `serialize` apis instead (see README)
|
/// You probably want to use one of the many serde `serialize` apis instead (see README)
|
||||||
/// ```
|
/// ```
|
||||||
|
/// use rand::rngs::OsRng;
|
||||||
/// use fuzzytags::RootSecret;
|
/// use fuzzytags::RootSecret;
|
||||||
/// let secret = RootSecret::<24>::generate();
|
/// let mut rng = OsRng;
|
||||||
|
/// let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
/// let tagging_key = secret.tagging_key();
|
/// let tagging_key = secret.tagging_key();
|
||||||
/// // extract a detection key
|
/// // extract a detection key
|
||||||
/// let detection_key = secret.extract_detection_key(5);
|
/// let detection_key = secret.extract_detection_key(5);
|
||||||
///
|
///
|
||||||
/// // Give tagging key to a another party...
|
/// // Give tagging key to a another party...
|
||||||
/// // and then they can do...
|
/// // and then they can do...
|
||||||
/// let tag = tagging_key.generate_tag();
|
/// let tag = tagging_key.generate_tag(&mut rng);
|
||||||
/// let compressed_tag = tag.compress();
|
/// let compressed_tag = tag.compress();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn compress(&self) -> Vec<u8> {
|
pub fn compress(&self) -> Vec<u8> {
|
||||||
|
@ -127,14 +131,16 @@ impl<const GAMMA: u8> Tag<{ GAMMA }> {
|
||||||
/// You probably want to use one of the many serde `deserialize` apis instead (see README)
|
/// You probably want to use one of the many serde `deserialize` apis instead (see README)
|
||||||
/// ```
|
/// ```
|
||||||
/// use fuzzytags::{RootSecret, Tag};
|
/// use fuzzytags::{RootSecret, Tag};
|
||||||
/// let secret = RootSecret::<24>::generate();
|
/// use rand::rngs::OsRng;
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
/// let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
/// let tagging_key = secret.tagging_key();
|
/// let tagging_key = secret.tagging_key();
|
||||||
/// // extract a detection key
|
/// // extract a detection key
|
||||||
/// let detection_key = secret.extract_detection_key(5);
|
/// let detection_key = secret.extract_detection_key(5);
|
||||||
///
|
///
|
||||||
/// // Give tagging key to a another party...
|
/// // Give tagging key to a another party...
|
||||||
/// // and then they can do...
|
/// // and then they can do...
|
||||||
/// let tag = tagging_key.generate_tag();
|
/// let tag = tagging_key.generate_tag(&mut rng);
|
||||||
/// let compressed_tag = tag.compress();
|
/// let compressed_tag = tag.compress();
|
||||||
/// let decompressed_tag = Tag::decompress(&compressed_tag).unwrap();
|
/// let decompressed_tag = Tag::decompress(&compressed_tag).unwrap();
|
||||||
/// assert_eq!(tag, decompressed_tag);
|
/// assert_eq!(tag, decompressed_tag);
|
||||||
|
@ -157,7 +163,10 @@ impl<const GAMMA: u8> Tag<{ GAMMA }> {
|
||||||
};
|
};
|
||||||
let mut ciphertexts = BitVec::from_bytes(ciphertext);
|
let mut ciphertexts = BitVec::from_bytes(ciphertext);
|
||||||
ciphertexts.truncate(GAMMA as usize);
|
ciphertexts.truncate(GAMMA as usize);
|
||||||
return match (CompressedRistretto::from_slice(u_bytes).decompress(), Scalar::from_canonical_bytes(y_bytes_fixed)) {
|
return match (
|
||||||
|
CompressedRistretto::from_slice(u_bytes).decompress(),
|
||||||
|
Scalar::from_canonical_bytes(y_bytes_fixed),
|
||||||
|
) {
|
||||||
(Some(u), Some(y)) => Some(Tag { u, y, ciphertexts }),
|
(Some(u), Some(y)) => Some(Tag { u, y, ciphertexts }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
@ -178,6 +187,12 @@ impl<const GAMMA: u8> Display for Tag<{ GAMMA }> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// PrecomputeH is an encapsulation around the precomputation of the H function which
|
||||||
|
/// significantly speeds up testing. We define it for some additional type safety (to
|
||||||
|
/// prevent us from passing an uninitialized hash function to post_h
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PrecomputeH(Sha3_256);
|
||||||
|
|
||||||
/// The complete secret. Can't directly be used for testing. Instead you will need to generate
|
/// The complete secret. Can't directly be used for testing. Instead you will need to generate
|
||||||
/// a DetectionKey using `extract_detection_key`
|
/// a DetectionKey using `extract_detection_key`
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -194,16 +209,17 @@ impl<const GAMMA: u8> RootSecret<{ GAMMA }> {
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// use fuzzytags::{RootSecret};
|
/// use fuzzytags::{RootSecret};
|
||||||
/// let secret = RootSecret::<24>::generate();
|
/// use rand::rngs::OsRng;
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
/// let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn generate() -> RootSecret<{ GAMMA }> {
|
pub fn generate<R: RngCore + CryptoRng>(rng: &mut R) -> RootSecret<{ GAMMA }> {
|
||||||
let mut rng = OsRng::default();
|
|
||||||
let mut secret = vec![];
|
let mut secret = vec![];
|
||||||
for _i in 0..GAMMA {
|
for _i in 0..GAMMA {
|
||||||
let sk_i = Scalar::random(&mut rng);
|
let sk_i = Scalar::random(rng);
|
||||||
secret.push(sk_i);
|
secret.push(sk_i);
|
||||||
}
|
}
|
||||||
RootSecret::<GAMMA> { secret: secret }
|
RootSecret::<GAMMA> { secret }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// extract a detection key for a given false positive (p = 2^-n)
|
/// extract a detection key for a given false positive (p = 2^-n)
|
||||||
|
@ -213,7 +229,9 @@ impl<const GAMMA: u8> RootSecret<{ GAMMA }> {
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// use fuzzytags::{RootSecret};
|
/// use fuzzytags::{RootSecret};
|
||||||
/// let secret = RootSecret::<24>::generate();
|
/// use rand::rngs::OsRng;
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
/// let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
/// let detection_key = secret.extract_detection_key(2);
|
/// let detection_key = secret.extract_detection_key(2);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn extract_detection_key(&self, n: usize) -> DetectionKey<{ GAMMA }> {
|
pub fn extract_detection_key(&self, n: usize) -> DetectionKey<{ GAMMA }> {
|
||||||
|
@ -225,7 +243,9 @@ impl<const GAMMA: u8> RootSecret<{ GAMMA }> {
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// use fuzzytags::RootSecret;
|
/// use fuzzytags::RootSecret;
|
||||||
/// let secret = RootSecret::<24>::generate();
|
/// use rand::rngs::OsRng;
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
/// let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
/// let tagging_key = secret.tagging_key();
|
/// let tagging_key = secret.tagging_key();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn tagging_key(&self) -> TaggingKey<{ GAMMA }> {
|
pub fn tagging_key(&self) -> TaggingKey<{ GAMMA }> {
|
||||||
|
@ -238,14 +258,19 @@ impl<const GAMMA: u8> RootSecret<{ GAMMA }> {
|
||||||
TaggingKey::<GAMMA> { 0: tagging_key }
|
TaggingKey::<GAMMA> { 0: tagging_key }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a hash function that takes 3 ristretto points as a parameter and outputs 0 or 1.
|
/// precompute the first part of h
|
||||||
fn h(u: RistrettoPoint, h: RistrettoPoint, w: RistrettoPoint) -> u8 {
|
fn pre_h(u: RistrettoPoint, w: RistrettoPoint) -> PrecomputeH {
|
||||||
let mut hash = sha3::Sha3_256::new();
|
let mut hash = sha3::Sha3_256::new();
|
||||||
hash.update(&[GAMMA]);
|
hash.update(&[GAMMA]);
|
||||||
hash.update(u.compress().as_bytes());
|
hash.update(u.compress().as_bytes());
|
||||||
hash.update(h.compress().as_bytes());
|
|
||||||
hash.update(w.compress().as_bytes());
|
hash.update(w.compress().as_bytes());
|
||||||
return hash.finalize().as_slice()[0] & 0x01;
|
return PrecomputeH(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// compute the rest of h from a precomputed hash
|
||||||
|
fn post_h(mut hash: PrecomputeH, h: RistrettoPoint) -> u8 {
|
||||||
|
hash.0.update(h.compress().as_bytes());
|
||||||
|
return hash.0.finalize().as_slice()[0] & 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a hash function which takes a ristretto point and a vector of ciphertexts and outputs a
|
/// a hash function which takes a ristretto point and a vector of ciphertexts and outputs a
|
||||||
|
@ -286,7 +311,9 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
||||||
/// calculate the ideal false positive rate of this detection key
|
/// calculate the ideal false positive rate of this detection key
|
||||||
/// ```
|
/// ```
|
||||||
/// use fuzzytags::RootSecret;
|
/// use fuzzytags::RootSecret;
|
||||||
/// let secret = RootSecret::<24>::generate();
|
/// use rand::rngs::OsRng;
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
/// let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
/// let tagging_key = secret.tagging_key();
|
/// let tagging_key = secret.tagging_key();
|
||||||
/// // extract a detection key
|
/// // extract a detection key
|
||||||
/// let detection_key = secret.extract_detection_key(5);
|
/// let detection_key = secret.extract_detection_key(5);
|
||||||
|
@ -300,14 +327,16 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// use fuzzytags::RootSecret;
|
/// use fuzzytags::RootSecret;
|
||||||
/// let secret = RootSecret::<24>::generate();
|
/// use rand::rngs::OsRng;
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
/// let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
/// let tagging_key = secret.tagging_key();
|
/// let tagging_key = secret.tagging_key();
|
||||||
/// // extract a detection key
|
/// // extract a detection key
|
||||||
/// let detection_key = secret.extract_detection_key(5);
|
/// let detection_key = secret.extract_detection_key(5);
|
||||||
///
|
///
|
||||||
/// // Give tagging key to a another party...
|
/// // Give tagging key to a another party...
|
||||||
/// // and then they can do...
|
/// // and then they can do...
|
||||||
/// let tag = tagging_key.generate_tag();
|
/// let tag = tagging_key.generate_tag(&mut rng);
|
||||||
///
|
///
|
||||||
/// // The server can now do this:
|
/// // The server can now do this:
|
||||||
/// if detection_key.test_tag(&tag) {
|
/// if detection_key.test_tag(&tag) {
|
||||||
|
@ -338,30 +367,28 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
||||||
// See below for a full explanation as to the reason for this:
|
// See below for a full explanation as to the reason for this:
|
||||||
let w = RistrettoPoint::multiscalar_mul(&[m, tag.y], &[g, tag.u]);
|
let w = RistrettoPoint::multiscalar_mul(&[m, tag.y], &[g, tag.u]);
|
||||||
|
|
||||||
|
let pre_h = RootSecret::<GAMMA>::pre_h(tag.u, w);
|
||||||
|
|
||||||
// for each secret part...
|
// for each secret part...
|
||||||
let mut result = true;
|
let mut result = 0;
|
||||||
for (i, x_i) in self.0.iter().enumerate() {
|
for (x_i, c_i) in self.0.iter().zip(&tag.ciphertexts) {
|
||||||
// re-derive the key from the tag
|
// re-derive the key from the tag
|
||||||
let k_i = RootSecret::<GAMMA>::h(tag.u, tag.u.mul(x_i), w);
|
let k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), tag.u.mul(x_i));
|
||||||
|
|
||||||
// calculate the "original" plaintext
|
// calculate the "original" plaintext
|
||||||
let c_i = match tag.ciphertexts.get(i) {
|
let b_i = k_i ^ (c_i as u8);
|
||||||
Some(true) => 0x01,
|
// short circuit
|
||||||
Some(false) => 0x00,
|
if b_i != 0x01 {
|
||||||
_ => 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 {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// assert that the plaintext is all 1's
|
// assert that the plaintext is all 1's
|
||||||
result = result & (b_i == 1);
|
result += 1;
|
||||||
}
|
}
|
||||||
return result;
|
// Assert that number of sequential ones is equal to the length of the detection key
|
||||||
|
// If it isn't it indicates that the tag ciphertext is shorter than the verification key,
|
||||||
|
// Given the checks on deserialization that should never happen, but we throw in a check
|
||||||
|
// here anyway for defense in depth.
|
||||||
|
return result == self.0.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A bulk testing function that takes in an vector of detection keys and returns a vector
|
/// A bulk testing function that takes in an vector of detection keys and returns a vector
|
||||||
|
@ -371,10 +398,12 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
||||||
/// ```
|
/// ```
|
||||||
/// use fuzzytags::{TaggingKey, DetectionKey};
|
/// use fuzzytags::{TaggingKey, DetectionKey};
|
||||||
/// use fuzzytags::RootSecret;
|
/// use fuzzytags::RootSecret;
|
||||||
/// let secrets: Vec<RootSecret<24>> = (0..2).map(|_x| RootSecret::<24>::generate()).collect();
|
/// use rand::rngs::OsRng;
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
/// let secrets: Vec<RootSecret<24>> = (0..2).map(|_x| RootSecret::<24>::generate(&mut rng)).collect();
|
||||||
/// let tagging_keys: Vec<TaggingKey<24>> = secrets.iter().map(|x| x.tagging_key()).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
|
/// // 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 entangled_tag = TaggingKey::generate_entangled_tag(tagging_keys, &mut rng, 16);
|
||||||
/// let detection_keys = secrets.iter().map(|x| x.extract_detection_key(16)).collect();
|
/// let detection_keys = secrets.iter().map(|x| x.extract_detection_key(16)).collect();
|
||||||
///
|
///
|
||||||
/// let results = DetectionKey::test_tag_bulk(&detection_keys, &entangled_tag);
|
/// let results = DetectionKey::test_tag_bulk(&detection_keys, &entangled_tag);
|
||||||
|
@ -403,41 +432,36 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
||||||
// See below for a full explanation as to the reason for this:
|
// See below for a full explanation as to the reason for this:
|
||||||
let w = RistrettoPoint::multiscalar_mul(&[m, tag.y], &[g, tag.u]);
|
let w = RistrettoPoint::multiscalar_mul(&[m, tag.y], &[g, tag.u]);
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
let pre_h = RootSecret::<GAMMA>::pre_h(tag.u, w);
|
||||||
|
|
||||||
// for each secret part...
|
// for each secret part...
|
||||||
let mut results: Vec<usize> = vec![];
|
let mut results: Vec<usize> = vec![];
|
||||||
detection_keys.par_iter().enumerate().for_each_with(tx.clone(), |tx, (index, detection_key)| {
|
detection_keys
|
||||||
let mut result = true;
|
.par_iter()
|
||||||
for (i, x_i) in detection_key.0.iter().enumerate() {
|
.enumerate()
|
||||||
// re-derive the key from the tag
|
.for_each_with(tx.clone(), |tx, (index, detection_key)| {
|
||||||
let k_i = RootSecret::<GAMMA>::h(tag.u, tag.u.mul(x_i), w);
|
let mut result = 0;
|
||||||
|
for (x_i, c_i) in detection_key.0.iter().zip(&tag.ciphertexts) {
|
||||||
|
// re-derive the key from the tag
|
||||||
|
let k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), tag.u.mul(x_i));
|
||||||
|
|
||||||
// calculate the "original" plaintext
|
// calculate the "original" plaintext
|
||||||
let c_i = match tag.ciphertexts.get(i) {
|
let b_i = k_i ^ (c_i as u8);
|
||||||
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 {
|
||||||
|
break;
|
||||||
if b_i != 1 {
|
}
|
||||||
result = false;
|
// assert that the plaintext is all 1's
|
||||||
break;
|
result += 1;
|
||||||
}
|
}
|
||||||
// assert that the plaintext is all 1's
|
if result == detection_key.0.len() {
|
||||||
result = result & (b_i == 1);
|
match tx.send(index) {
|
||||||
}
|
_ => {
|
||||||
if result {
|
// TODO...surface this error...
|
||||||
match tx.send(index) {
|
}
|
||||||
_ => {
|
|
||||||
// TODO...surface this error...
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
std::mem::drop(tx);
|
std::mem::drop(tx);
|
||||||
loop {
|
loop {
|
||||||
|
@ -473,24 +497,28 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// use fuzzytags::{RootSecret};
|
/// use fuzzytags::{RootSecret};
|
||||||
/// let secret = RootSecret::<24>::generate();
|
/// use rand::rngs::OsRng;
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
/// let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
/// let tagging_key = secret.tagging_key(); // give this to a sender
|
/// let tagging_key = secret.tagging_key(); // give this to a sender
|
||||||
/// let tag = tagging_key.generate_tag();
|
/// let tag = tagging_key.generate_tag(&mut rng);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn generate_tag(&self) -> Tag<{ GAMMA }> {
|
pub fn generate_tag<R: RngCore + CryptoRng>(&self, rng: &mut R) -> Tag<{ GAMMA }> {
|
||||||
let mut rng = OsRng::default();
|
|
||||||
let g = RISTRETTO_BASEPOINT_POINT;
|
|
||||||
|
|
||||||
// generate some random points...
|
// generate some random points...
|
||||||
let r = Scalar::random(&mut rng);
|
let r = Scalar::random(rng);
|
||||||
let u = g.mul(r);
|
let u = RISTRETTO_BASEPOINT_POINT.mul(r);
|
||||||
let z = Scalar::random(&mut rng);
|
|
||||||
let w = g.mul(z);
|
let z = Scalar::random(rng);
|
||||||
|
let w = RISTRETTO_BASEPOINT_POINT.mul(z);
|
||||||
|
|
||||||
|
// precompute the first part of the `H` hash function
|
||||||
|
let pre_h = RootSecret::<GAMMA>::pre_h(u, w);
|
||||||
|
|
||||||
// construct the ciphertext portion of the tag
|
// construct the ciphertext portion of the tag
|
||||||
let mut ciphertexts = BitVec::new();
|
let mut ciphertexts = BitVec::with_capacity(GAMMA.into());
|
||||||
for (_i, h_i) in self.0.iter().enumerate() {
|
|
||||||
let k_i = RootSecret::<GAMMA>::h(u, h_i.mul(r), w);
|
for h_i in self.0.iter() {
|
||||||
|
let k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), h_i.mul(r));
|
||||||
// encrypt a plaintext of all 1's
|
// encrypt a plaintext of all 1's
|
||||||
let c_i = k_i ^ 0x01;
|
let c_i = k_i ^ 0x01;
|
||||||
ciphertexts.push(c_i == 0x01);
|
ciphertexts.push(c_i == 0x01);
|
||||||
|
@ -524,20 +552,21 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// use fuzzytags::{RootSecret, TaggingKey};
|
/// use fuzzytags::{RootSecret, TaggingKey};
|
||||||
/// let secret_1 = RootSecret::<24>::generate();
|
/// use rand::rngs::OsRng;
|
||||||
/// let secret_2 = RootSecret::<24>::generate();
|
/// let mut rng = OsRng;
|
||||||
|
/// let secret_1 = RootSecret::<24>::generate(&mut rng);
|
||||||
|
/// let secret_2 = RootSecret::<24>::generate(&mut rng);
|
||||||
/// let tagging_key_1 = secret_1.tagging_key(); // give this to a sender
|
/// let tagging_key_1 = secret_1.tagging_key(); // give this to a sender
|
||||||
/// let tagging_key_2 = secret_2.tagging_key(); // give this to a sender
|
/// let tagging_key_2 = secret_2.tagging_key(); // give this to a sender
|
||||||
/// // Will validate for detection keys derived from both secret_1 and secret_2 up
|
/// // Will validate for detection keys derived from both secret_1 and secret_2 up
|
||||||
/// // to n=8
|
/// // to n=8
|
||||||
/// // Sender can now do...tag will validate on detection keys of length 8 or lower.
|
/// // Sender can now do...tag will validate on detection keys of length 8 or lower.
|
||||||
/// let tag = TaggingKey::generate_entangled_tag(vec![tagging_key_1,tagging_key_2], 8);
|
/// let tag = TaggingKey::generate_entangled_tag(vec![tagging_key_1,tagging_key_2], &mut rng, 8);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn generate_entangled_tag(tagging_keys: Vec<TaggingKey<{ GAMMA }>>, length: usize) -> Tag<{ GAMMA }> {
|
pub fn generate_entangled_tag<R: RngCore + CryptoRng>(tagging_keys: Vec<TaggingKey<{ GAMMA }>>, rng: &mut R, length: usize) -> Tag<{ GAMMA }> {
|
||||||
let mut rng = OsRng::default();
|
|
||||||
let g = RISTRETTO_BASEPOINT_POINT;
|
let g = RISTRETTO_BASEPOINT_POINT;
|
||||||
// generate some random points...
|
// generate some random points...
|
||||||
let r = Scalar::random(&mut rng);
|
let r = Scalar::random(rng);
|
||||||
let u = g.mul(r);
|
let u = g.mul(r);
|
||||||
|
|
||||||
// Compute and cache some public points that we will be using over and over again
|
// Compute and cache some public points that we will be using over and over again
|
||||||
|
@ -551,14 +580,16 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = brute_force::Config::default();
|
let config = brute_force::Config::default();
|
||||||
|
|
||||||
let f = |z: &Scalar| {
|
let f = |z: &Scalar| {
|
||||||
let w = g.mul(z);
|
let w = g.mul(z);
|
||||||
|
let pre_h = RootSecret::<GAMMA>::pre_h(u, w);
|
||||||
let mut key = vec![];
|
let mut key = vec![];
|
||||||
for (i, precompute) in tagging_key_precomputes[0].iter().enumerate() {
|
for (i, precompute) in tagging_key_precomputes[0].iter().enumerate() {
|
||||||
let k_i = RootSecret::<GAMMA>::h(u, *precompute, w);
|
let k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), *precompute);
|
||||||
if i < length {
|
if i < length {
|
||||||
for precompute in tagging_key_precomputes.iter().skip(1) {
|
for precompute in tagging_key_precomputes.iter().skip(1) {
|
||||||
let n_k_i = RootSecret::<GAMMA>::h(u, precompute[i], w);
|
let n_k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), precompute[i]);
|
||||||
if k_i != n_k_i {
|
if k_i != n_k_i {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -590,15 +621,18 @@ mod tests {
|
||||||
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;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use sha3::Digest;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_compression() {
|
fn test_compression() {
|
||||||
let secret = RootSecret::<24>::generate();
|
let mut rng = OsRng;
|
||||||
|
let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
let tagging_key = secret.tagging_key();
|
let tagging_key = secret.tagging_key();
|
||||||
|
|
||||||
// Give tagging key to a another party...
|
// Give tagging key to a another party...
|
||||||
// and then they can do...
|
// and then they can do...
|
||||||
let tag = tagging_key.generate_tag();
|
let tag = tagging_key.generate_tag(&mut rng);
|
||||||
let compressed_tag = tag.compress();
|
let compressed_tag = tag.compress();
|
||||||
let decompressed_tag = Tag::<24>::decompress(&compressed_tag).unwrap();
|
let decompressed_tag = Tag::<24>::decompress(&compressed_tag).unwrap();
|
||||||
assert_eq!(tag, decompressed_tag);
|
assert_eq!(tag, decompressed_tag);
|
||||||
|
@ -607,8 +641,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialization() {
|
fn test_serialization() {
|
||||||
// generate some new keys...
|
// generate some new keys...
|
||||||
let secret = RootSecret::<15>::generate();
|
let mut rng = OsRng;
|
||||||
let tag = secret.tagging_key().generate_tag();
|
let secret = RootSecret::<15>::generate(&mut rng);
|
||||||
|
let tag = secret.tagging_key().generate_tag(&mut rng);
|
||||||
let detection_key = secret.extract_detection_key(10);
|
let detection_key = secret.extract_detection_key(10);
|
||||||
let serialized_tag = serde_json::to_string(&tag).unwrap();
|
let serialized_tag = serde_json::to_string(&tag).unwrap();
|
||||||
println!("{}", serialized_tag);
|
println!("{}", serialized_tag);
|
||||||
|
@ -617,8 +652,8 @@ mod tests {
|
||||||
assert_eq!(true, detection_key.test_tag(&deserialized_tag));
|
assert_eq!(true, detection_key.test_tag(&deserialized_tag));
|
||||||
|
|
||||||
// generate some new keys...
|
// generate some new keys...
|
||||||
let secret = RootSecret::<24>::generate();
|
let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
let tag = secret.tagging_key().generate_tag();
|
let tag = secret.tagging_key().generate_tag(&mut rng);
|
||||||
let detection_key = secret.extract_detection_key(10);
|
let detection_key = secret.extract_detection_key(10);
|
||||||
let serialized_tag = serde_json::to_string(&tag).unwrap();
|
let serialized_tag = serde_json::to_string(&tag).unwrap();
|
||||||
let deserialized_tag: Tag<24> = serde_json::from_str(&serialized_tag).unwrap();
|
let deserialized_tag: Tag<24> = serde_json::from_str(&serialized_tag).unwrap();
|
||||||
|
@ -654,13 +689,18 @@ mod tests {
|
||||||
#[cfg(feature = "entangled")]
|
#[cfg(feature = "entangled")]
|
||||||
fn test_multiple() {
|
fn test_multiple() {
|
||||||
use crate::TaggingKey;
|
use crate::TaggingKey;
|
||||||
let secrets: Vec<RootSecret<24>> = (0..2).map(|_x| RootSecret::<24>::generate()).collect();
|
let mut rng = OsRng;
|
||||||
|
let secrets: Vec<RootSecret<24>> = (0..2)
|
||||||
|
.map(|_x| RootSecret::<24>::generate(&mut rng))
|
||||||
|
.collect();
|
||||||
let tagging_keys: Vec<TaggingKey<24>> = secrets.iter().map(|x| x.tagging_key()).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);
|
// it takes ~2 minutes on a standard desktop to find a length=24 match for 2 parties, so for testing let's keep things light
|
||||||
|
let len = 16;
|
||||||
|
let entangled_tag = TaggingKey::generate_entangled_tag(tagging_keys, &mut rng, len);
|
||||||
println!("{}", entangled_tag);
|
println!("{}", entangled_tag);
|
||||||
for secret in secrets.iter() {
|
for secret in secrets.iter() {
|
||||||
let detection_key = secret.extract_detection_key(16);
|
let detection_key = secret.extract_detection_key(len);
|
||||||
assert!(detection_key.test_tag(&entangled_tag));
|
assert!(detection_key.test_tag(&entangled_tag));
|
||||||
println!("{}", detection_key);
|
println!("{}", detection_key);
|
||||||
}
|
}
|
||||||
|
@ -670,11 +710,17 @@ mod tests {
|
||||||
#[cfg(feature = "bulk_verify")]
|
#[cfg(feature = "bulk_verify")]
|
||||||
fn test_check_multiple() {
|
fn test_check_multiple() {
|
||||||
use crate::TaggingKey;
|
use crate::TaggingKey;
|
||||||
let secrets: Vec<RootSecret<24>> = (0..2).map(|_x| RootSecret::<24>::generate()).collect();
|
let mut rng = OsRng;
|
||||||
|
let secrets: Vec<RootSecret<24>> = (0..2)
|
||||||
|
.map(|_x| RootSecret::<24>::generate(&mut rng))
|
||||||
|
.collect();
|
||||||
let tagging_keys: Vec<TaggingKey<24>> = secrets.iter().map(|x| x.tagging_key()).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
|
// it takes ~2 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 entangled_tag = TaggingKey::generate_entangled_tag(tagging_keys, &mut rng, 16);
|
||||||
let detection_keys = secrets.iter().map(|x| x.extract_detection_key(16)).collect();
|
let detection_keys = secrets
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.extract_detection_key(16))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let results = DetectionKey::test_tag_bulk(&detection_keys, &entangled_tag);
|
let results = DetectionKey::test_tag_bulk(&detection_keys, &entangled_tag);
|
||||||
assert_eq!(results.len(), 2);
|
assert_eq!(results.len(), 2);
|
||||||
|
@ -683,9 +729,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn correctness() {
|
fn correctness() {
|
||||||
let number_of_messages = 100;
|
let number_of_messages = 100;
|
||||||
let secret = RootSecret::<16>::generate();
|
let mut rng = OsRng;
|
||||||
|
let secret = RootSecret::<16>::generate(&mut rng);
|
||||||
for i in 0..number_of_messages {
|
for i in 0..number_of_messages {
|
||||||
let tag = secret.tagging_key().generate_tag();
|
let tag = secret.tagging_key().generate_tag(&mut rng);
|
||||||
println!("{}: {}", i, tag);
|
println!("{}: {}", i, tag);
|
||||||
assert!(secret.extract_detection_key(5).test_tag(&tag));
|
assert!(secret.extract_detection_key(5).test_tag(&tag));
|
||||||
}
|
}
|
||||||
|
@ -710,13 +757,39 @@ mod tests {
|
||||||
tag
|
tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a hash function that takes 3 ristretto points as a parameter and outputs 0 or 1.
|
||||||
|
fn h(u: RistrettoPoint, h: RistrettoPoint, w: RistrettoPoint) -> u8 {
|
||||||
|
let mut hash = sha3::Sha3_256::new();
|
||||||
|
hash.update(&[24]);
|
||||||
|
hash.update(u.compress().as_bytes());
|
||||||
|
hash.update(w.compress().as_bytes());
|
||||||
|
hash.update(h.compress().as_bytes());
|
||||||
|
return hash.finalize().as_slice()[0] & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assert_h_and_pre_post_h() {
|
||||||
|
let mut rng = OsRng;
|
||||||
|
|
||||||
|
for _ in 0..100 {
|
||||||
|
let a = RistrettoPoint::random(&mut rng);
|
||||||
|
let b = RistrettoPoint::random(&mut rng);
|
||||||
|
let c = RistrettoPoint::random(&mut rng);
|
||||||
|
assert_eq!(
|
||||||
|
RootSecret::<24>::post_h(RootSecret::<24>::pre_h(a, b), c),
|
||||||
|
h(a, c, b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// Thanks to Lee Bousfield who noticed an all zeros or all ones tag would
|
// Thanks to Lee Bousfield who noticed an all zeros or all ones tag would
|
||||||
// validate against a tagging key with 50% probability, allowing universal
|
// validate against a tagging key with 50% probability, allowing universal
|
||||||
// broadcast, which overall seems like a bad idea...
|
// broadcast, which overall seems like a bad idea...
|
||||||
// Test to make sure that doesn't happen.
|
// Test to make sure that doesn't happen.
|
||||||
fn test_zero_tag() {
|
fn test_zero_tag() {
|
||||||
let secret = RootSecret::<24>::generate();
|
let mut rng = OsRng;
|
||||||
|
let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
let tag = gen_zero_tag_zero();
|
let tag = gen_zero_tag_zero();
|
||||||
assert_eq!(false, secret.extract_detection_key(6).test_tag(&tag));
|
assert_eq!(false, secret.extract_detection_key(6).test_tag(&tag));
|
||||||
let tag = gen_zero_tag_one();
|
let tag = gen_zero_tag_one();
|
||||||
|
@ -725,12 +798,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn false_positives() {
|
fn false_positives() {
|
||||||
|
let mut rng = OsRng;
|
||||||
let number_of_messages = 1000;
|
let number_of_messages = 1000;
|
||||||
let secret = RootSecret::<24>::generate();
|
let secret = RootSecret::<24>::generate(&mut rng);
|
||||||
let mut false_positives = 0;
|
let mut false_positives = 0;
|
||||||
for _i in 0..number_of_messages {
|
for _i in 0..number_of_messages {
|
||||||
let secret2 = RootSecret::<24>::generate();
|
let secret2 = RootSecret::<24>::generate(&mut rng);
|
||||||
let tag = secret2.tagging_key().generate_tag();
|
let tag = secret2.tagging_key().generate_tag(&mut rng);
|
||||||
assert!(secret2.extract_detection_key(3).test_tag(&tag));
|
assert!(secret2.extract_detection_key(3).test_tag(&tag));
|
||||||
if secret.extract_detection_key(3).test_tag(&tag) == true {
|
if secret.extract_detection_key(3).test_tag(&tag) == true {
|
||||||
false_positives += 1;
|
false_positives += 1;
|
||||||
|
|
Loading…
Reference in New Issue