From 2d73a43d7fef2ad3c0efeb93abc39a34bccd2da9 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 20 May 2021 01:53:33 -0700 Subject: [PATCH] Clean up benchmarks, move RNG to function parameters + optimize generation --- README.md | 16 ++++++-- benches/entangled.rs | 4 +- benches/fuzzy_tags_benches.rs | 11 ++++-- src/lib.rs | 73 ++++++++++++++++++++++------------- 4 files changed, 68 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 8d595c6..438cc3d 100644 --- a/README.md +++ b/README.md @@ -104,12 +104,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: use fuzzytags::RootSecret; + use rand::rngs::OsRng; let secret = RootSecret::<24>::generate(); let tagging_key = secret.tagging_key(); // Give public key to a another party... // and then they can do... - let tag = tagging_key.generate_tag(); + let mut rng = OsRng::default(); + let tag = tagging_key.generate_tag(&mut rng); These tags can then be attached to a message in a metadata resistant system. @@ -120,6 +122,7 @@ 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.: use fuzzytags::RootSecret; + use rand::rngs::OsRng; let secret = RootSecret::<24>::generate(); let tagging_key = secret.tagging_key(); // extract a detection key @@ -127,7 +130,8 @@ This extracted key can then be given to an adversarial server. The server can th // Give the tagging key to a another party... // and then they can do... - let tag = tagging_key.generate_tag(); + let mut rng = OsRng::default(); + let tag = tagging_key.generate_tag(&mut rng); // The server can now do this: if detection_key.test_tag(&tag) { @@ -144,6 +148,8 @@ opens up applications like **multiple broadcast** and **deniable sending**. use fuzzytags::{RootSecret, TaggingKey}; + use rand::rngs::OsRng; + let mut rng = OsRng::default(); let secret_1 = RootSecret::<24>::generate(); let secret_2 = RootSecret::<24>::generate(); let tagging_key_1 = secret_1.tagging_key(); // give this to a sender @@ -151,7 +157,7 @@ opens up applications like **multiple broadcast** and **deniable sending**. // Will validate for detection keys derived from both secret_1 and secret_2 up // to n=8 #[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 @@ -161,13 +167,15 @@ of different approaches e.g.: use fuzzytags::RootSecret; use fuzzytags::Tag; + use rand::rngs::OsRng; + let mut rng = OsRng::default(); let secret = RootSecret::<24>::generate(); let tagging_key = secret.tagging_key(); // Give public key to a another party... // 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: let serialized_tag = serde_json::to_string(&tag).unwrap(); diff --git a/benches/entangled.rs b/benches/entangled.rs index c28cadf..9e93feb 100644 --- a/benches/entangled.rs +++ b/benches/entangled.rs @@ -4,8 +4,8 @@ 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); + group.measurement_time(Duration::new(5000, 0)); + group.sample_size(50); let secret_key_1 = RootSecret::<24>::generate(); let secret_key_2 = RootSecret::<24>::generate(); let public_key_1 = secret_key_1.tagging_key(); diff --git a/benches/fuzzy_tags_benches.rs b/benches/fuzzy_tags_benches.rs index 065bc77..1597a24 100644 --- a/benches/fuzzy_tags_benches.rs +++ b/benches/fuzzy_tags_benches.rs @@ -1,5 +1,6 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use fuzzytags::{RootSecret, TaggingKey}; +use rand::rngs::OsRng; use std::time::Duration; fn benchmark_generate_tag(c: &mut Criterion) { @@ -7,10 +8,11 @@ fn benchmark_generate_tag(c: &mut Criterion) { group.measurement_time(Duration::new(10, 0)); group.sample_size(1000); let secret_key = RootSecret::<24>::generate(); + let mut rng = OsRng::default(); + let public_key = secret_key.tagging_key(); 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| { - b.iter(|| public_key.generate_tag()) + b.iter(|| public_key.generate_tag(&mut rng)) }); } } @@ -20,11 +22,12 @@ fn benchmark_test_tag(c: &mut Criterion) { group.measurement_time(Duration::new(10, 0)); group.sample_size(1000); let secret_key = RootSecret::<24>::generate(); + let mut rng = OsRng::default(); - for p in [5, 10, 15].iter() { - 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); 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)) }); } diff --git a/src/lib.rs b/src/lib.rs index acb84ae..a3226a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::traits::MultiscalarMul; use rand::rngs::OsRng; +use rand::{CryptoRng, RngCore}; use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use sha3::{Sha3_256, Sha3_512}; use std::convert::TryFrom; @@ -107,6 +108,7 @@ impl Tag<{ GAMMA }> { /// 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) /// ``` + /// use rand::rngs::OsRng; /// use fuzzytags::RootSecret; /// let secret = RootSecret::<24>::generate(); /// let tagging_key = secret.tagging_key(); @@ -115,7 +117,8 @@ impl Tag<{ GAMMA }> { /// /// // Give tagging key to a another party... /// // and then they can do... - /// let tag = tagging_key.generate_tag(); + /// let mut rng = OsRng::default(); + /// let tag = tagging_key.generate_tag(&mut rng); /// let compressed_tag = tag.compress(); /// ``` pub fn compress(&self) -> Vec { @@ -137,7 +140,9 @@ impl Tag<{ GAMMA }> { /// /// // Give tagging key to a another party... /// // and then they can do... - /// let tag = tagging_key.generate_tag(); + /// use rand::rngs::OsRng; + /// let mut rng = OsRng::default(); + /// let tag = tagging_key.generate_tag(&mut rng); /// let compressed_tag = tag.compress(); /// let decompressed_tag = Tag::decompress(&compressed_tag).unwrap(); /// assert_eq!(tag, decompressed_tag); @@ -334,7 +339,9 @@ impl DetectionKey<{ GAMMA }> { /// /// // Give tagging key to a another party... /// // and then they can do... - /// let tag = tagging_key.generate_tag(); + /// use rand::rngs::OsRng; + /// let mut rng = OsRng::default(); + /// let tag = tagging_key.generate_tag(&mut rng); /// /// // The server can now do this: /// if detection_key.test_tag(&tag) { @@ -399,7 +406,9 @@ impl DetectionKey<{ GAMMA }> { /// let secrets: Vec> = (0..2).map(|_x| RootSecret::<24>::generate()).collect(); /// let tagging_keys: Vec> = 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); + /// use rand::rngs::OsRng; + /// let mut rng = OsRng::default(); + /// 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 results = DetectionKey::test_tag_bulk(&detection_keys, &entangled_tag); @@ -495,21 +504,25 @@ impl TaggingKey<{ GAMMA }> { /// use fuzzytags::{RootSecret}; /// let secret = RootSecret::<24>::generate(); /// let tagging_key = secret.tagging_key(); // give this to a sender - /// let tag = tagging_key.generate_tag(); + /// use rand::rngs::OsRng; + /// let mut rng = OsRng::default(); + /// let tag = tagging_key.generate_tag(&mut rng); /// ``` - pub fn generate_tag(&self) -> Tag<{ GAMMA }> { - let mut rng = OsRng::default(); - let g = RISTRETTO_BASEPOINT_POINT; - + pub fn generate_tag(&self, rng: &mut R) -> Tag<{ GAMMA }> { // generate some random points... - let r = Scalar::random(&mut rng); - let u = g.mul(r); - let z = Scalar::random(&mut rng); - let w = g.mul(z); + let r = Scalar::random(rng); + let u = RISTRETTO_BASEPOINT_POINT.mul(r); + + 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::::pre_h(u, w); + // construct the ciphertext portion of the tag - let mut ciphertexts = BitVec::new(); - for (_i, h_i) in self.0.iter().enumerate() { + let mut ciphertexts = BitVec::with_capacity(GAMMA.into()); + + for h_i in self.0.iter() { let k_i = RootSecret::::post_h(pre_h.clone(), h_i.mul(r)); // encrypt a plaintext of all 1's let c_i = k_i ^ 0x01; @@ -551,13 +564,14 @@ impl TaggingKey<{ GAMMA }> { /// // Will validate for detection keys derived from both secret_1 and secret_2 up /// // to n=8 /// // 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); + /// use rand::rngs::OsRng; + /// let mut rng = OsRng::default(); + /// let tag = TaggingKey::generate_entangled_tag(vec![tagging_key_1,tagging_key_2], &mut rng, 8); /// ``` - pub fn generate_entangled_tag(tagging_keys: Vec>, length: usize) -> Tag<{ GAMMA }> { - let mut rng = OsRng::default(); + pub fn generate_entangled_tag(tagging_keys: Vec>, rng: &mut R, length: usize) -> Tag<{ GAMMA }> { let g = RISTRETTO_BASEPOINT_POINT; // generate some random points... - let r = Scalar::random(&mut rng); + let r = Scalar::random(rng); let u = g.mul(r); // Compute and cache some public points that we will be using over and over again @@ -621,7 +635,8 @@ mod tests { // Give tagging key to a another party... // and then they can do... - let tag = tagging_key.generate_tag(); + let mut rng = OsRng::default(); + let tag = tagging_key.generate_tag(&mut rng); let compressed_tag = tag.compress(); let decompressed_tag = Tag::<24>::decompress(&compressed_tag).unwrap(); assert_eq!(tag, decompressed_tag); @@ -630,8 +645,10 @@ mod tests { #[test] fn test_serialization() { // generate some new keys... + let secret = RootSecret::<15>::generate(); - let tag = secret.tagging_key().generate_tag(); + let mut rng = OsRng::default(); + let tag = secret.tagging_key().generate_tag(&mut rng); let detection_key = secret.extract_detection_key(10); let serialized_tag = serde_json::to_string(&tag).unwrap(); println!("{}", serialized_tag); @@ -641,7 +658,7 @@ mod tests { // generate some new keys... let secret = RootSecret::<24>::generate(); - 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 serialized_tag = serde_json::to_string(&tag).unwrap(); let deserialized_tag: Tag<24> = serde_json::from_str(&serialized_tag).unwrap(); @@ -680,7 +697,8 @@ mod tests { let secrets: Vec> = (0..2).map(|_x| RootSecret::<24>::generate()).collect(); let tagging_keys: Vec> = 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 mut rng = OsRng::default(); + let entangled_tag = TaggingKey::generate_entangled_tag(tagging_keys, &mut rng, 16); println!("{}", entangled_tag); for secret in secrets.iter() { let detection_key = secret.extract_detection_key(16); @@ -696,7 +714,8 @@ mod tests { let secrets: Vec> = (0..2).map(|_x| RootSecret::<24>::generate()).collect(); let tagging_keys: Vec> = 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 mut rng = OsRng::default(); + let entangled_tag = TaggingKey::generate_entangled_tag(tagging_keys, &mut rng, 16); let detection_keys = secrets .iter() .map(|x| x.extract_detection_key(16)) @@ -710,8 +729,9 @@ mod tests { fn correctness() { let number_of_messages = 100; let secret = RootSecret::<16>::generate(); + let mut rng = OsRng::default(); 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); assert!(secret.extract_detection_key(5).test_tag(&tag)); } @@ -769,9 +789,10 @@ mod tests { let number_of_messages = 1000; let secret = RootSecret::<24>::generate(); let mut false_positives = 0; + let mut rng = OsRng::default(); for _i in 0..number_of_messages { let secret2 = RootSecret::<24>::generate(); - 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)); if secret.extract_detection_key(3).test_tag(&tag) == true { false_positives += 1;