Add checkblockage test with changes to lox_context

This commit is contained in:
onyinyang 2023-06-12 13:16:24 -04:00
parent e7db9e7151
commit e1f7cda652
No known key found for this signature in database
GPG Key ID: 156A6435430C2036
2 changed files with 176 additions and 88 deletions

View File

@ -9,48 +9,8 @@ use lox::{
BridgeAuth, BridgeDb, IssuerPubKey, BridgeAuth, BridgeDb, IssuerPubKey,
}; };
use lox_utils; use lox_utils;
use rand::RngCore;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
/// Create a random BridgeLine for testing ONLY. Do not use in production!
/// This was copied directly from lox/src/bridge_table.rs in order
/// to easily initialize a bridgedb/lox_auth with structurally
/// correct buckets to be used for Lox requests/verifications/responses.
/// In production, existing bridges should be translated into this format
/// in a private function and sorted into buckets (3 bridges/bucket is suggested
/// but experience may suggest something else) in some intelligent way.
pub fn random() -> BridgeLine {
let mut rng = rand::thread_rng();
let mut res: BridgeLine = BridgeLine::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",
base64::encode_config(cert, base64::STANDARD_NO_PAD)
);
res.info[..infostr.len()].copy_from_slice(infostr.as_bytes());
res
}
#[derive(Clone)] #[derive(Clone)]
pub struct LoxServerContext { pub struct LoxServerContext {
pub db: Arc<Mutex<BridgeDb>>, pub db: Arc<Mutex<BridgeDb>>,
@ -129,7 +89,7 @@ impl LoxServerContext {
#[cfg(test)] #[cfg(test)]
/// For testing only: manually advance the day by the given number /// For testing only: manually advance the day by the given number
/// of days. /// of days.
pub fn advance_days_TEST(&self, num: u16) { pub fn advance_days_test(&self, num: u16) {
let mut ba_obj = self.ba.lock().unwrap(); let mut ba_obj = self.ba.lock().unwrap();
ba_obj.advance_days(num); // FOR TESTING ONLY ba_obj.advance_days(num); // FOR TESTING ONLY
println!("Today's date according to server: {}", ba_obj.today()); println!("Today's date according to server: {}", ba_obj.today());
@ -191,26 +151,6 @@ impl LoxServerContext {
fn check_blockage(&self, req: check_blockage::Request) -> check_blockage::Response { fn check_blockage(&self, req: check_blockage::Request) -> check_blockage::Response {
let mut ba_obj = self.ba.lock().unwrap(); let mut ba_obj = self.ba.lock().unwrap();
// Created 5 buckets initially, so we will add 5 hot spares (for migration) and
// block all of the existing buckets to trigger migration table propagation
// FOR TESTING ONLY, ADD 5 NEW Buckets
for _ in 0..5 {
let bucket = [random(), random(), random()];
ba_obj.add_spare_bucket(bucket);
}
ba_obj.enc_bridge_table();
// FOR TESTING ONLY, BLOCK ALL BRIDGES
let mut db_obj = self.db.lock().unwrap();
for index in 0..5 {
let b0 = ba_obj.bridge_table.buckets[index][0];
let b1 = ba_obj.bridge_table.buckets[index][1];
let b2 = ba_obj.bridge_table.buckets[index][2];
ba_obj.bridge_unreachable(&b0, &mut db_obj);
ba_obj.bridge_unreachable(&b1, &mut db_obj);
ba_obj.bridge_unreachable(&b2, &mut db_obj);
}
ba_obj.enc_bridge_table();
ba_obj.handle_check_blockage(req).unwrap() ba_obj.handle_check_blockage(req).unwrap()
} }
@ -287,7 +227,6 @@ pub fn verify_and_send_redeem_invite(request: Bytes, context: LoxServerContext)
pub fn verify_and_send_check_blockage(request: Bytes, context: LoxServerContext) -> Response<Body> { pub fn verify_and_send_check_blockage(request: Bytes, context: LoxServerContext) -> Response<Body> {
let req: check_blockage::Request = serde_json::from_slice(&request).unwrap(); let req: check_blockage::Request = serde_json::from_slice(&request).unwrap();
let response = context.check_blockage(req); let response = context.check_blockage(req);
let check_blockage_resp_str = serde_json::to_string(&response).unwrap(); let check_blockage_resp_str = serde_json::to_string(&response).unwrap();
prepare_header(check_blockage_resp_str) prepare_header(check_blockage_resp_str)

View File

@ -8,7 +8,6 @@ pub async fn handle(
cloned_context: LoxServerContext, cloned_context: LoxServerContext,
req: Request<Body>, req: Request<Body>,
) -> Result<Response<Body>, Infallible> { ) -> Result<Response<Body>, Infallible> {
println!("Request: {:?}", req);
match req.method() { match req.method() {
&Method::OPTIONS => Ok(Response::builder() &Method::OPTIONS => Ok(Response::builder()
.header("Access-Control-Allow-Origin", HeaderValue::from_static("*")) .header("Access-Control-Allow-Origin", HeaderValue::from_static("*"))
@ -77,8 +76,13 @@ mod tests {
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
use julianday::JulianDay; use julianday::JulianDay;
use lox::{cred::BucketReachability, proto, BridgeAuth, BridgeDb}; use lox::{
bridge_table::{self, BridgeLine},
cred::BucketReachability,
proto, BridgeAuth, BridgeDb,
};
use lox_utils; use lox_utils;
use rand::RngCore;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
trait LoxClient { trait LoxClient {
@ -91,6 +95,8 @@ mod tests {
fn levelup(&self, request: proto::level_up::Request) -> Request<Body>; fn levelup(&self, request: proto::level_up::Request) -> Request<Body>;
fn issueinvite(&self, request: proto::issue_invite::Request) -> Request<Body>; fn issueinvite(&self, request: proto::issue_invite::Request) -> Request<Body>;
fn redeeminvite(&self, request: proto::redeem_invite::Request) -> Request<Body>; fn redeeminvite(&self, request: proto::redeem_invite::Request) -> Request<Body>;
fn checkblockage(&self, request: proto::check_blockage::Request) -> Request<Body>;
fn blockagemigration(&self, request: proto::blockage_migration::Request) -> Request<Body>;
} }
struct LoxClientMock {} struct LoxClientMock {}
@ -166,7 +172,6 @@ mod tests {
req req
} }
fn issueinvite(&self, request: proto::issue_invite::Request) -> Request<Body> { fn issueinvite(&self, request: proto::issue_invite::Request) -> Request<Body> {
let req_str = serde_json::to_string(&request).unwrap(); let req_str = serde_json::to_string(&request).unwrap();
let req = Request::builder() let req = Request::builder()
@ -188,6 +193,28 @@ mod tests {
.unwrap(); .unwrap();
req req
} }
fn checkblockage(&self, request: proto::check_blockage::Request) -> Request<Body> {
let req_str = serde_json::to_string(&request).unwrap();
let req = Request::builder()
.header("Content-Type", "application/json")
.method("POST")
.uri("http://localhost/checkblockage")
.body(Body::from(req_str))
.unwrap();
req
}
fn blockagemigration(&self, request: proto::blockage_migration::Request) -> Request<Body> {
let req_str = serde_json::to_string(&request).unwrap();
let req = Request::builder()
.header("Content-Type", "application/json")
.method("POST")
.uri("http://localhost/blockagemigration")
.body(Body::from(req_str))
.unwrap();
req
}
} }
struct TestHarness { struct TestHarness {
@ -201,21 +228,13 @@ mod tests {
// Make 3 x num_buckets open invitation bridges, in sets of 3 // Make 3 x num_buckets open invitation bridges, in sets of 3
for _ in 0..5 { for _ in 0..5 {
let bucket = [ let bucket = [random(), random(), random()];
lox_context::random(),
lox_context::random(),
lox_context::random(),
];
lox_auth.add_openinv_bridges(bucket, &mut bridgedb); lox_auth.add_openinv_bridges(bucket, &mut bridgedb);
} }
// Add hot_spare more hot spare buckets // Add hot_spare more hot spare buckets
for _ in 0..5 { for _ in 0..5 {
let bucket = [ let bucket = [random(), random(), random()];
lox_context::random(),
lox_context::random(),
lox_context::random(),
];
lox_auth.add_spare_bucket(bucket); lox_auth.add_spare_bucket(bucket);
} }
// Create the encrypted bridge table // Create the encrypted bridge table
@ -230,8 +249,65 @@ mod tests {
} }
fn advance_days(&mut self, days: u16) { fn advance_days(&mut self, days: u16) {
self.context.advance_days_TEST(days) self.context.advance_days_test(days)
} }
fn simulate_blocking(&mut self, cred: lox::cred::Lox) -> (lox::cred::Lox, u32, [u8; 16]) {
let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
let mut bdb = self.context.db.lock().unwrap();
let mut lox_auth = self.context.ba.lock().unwrap();
let encbuckets = lox_auth.enc_bridge_table();
let bucket =
bridge_table::BridgeTable::decrypt_bucket(id, &key, &encbuckets[id as usize])
.unwrap();
assert!(bucket.1.is_some());
// Block two of our bridges
lox_auth.bridge_unreachable(&bucket.0[0], &mut bdb);
lox_auth.bridge_unreachable(&bucket.0[2], &mut bdb);
(cred, id, key)
}
fn prep_next_day(&mut self, id: u32, key: [u8; 16]) {
let mut lox_auth = self.context.ba.lock().unwrap();
let encbuckets2 = lox_auth.enc_bridge_table();
let bucket2 =
bridge_table::BridgeTable::decrypt_bucket(id, &key, &encbuckets2[id as usize])
.unwrap();
// We should no longer have a Bridge Reachability credential
assert!(bucket2.1.is_none());
}
}
pub fn random() -> BridgeLine {
let mut rng = rand::thread_rng();
let mut res: BridgeLine = BridgeLine::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",
base64::encode_config(cert, base64::STANDARD_NO_PAD)
);
res.info[..infostr.len()].copy_from_slice(infostr.as_bytes());
res
} }
// This should only be used for testing, use today in production // This should only be used for testing, use today in production
@ -379,10 +455,7 @@ mod tests {
test_today(31 + 14), test_today(31 + 14),
) { ) {
Ok(level_up_result) => level_up_result, Ok(level_up_result) => level_up_result,
Err(e) => panic!( Err(e) => panic!("Error: Proof error from level up {:?}", e.to_string()),
"Error: Proof error from level up {:?}",
e.to_string()
),
}; };
let level_up_request = lc.levelup(level_up_result.0); let level_up_request = lc.levelup(level_up_result.0);
let level_up_response = handle(th.context.clone(), level_up_request).await.unwrap(); let level_up_response = handle(th.context.clone(), level_up_request).await.unwrap();
@ -421,8 +494,9 @@ mod tests {
), ),
}; };
let issue_invite_request = lc.issueinvite(issue_invite_result.0); let issue_invite_request = lc.issueinvite(issue_invite_result.0);
let issue_invite_response = handle(th.context.clone(), issue_invite_request).await.unwrap(); let issue_invite_response = handle(th.context.clone(), issue_invite_request)
println!("Server response?: {:?}", issue_invite_response); .await
.unwrap();
assert_eq!(issue_invite_response.status(), StatusCode::OK); assert_eq!(issue_invite_response.status(), StatusCode::OK);
// Test Redeem Invite // Test Redeem Invite
@ -443,16 +517,12 @@ mod tests {
test_today(31 + 14), test_today(31 + 14),
) { ) {
Ok(new_invite) => new_invite, Ok(new_invite) => new_invite,
Err(e) => panic!( Err(e) => panic!("Error: Proof error from level up {:?}", e.to_string()),
"Error: Proof error from level up {:?}",
e.to_string()
),
}; };
let new_redeem_invite_request = lc.redeeminvite(new_invite.0); let new_redeem_invite_request = lc.redeeminvite(new_invite.0);
let new_redeem_invite_response = handle(th.context.clone(), new_redeem_invite_request) let new_redeem_invite_response = handle(th.context.clone(), new_redeem_invite_request)
.await .await
.unwrap(); .unwrap();
println!("Server response?: {:?}", new_redeem_invite_response);
assert_eq!(new_redeem_invite_response.status(), StatusCode::OK); assert_eq!(new_redeem_invite_response.status(), StatusCode::OK);
let redeemed_cred_resp = body_to_string(new_redeem_invite_response).await; let redeemed_cred_resp = body_to_string(new_redeem_invite_response).await;
let redeemed_cred_resp_obj = serde_json::from_str(&redeemed_cred_resp).unwrap(); let redeemed_cred_resp_obj = serde_json::from_str(&redeemed_cred_resp).unwrap();
@ -469,5 +539,84 @@ mod tests {
), ),
}; };
//Test Check Blockage
th.advance_days(28); // First advance most recent credential to level 3
let new_reachability_request = lc.reachability();
let new_reachability_response = handle(th.context.clone(), new_reachability_request)
.await
.unwrap();
let encrypted_table = body_to_string(new_reachability_response).await;
let reachability_cred: BucketReachability =
lox_utils::generate_reachability_cred(&issue_invite_cred.0, encrypted_table);
let level_three_request = match proto::level_up::request(
&issue_invite_cred.0,
&reachability_cred,
&pubkeys_obj.lox_pub,
&pubkeys_obj.reachability_pub,
test_today(31 + 14 + 28),
) {
Ok(level_three_request) => level_three_request,
Err(e) => panic!("Error: Proof error from level up to 3 {:?}", e.to_string()),
};
let level_three_req = lc.levelup(level_three_request.0);
let level_three_response = handle(th.context.clone(), level_three_req).await.unwrap();
assert_eq!(level_three_response.status(), StatusCode::OK);
let levelup_resp = body_to_string(level_three_response).await;
let levelup_response_obj = serde_json::from_str(&levelup_resp).unwrap();
let level_three_cred = match lox::proto::level_up::handle_response(
level_three_request.1,
levelup_response_obj,
&pubkeys_obj.lox_pub,
) {
Ok(level_three_cred) => level_three_cred,
Err(e) => panic!("Error: Level two credential error {:?}", e.to_string()),
};
// Simulate blocking event
let passed_level_three_cred = th.simulate_blocking(level_three_cred);
th.advance_days(1);
th.prep_next_day(passed_level_three_cred.1, passed_level_three_cred.2);
let migration_cred_request = match proto::check_blockage::request(
&passed_level_three_cred.0,
&pubkeys_obj.lox_pub,
) {
Ok(migration_cred_request) => migration_cred_request,
Err(e) => panic!("Error: Proof error from level up to 3 {:?}", e.to_string()),
};
let migration_cred_req = lc.checkblockage(migration_cred_request.0);
let migration_cred_response = handle(th.context.clone(), migration_cred_req)
.await
.unwrap();
assert_eq!(migration_cred_response.status(), StatusCode::OK);
// Test Blockage Migration
let migration_resp = body_to_string(migration_cred_response).await;
let migration_response_obj = serde_json::from_str(&migration_resp).unwrap();
let mig_cred = match lox::proto::check_blockage::handle_response(
migration_cred_request.1,
migration_response_obj,
) {
Ok(mig_cred) => mig_cred,
Err(e) => panic!("Error: Migration token error {:?}", e.to_string()),
};
let migration_result = match proto::blockage_migration::request(
&passed_level_three_cred.0,
&mig_cred,
&pubkeys_obj.lox_pub,
&pubkeys_obj.migration_pub,
) {
Ok(migration_result) => migration_result,
Err(e) => panic!(
"Error: Proof error from trust migration {:?}",
e.to_string()
),
};
let blockagemig_request = lc.blockagemigration(migration_result.0);
let blockagemig_response = handle(th.context.clone(), blockagemig_request)
.await
.unwrap();
assert_eq!(blockagemig_response.status(), StatusCode::OK);
// Test Level up
} }
} }