diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7b190f8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "fuzzymetatag" +version = "0.1.0" +authors = ["Sarah Jamie Lewis "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hex = "0.4.2" +rand = "0.7.3" +curve25519-dalek = {version="3.0.0", features=["serde"]} +sha3 = "0.9.1" +serde = { version = "1", default_features = false, features = ["derive"], optional = true } +bit-vec = "0.6.3" \ No newline at end of file diff --git a/README.md b/README.md index 672e7b6..898342b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,6 @@ # fuzzymetatag +Experimental Rust implementation of https://eprint.iacr.org/2021/089 using Ristretto. + + + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f80d2b5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,138 @@ +#![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)); + } +} \ No newline at end of file