Endpoint for TP system to report bridges are blocked
This commit is contained in:
parent
07f0e38a40
commit
94dc1c063c
|
@ -794,6 +794,34 @@ impl LoxServerContext {
|
|||
|
||||
// Troll Patrol-related tasks
|
||||
|
||||
// Troll Patrol reports that a bridge is blocked somewhere
|
||||
pub fn report_blocked_bridges(self, request: Bytes) -> Response<Body> {
|
||||
let blocked_bridges: HashMap<String, HashSet<String>> =
|
||||
match serde_json::from_slice(&request) {
|
||||
Ok(req) => req,
|
||||
Err(e) => {
|
||||
let response = json!({"error": e.to_string()});
|
||||
let val = serde_json::to_string(&response).unwrap();
|
||||
return prepare_header(val);
|
||||
}
|
||||
};
|
||||
// TODO: Forward this information to rdsys
|
||||
for bridge_str in blocked_bridges.keys() {
|
||||
let bridge = array_bytes::hex2array(bridge_str).unwrap();
|
||||
if self.tp_bridge_infos.lock().unwrap().contains_key(&bridge) {
|
||||
let bl = self
|
||||
.tp_bridge_infos
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&bridge)
|
||||
.unwrap()
|
||||
.bridge_line;
|
||||
self.mark_blocked(bl);
|
||||
}
|
||||
}
|
||||
prepare_header("OK".to_string())
|
||||
}
|
||||
|
||||
// Verify one negative report, return true if verification succeeds
|
||||
pub fn verify_negative_report(&self, report: NegativeReport) -> bool {
|
||||
match self
|
||||
|
|
|
@ -18,6 +18,10 @@ pub async fn handle(
|
|||
.body(Body::from("Allow POST"))
|
||||
.unwrap()),
|
||||
_ => match (req.method(), req.uri().path()) {
|
||||
(&Method::POST, "/reportblocked") => Ok::<_, Infallible>({
|
||||
let bytes = body::to_bytes(req.into_body()).await.unwrap();
|
||||
cloned_context.report_blocked_bridges(bytes)
|
||||
}),
|
||||
(&Method::POST, "/verifynegative") => Ok::<_, Infallible>({
|
||||
let bytes = body::to_bytes(req.into_body()).await.unwrap();
|
||||
cloned_context.verify_negative_reports(bytes)
|
||||
|
@ -49,6 +53,7 @@ mod tests {
|
|||
proto::*,
|
||||
scalar_u32, BridgeAuth, BridgeDb,
|
||||
};
|
||||
use lox_zkp::ProofError;
|
||||
use rand::RngCore;
|
||||
use sha1::{Digest, Sha1};
|
||||
use std::{
|
||||
|
@ -65,6 +70,8 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
trait TpClient {
|
||||
fn reportblocked(&self, blocked_bridges: HashMap<String, HashSet<String>>)
|
||||
-> Request<Body>;
|
||||
fn verifynegative(&self, reports: BTreeMap<String, u32>) -> Request<Body>;
|
||||
fn verifypositive(&self, reports: Vec<SerializablePositiveReport>) -> Request<Body>;
|
||||
}
|
||||
|
@ -72,6 +79,18 @@ mod tests {
|
|||
struct TpClientMock {}
|
||||
|
||||
impl TpClient for TpClientMock {
|
||||
fn reportblocked(
|
||||
&self,
|
||||
blocked_bridges: HashMap<String, HashSet<String>>,
|
||||
) -> Request<Body> {
|
||||
let req = serde_json::to_string(&blocked_bridges).unwrap();
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("http://localhost/reportblocked")
|
||||
.body(Body::from(req))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn verifynegative(&self, reports: BTreeMap<String, u32>) -> Request<Body> {
|
||||
let req = serde_json::to_string(&reports).unwrap();
|
||||
Request::builder()
|
||||
|
@ -165,6 +184,19 @@ mod tests {
|
|||
String::from_utf8(body_bytes.to_vec()).unwrap()
|
||||
}
|
||||
|
||||
async fn get_bucket(
|
||||
th: &mut TestHarness,
|
||||
cred: &Lox,
|
||||
) -> [BridgeLine; bridge_table::MAX_BRIDGES_PER_BUCKET] {
|
||||
let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
|
||||
let mut ba = th.context.ba.lock().unwrap();
|
||||
let encbuckets = ba.enc_bridge_table();
|
||||
let bucket =
|
||||
bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
|
||||
.unwrap();
|
||||
bucket.0
|
||||
}
|
||||
|
||||
async fn get_new_credential(th: &mut TestHarness) -> Lox {
|
||||
let inv = th.context.db.lock().unwrap().invite().unwrap();
|
||||
let (req, state) = open_invite::request(&inv);
|
||||
|
@ -181,7 +213,7 @@ mod tests {
|
|||
cred
|
||||
}
|
||||
|
||||
async fn level_up(th: &mut TestHarness, cred: &Lox) -> Lox {
|
||||
async fn level_up(th: &mut TestHarness, cred: &Lox) -> Result<Lox, ProofError> {
|
||||
let current_level = scalar_u32(&cred.trust_level).unwrap();
|
||||
if current_level == 0 {
|
||||
th.context
|
||||
|
@ -189,13 +221,13 @@ mod tests {
|
|||
let mut ba = th.context.ba.lock().unwrap();
|
||||
let (promreq, promstate) =
|
||||
trust_promotion::request(cred, &ba.lox_pub, ba.today()).unwrap();
|
||||
let promresp = ba.handle_trust_promotion(promreq).unwrap();
|
||||
let migcred = trust_promotion::handle_response(promstate, promresp).unwrap();
|
||||
let promresp = ba.handle_trust_promotion(promreq)?;
|
||||
let migcred = trust_promotion::handle_response(promstate, promresp)?;
|
||||
let (migreq, migstate) =
|
||||
migration::request(cred, &migcred, &ba.lox_pub, &ba.migration_pub).unwrap();
|
||||
let migresp = ba.handle_migration(migreq).unwrap();
|
||||
let migresp = ba.handle_migration(migreq)?;
|
||||
let new_cred = migration::handle_response(migstate, migresp, &ba.lox_pub).unwrap();
|
||||
new_cred
|
||||
Ok(new_cred)
|
||||
} else {
|
||||
th.context.advance_days_test(
|
||||
level_up::LEVEL_INTERVAL[usize::try_from(current_level).unwrap()]
|
||||
|
@ -208,7 +240,11 @@ mod tests {
|
|||
let bucket =
|
||||
bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
|
||||
.unwrap();
|
||||
let reachcred = bucket.1.unwrap();
|
||||
let reachcred = match bucket.1 {
|
||||
Some(v) => v,
|
||||
None => return Err(ProofError::VerificationFailure),
|
||||
};
|
||||
//let reachcred = bucket.1.unwrap();
|
||||
let (lvreq, lvstate) = level_up::request(
|
||||
cred,
|
||||
&reachcred,
|
||||
|
@ -217,12 +253,103 @@ mod tests {
|
|||
ba.today(),
|
||||
)
|
||||
.unwrap();
|
||||
let lvresp = ba.handle_level_up(lvreq).unwrap();
|
||||
let lvresp = ba.handle_level_up(lvreq)?;
|
||||
let new_cred = level_up::handle_response(lvstate, lvresp, &ba.lox_pub).unwrap();
|
||||
new_cred
|
||||
Ok(new_cred)
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_report_blocked_bridges() {
|
||||
let mut th = TestHarness::new();
|
||||
let tpc = TpClientMock {};
|
||||
let mut Htables = HashMap::<u32, RistrettoBasepointTable>::new();
|
||||
|
||||
// helper function to create map of bridges from bucket to mark blocked
|
||||
fn bridges_to_block(
|
||||
bucket: [BridgeLine; bridge_table::MAX_BRIDGES_PER_BUCKET],
|
||||
num_bridges_to_block: usize,
|
||||
) -> HashMap<String, HashSet<String>> {
|
||||
let mut blocked_bridges = HashMap::<String, HashSet<String>>::new();
|
||||
for i in 0..num_bridges_to_block {
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(bucket[i].fingerprint);
|
||||
let mut countries = HashSet::<String>::new();
|
||||
countries.insert("RU".to_string());
|
||||
blocked_bridges.insert(array_bytes::bytes2hex("", hasher.finalize()), countries);
|
||||
}
|
||||
blocked_bridges
|
||||
}
|
||||
|
||||
// Get new level 0 credential
|
||||
let cred = get_new_credential(&mut th).await;
|
||||
|
||||
th.context.generate_tp_bridge_infos();
|
||||
|
||||
let bridges = get_bucket(&mut th, &cred).await;
|
||||
|
||||
// Block our first (and only) bridge
|
||||
let blocked_bridges = bridges_to_block(bridges, 1);
|
||||
let request = tpc.reportblocked(blocked_bridges);
|
||||
let response = handle(th.context.clone(), &mut Htables, request)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let resp_str = body_to_string(response).await;
|
||||
assert_eq!(resp_str, "OK");
|
||||
|
||||
th.context.generate_tp_bridge_infos();
|
||||
|
||||
// We should not be able to migrate to level 1
|
||||
assert!(level_up(&mut th, &cred).await.is_err());
|
||||
|
||||
// Get new level 1 credential
|
||||
let cred = get_new_credential(&mut th).await;
|
||||
let cred = level_up(&mut th, &cred).await.unwrap();
|
||||
|
||||
th.context.generate_tp_bridge_infos();
|
||||
|
||||
let bridges = get_bucket(&mut th, &cred).await;
|
||||
|
||||
// Block as many bridges as possible without preventing level up
|
||||
let blocked_bridges = bridges_to_block(
|
||||
bridges,
|
||||
bridge_table::MAX_BRIDGES_PER_BUCKET - bridge_table::MIN_BUCKET_REACHABILITY,
|
||||
);
|
||||
let request = tpc.reportblocked(blocked_bridges);
|
||||
let response = handle(th.context.clone(), &mut Htables, request)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let resp_str = body_to_string(response).await;
|
||||
assert_eq!(resp_str, "OK");
|
||||
|
||||
th.context.generate_tp_bridge_infos();
|
||||
|
||||
// We should still be able to level up
|
||||
let cred = level_up(&mut th, &cred).await.unwrap();
|
||||
|
||||
th.context.generate_tp_bridge_infos();
|
||||
|
||||
let bridges = get_bucket(&mut th, &cred).await;
|
||||
|
||||
// Block enough bridges to prevent level up
|
||||
let blocked_bridges = bridges_to_block(
|
||||
bridges,
|
||||
bridge_table::MAX_BRIDGES_PER_BUCKET - bridge_table::MIN_BUCKET_REACHABILITY + 1,
|
||||
);
|
||||
let request = tpc.reportblocked(blocked_bridges);
|
||||
let response = handle(th.context.clone(), &mut Htables, request)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let resp_str = body_to_string(response).await;
|
||||
assert_eq!(resp_str, "OK");
|
||||
|
||||
// We should not be able to level up
|
||||
assert!(level_up(&mut th, &cred).await.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_negative_reports() {
|
||||
let mut th = TestHarness::new();
|
||||
|
@ -231,19 +358,11 @@ mod tests {
|
|||
|
||||
// Get new level 1 credential
|
||||
let cred = get_new_credential(&mut th).await;
|
||||
let cred = level_up(&mut th, &cred).await;
|
||||
let cred = level_up(&mut th, &cred).await.unwrap();
|
||||
|
||||
th.context.generate_tp_bridge_infos();
|
||||
|
||||
let mut ba = th.context.ba.lock().unwrap();
|
||||
|
||||
// Get bucket
|
||||
let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
|
||||
let encbuckets = ba.enc_bridge_table();
|
||||
let bucket =
|
||||
bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
|
||||
.unwrap();
|
||||
let bridges = bucket.0;
|
||||
let bridges = get_bucket(&mut th, &cred).await;
|
||||
|
||||
// Create random number of negative reports for each bridge in bucket
|
||||
let mut rng = rand::thread_rng();
|
||||
|
@ -340,21 +459,13 @@ mod tests {
|
|||
|
||||
// Get new level 3 credential
|
||||
let cred = get_new_credential(&mut th).await;
|
||||
let cred = level_up(&mut th, &cred).await;
|
||||
let cred = level_up(&mut th, &cred).await;
|
||||
let cred = level_up(&mut th, &cred).await;
|
||||
let cred = level_up(&mut th, &cred).await.unwrap();
|
||||
let cred = level_up(&mut th, &cred).await.unwrap();
|
||||
let cred = level_up(&mut th, &cred).await.unwrap();
|
||||
|
||||
th.context.generate_tp_bridge_infos();
|
||||
|
||||
let mut ba = th.context.ba.lock().unwrap();
|
||||
|
||||
// Get bucket
|
||||
let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
|
||||
let encbuckets = ba.enc_bridge_table();
|
||||
let bucket =
|
||||
bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
|
||||
.unwrap();
|
||||
let bridges = bucket.0;
|
||||
let bridges = get_bucket(&mut th, &cred).await;
|
||||
|
||||
// Create a positive report for each bridge in bucket
|
||||
let mut reports = Vec::<SerializablePositiveReport>::new();
|
||||
|
@ -363,7 +474,7 @@ mod tests {
|
|||
bridge.fingerprint,
|
||||
None,
|
||||
&cred,
|
||||
&ba.lox_pub,
|
||||
&th.context.ba.lock().unwrap().lox_pub,
|
||||
"ru".to_string(),
|
||||
);
|
||||
reports.push(report.to_serializable_report());
|
||||
|
@ -377,7 +488,7 @@ mod tests {
|
|||
bridges[0].fingerprint,
|
||||
None,
|
||||
&cred,
|
||||
&ba.lox_pub,
|
||||
&th.context.ba.lock().unwrap().lox_pub,
|
||||
"ru".to_string(),
|
||||
);
|
||||
invalid_report_1.date = invalid_report_1.date + 2;
|
||||
|
@ -388,7 +499,7 @@ mod tests {
|
|||
bridges[1].fingerprint,
|
||||
None,
|
||||
&cred,
|
||||
&ba.lox_pub,
|
||||
&th.context.ba.lock().unwrap().lox_pub,
|
||||
"xx".to_string(),
|
||||
);
|
||||
reports.push(invalid_report_2.to_serializable_report());
|
||||
|
@ -404,15 +515,12 @@ mod tests {
|
|||
bridges[2].fingerprint,
|
||||
None,
|
||||
&cred,
|
||||
&ba.lox_pub,
|
||||
&th.context.ba.lock().unwrap().lox_pub,
|
||||
"ru".to_string(),
|
||||
);
|
||||
invalid_report_3.fingerprint = empty_bridgeline_fingerprint;
|
||||
reports.push(invalid_report_3.to_serializable_report());
|
||||
|
||||
// Release lock so BA can be used to verify reports below
|
||||
drop(ba);
|
||||
|
||||
let request = tpc.verifypositive(reports);
|
||||
let response = handle(th.context.clone(), &mut Htables, request)
|
||||
.await
|
||||
|
|
Loading…
Reference in New Issue