lox-extensions 0.1.0

A reputation-based bridge distribution system that provides privacy protection to users and their social graph while remaining open to all users.
Documentation
#[cfg(all(test, feature = "bridgeauth"))]
use super::proto::{
    blockage_migration, check_blockage, issue_invite,
    level_up::{self, LEVEL_INTERVAL},
    migration, open_invite,
    trust_promotion::{self, UNTRUSTED_INTERVAL},
};
#[cfg(all(test, feature = "bridgeauth"))]
use super::*;

#[cfg(all(test, feature = "bridgeauth"))]
use crate::{bridge_table::BridgeLine, lox_creds::BucketReachability};

#[cfg(all(test, feature = "bridgeauth"))]
use rand::RngCore;

#[cfg(all(test, feature = "bridgeauth"))]
#[allow(unused_imports)]
use base64::{engine::general_purpose, Engine as _};

#[derive(Default)]
#[cfg(all(test, feature = "bridgeauth"))]
pub struct TestHarness {
    pub bdb: BridgeDb,
    pub ba: BridgeAuth,
}

#[cfg(all(test, feature = "bridgeauth"))]
impl TestHarness {
    pub fn new() -> Self {
        TestHarness::new_buckets(5, 5)
    }

    pub fn new_buckets(num_buckets: u16, hot_spare: u16) -> Self {
        // Create a BridegDb
        let mut bdb = BridgeDb::new();
        let mut rng = rand::thread_rng();
        // Create a BridgeAuth
        let mut ba = BridgeAuth::new(bdb.pubkey.clone(), &mut rng);

        // Make 3 x num_buckets open invitation bridges, in sets of 3
        for _ in 0..num_buckets {
            let bucket = [random(), random(), random()];
            let _ = ba.add_openinv_bridges(bucket, &mut bdb);
        }
        // Add hot_spare more hot spare buckets
        for _ in 0..hot_spare {
            let bucket = [random(), random(), random()];
            let _ = ba.add_spare_bucket(bucket, &mut bdb);
        }
        // Create the encrypted bridge table
        ba.enc_bridge_table();

        Self { bdb, ba }
    }

    pub fn advance_days(&mut self, days: u32) {
        if days > 0 {
            self.ba.time_offset += time::Duration::days(days.into());
        }
    }

    /// Verify the two MACs on a Lox credential
    pub fn verify_lox(&self, cred: &lox_creds::Lox) {
        assert!(
            cred.verify_MAC(&self.ba.lox_priv).is_ok(),
            "Lox cred's MAC should verify"
        );
    }

    /// Verify the MAC on a Migration credential
    pub fn verify_migration(&self, cred: &lox_creds::Migration) {
        assert!(
            cred.verify_MAC(&self.ba.migration_priv).is_ok(),
            "Migration cred's MAC should verify"
        );
    }

    /// Verify the MAC on a Bucket Reachability credential
    pub fn verify_reachability(&self, cred: &lox_creds::BucketReachability) {
        assert!(
            cred.verify_MAC(&self.ba.reachability_priv).is_ok(),
            "Reachability cred's MAC should verify"
        );
    }

    /// Verify the MAC on a Invitation credential
    pub fn verify_invitation(&mut self, cred: &lox_creds::Invitation) {
        assert!(
            cred.verify_MAC(&self.ba.invitation_priv).is_ok(),
            "Invitation cred's MAC should verify"
        );
    }

    pub fn open_invite(
        &mut self,
        rng: &mut (impl CryptoRng + RngCore),
        invite: &[u8; OPENINV_LENGTH],
    ) -> Lox {
        let open_invitation_request = open_invite::request(rng, self.ba.lox_pub.clone());
        assert!(
            open_invitation_request.is_ok(),
            "Open invitation request should succeed"
        );
        let (request, client_state) = open_invitation_request.unwrap();
        let open_invitation_response = self.ba.open_invitation(request, invite);
        assert!(
            open_invitation_response.is_ok(),
            "Open invitation response from server should succeed"
        );
        let (response, _) = open_invitation_response.unwrap();
        let lox_cred = open_invite::handle_response(client_state, response);
        assert!(lox_cred.is_ok(), "Handle response should succeed");
        lox_cred.unwrap()
    }

    pub fn trust_promotion(
        &mut self,
        rng: &mut (impl CryptoRng + RngCore),
        cred: Lox,
    ) -> Migration {
        self.advance_days((UNTRUSTED_INTERVAL + 1).try_into().unwrap());
        let trust_promo_request =
            trust_promotion::request(rng, cred, self.ba.migrationkey_pub.clone(), self.ba.today());
        assert!(
            trust_promo_request.is_ok(),
            "Trust Promotion request should succeed"
        );
        let (tp_request, tp_client_state) = trust_promo_request.unwrap();
        let trust_promo_response = self.ba.handle_trust_promotion(tp_request);
        assert!(
            trust_promo_response.is_ok(),
            "Trust promotion response from server should succeed"
        );
        let (response, enc) = trust_promo_response.unwrap();
        let mig_cred = trust_promotion::handle_response(
            self.ba.migration_pub.clone(),
            tp_client_state,
            response,
            enc,
        );
        assert!(mig_cred.is_ok(), "Handle response should succeed");
        mig_cred.unwrap()
    }

    pub fn migration(
        &mut self,
        rng: &mut (impl CryptoRng + RngCore),
        cred: Lox,
        mig_cred: Migration,
    ) -> Lox {
        let migration_request = migration::request(rng, cred, mig_cred);
        assert!(
            migration_request.is_ok(),
            "Migration request should succeed"
        );
        let (mig_request, mig_client_state) = migration_request.unwrap();
        let migration_response = self.ba.handle_migration(mig_request);
        assert!(
            migration_response.is_ok(),
            "Migration response from server should succeed"
        );
        let response = migration_response.unwrap();
        let new_cred = migration::handle_response(mig_client_state, response);
        assert!(new_cred.is_ok(), "Handle response should succeed");
        new_cred.unwrap()
    }

    fn reach_cred(&mut self, cred: Lox) -> BucketReachability {
        let (id, key) = bridge_table::from_scalar(cred.bucket.unwrap()).unwrap();
        let encbuckets = self.ba.enc_bridge_table().clone();
        let reach_pub = self.ba.reachability_pub.clone();
        let bucket = bridge_table::BridgeTable::decrypt_bucket(
            id,
            &key,
            encbuckets.get(&id).unwrap(),
            &reach_pub,
        )
        .unwrap();
        bucket.1.unwrap()
    }

    pub fn level_up(&mut self, rng: &mut (impl CryptoRng + RngCore), cred: Lox) -> Lox {
        let trust_level: u32 = scalar_u32(&cred.trust_level.unwrap()).unwrap();
        self.advance_days(LEVEL_INTERVAL[trust_level as usize] + 1);
        let reachcred = self.reach_cred(cred.clone());
        let level_up_request = level_up::request(rng, cred.clone(), reachcred, self.ba.today());
        assert!(level_up_request.is_ok(), "Level up request should succeed");
        let (level_up_request, level_up_client_state) = level_up_request.unwrap();
        let level_up_response = self.ba.handle_level_up(level_up_request);
        assert!(
            level_up_response.is_ok(),
            "Level up response from server should succeed"
        );
        let response = level_up_response.unwrap();
        let new_cred = level_up::handle_response(level_up_client_state, response);
        assert!(new_cred.is_ok(), "Handle response should succeed");
        new_cred.unwrap()
    }

    pub fn issue_invite(
        &mut self,
        rng: &mut (impl CryptoRng + RngCore),
        cred: Lox,
    ) -> (Invitation, Lox) {
        let reachcred = self.reach_cred(cred.clone());
        let issue_invite_request = issue_invite::request(
            rng,
            cred.clone(),
            reachcred,
            self.ba.invitation_pub.clone(),
            self.ba.today(),
        );
        let (issue_invite_request, issue_invite_client_state) = issue_invite_request.unwrap();
        let issue_invite_response = self.ba.handle_issue_invite(issue_invite_request);
        assert!(
            issue_invite_response.is_ok(),
            "Issue Invite response from server should succeed"
        );
        let response = issue_invite_response.unwrap();
        let i_cred = issue_invite::handle_response(issue_invite_client_state, response);
        assert!(i_cred.is_ok(), "Handle response should succeed");
        i_cred.unwrap()
    }

    pub fn block_bridges(&mut self, cred: Lox) {
        // Get our bridges
        let (id, key) = bridge_table::from_scalar(cred.bucket.unwrap()).unwrap();
        let encbuckets = self.ba.enc_bridge_table().clone();
        let bucket = bridge_table::BridgeTable::decrypt_bucket(
            id,
            &key,
            encbuckets.get(&id).unwrap(),
            &self.ba.reachability_pub.clone(),
        )
        .unwrap();
        // We should have a Bridge Reachability credential
        assert!(bucket.1.is_some());
        // Oh, no!  Two of our bridges are blocked!
        self.ba.bridge_blocked(&bucket.0[0], &mut self.bdb);
        self.ba.bridge_blocked(&bucket.0[2], &mut self.bdb);
        self.advance_days(1);
    }

    pub fn check_blockage(&mut self, rng: &mut (impl CryptoRng + RngCore), cred: Lox) -> Migration {
        let check_blockage_request =
            check_blockage::request(rng, cred, self.ba.migrationkey_pub.clone());
        assert!(
            check_blockage_request.is_ok(),
            "Check blockage request should succeed"
        );
        let (check_blockage_request, check_blockage_client_state) = check_blockage_request.unwrap();
        let check_blockage_response = self.ba.handle_check_blockage(check_blockage_request);
        assert!(
            check_blockage_response.is_ok(),
            "Check blockage response from server should succeed"
        );
        let (response, enc) = check_blockage_response.unwrap();
        let mig_cred = check_blockage::handle_response(
            self.ba.migration_pub.clone(),
            check_blockage_client_state,
            response,
            enc,
        );
        assert!(mig_cred.is_ok(), "Handle response should succeed");
        mig_cred.unwrap()
    }

    pub fn blockage_migration(
        &mut self,
        rng: &mut (impl CryptoRng + RngCore),
        cred: Lox,
        mig_cred: Migration,
    ) -> Lox {
        let migration_request = blockage_migration::request(rng, cred, mig_cred);
        assert!(
            migration_request.is_ok(),
            "Migration request should succeed"
        );
        let (mig_request, mig_client_state) = migration_request.unwrap();
        let migration_response = self.ba.handle_blockage_migration(mig_request);
        assert!(
            migration_response.is_ok(),
            "Migration response from server should succeed"
        );
        let response = migration_response.unwrap();
        let new_cred = blockage_migration::handle_response(mig_client_state, response);
        assert!(new_cred.is_ok(), "Handle response should succeed");
        new_cred.unwrap()
    }
}

/// Create a random BridgeLine for testing
#[cfg(all(test, feature = "bridgeauth"))]
pub fn random() -> BridgeLine {
    let mut rng = rand::rngs::OsRng;
    let mut res: BridgeLine = Default::default();
    // Pick a random 4-byte address
    let mut addr: [u8; 4] = [0; 4];
    rng.fill_bytes(&mut addr);
    // If the leading byte is 224 or more, that's not a valid IPv4
    // address.  Choose an IPv6 address instead (but don't worry too
    // much about it being well formed).
    if addr[0] >= 224 {
        rng.fill_bytes(&mut res.addr);
    } else {
        // Store an IPv4 address as a v4-mapped IPv6 address
        res.addr[10] = 255;
        res.addr[11] = 255;
        res.addr[12..16].copy_from_slice(&addr);
    };
    let ports: [u16; 4] = [443, 4433, 8080, 43079];
    let portidx = (rng.next_u32() % 4) as usize;
    res.port = ports[portidx];
    res.uid_fingerprint = rng.next_u64();
    let mut cert: [u8; 52] = [0; 52];
    rng.fill_bytes(&mut cert);
    let infostr: String = format!(
        "obfs4 cert={}, iat-mode=0",
        general_purpose::STANDARD_NO_PAD.encode(cert)
    );
    res.info[..infostr.len()].copy_from_slice(infostr.as_bytes());
    res
}