2021-01-30 02:24:27 +00:00
#![ deny(missing_docs) ]
2021-08-16 22:46:04 +00:00
#![ doc = include_str!( " ../README.md " ) ]
#![ doc = include_str!( " ../ANONYMITY.md " ) ]
2021-02-16 22:20:20 +00:00
#![ doc(html_logo_url = " https://git.openprivacy.ca/openprivacy/fuzzytags/media/branch/trunk/FuzzyTags_Logo.png " ) ]
2021-01-29 23:47:40 +00:00
use bit_vec ::BitVec ;
use curve25519_dalek ::constants ::RISTRETTO_BASEPOINT_POINT ;
use curve25519_dalek ::digest ::Digest ;
2021-02-04 07:32:10 +00:00
use curve25519_dalek ::ristretto ::{ CompressedRistretto , RistrettoPoint } ;
2021-01-29 23:47:40 +00:00
use curve25519_dalek ::scalar ::Scalar ;
2021-02-01 21:25:26 +00:00
use curve25519_dalek ::traits ::MultiscalarMul ;
2021-02-04 07:32:10 +00:00
use serde ::{ de ::Visitor , Deserialize , Deserializer , Serialize , Serializer } ;
2021-05-20 06:31:14 +00:00
use sha3 ::{ Sha3_256 , Sha3_512 } ;
2021-02-04 07:32:10 +00:00
use std ::convert ::TryFrom ;
2021-01-29 21:52:34 +00:00
use std ::fmt ;
2021-01-29 23:47:40 +00:00
use std ::fmt ::{ Display , Formatter } ;
2021-02-01 21:25:26 +00:00
use std ::ops ::{ Mul , Sub } ;
2021-02-03 00:19:20 +00:00
2021-10-02 22:59:39 +00:00
use rand ::Rng ;
2021-05-22 19:49:11 +00:00
use rand_core ::{ CryptoRng , RngCore } ;
2021-02-13 20:11:18 +00:00
#[ cfg(feature = " bulk_verify " ) ]
use rayon ::iter ::IndexedParallelIterator ;
#[ cfg(feature = " bulk_verify " ) ]
use rayon ::iter ::IntoParallelRefIterator ;
#[ cfg(feature = " bulk_verify " ) ]
use rayon ::iter ::ParallelIterator ;
2021-10-02 22:59:39 +00:00
use std ::borrow ::BorrowMut ;
2021-02-14 04:39:57 +00:00
#[ cfg(feature = " bulk_verify " ) ]
use std ::sync ::mpsc ::channel ;
2021-02-13 20:11:18 +00:00
2021-02-05 21:57:25 +00:00
/// A tag is a probabilistic cryptographic structure. When constructed for a given `TaggingKey`
/// it will pass the `DetectionKey::test_tag` 100% of the time. For other tagging keys
2021-02-04 20:54:09 +00:00
/// it will pass the test with probability `GAMMA` related to the security parameter of the system.
2021-01-30 02:18:08 +00:00
/// This system provides the following security properties:
2021-02-05 21:57:25 +00:00
/// * Correctness: Valid tags constructed for a specific tagging key will always validate when tested using the detection key
2021-01-30 02:47:49 +00:00
/// * Fuzziness: Invalid tags will produce false positives with probability _p_ related to the security property (_γ _)
/// * Security: An adversarial server with access to the detection key is unable to distinguish false positives from true positives. (Detection Ambiguity)
2021-02-04 07:32:10 +00:00
#[ derive(Clone, Debug, Eq, PartialEq) ]
2021-02-05 21:57:25 +00:00
pub struct Tag < const GAMMA : u8 > {
2021-01-29 21:52:34 +00:00
u : RistrettoPoint ,
y : Scalar ,
2021-10-02 22:59:39 +00:00
z : Vec < u8 > ,
2021-01-29 23:47:40 +00:00
ciphertexts : BitVec ,
2021-01-29 21:52:34 +00:00
}
2021-02-05 21:57:25 +00:00
impl < const GAMMA : u8 > Serialize for Tag < { GAMMA } > {
2021-02-04 07:32:10 +00:00
fn serialize < S > ( & self , serializer : S ) -> Result < S ::Ok , S ::Error >
where
S : Serializer ,
{
use serde ::ser ::SerializeTuple ;
let compressed = self . compress ( ) ;
let mut tup = serializer . serialize_tuple ( compressed . len ( ) ) ? ;
for byte in compressed . iter ( ) {
tup . serialize_element ( byte ) ? ;
}
tup . end ( )
}
}
2021-02-05 21:57:25 +00:00
impl < ' de , const GAMMA : u8 > Deserialize < ' de > for Tag < { GAMMA } > {
2021-02-04 07:32:10 +00:00
fn deserialize < D > ( deserializer : D ) -> Result < Self , D ::Error >
where
D : Deserializer < ' de > ,
{
2021-02-04 20:54:09 +00:00
struct FuzzyTagVisitor < const GAMMA : u8 > ;
2021-02-04 07:32:10 +00:00
2021-02-04 20:54:09 +00:00
impl < ' de , const GAMMA : u8 > Visitor < ' de > for FuzzyTagVisitor < { GAMMA } > {
2021-02-05 21:57:25 +00:00
type Value = Tag < { GAMMA } > ;
2021-02-04 07:32:10 +00:00
fn expecting ( & self , formatter : & mut ::core ::fmt ::Formatter ) -> ::core ::fmt ::Result {
2021-10-02 22:59:39 +00:00
formatter . write_str ( " 64 bytes + GAMMA Bytes + GAMMA + bits of data " )
2021-02-04 07:32:10 +00:00
}
2021-02-05 21:57:25 +00:00
fn visit_seq < A > ( self , mut seq : A ) -> Result < Tag < { GAMMA } > , A ::Error >
2021-02-04 07:32:10 +00:00
where
A : serde ::de ::SeqAccess < ' de > ,
{
let mut bytes = vec! [ ] ;
for i in 0 .. 64 {
2021-05-20 06:31:14 +00:00
bytes . push ( seq . next_element ( ) ? . ok_or ( serde ::de ::Error ::invalid_length (
i ,
& " expected at least 64 bytes " ,
) ) ? ) ;
2021-02-04 07:32:10 +00:00
}
loop {
match seq . next_element ( ) . unwrap_or ( None ) {
Some ( x ) = > bytes . push ( x ) ,
_ = > break ,
}
}
2021-10-02 22:59:39 +00:00
2021-02-05 21:57:25 +00:00
Tag ::< GAMMA > ::decompress ( & bytes ) . ok_or ( serde ::de ::Error ::custom ( " invalid fuzzytag " ) )
2021-02-04 07:32:10 +00:00
}
}
2021-02-04 20:54:09 +00:00
// support up to GAMMA = 64
2021-10-02 22:59:39 +00:00
deserializer . deserialize_tuple ( ( 72 + GAMMA ) as usize , FuzzyTagVisitor ::< GAMMA > )
2021-02-04 07:32:10 +00:00
}
}
2021-02-05 21:57:25 +00:00
impl < const GAMMA : u8 > Tag < { GAMMA } > {
2021-02-04 07:32:10 +00:00
/// An optimal sized copy of the tag
/// Compressed u || y || ciphertext
/// 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)
/// ```
2021-05-20 08:53:33 +00:00
/// use rand::rngs::OsRng;
2021-02-05 21:57:25 +00:00
/// use fuzzytags::RootSecret;
2021-05-22 19:39:04 +00:00
/// let mut rng = OsRng;
/// let secret = RootSecret::<24>::generate(&mut rng);
2021-02-05 21:57:25 +00:00
/// let tagging_key = secret.tagging_key();
2021-02-04 07:32:10 +00:00
/// // extract a detection key
2021-02-05 21:57:25 +00:00
/// let detection_key = secret.extract_detection_key(5);
2021-02-04 07:32:10 +00:00
///
2021-02-05 21:57:25 +00:00
/// // Give tagging key to a another party...
2021-02-04 07:32:10 +00:00
/// // and then they can do...
2021-05-20 08:53:33 +00:00
/// let tag = tagging_key.generate_tag(&mut rng);
2021-02-04 07:32:10 +00:00
/// let compressed_tag = tag.compress();
/// ```
pub fn compress ( & self ) -> Vec < u8 > {
let mut bytes = vec! [ ] ;
bytes . extend_from_slice ( self . u . compress ( ) . as_bytes ( ) ) ;
bytes . extend_from_slice ( self . y . as_bytes ( ) ) ;
2021-10-02 22:59:39 +00:00
bytes . extend_from_slice ( self . z . as_slice ( ) ) ;
2021-02-04 07:32:10 +00:00
bytes . extend_from_slice ( self . ciphertexts . to_bytes ( ) . as_slice ( ) ) ;
bytes
}
/// decompress an optimally encoded fuzzytag byte array, returns None if invalid
/// You probably want to use one of the many serde `deserialize` apis instead (see README)
/// ```
2021-02-05 21:57:25 +00:00
/// use fuzzytags::{RootSecret, Tag};
2021-05-22 19:39:04 +00:00
/// use rand::rngs::OsRng;
/// let mut rng = OsRng;
/// let secret = RootSecret::<24>::generate(&mut rng);
2021-02-05 21:57:25 +00:00
/// let tagging_key = secret.tagging_key();
2021-02-04 07:32:10 +00:00
/// // extract a detection key
2021-02-05 21:57:25 +00:00
/// let detection_key = secret.extract_detection_key(5);
2021-02-04 07:32:10 +00:00
///
2021-02-05 21:57:25 +00:00
/// // Give tagging key to a another party...
2021-02-04 07:32:10 +00:00
/// // and then they can do...
2021-05-20 08:53:33 +00:00
/// let tag = tagging_key.generate_tag(&mut rng);
2021-02-04 07:32:10 +00:00
/// let compressed_tag = tag.compress();
2021-02-05 21:57:25 +00:00
/// let decompressed_tag = Tag::decompress(&compressed_tag).unwrap();
2021-02-04 17:36:34 +00:00
/// assert_eq!(tag, decompressed_tag);
2021-02-04 07:32:10 +00:00
/// ```
2021-02-05 21:57:25 +00:00
pub fn decompress ( bytes : & [ u8 ] ) -> Option < Tag < { GAMMA } > > {
2021-10-02 22:59:39 +00:00
if bytes . len ( ) > ( 64 + GAMMA as usize ) {
2021-02-04 07:32:10 +00:00
let ( u_bytes , rest ) = bytes . split_at ( 32 ) ;
2021-10-02 22:59:39 +00:00
let ( y_bytes , rest ) = rest . split_at ( 32 ) ;
let ( z_bytes , ciphertext ) = rest . split_at ( { GAMMA } as usize ) ;
2021-02-04 20:54:09 +00:00
// if the ciphertext is too short, then this is an invalid tag
let min_bytes = GAMMA / 8 ;
if ciphertext . len ( ) < min_bytes as usize {
return None ;
}
// This shouldn't actually fail, but for the safety...
let y_bytes_fixed = match < [ u8 ; 32 ] > ::try_from ( y_bytes ) {
Ok ( fixed_size ) = > fixed_size ,
_ = > return None ,
} ;
2021-10-02 22:59:39 +00:00
2021-02-04 20:54:09 +00:00
let mut ciphertexts = BitVec ::from_bytes ( ciphertext ) ;
ciphertexts . truncate ( GAMMA as usize ) ;
2021-05-20 06:31:14 +00:00
return match (
CompressedRistretto ::from_slice ( u_bytes ) . decompress ( ) ,
Scalar ::from_canonical_bytes ( y_bytes_fixed ) ,
2021-10-02 22:59:39 +00:00
z_bytes . to_vec ( ) ,
2021-05-20 06:31:14 +00:00
) {
2021-10-02 22:59:39 +00:00
( Some ( u ) , Some ( y ) , z ) = > Some ( Tag {
u ,
y ,
z : z ,
ciphertexts ,
} ) ,
2021-02-04 07:32:10 +00:00
_ = > None ,
} ;
}
None
}
}
2021-02-05 21:57:25 +00:00
impl < const GAMMA : u8 > Display for Tag < { GAMMA } > {
2021-02-01 19:28:40 +00:00
fn fmt ( & self , f : & mut Formatter < '_ > ) -> fmt ::Result {
write! (
f ,
2021-10-02 22:59:39 +00:00
" {} {} {} {} " ,
2021-02-01 19:28:40 +00:00
hex ::encode ( self . u . compress ( ) . as_bytes ( ) ) ,
hex ::encode ( self . y . as_bytes ( ) ) ,
2021-10-02 22:59:39 +00:00
hex ::encode ( self . z . as_slice ( ) ) ,
2021-02-01 19:28:40 +00:00
hex ::encode ( self . ciphertexts . to_bytes ( ) )
)
}
}
2021-05-20 06:31:14 +00:00
/// PrecomputeH is an encapsulation around the precomputation of the H function which
/// significantly speeds up testing. We define it for some additional type safety (to
/// prevent us from passing an uninitialized hash function to post_h
#[ derive(Clone) ]
struct PrecomputeH ( Sha3_256 ) ;
2021-02-05 21:57:25 +00:00
/// The complete secret. Can't directly be used for testing. Instead you will need to generate
/// a DetectionKey using `extract_detection_key`
2021-02-10 07:54:55 +00:00
#[ derive(Serialize, Deserialize) ]
2021-02-05 21:57:25 +00:00
pub struct RootSecret < const GAMMA : u8 > {
2021-02-01 22:20:00 +00:00
/// the detection key - this can be given to adversarial servers to help probabilistically
/// filter messages (with a false-positive rate derived from γ and a 0% false negative rate)
2021-02-05 21:57:25 +00:00
secret : Vec < Scalar > ,
2021-02-01 22:20:00 +00:00
}
2021-01-30 10:15:08 +00:00
2021-02-05 21:57:25 +00:00
impl < const GAMMA : u8 > RootSecret < { GAMMA } > {
2021-02-04 20:54:09 +00:00
/// Generate a new Key Pair given a security parameter `GAMMA`. Tags generated for a given
2021-02-05 21:57:25 +00:00
/// `TaggingKey::generate_tag` will pass the `DetectionKey::test_tag` for other tagging
2021-02-01 22:20:00 +00:00
/// keys with probability $ 2 ^ -8 $
/// Example:
/// ```
2021-02-05 21:57:25 +00:00
/// use fuzzytags::{RootSecret};
2021-05-22 19:39:04 +00:00
/// use rand::rngs::OsRng;
/// let mut rng = OsRng;
/// let secret = RootSecret::<24>::generate(&mut rng);
2021-02-01 22:20:00 +00:00
/// ```
2021-05-22 19:39:04 +00:00
pub fn generate < R : RngCore + CryptoRng > ( rng : & mut R ) -> RootSecret < { GAMMA } > {
2021-02-05 21:57:25 +00:00
let mut secret = vec! [ ] ;
2021-02-04 20:54:09 +00:00
for _i in 0 .. GAMMA {
2021-05-22 19:39:04 +00:00
let sk_i = Scalar ::random ( rng ) ;
2021-02-05 21:57:25 +00:00
secret . push ( sk_i ) ;
2021-02-01 22:20:00 +00:00
}
2021-05-20 06:31:14 +00:00
RootSecret ::< GAMMA > { secret }
2021-02-01 22:20:00 +00:00
}
2021-01-30 10:15:08 +00:00
/// extract a detection key for a given false positive (p = 2^-n)
2021-02-05 21:57:25 +00:00
/// This is the key that can be given to adversarail servers so that they can
/// detected messages that *may* be tagged for a given detection key with an
/// ideal false positive rate 2^{-n}
2021-01-31 23:42:37 +00:00
/// Example:
/// ```
2021-02-05 21:57:25 +00:00
/// use fuzzytags::{RootSecret};
2021-05-22 19:39:04 +00:00
/// use rand::rngs::OsRng;
/// let mut rng = OsRng;
/// let secret = RootSecret::<24>::generate(&mut rng);
2021-02-05 21:57:25 +00:00
/// let detection_key = secret.extract_detection_key(2);
2021-01-31 23:42:37 +00:00
/// ```
2021-02-05 21:57:25 +00:00
pub fn extract_detection_key ( & self , n : usize ) -> DetectionKey < { GAMMA } > {
let parts = self . secret . iter ( ) . take ( n ) . cloned ( ) . collect ( ) ;
DetectionKey ::< GAMMA > { 0 : parts }
2021-01-30 10:15:08 +00:00
}
2021-02-01 22:20:00 +00:00
2021-02-05 21:57:25 +00:00
/// derive the tagging key for this secret
2021-02-01 22:20:00 +00:00
/// Example:
/// ```
2021-02-05 21:57:25 +00:00
/// use fuzzytags::RootSecret;
2021-05-22 19:39:04 +00:00
/// use rand::rngs::OsRng;
/// let mut rng = OsRng;
/// let secret = RootSecret::<24>::generate(&mut rng);
2021-02-05 21:57:25 +00:00
/// let tagging_key = secret.tagging_key();
2021-02-01 22:20:00 +00:00
/// ```
2021-02-05 21:57:25 +00:00
pub fn tagging_key ( & self ) -> TaggingKey < { GAMMA } > {
2021-02-10 07:54:55 +00:00
let g = RISTRETTO_BASEPOINT_POINT ;
let mut tagging_key = vec! [ ] ;
for sk_i in self . secret . iter ( ) {
let pk_i = g . mul ( sk_i ) ;
tagging_key . push ( pk_i ) ;
}
2021-02-14 04:39:57 +00:00
TaggingKey ::< GAMMA > { 0 : tagging_key }
2021-02-01 22:20:00 +00:00
}
2021-10-02 22:59:39 +00:00
/// 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 }
}
2021-05-20 06:31:14 +00:00
/// precompute the first part of h
fn pre_h ( u : RistrettoPoint , w : RistrettoPoint ) -> PrecomputeH {
let mut hash = sha3 ::Sha3_256 ::new ( ) ;
hash . update ( & [ GAMMA ] ) ;
hash . update ( u . compress ( ) . as_bytes ( ) ) ;
hash . update ( w . compress ( ) . as_bytes ( ) ) ;
return PrecomputeH ( hash ) ;
}
2021-10-02 22:59:39 +00:00
fn nonce_h ( mut hash : PrecomputeH , nonce : u8 ) -> PrecomputeH {
hash . 0. update ( vec! [ nonce ] ) ;
hash
}
2021-05-20 06:31:14 +00:00
/// 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 ( ) ) ;
return hash . 0. finalize ( ) . as_slice ( ) [ 0 ] & 0x01 ;
}
2021-02-01 22:20:00 +00:00
/// a hash function which takes a ristretto point and a vector of ciphertexts and outputs a
/// ristretto scalar.
fn g ( u : RistrettoPoint , points : & BitVec ) -> Scalar {
2021-02-04 20:54:09 +00:00
let mut input = vec! [ ] ;
input . push ( GAMMA ) ;
input . extend_from_slice ( points . to_bytes ( ) . as_slice ( ) ) ;
2021-02-01 22:20:00 +00:00
input . extend_from_slice ( u . compress ( ) . as_bytes ( ) ) ;
Scalar ::hash_from_bytes ::< Sha3_512 > ( input . as_slice ( ) )
}
2021-01-30 10:15:08 +00:00
}
2021-01-30 19:33:30 +00:00
/// A collection of "secret" data that can be used to determine if a `FuzzyTag` was intended for
2021-02-05 21:57:25 +00:00
/// the derived tagging key with probability p
2021-02-01 19:28:40 +00:00
#[ derive(Clone, Debug, Serialize, Deserialize) ]
2021-02-05 21:57:25 +00:00
pub struct DetectionKey < const GAMMA : u8 > ( Vec < Scalar > ) ;
2021-01-30 02:18:08 +00:00
2021-02-05 21:57:25 +00:00
impl < const GAMMA : u8 > DetectionKey < { GAMMA } > {
2021-02-01 19:28:40 +00:00
/// a convenient id for a detection key for internal accounting purposes
/// do not expose this to applications
pub fn id ( & self ) -> String {
let mut hash = sha3 ::Sha3_256 ::new ( ) ;
for s in self . 0. iter ( ) {
hash . update ( s . as_bytes ( ) )
}
2021-02-01 21:25:26 +00:00
format! ( " {} " , hex ::encode ( hash . finalize ( ) . as_slice ( ) ) , )
2021-02-01 19:28:40 +00:00
}
}
2021-02-05 21:57:25 +00:00
impl < const GAMMA : u8 > Display for DetectionKey < { GAMMA } > {
2021-02-01 19:28:40 +00:00
fn fmt ( & self , f : & mut Formatter < '_ > ) -> fmt ::Result {
write! ( f , " {} " , self . id ( ) )
}
}
2021-02-05 21:57:25 +00:00
impl < const GAMMA : u8 > DetectionKey < { GAMMA } > {
2021-01-31 21:21:44 +00:00
/// calculate the ideal false positive rate of this detection key
2021-01-31 23:42:37 +00:00
/// ```
2021-02-05 21:57:25 +00:00
/// use fuzzytags::RootSecret;
2021-05-22 19:39:04 +00:00
/// use rand::rngs::OsRng;
/// let mut rng = OsRng;
/// let secret = RootSecret::<24>::generate(&mut rng);
2021-02-05 21:57:25 +00:00
/// let tagging_key = secret.tagging_key();
2021-01-31 23:42:37 +00:00
/// // extract a detection key
2021-02-05 21:57:25 +00:00
/// let detection_key = secret.extract_detection_key(5);
2021-01-31 23:42:37 +00:00
/// detection_key.false_positive_probability();
/// ```
2021-01-31 21:21:44 +00:00
pub fn false_positive_probability ( & self ) -> f64 {
( 2.0_ f64 ) . powi ( 0 - ( self . 0. len ( ) as i32 ) )
}
2021-01-30 02:18:08 +00:00
/// returns true if the tag was intended for this key
2021-01-31 23:42:37 +00:00
/// Example:
/// ```
2021-02-05 21:57:25 +00:00
/// use fuzzytags::RootSecret;
2021-05-22 19:39:04 +00:00
/// use rand::rngs::OsRng;
/// let mut rng = OsRng;
/// let secret = RootSecret::<24>::generate(&mut rng);
2021-02-05 21:57:25 +00:00
/// let tagging_key = secret.tagging_key();
2021-01-31 23:42:37 +00:00
/// // extract a detection key
2021-02-05 21:57:25 +00:00
/// let detection_key = secret.extract_detection_key(5);
2021-01-31 23:42:37 +00:00
///
2021-02-05 21:57:25 +00:00
/// // Give tagging key to a another party...
2021-01-31 23:42:37 +00:00
/// // and then they can do...
2021-05-20 08:53:33 +00:00
/// let tag = tagging_key.generate_tag(&mut rng);
2021-01-31 23:42:37 +00:00
///
/// // The server can now do this:
/// if detection_key.test_tag(&tag) {
/// // the message attached to this tag *might* be for the party associated with the detection key
/// } else {
/// // the message attached to this tag is definitely *not* for the party associated with the detection key.
/// }
/// ```
2021-02-05 21:57:25 +00:00
pub fn test_tag ( & self , tag : & Tag < { GAMMA } > ) -> bool {
2021-02-03 02:20:45 +00:00
// A few checks to make sure the tag is well formed.
2021-02-05 21:57:25 +00:00
// All zeros in u or y can lead to a tag that validates against *all* tagging keys
2021-02-03 02:20:45 +00:00
// That doesn't seem like a great idea, so we return false to be safe.
// Zero values should never appear in well generated tags.
if tag . u . eq ( & RistrettoPoint ::default ( ) ) | | tag . y . eq ( & Scalar ::zero ( ) ) {
return false ;
}
2021-01-30 02:18:08 +00:00
2021-10-02 22:59:39 +00:00
let mut bitvec = BitVec ::from_bytes ( & * tag . z ) ;
bitvec . append ( tag . ciphertexts . clone ( ) . borrow_mut ( ) ) ;
let m = RootSecret ::< GAMMA > ::g ( tag . u , & bitvec ) ;
2021-01-30 02:18:08 +00:00
let g = RISTRETTO_BASEPOINT_POINT ;
// Re-derive w = g^z from the public tag.
2021-02-05 21:57:25 +00:00
// y = (1/r) * (z-m)
2021-01-30 02:18:08 +00:00
// 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
2021-01-30 07:09:02 +00:00
// See below for a full explanation as to the reason for this:
2021-02-01 21:25:26 +00:00
let w = RistrettoPoint ::multiscalar_mul ( & [ m , tag . y ] , & [ g , tag . u ] ) ;
2021-01-30 02:18:08 +00:00
2021-05-20 06:31:14 +00:00
let pre_h = RootSecret ::< GAMMA > ::pre_h ( tag . u , w ) ;
2021-02-05 21:57:25 +00:00
// for each secret part...
2021-05-20 06:31:14 +00:00
let mut result = 0 ;
2021-10-02 22:59:39 +00:00
for ( nonce_i , ( x_i , c_i ) ) in self . 0. iter ( ) . zip ( & tag . ciphertexts ) . enumerate ( ) {
2021-01-30 02:18:08 +00:00
// re-derive the key from the tag
2021-10-02 22:59:39 +00:00
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 ) ) ;
2021-01-30 02:18:08 +00:00
// calculate the "original" plaintext
2021-05-20 06:31:14 +00:00
let b_i = k_i ^ ( c_i as u8 ) ;
// short circuit
if b_i ! = 0x01 {
2021-02-13 20:11:18 +00:00
return false ;
}
2021-01-30 07:38:20 +00:00
// assert that the plaintext is all 1's
2021-05-20 06:31:14 +00:00
result + = 1 ;
2021-01-30 02:18:08 +00:00
}
2021-05-20 06:31:14 +00:00
// Assert that number of sequential ones is equal to the length of the detection key
// If it isn't it indicates that the tag ciphertext is shorter than the verification key,
// Given the checks on deserialization that should never happen, but we throw in a check
// here anyway for defense in depth.
return result = = self . 0. len ( ) ;
2021-01-30 02:18:08 +00:00
}
2021-02-13 20:11:18 +00:00
/// A bulk testing function that takes in an vector of detection keys and returns a vector
2021-02-16 22:33:37 +00:00
/// of indexes where the tag matched. (Indexes not guarenteed to be ordered).
/// This function may spin up additional threads depending on the number of detection
/// keys provided.
/// ```
/// use fuzzytags::{TaggingKey, DetectionKey};
/// use fuzzytags::RootSecret;
2021-05-22 19:39:04 +00:00
/// use rand::rngs::OsRng;
/// let mut rng = OsRng;
/// let secrets: Vec<RootSecret<24>> = (0..2).map(|_x| RootSecret::<24>::generate(&mut rng)).collect();
2021-02-16 22:33:37 +00:00
/// 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
2021-05-20 08:53:33 +00:00
/// let entangled_tag = TaggingKey::generate_entangled_tag(tagging_keys, &mut rng, 16);
2021-02-16 22:33:37 +00:00
/// 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(), 2);
/// ```
2021-02-13 20:11:18 +00:00
#[ cfg(feature = " bulk_verify " ) ]
2021-02-14 04:39:57 +00:00
pub fn test_tag_bulk ( detection_keys : & Vec < DetectionKey < { GAMMA } > > , tag : & Tag < { GAMMA } > ) -> Vec < usize > {
2021-02-13 20:11:18 +00:00
// A few checks to make sure the tag is well formed.
// All zeros in u or y can lead to a tag that validates against *all* tagging keys
// That doesn't seem like a great idea, so we return false to be safe.
// Zero values should never appear in well generated tags.
if tag . u . eq ( & RistrettoPoint ::default ( ) ) | | tag . y . eq ( & Scalar ::zero ( ) ) {
return vec! [ ] ;
}
2021-10-02 22:59:39 +00:00
let mut bitvec = BitVec ::from_bytes ( & * tag . z ) ;
bitvec . append ( tag . ciphertexts . clone ( ) . borrow_mut ( ) ) ;
let m = RootSecret ::< GAMMA > ::g ( tag . u , & bitvec ) ;
2021-02-13 20:11:18 +00:00
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
// See below for a full explanation as to the reason for this:
let w = RistrettoPoint ::multiscalar_mul ( & [ m , tag . y ] , & [ g , tag . u ] ) ;
let ( tx , rx ) = channel ( ) ;
2021-05-20 08:11:07 +00:00
let pre_h = RootSecret ::< GAMMA > ::pre_h ( tag . u , w ) ;
2021-02-13 20:11:18 +00:00
// for each secret part...
2021-02-14 04:39:57 +00:00
let mut results : Vec < usize > = vec! [ ] ;
2021-05-20 06:31:14 +00:00
detection_keys
. par_iter ( )
. enumerate ( )
. for_each_with ( tx . clone ( ) , | tx , ( index , detection_key ) | {
2021-05-20 08:11:07 +00:00
let mut result = 0 ;
2021-10-02 22:59:39 +00:00
for ( nonce_i , ( x_i , c_i ) ) in detection_key . 0. iter ( ) . zip ( & tag . ciphertexts ) . enumerate ( ) {
2021-05-20 06:31:14 +00:00
// re-derive the key from the tag
2021-10-02 22:59:39 +00:00
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 ) ) ;
2021-05-20 06:31:14 +00:00
// calculate the "original" plaintext
2021-05-20 08:11:07 +00:00
let b_i = k_i ^ ( c_i as u8 ) ;
2021-05-20 06:31:14 +00:00
if b_i ! = 1 {
break ;
}
// assert that the plaintext is all 1's
2021-05-20 08:11:07 +00:00
result + = 1 ;
2021-02-13 20:11:18 +00:00
}
2021-05-20 08:11:07 +00:00
if result = = detection_key . 0. len ( ) {
2021-05-20 06:31:14 +00:00
match tx . send ( index ) {
_ = > {
// TODO...surface this error...
}
2021-02-14 04:39:57 +00:00
}
}
2021-05-20 06:31:14 +00:00
} ) ;
2021-02-13 20:11:18 +00:00
std ::mem ::drop ( tx ) ;
loop {
let result = rx . recv ( ) ;
match result {
2021-02-14 04:39:57 +00:00
Ok ( index ) = > results . push ( index ) ,
2021-02-13 20:11:18 +00:00
_ = > {
break ;
}
}
}
return results ;
}
2021-01-30 02:18:08 +00:00
}
/// A public identity that others can create tags for.
2021-01-31 23:42:37 +00:00
#[ derive(Clone, Debug, Serialize, Deserialize) ]
2021-02-05 21:57:25 +00:00
pub struct TaggingKey < const GAMMA : u8 > ( Vec < RistrettoPoint > ) ;
2021-01-30 02:18:08 +00:00
2021-02-05 21:57:25 +00:00
impl < const GAMMA : u8 > TaggingKey < { GAMMA } > {
/// a convenient id for a tagging key for internal accounting purposes
2021-02-01 19:28:40 +00:00
/// do not expose this to applications
pub fn id ( & self ) -> String {
let mut hash = sha3 ::Sha3_256 ::new ( ) ;
for s in self . 0. iter ( ) {
hash . update ( s . compress ( ) . as_bytes ( ) )
}
2021-02-01 21:25:26 +00:00
format! ( " {} " , hex ::encode ( hash . finalize ( ) . as_slice ( ) ) , )
2021-02-01 19:28:40 +00:00
}
2021-02-05 21:57:25 +00:00
/// generate a new tag for this tagging key
2021-01-31 23:42:37 +00:00
/// Example:
/// ```
2021-02-05 21:57:25 +00:00
/// use fuzzytags::{RootSecret};
2021-05-20 08:53:33 +00:00
/// use rand::rngs::OsRng;
2021-05-22 19:39:04 +00:00
/// let mut rng = OsRng;
/// let secret = RootSecret::<24>::generate(&mut rng);
/// let tagging_key = secret.tagging_key(); // give this to a sender
2021-05-20 08:53:33 +00:00
/// let tag = tagging_key.generate_tag(&mut rng);
2021-01-31 23:42:37 +00:00
/// ```
2021-05-20 08:53:33 +00:00
pub fn generate_tag < R : RngCore + CryptoRng > ( & self , rng : & mut R ) -> Tag < { GAMMA } > {
2021-01-30 02:18:08 +00:00
// generate some random points...
2021-05-20 08:53:33 +00:00
let r = Scalar ::random ( rng ) ;
let u = RISTRETTO_BASEPOINT_POINT . mul ( r ) ;
let z = Scalar ::random ( rng ) ;
let w = RISTRETTO_BASEPOINT_POINT . mul ( z ) ;
// precompute the first part of the `H` hash function
2021-05-20 08:11:07 +00:00
let pre_h = RootSecret ::< GAMMA > ::pre_h ( u , w ) ;
2021-05-20 08:53:33 +00:00
2021-01-30 02:18:08 +00:00
// construct the ciphertext portion of the tag
2021-05-20 08:53:33 +00:00
let mut ciphertexts = BitVec ::with_capacity ( GAMMA . into ( ) ) ;
2021-10-02 22:59:39 +00:00
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 ) ) ;
2021-01-30 07:38:20 +00:00
// encrypt a plaintext of all 1's
2021-01-30 02:18:08 +00:00
let c_i = k_i ^ 0x01 ;
ciphertexts . push ( c_i = = 0x01 ) ;
}
2021-01-30 07:09:02 +00:00
// Without this next part, this scheme would not be CCA-secure. Consider a scheme with just
// u = ^r and and h_i^r = g^(x_i*r)
// An adversarial server with access to a Test oracle (i.e. the decryption key) may be able
// to maul a challenge ciphertext by e.g. replacing the order of the ciphertexts.
2021-01-30 02:18:08 +00:00
// From the paper:
// "The value w corresponds to a chameleon hash [KR00] computed on the message (0,z), where z is chosen at random.
2021-02-05 21:57:25 +00:00
// Once the ciphertext has been computed, we use a master trapdoor for the chameleon hash (which is part of the scheme’ s DetectionKey) in order to compute a collision (y,m) where m
2021-01-30 02:18:08 +00:00
// is a hash of the remaining components of the ciphertext"
2021-05-22 20:01:43 +00:00
// Translated, m is a challenge over the random element u and the ordered ciphertexts
2021-01-30 07:09:02 +00:00
// It is then used to construct a response y which can be used to recover w the random element
// used to derive the key.
2021-01-30 02:18:08 +00:00
// finally calculate a `y` = 1/r * (z-m) which will be used to re-derive `w`
2021-10-02 22:59:39 +00:00
let mut bitvec = BitVec ::from_bytes ( & * nonces ) ;
bitvec . append ( ciphertexts . clone ( ) . borrow_mut ( ) ) ;
let m = RootSecret ::< GAMMA > ::g ( u , & bitvec ) ;
2021-01-30 02:18:08 +00:00
let y = r . invert ( ) . mul ( z . sub ( m ) ) ;
2021-10-02 22:59:39 +00:00
return Tag {
u ,
y ,
z : nonces ,
ciphertexts ,
} ;
2021-01-30 02:18:08 +00:00
}
2021-02-02 06:27:37 +00:00
2021-02-03 00:19:20 +00:00
#[ cfg(feature = " entangled " ) ]
2021-02-02 06:27:37 +00:00
/// WARNING: if you pass in a large length into this function it will take a long time!
2021-02-03 04:50:00 +00:00
/// This begins a very slow, but parallel, search for a tag that will validate of the given
2021-02-05 21:57:25 +00:00
/// tagging keys up to a given false positive rate 2^-l
2021-02-03 00:19:20 +00:00
/// Example:
/// ```
2021-02-05 21:57:25 +00:00
/// use fuzzytags::{RootSecret, TaggingKey};
2021-05-22 19:39:04 +00:00
/// use rand::rngs::OsRng;
/// let mut rng = OsRng;
/// let secret_1 = RootSecret::<24>::generate(&mut rng);
/// let secret_2 = RootSecret::<24>::generate(&mut rng);
2021-02-05 21:57:25 +00:00
/// let tagging_key_1 = secret_1.tagging_key(); // give this to a sender
/// let tagging_key_2 = secret_2.tagging_key(); // give this to a sender
/// // Will validate for detection keys derived from both secret_1 and secret_2 up
2021-02-03 00:19:20 +00:00
/// // to n=8
2021-02-03 04:50:00 +00:00
/// // Sender can now do...tag will validate on detection keys of length 8 or lower.
2021-05-20 08:53:33 +00:00
/// let tag = TaggingKey::generate_entangled_tag(vec![tagging_key_1,tagging_key_2], &mut rng, 8);
2021-02-03 00:19:20 +00:00
/// ```
2021-05-20 08:53:33 +00:00
pub fn generate_entangled_tag < R : RngCore + CryptoRng > ( tagging_keys : Vec < TaggingKey < { GAMMA } > > , rng : & mut R , length : usize ) -> Tag < { GAMMA } > {
2021-02-02 06:27:37 +00:00
let g = RISTRETTO_BASEPOINT_POINT ;
// generate some random points...
2021-05-20 08:53:33 +00:00
let r = Scalar ::random ( rng ) ;
2021-02-02 06:27:37 +00:00
let u = g . mul ( r ) ;
2021-02-03 04:50:00 +00:00
// Compute and cache some public points that we will be using over and over again
2021-02-05 21:57:25 +00:00
let mut tagging_key_precomputes = vec! [ ] ;
for tagging_key in tagging_keys . iter ( ) {
2021-02-02 06:27:37 +00:00
let mut precompute = vec! [ ] ;
2021-02-05 21:57:25 +00:00
for i in tagging_key . 0. iter ( ) {
2021-02-02 06:27:37 +00:00
precompute . push ( i . mul ( r ) ) ;
}
2021-02-05 21:57:25 +00:00
tagging_key_precomputes . push ( precompute ) ;
2021-02-02 06:27:37 +00:00
}
2021-10-02 22:59:39 +00:00
// generate some random points...
2021-05-20 08:11:07 +00:00
2021-10-02 22:59:39 +00:00
loop {
let z = Scalar ::random ( rng ) ;
2021-02-02 06:27:37 +00:00
let w = g . mul ( z ) ;
2021-05-20 08:11:07 +00:00
let pre_h = RootSecret ::< GAMMA > ::pre_h ( u , w ) ;
2021-02-04 04:15:09 +00:00
let mut key = vec! [ ] ;
2021-10-02 22:59:39 +00:00
let mut nonces = vec! [ ] ;
2021-02-05 21:57:25 +00:00
for ( i , precompute ) in tagging_key_precomputes [ 0 ] . iter ( ) . enumerate ( ) {
2021-10-02 22:59:39 +00:00
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 ;
}
2021-02-02 06:27:37 +00:00
}
}
2021-10-02 22:59:39 +00:00
if success {
nonces . push ( nonce ) ;
key . push ( k_i ) ;
break ;
}
2021-02-02 06:27:37 +00:00
}
2021-10-02 22:59:39 +00:00
continue ;
2021-02-04 04:15:09 +00:00
}
// generate the tag
let mut ciphertexts = BitVec ::new ( ) ;
for k_i in key . iter ( ) {
2021-02-02 06:27:37 +00:00
// encrypt a plaintext of all 1's
let c_i = k_i ^ 0x01 ;
ciphertexts . push ( c_i = = 0x01 ) ;
}
2021-02-04 04:15:09 +00:00
// This is the same as generate_tag, kept separate to avoid over-decomposition
2021-10-02 22:59:39 +00:00
let mut bitvec = BitVec ::from_bytes ( & * nonces ) ;
bitvec . append ( ciphertexts . clone ( ) . borrow_mut ( ) ) ;
let m = RootSecret ::< GAMMA > ::g ( u , & bitvec ) ;
2021-02-04 04:15:09 +00:00
let y = r . invert ( ) . mul ( z . sub ( m ) ) ;
2021-10-02 22:59:39 +00:00
return Tag {
u ,
y ,
z : nonces ,
ciphertexts ,
} ;
}
2021-02-02 06:27:37 +00:00
}
2021-01-30 02:18:08 +00:00
}
2021-01-29 21:52:34 +00:00
#[ cfg(test) ]
mod tests {
2021-05-20 08:11:07 +00:00
use crate ::{ DetectionKey , RootSecret , Tag } ;
2021-02-03 02:20:45 +00:00
use bit_vec ::BitVec ;
use curve25519_dalek ::ristretto ::RistrettoPoint ;
use curve25519_dalek ::scalar ::Scalar ;
2021-05-20 06:31:14 +00:00
use rand ::rngs ::OsRng ;
2021-05-22 19:39:04 +00:00
use sha3 ::Digest ;
2021-01-29 21:52:34 +00:00
2021-02-04 17:36:34 +00:00
#[ test ]
fn test_compression ( ) {
2021-05-22 19:39:04 +00:00
let mut rng = OsRng ;
let secret = RootSecret ::< 24 > ::generate ( & mut rng ) ;
2021-02-05 21:57:25 +00:00
let tagging_key = secret . tagging_key ( ) ;
2021-02-04 17:36:34 +00:00
2021-02-05 21:57:25 +00:00
// Give tagging key to a another party...
2021-02-04 17:36:34 +00:00
// and then they can do...
2021-05-20 08:53:33 +00:00
let tag = tagging_key . generate_tag ( & mut rng ) ;
2021-02-04 17:36:34 +00:00
let compressed_tag = tag . compress ( ) ;
2021-02-05 21:57:25 +00:00
let decompressed_tag = Tag ::< 24 > ::decompress ( & compressed_tag ) . unwrap ( ) ;
2021-02-04 17:36:34 +00:00
assert_eq! ( tag , decompressed_tag ) ;
}
2021-01-30 10:15:08 +00:00
#[ test ]
fn test_serialization ( ) {
2021-02-04 20:54:09 +00:00
// generate some new keys...
2021-05-22 19:39:04 +00:00
let mut rng = OsRng ;
let secret = RootSecret ::< 15 > ::generate ( & mut rng ) ;
2021-05-20 08:53:33 +00:00
let tag = secret . tagging_key ( ) . generate_tag ( & mut rng ) ;
2021-02-05 21:57:25 +00:00
let detection_key = secret . extract_detection_key ( 10 ) ;
2021-02-04 20:54:09 +00:00
let serialized_tag = serde_json ::to_string ( & tag ) . unwrap ( ) ;
2021-02-08 16:30:16 +00:00
println! ( " {} " , serialized_tag ) ;
2021-02-05 21:57:25 +00:00
let deserialized_tag : Tag < 15 > = serde_json ::from_str ( & serialized_tag ) . unwrap ( ) ;
2021-02-04 20:54:09 +00:00
assert_eq! ( tag . compress ( ) , deserialized_tag . compress ( ) ) ;
assert_eq! ( true , detection_key . test_tag ( & deserialized_tag ) ) ;
// generate some new keys...
2021-05-22 19:39:04 +00:00
let secret = RootSecret ::< 24 > ::generate ( & mut rng ) ;
2021-05-20 08:53:33 +00:00
let tag = secret . tagging_key ( ) . generate_tag ( & mut rng ) ;
2021-02-05 21:57:25 +00:00
let detection_key = secret . extract_detection_key ( 10 ) ;
2021-02-04 20:54:09 +00:00
let serialized_tag = serde_json ::to_string ( & tag ) . unwrap ( ) ;
2021-02-05 21:57:25 +00:00
let deserialized_tag : Tag < 24 > = serde_json ::from_str ( & serialized_tag ) . unwrap ( ) ;
2021-02-04 20:54:09 +00:00
assert_eq! ( tag . compress ( ) , deserialized_tag . compress ( ) ) ;
assert_eq! ( true , detection_key . test_tag ( & deserialized_tag ) ) ;
// Test some bincode...
let bincode_tag = bincode ::serialize ( & tag ) ;
2021-10-02 22:59:39 +00:00
// println!("Serialized: {:?}", bincode_tag);
2021-02-05 21:57:25 +00:00
let deserialized_tag : Tag < 24 > = bincode ::deserialize ( & bincode_tag . unwrap ( ) ) . unwrap ( ) ;
2021-02-04 20:54:09 +00:00
//println!("Deserialized: {}", deserialized_tag);
//assert_eq!(tag.compress(), deserialized_tag.compress());
assert_eq! ( true , detection_key . test_tag ( & deserialized_tag ) ) ;
2021-01-30 10:15:08 +00:00
}
2021-02-08 16:30:16 +00:00
#[ test ]
fn test_invalid_serializations ( ) {
let deserialized_tag : Option < Tag < 24 > > = serde_json ::from_str ( " [0,1,2] " ) . unwrap_or ( None ) ;
assert_eq! ( deserialized_tag , None ) ;
// too short (ciphertext)
let deserialized_tag : Result < Tag < 15 > , serde_json ::Error > = serde_json ::from_str ( " [140,198,182,161,124,132,111,222,62,235,59,249,152,203,170,89,150,27,251,252,41,159,134,34,112,61,117,249,35,126,29,1,100,157,229,106,42,68,167,89,109,137,234,37,124,139,59,116,221,74,24,229,97,154,7,34,236,248,90,130,150,116,182,11]
" );
assert_eq! ( deserialized_tag . is_ok ( ) , false ) ;
// much too short
let deserialized_tag : Option < Tag < 15 > > = serde_json ::from_str ( " [140,198,182,161,124,132,111,222,62,235,59,249,152,203,170,89,150,27,251,252,41,159,134,34,112,61,117,249,35,126,29,1,100,157,229,106,42,68,167,89,109,137,234,37,124,139,59,116,221,74,24,229,97,154,7,34,236]
" ).unwrap_or(None);
assert_eq! ( deserialized_tag , None ) ;
}
2021-02-02 06:27:37 +00:00
#[ test ]
2021-02-03 00:19:20 +00:00
#[ cfg(feature = " entangled " ) ]
2021-02-02 06:27:37 +00:00
fn test_multiple ( ) {
2021-02-05 21:57:25 +00:00
use crate ::TaggingKey ;
2021-05-22 19:39:04 +00:00
let mut rng = OsRng ;
2021-05-22 19:49:11 +00:00
let secrets : Vec < RootSecret < 24 > > = ( 0 .. 2 )
. map ( | _x | RootSecret ::< 24 > ::generate ( & mut rng ) )
. collect ( ) ;
2021-02-05 21:57:25 +00:00
let tagging_keys : Vec < TaggingKey < 24 > > = secrets . iter ( ) . map ( | x | x . tagging_key ( ) ) . collect ( ) ;
2021-05-22 19:39:04 +00:00
// 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 ) ;
2021-10-02 22:59:39 +00:00
println! ( " entangled tag: {} " , entangled_tag ) ;
2021-02-05 21:57:25 +00:00
for secret in secrets . iter ( ) {
2021-05-22 19:39:04 +00:00
let detection_key = secret . extract_detection_key ( len ) ;
2021-02-02 06:27:37 +00:00
assert! ( detection_key . test_tag ( & entangled_tag ) ) ;
println! ( " {} " , detection_key ) ;
}
}
2021-02-13 20:11:18 +00:00
#[ test ]
#[ cfg(feature = " bulk_verify " ) ]
fn test_check_multiple ( ) {
use crate ::TaggingKey ;
2021-05-22 19:39:04 +00:00
let mut rng = OsRng ;
2021-10-02 22:59:39 +00:00
let secrets : Vec < RootSecret < 24 > > = ( 0 .. 5 )
2021-05-22 19:49:11 +00:00
. map ( | _x | RootSecret ::< 24 > ::generate ( & mut rng ) )
. collect ( ) ;
2021-02-13 20:11:18 +00:00
let tagging_keys : Vec < TaggingKey < 24 > > = secrets . iter ( ) . map ( | x | x . tagging_key ( ) ) . collect ( ) ;
2021-05-22 19:39:04 +00:00
// 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
2021-10-02 22:59:39 +00:00
let entangled_tag = TaggingKey ::generate_entangled_tag ( tagging_keys , & mut rng , 24 ) ;
2021-05-20 06:31:14 +00:00
let detection_keys = secrets
. iter ( )
. map ( | x | x . extract_detection_key ( 16 ) )
. collect ( ) ;
2021-02-13 20:11:18 +00:00
let results = DetectionKey ::test_tag_bulk ( & detection_keys , & entangled_tag ) ;
2021-10-02 22:59:39 +00:00
assert_eq! ( results . len ( ) , 5 ) ;
2021-02-13 20:11:18 +00:00
}
2021-01-29 21:52:34 +00:00
#[ test ]
fn correctness ( ) {
let number_of_messages = 100 ;
2021-05-22 19:39:04 +00:00
let mut rng = OsRng ;
2021-10-02 22:59:39 +00:00
let secret = RootSecret ::< 24 > ::generate ( & mut rng ) ;
2021-01-29 21:52:34 +00:00
for i in 0 .. number_of_messages {
2021-05-20 08:53:33 +00:00
let tag = secret . tagging_key ( ) . generate_tag ( & mut rng ) ;
2021-01-29 21:52:34 +00:00
println! ( " {} : {} " , i , tag ) ;
2021-02-05 21:57:25 +00:00
assert! ( secret . extract_detection_key ( 5 ) . test_tag ( & tag ) ) ;
2021-01-29 21:52:34 +00:00
}
}
2021-02-05 21:57:25 +00:00
fn gen_zero_tag_zero ( ) -> Tag < 24 > {
let tag = Tag {
2021-02-03 02:20:45 +00:00
u : RistrettoPoint ::default ( ) ,
y : Scalar ::default ( ) ,
2021-10-02 22:59:39 +00:00
z : vec ! [ ] ,
2021-02-04 20:54:09 +00:00
ciphertexts : BitVec ::from_elem ( 24 , false ) ,
2021-02-03 02:20:45 +00:00
} ;
tag
}
2021-02-05 21:57:25 +00:00
fn gen_zero_tag_one ( ) -> Tag < 24 > {
let mut tag = Tag {
2021-02-03 02:20:45 +00:00
u : RistrettoPoint ::default ( ) ,
y : Scalar ::default ( ) ,
2021-10-02 22:59:39 +00:00
z : vec ! [ ] ,
2021-02-04 20:54:09 +00:00
ciphertexts : BitVec ::from_elem ( 24 , false ) ,
2021-02-03 02:20:45 +00:00
} ;
tag . ciphertexts . set_all ( ) ;
tag
}
2021-05-22 19:39:04 +00:00
/// a hash function that takes 3 ristretto points as a parameter and outputs 0 or 1.
fn h ( u : RistrettoPoint , h : RistrettoPoint , w : RistrettoPoint ) -> u8 {
let mut hash = sha3 ::Sha3_256 ::new ( ) ;
hash . update ( & [ 24 ] ) ;
hash . update ( u . compress ( ) . as_bytes ( ) ) ;
hash . update ( w . compress ( ) . as_bytes ( ) ) ;
hash . update ( h . compress ( ) . as_bytes ( ) ) ;
return hash . finalize ( ) . as_slice ( ) [ 0 ] & 0x01 ;
}
2021-05-20 06:31:14 +00:00
#[ test ]
fn assert_h_and_pre_post_h ( ) {
2021-05-22 19:39:04 +00:00
let mut rng = OsRng ;
2021-05-20 06:31:14 +00:00
for _ in 0 .. 100 {
let a = RistrettoPoint ::random ( & mut rng ) ;
let b = RistrettoPoint ::random ( & mut rng ) ;
let c = RistrettoPoint ::random ( & mut rng ) ;
assert_eq! (
RootSecret ::< 24 > ::post_h ( RootSecret ::< 24 > ::pre_h ( a , b ) , c ) ,
2021-05-22 19:39:04 +00:00
h ( a , c , b )
2021-05-20 06:31:14 +00:00
) ;
}
}
2021-02-03 02:20:45 +00:00
#[ test ]
// Thanks to Lee Bousfield who noticed an all zeros or all ones tag would
2021-02-05 21:57:25 +00:00
// validate against a tagging key with 50% probability, allowing universal
2021-02-03 02:20:45 +00:00
// broadcast, which overall seems like a bad idea...
// Test to make sure that doesn't happen.
fn test_zero_tag ( ) {
2021-05-22 19:39:04 +00:00
let mut rng = OsRng ;
let secret = RootSecret ::< 24 > ::generate ( & mut rng ) ;
2021-02-04 20:54:09 +00:00
let tag = gen_zero_tag_zero ( ) ;
2021-02-05 21:57:25 +00:00
assert_eq! ( false , secret . extract_detection_key ( 6 ) . test_tag ( & tag ) ) ;
2021-02-04 20:54:09 +00:00
let tag = gen_zero_tag_one ( ) ;
2021-02-05 21:57:25 +00:00
assert_eq! ( false , secret . extract_detection_key ( 6 ) . test_tag ( & tag ) ) ;
2021-02-03 02:20:45 +00:00
}
2021-01-30 08:23:46 +00:00
#[ test ]
2021-02-01 22:20:00 +00:00
fn false_positives ( ) {
2021-05-22 19:39:04 +00:00
let mut rng = OsRng ;
2021-10-02 22:59:39 +00:00
let number_of_reference_checks = 100 ;
let number_of_messages = 100 ;
let limit = 1 ;
2021-01-29 23:47:40 +00:00
let mut false_positives = 0 ;
2021-10-02 22:59:39 +00:00
let total_attempts = number_of_reference_checks * number_of_messages ;
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 ;
}
2021-01-29 23:47:40 +00:00
}
}
println! (
" Expected False Positive Rate: {} \n Actual False Positive Rate: {} " ,
2021-10-02 22:59:39 +00:00
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
2021-01-29 23:47:40 +00:00
) ;
2021-01-29 21:52:34 +00:00
}
2021-01-29 23:47:40 +00:00
}