|
|
|
@ -15,7 +15,11 @@ use std::fmt;
|
|
|
|
|
use std::fmt::{Display, Formatter};
|
|
|
|
|
use std::ops::{Mul, Sub};
|
|
|
|
|
|
|
|
|
|
use rand::Rng;
|
|
|
|
|
#[cfg(feature = "entangled")]
|
|
|
|
|
use brute_force::adaptors;
|
|
|
|
|
#[cfg(feature = "entangled")]
|
|
|
|
|
use brute_force::brute_force;
|
|
|
|
|
|
|
|
|
|
use rand_core::{CryptoRng, RngCore};
|
|
|
|
|
#[cfg(feature = "bulk_verify")]
|
|
|
|
|
use rayon::iter::IndexedParallelIterator;
|
|
|
|
@ -23,7 +27,6 @@ use rayon::iter::IndexedParallelIterator;
|
|
|
|
|
use rayon::iter::IntoParallelRefIterator;
|
|
|
|
|
#[cfg(feature = "bulk_verify")]
|
|
|
|
|
use rayon::iter::ParallelIterator;
|
|
|
|
|
use std::borrow::BorrowMut;
|
|
|
|
|
#[cfg(feature = "bulk_verify")]
|
|
|
|
|
use std::sync::mpsc::channel;
|
|
|
|
|
|
|
|
|
@ -38,7 +41,6 @@ use std::sync::mpsc::channel;
|
|
|
|
|
pub struct Tag<const GAMMA: u8> {
|
|
|
|
|
u: RistrettoPoint,
|
|
|
|
|
y: Scalar,
|
|
|
|
|
z: Vec<u8>,
|
|
|
|
|
ciphertexts: BitVec,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -68,7 +70,7 @@ impl<'de, const GAMMA: u8> Deserialize<'de> for Tag<{ GAMMA }> {
|
|
|
|
|
type Value = Tag<{ GAMMA }>;
|
|
|
|
|
|
|
|
|
|
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
|
|
|
formatter.write_str("64 bytes + GAMMA Bytes + GAMMA + bits of data")
|
|
|
|
|
formatter.write_str("64 bytes + GAMMA+bits of data")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn visit_seq<A>(self, mut seq: A) -> Result<Tag<{ GAMMA }>, A::Error>
|
|
|
|
@ -88,13 +90,12 @@ impl<'de, const GAMMA: u8> Deserialize<'de> for Tag<{ GAMMA }> {
|
|
|
|
|
_ => break,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Tag::<GAMMA>::decompress(&bytes).ok_or(serde::de::Error::custom("invalid fuzzytag"))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// support up to GAMMA = 64
|
|
|
|
|
deserializer.deserialize_tuple((72 + GAMMA) as usize, FuzzyTagVisitor::<GAMMA>)
|
|
|
|
|
deserializer.deserialize_tuple(72, FuzzyTagVisitor::<GAMMA>)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -121,7 +122,6 @@ impl<const GAMMA: u8> Tag<{ GAMMA }> {
|
|
|
|
|
let mut bytes = vec![];
|
|
|
|
|
bytes.extend_from_slice(self.u.compress().as_bytes());
|
|
|
|
|
bytes.extend_from_slice(self.y.as_bytes());
|
|
|
|
|
bytes.extend_from_slice(self.z.as_slice());
|
|
|
|
|
bytes.extend_from_slice(self.ciphertexts.to_bytes().as_slice());
|
|
|
|
|
bytes
|
|
|
|
|
}
|
|
|
|
@ -145,10 +145,9 @@ impl<const GAMMA: u8> Tag<{ GAMMA }> {
|
|
|
|
|
/// assert_eq!(tag, decompressed_tag);
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn decompress(bytes: &[u8]) -> Option<Tag<{ GAMMA }>> {
|
|
|
|
|
if bytes.len() > (64 + GAMMA as usize) {
|
|
|
|
|
if bytes.len() > 64 {
|
|
|
|
|
let (u_bytes, rest) = bytes.split_at(32);
|
|
|
|
|
let (y_bytes, rest) = rest.split_at(32);
|
|
|
|
|
let (z_bytes, ciphertext) = rest.split_at({ GAMMA } as usize);
|
|
|
|
|
let (y_bytes, ciphertext) = rest.split_at(32);
|
|
|
|
|
|
|
|
|
|
// if the ciphertext is too short, then this is an invalid tag
|
|
|
|
|
let min_bytes = GAMMA / 8;
|
|
|
|
@ -161,20 +160,13 @@ impl<const GAMMA: u8> Tag<{ GAMMA }> {
|
|
|
|
|
Ok(fixed_size) => fixed_size,
|
|
|
|
|
_ => return None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut ciphertexts = BitVec::from_bytes(ciphertext);
|
|
|
|
|
ciphertexts.truncate(GAMMA as usize);
|
|
|
|
|
return match (
|
|
|
|
|
CompressedRistretto::from_slice(u_bytes).decompress(),
|
|
|
|
|
Scalar::from_canonical_bytes(y_bytes_fixed),
|
|
|
|
|
z_bytes.to_vec(),
|
|
|
|
|
) {
|
|
|
|
|
(Some(u), Some(y), z) => Some(Tag {
|
|
|
|
|
u,
|
|
|
|
|
y,
|
|
|
|
|
z: z,
|
|
|
|
|
ciphertexts,
|
|
|
|
|
}),
|
|
|
|
|
(Some(u), Some(y)) => Some(Tag { u, y, ciphertexts }),
|
|
|
|
|
_ => None,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
@ -186,10 +178,9 @@ impl<const GAMMA: u8> Display for Tag<{ GAMMA }> {
|
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
|
write!(
|
|
|
|
|
f,
|
|
|
|
|
"{} {} {} {}",
|
|
|
|
|
"{} {} {}",
|
|
|
|
|
hex::encode(self.u.compress().as_bytes()),
|
|
|
|
|
hex::encode(self.y.as_bytes()),
|
|
|
|
|
hex::encode(self.z.as_slice()),
|
|
|
|
|
hex::encode(self.ciphertexts.to_bytes())
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
@ -266,25 +257,6 @@ impl<const GAMMA: u8> RootSecret<{ GAMMA }> {
|
|
|
|
|
TaggingKey::<GAMMA> { 0: tagging_key }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// derive the tagging key for this secret
|
|
|
|
|
/// Example:
|
|
|
|
|
/// ```
|
|
|
|
|
/// use fuzzytags::RootSecret;
|
|
|
|
|
/// use rand::rngs::OsRng;
|
|
|
|
|
/// let mut rng = OsRng;
|
|
|
|
|
/// let secret = RootSecret::<24>::generate(&mut rng);
|
|
|
|
|
/// let anti_key = secret.anti_key();
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn anti_key(&self) -> TaggingKey<{ GAMMA }> {
|
|
|
|
|
let g = RISTRETTO_BASEPOINT_POINT;
|
|
|
|
|
let mut tagging_key = vec![];
|
|
|
|
|
for sk_i in self.secret.iter() {
|
|
|
|
|
let pk_i = g.mul(sk_i.invert());
|
|
|
|
|
tagging_key.push(pk_i);
|
|
|
|
|
}
|
|
|
|
|
TaggingKey::<GAMMA> { 0: tagging_key }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// precompute the first part of h
|
|
|
|
|
fn pre_h(u: RistrettoPoint, w: RistrettoPoint) -> PrecomputeH {
|
|
|
|
|
let mut hash = sha3::Sha3_256::new();
|
|
|
|
@ -294,11 +266,6 @@ impl<const GAMMA: u8> RootSecret<{ GAMMA }> {
|
|
|
|
|
return PrecomputeH(hash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn nonce_h(mut hash: PrecomputeH, nonce: u8) -> PrecomputeH {
|
|
|
|
|
hash.0.update(vec![nonce]);
|
|
|
|
|
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());
|
|
|
|
@ -386,9 +353,7 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut bitvec = BitVec::from_bytes(&*tag.z);
|
|
|
|
|
bitvec.append(tag.ciphertexts.clone().borrow_mut());
|
|
|
|
|
let m = RootSecret::<GAMMA>::g(tag.u, &bitvec);
|
|
|
|
|
let m = RootSecret::<GAMMA>::g(tag.u, &tag.ciphertexts);
|
|
|
|
|
let g = RISTRETTO_BASEPOINT_POINT;
|
|
|
|
|
|
|
|
|
|
// Re-derive w = g^z from the public tag.
|
|
|
|
@ -405,10 +370,9 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
|
|
|
|
|
|
|
|
|
// for each secret part...
|
|
|
|
|
let mut result = 0;
|
|
|
|
|
for (nonce_i, (x_i, c_i)) in self.0.iter().zip(&tag.ciphertexts).enumerate() {
|
|
|
|
|
for (x_i, c_i) in self.0.iter().zip(&tag.ciphertexts) {
|
|
|
|
|
// re-derive the key from the tag
|
|
|
|
|
let nonce_h = RootSecret::<GAMMA>::nonce_h(pre_h.clone(), tag.z[nonce_i]);
|
|
|
|
|
let k_i = RootSecret::<GAMMA>::post_h(nonce_h, tag.u.mul(x_i));
|
|
|
|
|
let k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), tag.u.mul(x_i));
|
|
|
|
|
|
|
|
|
|
// calculate the "original" plaintext
|
|
|
|
|
let b_i = k_i ^ (c_i as u8);
|
|
|
|
@ -454,10 +418,7 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
|
|
|
|
return vec![];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut bitvec = BitVec::from_bytes(&*tag.z);
|
|
|
|
|
bitvec.append(tag.ciphertexts.clone().borrow_mut());
|
|
|
|
|
let m = RootSecret::<GAMMA>::g(tag.u, &bitvec);
|
|
|
|
|
|
|
|
|
|
let m = RootSecret::<GAMMA>::g(tag.u, &tag.ciphertexts);
|
|
|
|
|
let g = RISTRETTO_BASEPOINT_POINT;
|
|
|
|
|
|
|
|
|
|
// Re-derive w = g^z from the public tag.
|
|
|
|
@ -479,10 +440,9 @@ impl<const GAMMA: u8> DetectionKey<{ GAMMA }> {
|
|
|
|
|
.enumerate()
|
|
|
|
|
.for_each_with(tx.clone(), |tx, (index, detection_key)| {
|
|
|
|
|
let mut result = 0;
|
|
|
|
|
for (nonce_i, (x_i, c_i)) in detection_key.0.iter().zip(&tag.ciphertexts).enumerate() {
|
|
|
|
|
for (x_i, c_i) in detection_key.0.iter().zip(&tag.ciphertexts) {
|
|
|
|
|
// re-derive the key from the tag
|
|
|
|
|
let nonce_h = RootSecret::<GAMMA>::nonce_h(pre_h.clone(), tag.z[nonce_i]);
|
|
|
|
|
let k_i = RootSecret::<GAMMA>::post_h(nonce_h, tag.u.mul(x_i));
|
|
|
|
|
let k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), tag.u.mul(x_i));
|
|
|
|
|
|
|
|
|
|
// calculate the "original" plaintext
|
|
|
|
|
let b_i = k_i ^ (c_i as u8);
|
|
|
|
@ -556,14 +516,8 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
|
|
|
|
|
// construct the ciphertext portion of the tag
|
|
|
|
|
let mut ciphertexts = BitVec::with_capacity(GAMMA.into());
|
|
|
|
|
|
|
|
|
|
let mut nonces = vec![];
|
|
|
|
|
for _i in 0..GAMMA {
|
|
|
|
|
nonces.push(rng.gen())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (nonce_i, h_i) in self.0.iter().enumerate() {
|
|
|
|
|
let nonce_h = RootSecret::<GAMMA>::nonce_h(pre_h.clone(), nonces[nonce_i]);
|
|
|
|
|
let k_i = RootSecret::<GAMMA>::post_h(nonce_h, h_i.mul(r));
|
|
|
|
|
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
|
|
|
|
|
let c_i = k_i ^ 0x01;
|
|
|
|
|
ciphertexts.push(c_i == 0x01);
|
|
|
|
@ -584,17 +538,10 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
|
|
|
|
|
// used to derive the key.
|
|
|
|
|
|
|
|
|
|
// finally calculate a `y` = 1/r * (z-m) which will be used to re-derive `w`
|
|
|
|
|
let mut bitvec = BitVec::from_bytes(&*nonces);
|
|
|
|
|
bitvec.append(ciphertexts.clone().borrow_mut());
|
|
|
|
|
let m = RootSecret::<GAMMA>::g(u, &bitvec);
|
|
|
|
|
let m = RootSecret::<GAMMA>::g(u, &ciphertexts);
|
|
|
|
|
let y = r.invert().mul(z.sub(m));
|
|
|
|
|
|
|
|
|
|
return Tag {
|
|
|
|
|
u,
|
|
|
|
|
y,
|
|
|
|
|
z: nonces,
|
|
|
|
|
ciphertexts,
|
|
|
|
|
};
|
|
|
|
|
return Tag { u, y, ciphertexts };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "entangled")]
|
|
|
|
@ -631,41 +578,23 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
|
|
|
|
|
tagging_key_precomputes.push(precompute);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generate some random points...
|
|
|
|
|
let config = brute_force::Config::default();
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let z = Scalar::random(rng);
|
|
|
|
|
let f = |z: &Scalar| {
|
|
|
|
|
let w = g.mul(z);
|
|
|
|
|
let pre_h = RootSecret::<GAMMA>::pre_h(u, w);
|
|
|
|
|
let mut key = vec![];
|
|
|
|
|
|
|
|
|
|
let mut nonces = vec![];
|
|
|
|
|
|
|
|
|
|
for (i, precompute) in tagging_key_precomputes[0].iter().enumerate() {
|
|
|
|
|
for _nonce_attmepts in 0..=255 {
|
|
|
|
|
let nonce: u8 = rng.gen();
|
|
|
|
|
let mut success = true;
|
|
|
|
|
let nonce_h = RootSecret::<GAMMA>::nonce_h(pre_h.clone(), nonce);
|
|
|
|
|
let k_i = RootSecret::<GAMMA>::post_h(nonce_h.clone(), *precompute);
|
|
|
|
|
|
|
|
|
|
if i < length {
|
|
|
|
|
for precompute in tagging_key_precomputes.iter().skip(1) {
|
|
|
|
|
let n_k_i = RootSecret::<GAMMA>::post_h(nonce_h.clone(), precompute[i]);
|
|
|
|
|
if k_i != n_k_i {
|
|
|
|
|
success = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
let k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), *precompute);
|
|
|
|
|
if i < length {
|
|
|
|
|
for precompute in tagging_key_precomputes.iter().skip(1) {
|
|
|
|
|
let n_k_i = RootSecret::<GAMMA>::post_h(pre_h.clone(), precompute[i]);
|
|
|
|
|
if k_i != n_k_i {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if success {
|
|
|
|
|
nonces.push(nonce);
|
|
|
|
|
key.push(k_i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
key.push(k_i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generate the tag
|
|
|
|
@ -677,17 +606,11 @@ impl<const GAMMA: u8> TaggingKey<{ GAMMA }> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is the same as generate_tag, kept separate to avoid over-decomposition
|
|
|
|
|
let mut bitvec = BitVec::from_bytes(&*nonces);
|
|
|
|
|
bitvec.append(ciphertexts.clone().borrow_mut());
|
|
|
|
|
let m = RootSecret::<GAMMA>::g(u, &bitvec);
|
|
|
|
|
let m = RootSecret::<GAMMA>::g(u, &ciphertexts);
|
|
|
|
|
let y = r.invert().mul(z.sub(m));
|
|
|
|
|
return Tag {
|
|
|
|
|
u,
|
|
|
|
|
y,
|
|
|
|
|
z: nonces,
|
|
|
|
|
ciphertexts,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return Some(Tag { u, y, ciphertexts });
|
|
|
|
|
};
|
|
|
|
|
brute_force(config, adaptors::auto_advance(f))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -738,7 +661,7 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
// Test some bincode...
|
|
|
|
|
let bincode_tag = bincode::serialize(&tag);
|
|
|
|
|
// println!("Serialized: {:?}", bincode_tag);
|
|
|
|
|
// println!("Serialized: {:?}", bincode_tag);
|
|
|
|
|
let deserialized_tag: Tag<24> = bincode::deserialize(&bincode_tag.unwrap()).unwrap();
|
|
|
|
|
//println!("Deserialized: {}", deserialized_tag);
|
|
|
|
|
//assert_eq!(tag.compress(), deserialized_tag.compress());
|
|
|
|
@ -774,7 +697,7 @@ mod tests {
|
|
|
|
|
// 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: {}", entangled_tag);
|
|
|
|
|
println!("{}", entangled_tag);
|
|
|
|
|
for secret in secrets.iter() {
|
|
|
|
|
let detection_key = secret.extract_detection_key(len);
|
|
|
|
|
assert!(detection_key.test_tag(&entangled_tag));
|
|
|
|
@ -787,26 +710,26 @@ mod tests {
|
|
|
|
|
fn test_check_multiple() {
|
|
|
|
|
use crate::TaggingKey;
|
|
|
|
|
let mut rng = OsRng;
|
|
|
|
|
let secrets: Vec<RootSecret<24>> = (0..5)
|
|
|
|
|
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();
|
|
|
|
|
// 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, &mut rng, 24);
|
|
|
|
|
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);
|
|
|
|
|
assert_eq!(results.len(), 5);
|
|
|
|
|
assert_eq!(results.len(), 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn correctness() {
|
|
|
|
|
let number_of_messages = 100;
|
|
|
|
|
let mut rng = OsRng;
|
|
|
|
|
let secret = RootSecret::<24>::generate(&mut rng);
|
|
|
|
|
let secret = RootSecret::<16>::generate(&mut rng);
|
|
|
|
|
for i in 0..number_of_messages {
|
|
|
|
|
let tag = secret.tagging_key().generate_tag(&mut rng);
|
|
|
|
|
println!("{}: {}", i, tag);
|
|
|
|
@ -818,7 +741,6 @@ mod tests {
|
|
|
|
|
let tag = Tag {
|
|
|
|
|
u: RistrettoPoint::default(),
|
|
|
|
|
y: Scalar::default(),
|
|
|
|
|
z: vec![],
|
|
|
|
|
ciphertexts: BitVec::from_elem(24, false),
|
|
|
|
|
};
|
|
|
|
|
tag
|
|
|
|
@ -828,7 +750,6 @@ mod tests {
|
|
|
|
|
let mut tag = Tag {
|
|
|
|
|
u: RistrettoPoint::default(),
|
|
|
|
|
y: Scalar::default(),
|
|
|
|
|
z: vec![],
|
|
|
|
|
ciphertexts: BitVec::from_elem(24, false),
|
|
|
|
|
};
|
|
|
|
|
tag.ciphertexts.set_all();
|
|
|
|
@ -877,37 +798,21 @@ mod tests {
|
|
|
|
|
#[test]
|
|
|
|
|
fn false_positives() {
|
|
|
|
|
let mut rng = OsRng;
|
|
|
|
|
let number_of_reference_checks = 100;
|
|
|
|
|
let number_of_messages = 100;
|
|
|
|
|
let limit = 1;
|
|
|
|
|
let mut false_positives = 0;
|
|
|
|
|
let total_attempts = number_of_reference_checks * number_of_messages;
|
|
|
|
|
|
|
|
|
|
let number_of_messages = 1000;
|
|
|
|
|
let secret = RootSecret::<24>::generate(&mut rng);
|
|
|
|
|
for _i in 0..number_of_reference_checks {
|
|
|
|
|
let secret = RootSecret::<24>::generate(&mut rng);
|
|
|
|
|
for _i in 0..number_of_messages {
|
|
|
|
|
let secret2 = RootSecret::<24>::generate(&mut rng);
|
|
|
|
|
let tag = secret2.tagging_key().generate_tag(&mut rng);
|
|
|
|
|
assert!(secret2.extract_detection_key(limit).test_tag(&tag));
|
|
|
|
|
if secret.extract_detection_key(limit).test_tag(&tag) == true {
|
|
|
|
|
false_positives += 1;
|
|
|
|
|
}
|
|
|
|
|
let mut false_positives = 0;
|
|
|
|
|
for _i in 0..number_of_messages {
|
|
|
|
|
let secret2 = RootSecret::<24>::generate(&mut rng);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
println!(
|
|
|
|
|
"Expected False Positive Rate: {}\nActual False Positive Rate: {}",
|
|
|
|
|
secret
|
|
|
|
|
.extract_detection_key(limit)
|
|
|
|
|
.false_positive_probability(),
|
|
|
|
|
(false_positives as f64 / (total_attempts) as f64)
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
secret
|
|
|
|
|
.extract_detection_key(limit)
|
|
|
|
|
.false_positive_probability()
|
|
|
|
|
- (false_positives as f64 / (total_attempts) as f64)
|
|
|
|
|
< 0.01
|
|
|
|
|
secret.extract_detection_key(3).false_positive_probability(),
|
|
|
|
|
(false_positives as f64 / number_of_messages as f64)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|