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-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
|
// Verify one negative report, return true if verification succeeds
|
||||||
pub fn verify_negative_report(&self, report: NegativeReport) -> bool {
|
pub fn verify_negative_report(&self, report: NegativeReport) -> bool {
|
||||||
match self
|
match self
|
||||||
|
|
|
@ -18,6 +18,10 @@ pub async fn handle(
|
||||||
.body(Body::from("Allow POST"))
|
.body(Body::from("Allow POST"))
|
||||||
.unwrap()),
|
.unwrap()),
|
||||||
_ => match (req.method(), req.uri().path()) {
|
_ => 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>({
|
(&Method::POST, "/verifynegative") => Ok::<_, Infallible>({
|
||||||
let bytes = body::to_bytes(req.into_body()).await.unwrap();
|
let bytes = body::to_bytes(req.into_body()).await.unwrap();
|
||||||
cloned_context.verify_negative_reports(bytes)
|
cloned_context.verify_negative_reports(bytes)
|
||||||
|
@ -49,6 +53,7 @@ mod tests {
|
||||||
proto::*,
|
proto::*,
|
||||||
scalar_u32, BridgeAuth, BridgeDb,
|
scalar_u32, BridgeAuth, BridgeDb,
|
||||||
};
|
};
|
||||||
|
use lox_zkp::ProofError;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -65,6 +70,8 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
trait TpClient {
|
trait TpClient {
|
||||||
|
fn reportblocked(&self, blocked_bridges: HashMap<String, HashSet<String>>)
|
||||||
|
-> Request<Body>;
|
||||||
fn verifynegative(&self, reports: BTreeMap<String, u32>) -> Request<Body>;
|
fn verifynegative(&self, reports: BTreeMap<String, u32>) -> Request<Body>;
|
||||||
fn verifypositive(&self, reports: Vec<SerializablePositiveReport>) -> Request<Body>;
|
fn verifypositive(&self, reports: Vec<SerializablePositiveReport>) -> Request<Body>;
|
||||||
}
|
}
|
||||||
|
@ -72,6 +79,18 @@ mod tests {
|
||||||
struct TpClientMock {}
|
struct TpClientMock {}
|
||||||
|
|
||||||
impl TpClient for 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> {
|
fn verifynegative(&self, reports: BTreeMap<String, u32>) -> Request<Body> {
|
||||||
let req = serde_json::to_string(&reports).unwrap();
|
let req = serde_json::to_string(&reports).unwrap();
|
||||||
Request::builder()
|
Request::builder()
|
||||||
|
@ -165,6 +184,19 @@ mod tests {
|
||||||
String::from_utf8(body_bytes.to_vec()).unwrap()
|
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 {
|
async fn get_new_credential(th: &mut TestHarness) -> Lox {
|
||||||
let inv = th.context.db.lock().unwrap().invite().unwrap();
|
let inv = th.context.db.lock().unwrap().invite().unwrap();
|
||||||
let (req, state) = open_invite::request(&inv);
|
let (req, state) = open_invite::request(&inv);
|
||||||
|
@ -181,7 +213,7 @@ mod tests {
|
||||||
cred
|
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();
|
let current_level = scalar_u32(&cred.trust_level).unwrap();
|
||||||
if current_level == 0 {
|
if current_level == 0 {
|
||||||
th.context
|
th.context
|
||||||
|
@ -189,13 +221,13 @@ mod tests {
|
||||||
let mut ba = th.context.ba.lock().unwrap();
|
let mut ba = th.context.ba.lock().unwrap();
|
||||||
let (promreq, promstate) =
|
let (promreq, promstate) =
|
||||||
trust_promotion::request(cred, &ba.lox_pub, ba.today()).unwrap();
|
trust_promotion::request(cred, &ba.lox_pub, ba.today()).unwrap();
|
||||||
let promresp = ba.handle_trust_promotion(promreq).unwrap();
|
let promresp = ba.handle_trust_promotion(promreq)?;
|
||||||
let migcred = trust_promotion::handle_response(promstate, promresp).unwrap();
|
let migcred = trust_promotion::handle_response(promstate, promresp)?;
|
||||||
let (migreq, migstate) =
|
let (migreq, migstate) =
|
||||||
migration::request(cred, &migcred, &ba.lox_pub, &ba.migration_pub).unwrap();
|
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();
|
let new_cred = migration::handle_response(migstate, migresp, &ba.lox_pub).unwrap();
|
||||||
new_cred
|
Ok(new_cred)
|
||||||
} else {
|
} else {
|
||||||
th.context.advance_days_test(
|
th.context.advance_days_test(
|
||||||
level_up::LEVEL_INTERVAL[usize::try_from(current_level).unwrap()]
|
level_up::LEVEL_INTERVAL[usize::try_from(current_level).unwrap()]
|
||||||
|
@ -208,7 +240,11 @@ mod tests {
|
||||||
let bucket =
|
let bucket =
|
||||||
bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
|
bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
|
||||||
.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(
|
let (lvreq, lvstate) = level_up::request(
|
||||||
cred,
|
cred,
|
||||||
&reachcred,
|
&reachcred,
|
||||||
|
@ -217,12 +253,103 @@ mod tests {
|
||||||
ba.today(),
|
ba.today(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.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();
|
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]
|
#[tokio::test]
|
||||||
async fn test_negative_reports() {
|
async fn test_negative_reports() {
|
||||||
let mut th = TestHarness::new();
|
let mut th = TestHarness::new();
|
||||||
|
@ -231,19 +358,11 @@ mod tests {
|
||||||
|
|
||||||
// Get new level 1 credential
|
// Get new level 1 credential
|
||||||
let cred = get_new_credential(&mut th).await;
|
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();
|
th.context.generate_tp_bridge_infos();
|
||||||
|
|
||||||
let mut ba = th.context.ba.lock().unwrap();
|
let bridges = get_bucket(&mut th, &cred).await;
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Create random number of negative reports for each bridge in bucket
|
// Create random number of negative reports for each bridge in bucket
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
@ -340,21 +459,13 @@ mod tests {
|
||||||
|
|
||||||
// Get new level 3 credential
|
// Get new level 3 credential
|
||||||
let cred = get_new_credential(&mut th).await;
|
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();
|
||||||
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;
|
let cred = level_up(&mut th, &cred).await.unwrap();
|
||||||
|
|
||||||
th.context.generate_tp_bridge_infos();
|
th.context.generate_tp_bridge_infos();
|
||||||
|
|
||||||
let mut ba = th.context.ba.lock().unwrap();
|
let bridges = get_bucket(&mut th, &cred).await;
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Create a positive report for each bridge in bucket
|
// Create a positive report for each bridge in bucket
|
||||||
let mut reports = Vec::<SerializablePositiveReport>::new();
|
let mut reports = Vec::<SerializablePositiveReport>::new();
|
||||||
|
@ -363,7 +474,7 @@ mod tests {
|
||||||
bridge.fingerprint,
|
bridge.fingerprint,
|
||||||
None,
|
None,
|
||||||
&cred,
|
&cred,
|
||||||
&ba.lox_pub,
|
&th.context.ba.lock().unwrap().lox_pub,
|
||||||
"ru".to_string(),
|
"ru".to_string(),
|
||||||
);
|
);
|
||||||
reports.push(report.to_serializable_report());
|
reports.push(report.to_serializable_report());
|
||||||
|
@ -377,7 +488,7 @@ mod tests {
|
||||||
bridges[0].fingerprint,
|
bridges[0].fingerprint,
|
||||||
None,
|
None,
|
||||||
&cred,
|
&cred,
|
||||||
&ba.lox_pub,
|
&th.context.ba.lock().unwrap().lox_pub,
|
||||||
"ru".to_string(),
|
"ru".to_string(),
|
||||||
);
|
);
|
||||||
invalid_report_1.date = invalid_report_1.date + 2;
|
invalid_report_1.date = invalid_report_1.date + 2;
|
||||||
|
@ -388,7 +499,7 @@ mod tests {
|
||||||
bridges[1].fingerprint,
|
bridges[1].fingerprint,
|
||||||
None,
|
None,
|
||||||
&cred,
|
&cred,
|
||||||
&ba.lox_pub,
|
&th.context.ba.lock().unwrap().lox_pub,
|
||||||
"xx".to_string(),
|
"xx".to_string(),
|
||||||
);
|
);
|
||||||
reports.push(invalid_report_2.to_serializable_report());
|
reports.push(invalid_report_2.to_serializable_report());
|
||||||
|
@ -404,15 +515,12 @@ mod tests {
|
||||||
bridges[2].fingerprint,
|
bridges[2].fingerprint,
|
||||||
None,
|
None,
|
||||||
&cred,
|
&cred,
|
||||||
&ba.lox_pub,
|
&th.context.ba.lock().unwrap().lox_pub,
|
||||||
"ru".to_string(),
|
"ru".to_string(),
|
||||||
);
|
);
|
||||||
invalid_report_3.fingerprint = empty_bridgeline_fingerprint;
|
invalid_report_3.fingerprint = empty_bridgeline_fingerprint;
|
||||||
reports.push(invalid_report_3.to_serializable_report());
|
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 request = tpc.verifypositive(reports);
|
||||||
let response = handle(th.context.clone(), &mut Htables, request)
|
let response = handle(th.context.clone(), &mut Htables, request)
|
||||||
.await
|
.await
|
||||||
|
|
Loading…
Reference in New Issue