From e1f7cda652880808a49e17e2b7550bba8b3bf356 Mon Sep 17 00:00:00 2001 From: onyinyang Date: Mon, 12 Jun 2023 13:16:24 -0400 Subject: [PATCH] Add checkblockage test with changes to lox_context --- crates/lox-distributor/src/lox_context.rs | 63 +----- crates/lox-distributor/src/request_handler.rs | 201 +++++++++++++++--- 2 files changed, 176 insertions(+), 88 deletions(-) diff --git a/crates/lox-distributor/src/lox_context.rs b/crates/lox-distributor/src/lox_context.rs index 38fa80a..7ffca61 100644 --- a/crates/lox-distributor/src/lox_context.rs +++ b/crates/lox-distributor/src/lox_context.rs @@ -9,48 +9,8 @@ use lox::{ BridgeAuth, BridgeDb, IssuerPubKey, }; use lox_utils; -use rand::RngCore; 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)] pub struct LoxServerContext { pub db: Arc>, @@ -129,7 +89,7 @@ impl LoxServerContext { #[cfg(test)] /// For testing only: manually advance the day by the given number /// 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(); ba_obj.advance_days(num); // FOR TESTING ONLY 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 { 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() } @@ -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 { let req: check_blockage::Request = serde_json::from_slice(&request).unwrap(); - let response = context.check_blockage(req); let check_blockage_resp_str = serde_json::to_string(&response).unwrap(); prepare_header(check_blockage_resp_str) diff --git a/crates/lox-distributor/src/request_handler.rs b/crates/lox-distributor/src/request_handler.rs index bf8c62b..91e01c4 100644 --- a/crates/lox-distributor/src/request_handler.rs +++ b/crates/lox-distributor/src/request_handler.rs @@ -8,7 +8,6 @@ pub async fn handle( cloned_context: LoxServerContext, req: Request, ) -> Result, Infallible> { - println!("Request: {:?}", req); match req.method() { &Method::OPTIONS => Ok(Response::builder() .header("Access-Control-Allow-Origin", HeaderValue::from_static("*")) @@ -77,8 +76,13 @@ mod tests { use chrono::{Duration, Utc}; 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 rand::RngCore; use std::sync::{Arc, Mutex}; trait LoxClient { @@ -91,6 +95,8 @@ mod tests { fn levelup(&self, request: proto::level_up::Request) -> Request; fn issueinvite(&self, request: proto::issue_invite::Request) -> Request; fn redeeminvite(&self, request: proto::redeem_invite::Request) -> Request; + fn checkblockage(&self, request: proto::check_blockage::Request) -> Request; + fn blockagemigration(&self, request: proto::blockage_migration::Request) -> Request; } struct LoxClientMock {} @@ -166,7 +172,6 @@ mod tests { req } - fn issueinvite(&self, request: proto::issue_invite::Request) -> Request { let req_str = serde_json::to_string(&request).unwrap(); let req = Request::builder() @@ -188,6 +193,28 @@ mod tests { .unwrap(); req } + + fn checkblockage(&self, request: proto::check_blockage::Request) -> Request { + 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 { + 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 { @@ -201,21 +228,13 @@ mod tests { // Make 3 x num_buckets open invitation bridges, in sets of 3 for _ in 0..5 { - let bucket = [ - lox_context::random(), - lox_context::random(), - lox_context::random(), - ]; + let bucket = [random(), random(), random()]; lox_auth.add_openinv_bridges(bucket, &mut bridgedb); } // Add hot_spare more hot spare buckets for _ in 0..5 { - let bucket = [ - lox_context::random(), - lox_context::random(), - lox_context::random(), - ]; + let bucket = [random(), random(), random()]; lox_auth.add_spare_bucket(bucket); } // Create the encrypted bridge table @@ -230,8 +249,65 @@ mod tests { } 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 @@ -379,10 +455,7 @@ mod tests { test_today(31 + 14), ) { Ok(level_up_result) => level_up_result, - Err(e) => panic!( - "Error: Proof error from level up {:?}", - e.to_string() - ), + Err(e) => panic!("Error: Proof error from level up {:?}", e.to_string()), }; let level_up_request = lc.levelup(level_up_result.0); 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_response = handle(th.context.clone(), issue_invite_request).await.unwrap(); - println!("Server response?: {:?}", issue_invite_response); + let issue_invite_response = handle(th.context.clone(), issue_invite_request) + .await + .unwrap(); assert_eq!(issue_invite_response.status(), StatusCode::OK); // Test Redeem Invite @@ -430,7 +504,7 @@ mod tests { let invite_response_obj = serde_json::from_str(&invite_resp).unwrap(); let issue_invite_cred = match lox::proto::issue_invite::handle_response( issue_invite_result.1, - invite_response_obj, + invite_response_obj, &pubkeys_obj.lox_pub, &pubkeys_obj.invitation_pub, ) { @@ -443,16 +517,12 @@ mod tests { test_today(31 + 14), ) { Ok(new_invite) => new_invite, - Err(e) => panic!( - "Error: Proof error from level up {:?}", - e.to_string() - ), + Err(e) => panic!("Error: Proof error from level up {:?}", e.to_string()), }; let new_redeem_invite_request = lc.redeeminvite(new_invite.0); let new_redeem_invite_response = handle(th.context.clone(), new_redeem_invite_request) .await .unwrap(); - println!("Server response?: {:?}", new_redeem_invite_response); 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_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 } }