#![feature(test)] use std::ops::{Mul, Sub, Add}; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::ristretto::RistrettoPoint; use rand::rngs::OsRng; use curve25519_dalek::digest::Digest; use sha3::{Sha3_512}; use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; use std::fmt::{Display, Formatter}; use bit_vec::BitVec; use std::fmt; #[derive(Debug)] pub struct FuzzyMetaTag { u: RistrettoPoint, y: Scalar, ciphertexts: BitVec } impl Display for FuzzyMetaTag { 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.ciphertexts.to_bytes())) } } #[derive(Debug)] pub struct FuzzyMetaTagKey { s_keys: Vec, p_keys: Vec } impl FuzzyMetaTagKey { pub fn generate(gamma: usize) -> FuzzyMetaTagKey { let mut rng = OsRng::default(); let g = RISTRETTO_BASEPOINT_POINT; let mut s_keys = vec![]; let mut p_keys = vec![]; for _i in 0..gamma { let sk_i = Scalar::random(&mut rng); let pk_i = g.mul(sk_i); s_keys.push(sk_i); p_keys.push(pk_i); } FuzzyMetaTagKey { s_keys, p_keys } } fn h(u: RistrettoPoint, h :RistrettoPoint, w: RistrettoPoint) -> u8 { let hash = sha3::Sha3_256::digest(format!("{}{}{}", hex::encode(u.compress().as_bytes()), hex::encode(h.compress().as_bytes()), hex::encode(w.compress().as_bytes())).as_bytes()); return hash.as_slice()[0] & 0x01 } fn g(u: RistrettoPoint, points: &BitVec) -> Scalar { Scalar::hash_from_bytes::(format!("{}{}", hex::encode(u.compress().as_bytes()), hex::encode(points.to_bytes())).as_bytes()) } pub fn flag(&self) -> FuzzyMetaTag { let mut rng = OsRng::default(); let g = RISTRETTO_BASEPOINT_POINT; let r = Scalar::random(&mut rng); let u = g.mul(r); let z = Scalar::random(&mut rng); let w = g.mul(z); let mut ciphertexts = BitVec::new(); for (_i, h_i) in self.p_keys.iter().enumerate() { let k_i = FuzzyMetaTagKey::h(u, h_i.mul(r), w); let c_i = k_i ^ 0x01; ciphertexts.push(c_i == 0x01); } let m = FuzzyMetaTagKey::g(u, &ciphertexts); let y = r.invert().mul(z.sub(m)); return FuzzyMetaTag { u, y, ciphertexts } } pub fn test(&self, tag: &FuzzyMetaTag) -> bool { let m = FuzzyMetaTagKey::g(tag.u, &tag.ciphertexts); let g = RISTRETTO_BASEPOINT_POINT; let w = g.mul(m).add(tag.u.mul(tag.y)); for (i,x_i) in self.s_keys.iter().enumerate() { let k_i = FuzzyMetaTagKey::h(tag.u, tag.u.mul(x_i), w); let c_i = match tag.ciphertexts.get(i).unwrap() { true => 0x01, false => 0x00, }; let b_i = k_i ^ c_i; if b_i != 1 { return false } } return true } } #[cfg(test)] mod tests { extern crate test; use crate::FuzzyMetaTagKey; use test::Bencher; #[test] fn correctness() { let number_of_messages = 100; let key = FuzzyMetaTagKey::generate(3); for i in 0..number_of_messages { let tag = key.flag(); println!("{}: {}", i, tag); assert_eq!(true, key.test(&tag)); } } #[bench] fn generate(b: &mut Bencher) { let number_of_messages = 1000; let key = FuzzyMetaTagKey::generate(3); let mut false_positives = 0; for _i in 0..number_of_messages { let key2 = FuzzyMetaTagKey::generate(3); let tag = key2.flag(); assert!(key2.test(&tag)); if key.test(&tag) == true { false_positives += 1; } } println!("Expected False Positive Rate: {}\nActual False Positive Rate: {}", (2.0_f64).powi(-3), (false_positives as f64 / number_of_messages as f64)); } }