Clean up benchmarks, move RNG to function parameters + optimize generation

This commit is contained in:
Sarah Jamie Lewis 2021-05-20 01:53:33 -07:00
parent 1124ffc1a6
commit 2d73a43d7f
4 changed files with 68 additions and 36 deletions

View File

@ -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: 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;
use rand::rngs::OsRng;
let secret = RootSecret::<24>::generate(); let secret = RootSecret::<24>::generate();
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 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. 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.: 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;
use rand::rngs::OsRng;
let secret = RootSecret::<24>::generate(); let secret = RootSecret::<24>::generate();
let tagging_key = secret.tagging_key(); let tagging_key = secret.tagging_key();
// extract a detection 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... // 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 mut rng = OsRng::default();
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,6 +148,8 @@ opens up applications like **multiple broadcast** and **deniable sending**.
use fuzzytags::{RootSecret, TaggingKey}; use fuzzytags::{RootSecret, TaggingKey};
use rand::rngs::OsRng;
let mut rng = OsRng::default();
let secret_1 = RootSecret::<24>::generate(); let secret_1 = RootSecret::<24>::generate();
let secret_2 = RootSecret::<24>::generate(); let secret_2 = RootSecret::<24>::generate();
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
@ -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 // 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 +167,15 @@ of different approaches e.g.:
use fuzzytags::RootSecret; use fuzzytags::RootSecret;
use fuzzytags::Tag; use fuzzytags::Tag;
use rand::rngs::OsRng;
let mut rng = OsRng::default();
let secret = RootSecret::<24>::generate(); let secret = RootSecret::<24>::generate();
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();

View File

@ -4,8 +4,8 @@ use std::time::Duration;
fn benchmark_entangled(c: &mut Criterion) { fn benchmark_entangled(c: &mut Criterion) {
let mut group = c.benchmark_group("entangling"); let mut group = c.benchmark_group("entangling");
group.measurement_time(Duration::new(10, 0)); group.measurement_time(Duration::new(5000, 0));
group.sample_size(10); group.sample_size(50);
let secret_key_1 = RootSecret::<24>::generate(); let secret_key_1 = RootSecret::<24>::generate();
let secret_key_2 = RootSecret::<24>::generate(); let secret_key_2 = RootSecret::<24>::generate();
let public_key_1 = secret_key_1.tagging_key(); let public_key_1 = secret_key_1.tagging_key();

View File

@ -1,5 +1,6 @@
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use fuzzytags::{RootSecret, TaggingKey}; 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) {
@ -7,10 +8,11 @@ fn benchmark_generate_tag(c: &mut Criterion) {
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 secret_key = RootSecret::<24>::generate();
let mut rng = OsRng::default();
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))
}); });
} }
} }
@ -20,11 +22,12 @@ fn benchmark_test_tag(c: &mut Criterion) {
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 secret_key = RootSecret::<24>::generate();
let mut rng = OsRng::default();
for p in [5, 10, 15].iter() { for p in [5, 10, 15, 24].iter() {
let tag = secret_key.tagging_key().generate_tag();
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| { 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)) b.iter(|| detection_key.test_tag(&tag))
}); });
} }

View File

@ -11,6 +11,7 @@ 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 rand::rngs::OsRng;
use rand::{CryptoRng, RngCore};
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use sha3::{Sha3_256, Sha3_512}; use sha3::{Sha3_256, Sha3_512};
use std::convert::TryFrom; use std::convert::TryFrom;
@ -107,6 +108,7 @@ 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 secret = RootSecret::<24>::generate();
/// let tagging_key = secret.tagging_key(); /// let tagging_key = secret.tagging_key();
@ -115,7 +117,8 @@ impl<const GAMMA: u8> Tag<{ GAMMA }> {
/// ///
/// // 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 mut rng = OsRng::default();
/// 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> {
@ -137,7 +140,9 @@ impl<const GAMMA: u8> Tag<{ GAMMA }> {
/// ///
/// // 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(); /// use rand::rngs::OsRng;
/// let mut rng = OsRng::default();
/// 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);
@ -334,7 +339,9 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
/// ///
/// // 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(); /// use rand::rngs::OsRng;
/// let mut rng = OsRng::default();
/// 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) {
@ -399,7 +406,9 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
/// let secrets: Vec<RootSecret<24>> = (0..2).map(|_x| RootSecret::<24>::generate()).collect(); /// 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(); /// 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); /// 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 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);
@ -495,21 +504,25 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
/// use fuzzytags::{RootSecret}; /// use fuzzytags::{RootSecret};
/// let secret = RootSecret::<24>::generate(); /// let secret = RootSecret::<24>::generate();
/// 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(); /// use rand::rngs::OsRng;
/// let mut rng = OsRng::default();
/// 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); 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() {
for h_i in self.0.iter() {
let k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), h_i.mul(r)); 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;
@ -551,13 +564,14 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
/// // 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); /// 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<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
@ -621,7 +635,8 @@ mod tests {
// 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 mut rng = OsRng::default();
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);
@ -630,8 +645,10 @@ mod tests {
#[test] #[test]
fn test_serialization() { fn test_serialization() {
// generate some new keys... // generate some new keys...
let secret = RootSecret::<15>::generate(); 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 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);
@ -641,7 +658,7 @@ mod tests {
// generate some new keys... // generate some new keys...
let secret = RootSecret::<24>::generate(); 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 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();
@ -680,7 +697,8 @@ mod tests {
let secrets: Vec<RootSecret<24>> = (0..2).map(|_x| RootSecret::<24>::generate()).collect(); 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(); 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 mut rng = OsRng::default();
let entangled_tag = TaggingKey::generate_entangled_tag(tagging_keys, &mut rng, 16);
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(16);
@ -696,7 +714,8 @@ mod tests {
let secrets: Vec<RootSecret<24>> = (0..2).map(|_x| RootSecret::<24>::generate()).collect(); 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(); 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 mut rng = OsRng::default();
let entangled_tag = TaggingKey::generate_entangled_tag(tagging_keys, &mut rng, 16);
let detection_keys = secrets let detection_keys = secrets
.iter() .iter()
.map(|x| x.extract_detection_key(16)) .map(|x| x.extract_detection_key(16))
@ -710,8 +729,9 @@ mod tests {
fn correctness() { fn correctness() {
let number_of_messages = 100; let number_of_messages = 100;
let secret = RootSecret::<16>::generate(); let secret = RootSecret::<16>::generate();
let mut rng = OsRng::default();
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));
} }
@ -769,9 +789,10 @@ mod tests {
let number_of_messages = 1000; let number_of_messages = 1000;
let secret = RootSecret::<24>::generate(); let secret = RootSecret::<24>::generate();
let mut false_positives = 0; let mut false_positives = 0;
let mut rng = OsRng::default();
for _i in 0..number_of_messages { for _i in 0..number_of_messages {
let secret2 = RootSecret::<24>::generate(); 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)); 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;