/*! Implementation of a new style of bridge authority for Tor that allows users to invite other users, while protecting the social graph from the bridge authority itself. We use CMZ14 credentials (GGM version, which is more efficient, but makes a stronger security assumption): "Algebraic MACs and Keyed-Verification Anonymous Credentials" (Chase, Meiklejohn, and Zaverucha, CCS 2014) The notation follows that of the paper "Hyphae: Social Secret Sharing" (Lovecruft and de Valence, 2017), Section 4. */ // We really want points to be capital letters and scalars to be // lowercase letters #![allow(non_snake_case)] #[macro_use] extern crate zkp; pub mod dup_filter; use sha2::Sha512; use rand::rngs::OsRng; use rand::RngCore; use std::convert::{TryFrom, TryInto}; use curve25519_dalek::constants as dalek_constants; use curve25519_dalek::ristretto::RistrettoBasepointTable; use curve25519_dalek::ristretto::RistrettoPoint; use curve25519_dalek::scalar::Scalar; use ed25519_dalek::{Keypair, PublicKey, Signature, SignatureError, Signer, Verifier}; use lazy_static::lazy_static; lazy_static! { pub static ref CMZ_A: RistrettoPoint = RistrettoPoint::hash_from_bytes::(b"CMZ Generator A"); pub static ref CMZ_B: RistrettoPoint = dalek_constants::RISTRETTO_BASEPOINT_POINT; pub static ref CMZ_A_TABLE: RistrettoBasepointTable = RistrettoBasepointTable::create(&CMZ_A); pub static ref CMZ_B_TABLE: RistrettoBasepointTable = dalek_constants::RISTRETTO_BASEPOINT_TABLE; } #[derive(Clone, Debug)] pub struct IssuerPrivKey { x0tilde: Scalar, x: Vec, } impl IssuerPrivKey { /// Create an IssuerPrivKey for credentials with the given number of /// attributes. pub fn new(n: u16) -> IssuerPrivKey { let mut rng = rand::thread_rng(); let x0tilde = Scalar::random(&mut rng); let mut x: Vec = Vec::with_capacity((n + 1) as usize); // Set x to a vector of n+1 random Scalars x.resize_with((n + 1) as usize, || Scalar::random(&mut rng)); IssuerPrivKey { x0tilde, x } } } #[derive(Clone, Debug)] pub struct IssuerPubKey { X: Vec, } impl IssuerPubKey { /// Create an IssuerPubKey from the corresponding IssuerPrivKey pub fn new(privkey: &IssuerPrivKey) -> IssuerPubKey { let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE; let n_plus_one = privkey.x.len(); let mut X: Vec = Vec::with_capacity(n_plus_one); // The first element is a special case; it is // X[0] = x0tilde*A + x[0]*B X.push(&privkey.x0tilde * Atable + &privkey.x[0] * Btable); // The other elements (1 through n) are X[i] = x[i]*A for i in 1..n_plus_one { X.push(&privkey.x[i] * Atable); } IssuerPubKey { X } } } /// The BridgeDb. This will typically be a singleton object. The /// BridgeDb's role is simply to issue signed "open invitations" to /// people who are not yet part of the system. #[derive(Debug)] pub struct BridgeDb { /// The keypair for signing open invitations keypair: Keypair, /// The public key for verifying open invitations pub pubkey: PublicKey, /// The number of open-invitation buckets num_openinv_buckets: u32, } /// An open invitation is a [u8; OPENINV_LENGTH] where the first 32 /// bytes are the serialization of a random Scalar (the invitation id), /// the next 4 bytes are a little-endian bucket number, and the last /// SIGNATURE_LENGTH bytes are the signature on the first 36 bytes. pub const OPENINV_LENGTH: usize = 32 // the length of the random // invitation id (a Scalar) + 4 // the length of the u32 for the bucket number + ed25519_dalek::SIGNATURE_LENGTH; // the length of the signature impl BridgeDb { /// Create the BridgeDb. pub fn new(num_openinv_buckets: u32) -> BridgeDb { let mut csprng = OsRng {}; let keypair = Keypair::generate(&mut csprng); let pubkey = keypair.public; BridgeDb { keypair, pubkey, num_openinv_buckets, } } /// Produce an open invitation. In this example code, we just /// choose a random open-invitation bucket. pub fn invite(&self) -> [u8; OPENINV_LENGTH] { let mut res: [u8; OPENINV_LENGTH] = [0; OPENINV_LENGTH]; let mut rng = rand::thread_rng(); // Choose a random invitation id (a Scalar) and serialize it let id = Scalar::random(&mut rng); res[0..32].copy_from_slice(&id.to_bytes()); // Choose a random bucket number (mod num_openinv_buckets) and // serialize it let bucket_num = rng.next_u32() % self.num_openinv_buckets; res[32..(32 + 4)].copy_from_slice(&bucket_num.to_le_bytes()); // Sign the first 36 bytes and serialize it let sig = self.keypair.sign(&res[0..(32 + 4)]); res[(32 + 4)..].copy_from_slice(&sig.to_bytes()); res } /// Verify an open invitation. Returns the invitation id and the /// bucket number if the signature checked out. It is up to the /// caller to then check that the invitation id has not been used /// before. pub fn verify( invitation: [u8; OPENINV_LENGTH], pubkey: PublicKey, ) -> Result<(Scalar, u32), SignatureError> { // Pull out the signature and verify it let sig = Signature::try_from(&invitation[(32 + 4)..])?; pubkey.verify(&invitation[0..(32 + 4)], &sig)?; // The signature passed. Pull out the bucket number and then // the invitation id let bucket = u32::from_le_bytes(invitation[32..(32 + 4)].try_into().unwrap()); match Scalar::from_canonical_bytes(invitation[0..32].try_into().unwrap()) { // It should never happen that there's a valid signature on // an invalid serialization of a Scalar, but check anyway. None => Err(SignatureError::new()), Some(s) => Ok((s, bucket)), } } }