#[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 {
let mut bdb = BridgeDb::new();
let mut rng = rand::thread_rng();
let mut ba = BridgeAuth::new(bdb.pubkey.clone(), &mut rng);
for _ in 0..num_buckets {
let bucket = [random(), random(), random()];
let _ = ba.add_openinv_bridges(bucket, &mut bdb);
}
for _ in 0..hot_spare {
let bucket = [random(), random(), random()];
let _ = ba.add_spare_bucket(bucket, &mut bdb);
}
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());
}
}
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"
);
}
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"
);
}
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"
);
}
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) {
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();
assert!(bucket.1.is_some());
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()
}
}
#[cfg(all(test, feature = "bridgeauth"))]
pub fn random() -> BridgeLine {
let mut rng = rand::rngs::OsRng;
let mut res: BridgeLine = Default::default();
let mut addr: [u8; 4] = [0; 4];
rng.fill_bytes(&mut addr);
if addr[0] >= 224 {
rng.fill_bytes(&mut res.addr);
} else {
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
}