diff --git a/src/lib.rs b/src/lib.rs index 31345a8..b2d1951 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,13 @@ use std::fmt; use std::fmt::{Display, Formatter}; use std::ops::{Add, Mul, Sub}; +/// A tag is a probabilistic cryptographic structure. When constructed for a given `FuzzyMetaPublicKey` +/// it will pass the `FuzzyMetaDetectionKey::test` 100% of the time. For other public keys +/// it will pass the test with probability `gamma` related to the security parameter of the system. +/// This system provides the following security properties: +/// - Correctness: Valid tags for a public key for a key pair always validate when tested against the secret key +/// - Fuzziness: Invalid matches should produce false positives with probability p related to the security property (γ) +/// - Security: An adversarial server with access to Test oracle (i.e. the detection key) is unable to distinguish false positives from true positives. (Detection Ambiguity) #[derive(Debug)] pub struct FuzzyMetaTag { u: RistrettoPoint, @@ -17,6 +24,82 @@ pub struct FuzzyMetaTag { ciphertexts: BitVec, } +/// A collection of "secret" data that can be used to determine if a `FuzzyMetaTag` was intended for +/// the derived public key. +pub struct FuzzyMetaDetectionKey(Vec); + +impl FuzzyMetaDetectionKey { + /// returns true if the tag was intended for this key + pub fn test(&self, tag: &FuzzyMetaTag) -> bool { + let m = FuzzyMetaTagKeyPair::g(tag.u, &tag.ciphertexts); + + let g = RISTRETTO_BASEPOINT_POINT; + + // Re-derive w = g^z from the public tag. + // y = (1/r * (z-m) + // u = g^r + // so w = g^m + u^y + // w = g^m + g^(r * 1/r * (z-m)) + // w = g^m + g^(z-m) + // w = g^z + let w = g.mul(m).add(tag.u.mul(tag.y)); + + // for each secret key part... + for (i, x_i) in self.0.iter().enumerate() { + // re-derive the key from the tag + let k_i = FuzzyMetaTagKeyPair::h(tag.u, tag.u.mul(x_i), w); + + // calculate the "original" plaintext + let c_i = match tag.ciphertexts.get(i).unwrap() { + true => 0x01, + false => 0x00, + }; + let b_i = k_i ^ c_i; + // if these don't match then return false + if b_i != 1 { + return false; + } + } + return true; + } +} + +/// A public identity that others can create tags for. +pub struct FuzzyMetaPublicKey(Vec); + +impl FuzzyMetaPublicKey { + /// creates a new tag for this public key + pub fn flag(&self) -> FuzzyMetaTag { + let mut rng = OsRng::default(); + let g = RISTRETTO_BASEPOINT_POINT; + + // 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); + + // construct the ciphertext portion of the tag + let mut ciphertexts = BitVec::new(); + for (_i, h_i) in self.0.iter().enumerate() { + let k_i = FuzzyMetaTagKeyPair::h(u, h_i.mul(r), w); + let c_i = k_i ^ 0x01; + ciphertexts.push(c_i == 0x01); + } + + // From the paper: + // "The value w corresponds to a chameleon hash [KR00] computed on the message (0,z), where z is chosen at random. + // Once the ciphertext has been computed, we use a master trapdoor for the chameleon hash (which is part of the scheme’s secret key) in order to compute a collision (y,m) where m + // is a hash of the remaining components of the ciphertext" + + // finally calculate a `y` = 1/r * (z-m) which will be used to re-derive `w` + let m = FuzzyMetaTagKeyPair::g(u, &ciphertexts); + let y = r.invert().mul(z.sub(m)); + + return FuzzyMetaTag { u, y, ciphertexts }; + } +} + impl Display for FuzzyMetaTag { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( @@ -29,14 +112,17 @@ impl Display for FuzzyMetaTag { } } -#[derive(Debug)] -pub struct FuzzyMetaTagKey { - s_keys: Vec, - p_keys: Vec, +/// An identity keypair for generating and validating fuzzy meta tags. +pub struct FuzzyMetaTagKeyPair { + pub detection_key: FuzzyMetaDetectionKey, + pub public_key: FuzzyMetaPublicKey, } -impl FuzzyMetaTagKey { - pub fn generate(gamma: usize) -> FuzzyMetaTagKey { +impl FuzzyMetaTagKeyPair { + /// Generate a new Key Pair given a security parameter `gamma`. Tags generated for a given + /// `FuzzyMetaPublicKey::flag` will pass the `FuzzyMetaDetectionKey::test` for other public + /// keys with probability $ 2 ^ -8 $ + pub fn generate(gamma: usize) -> FuzzyMetaTagKeyPair { let mut rng = OsRng::default(); let g = RISTRETTO_BASEPOINT_POINT; let mut s_keys = vec![]; @@ -47,9 +133,13 @@ impl FuzzyMetaTagKey { s_keys.push(sk_i); p_keys.push(pk_i); } - FuzzyMetaTagKey { s_keys, p_keys } + FuzzyMetaTagKeyPair { + detection_key: FuzzyMetaDetectionKey { 0: s_keys }, + public_key: FuzzyMetaPublicKey { 0: p_keys }, + } } + /// a hash function that takes 3 risretto points as a parameter and outputs 0 or 1. fn h(u: RistrettoPoint, h: RistrettoPoint, w: RistrettoPoint) -> u8 { let hash = sha3::Sha3_256::digest( format!( @@ -63,78 +153,40 @@ impl FuzzyMetaTagKey { return hash.as_slice()[0] & 0x01; } + /// a hash function which takes a ristretto point and a vector of ciphertexts and ouputs a + /// ristretto scalar. 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 crate::FuzzyMetaTagKeyPair; use test::Bencher; #[test] fn correctness() { let number_of_messages = 100; - let key = FuzzyMetaTagKey::generate(3); + let key = FuzzyMetaTagKeyPair::generate(16); for i in 0..number_of_messages { - let tag = key.flag(); + let tag = key.public_key.flag(); println!("{}: {}", i, tag); - assert_eq!(true, key.test(&tag)); + assert!(key.detection_key.test(&tag)); } } #[bench] - fn generate(b: &mut Bencher) { + fn generate(_b: &mut Bencher) { let number_of_messages = 1000; - let key = FuzzyMetaTagKey::generate(3); + let key = FuzzyMetaTagKeyPair::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 { + let key2 = FuzzyMetaTagKeyPair::generate(3); + let tag = key2.public_key.flag(); + assert!(key2.detection_key.test(&tag)); + if key.detection_key.test(&tag) == true { false_positives += 1; } }