Compare commits

..

No commits in common. "7acba0a6f00c6ffdb429b4993ee109a8e125b466" and "dc7531689c2a5ec5b4c757f95a87f576c4d4020c" have entirely different histories.

4 changed files with 1 additions and 232 deletions

View File

@ -8,15 +8,11 @@ use std::{
collections::{BTreeMap, HashSet},
};
#[cfg(feature = "simulation")]
use crate::get_date;
/// Provides a function for predicting which countries block this bridge
pub trait Analyzer {
/// Evaluate open-entry bridge. Returns true if blocked, false otherwise.
fn stage_one(
&self,
age: u32,
confidence: f64,
bridge_ips: &[u32],
bridge_ips_today: u32,
@ -28,7 +24,6 @@ pub trait Analyzer {
/// blocked, false otherwise.
fn stage_two(
&self,
age: u32,
confidence: f64,
bridge_ips: &[u32],
bridge_ips_today: u32,
@ -40,7 +35,6 @@ pub trait Analyzer {
/// blocked, false otherwise.
fn stage_three(
&self,
age: u32,
confidence: f64,
bridge_ips: &[u32],
bridge_ips_today: u32,
@ -124,7 +118,6 @@ pub fn blocked_in(
// open-entry bridge and/or not enough days of
// historical days for stages 2 and 3
if analyzer.stage_one(
age,
confidence,
&bridge_ips,
bridge_ips_today,
@ -139,7 +132,6 @@ pub fn blocked_in(
// invite-only bridge without min_historical_days of
// historical data on positive reports
if analyzer.stage_two(
age,
confidence,
&bridge_ips,
bridge_ips_today,
@ -152,7 +144,6 @@ pub fn blocked_in(
// invite-only bridge that has min_historical_days or
// more of historical data since the first positive report
if analyzer.stage_three(
age,
confidence,
&bridge_ips,
bridge_ips_today,
@ -162,23 +153,6 @@ pub fn blocked_in(
positive_reports_today,
) {
blocked_in.insert(country.to_string());
} else {
// Logging in simulation mode
#[cfg(feature = "simulation")]
if analyzer.stage_two(
age,
confidence,
&bridge_ips,
bridge_ips_today,
&negative_reports,
negative_reports_today,
) {
println!(
"{} detected not blocked due to positive reports on day {}",
array_bytes::bytes2hex("", bridge_info.fingerprint),
get_date()
);
}
}
}
}
@ -194,7 +168,6 @@ pub struct ExampleAnalyzer {}
impl Analyzer for ExampleAnalyzer {
fn stage_one(
&self,
_age: u32,
_confidence: f64,
_bridge_ips: &[u32],
_bridge_ips_today: u32,
@ -206,7 +179,6 @@ impl Analyzer for ExampleAnalyzer {
fn stage_two(
&self,
_age: u32,
_confidence: f64,
_bridge_ips: &[u32],
_bridge_ips_today: u32,
@ -218,7 +190,6 @@ impl Analyzer for ExampleAnalyzer {
fn stage_three(
&self,
_age: u32,
_confidence: f64,
_bridge_ips: &[u32],
_bridge_ips_today: u32,
@ -250,7 +221,6 @@ impl Analyzer for NormalAnalyzer {
/// Evaluate open-entry bridge based on only today's data
fn stage_one(
&self,
_age: u32,
_confidence: f64,
_bridge_ips: &[u32],
bridge_ips_today: u32,
@ -264,7 +234,6 @@ impl Analyzer for NormalAnalyzer {
/// Evaluate invite-only bridge based on historical data
fn stage_two(
&self,
_age: u32,
confidence: f64,
bridge_ips: &[u32],
bridge_ips_today: u32,
@ -336,7 +305,6 @@ impl Analyzer for NormalAnalyzer {
/// Evaluate invite-only bridge with lv3+ users submitting positive reports
fn stage_three(
&self,
age: u32,
confidence: f64,
bridge_ips: &[u32],
bridge_ips_today: u32,
@ -433,7 +401,6 @@ impl Analyzer for NormalAnalyzer {
// evaluate each variable. Ignore positive reports and
// compute as in stage 2
if self.stage_two(
age,
confidence,
bridge_ips,
bridge_ips_today,

View File

@ -23,7 +23,6 @@ pub mod analysis;
pub mod bridge_verification_info;
pub mod crypto;
pub mod extra_info;
pub mod lox_analysis;
pub mod negative_report;
pub mod positive_report;
pub mod request_handler;

View File

@ -1,196 +0,0 @@
use crate::analysis::Analyzer;
use lox_library::proto::{level_up::LEVEL_INTERVAL, trust_promotion::UNTRUSTED_INTERVAL};
use std::cmp::min;
// Get max value from slice, or 0 if empty
fn max(nums: &[u32]) -> u32 {
let mut max_num = 0;
for i in nums {
if *i > max_num {
max_num = *i;
}
}
max_num
}
// Safe u32 subtraction: x - y or 0 if y > x
fn ssub(x: u32, y: u32) -> u32 {
if y > x {
0
} else {
x - y
}
}
/// Analyzer for Lox bridges based on how long they've been active
pub struct LoxAnalyzer {
// simple variable threshold from 0-4 or something
harshness: u32,
}
impl Analyzer for LoxAnalyzer {
// At this stage, we expect 0-10 users, so 0, 8, or 16 bridge-ips
fn stage_one(
&self,
age: u32,
_confidence: f64,
bridge_ips: &[u32],
bridge_ips_today: u32,
_negative_reports: &[u32],
negative_reports_today: u32,
) -> bool {
// Get max bridge_ips we've seen
let max_bridge_ips = max(bridge_ips);
if self.too_few_bridge_ips(age, max_bridge_ips, bridge_ips_today, 0) {
return true;
}
// If we have too many negative reports, consider the bridge
// blocked
self.too_many_negative_reports(negative_reports_today)
}
fn stage_two(
&self,
age: u32,
_confidence: f64,
bridge_ips: &[u32],
bridge_ips_today: u32,
_negative_reports: &[u32],
negative_reports_today: u32,
) -> bool {
// Get max bridge_ips we've seen
let max_bridge_ips = max(bridge_ips);
if self.too_few_bridge_ips(age, max_bridge_ips, bridge_ips_today, 0) {
return true;
}
// If we have too many negative reports, consider the bridge
// blocked
self.too_many_negative_reports(negative_reports_today)
}
fn stage_three(
&self,
age: u32,
_confidence: f64,
bridge_ips: &[u32],
bridge_ips_today: u32,
_negative_reports: &[u32],
negative_reports_today: u32,
_positive_reports: &[u32],
positive_reports_today: u32,
) -> bool {
// Get max bridge_ips we've seen
let max_bridge_ips = max(bridge_ips);
if self.too_few_bridge_ips(
age,
max_bridge_ips,
bridge_ips_today,
positive_reports_today,
) {
return true;
}
// If we have too many negative reports, consider the bridge
// blocked
self.too_many_negative_reports(negative_reports_today)
}
}
impl LoxAnalyzer {
pub fn new(harshness: u32) -> Self {
Self { harshness }
}
// Maximum number of negative reports without considering the bridge blocked
fn too_many_negative_reports(&self, negative_reports_today: u32) -> bool {
// If we have more negative reports than 4 - harshness
negative_reports_today > 4 - self.harshness
// harshness: 4, blocked if 1 negative report
// harshness: 3, blocked if 2 negative reports
// harshness: 2, blocked if 3 negative reports
// harshness: 1, blocked if 4 negative reports
// harshness: 0, blocked if 5 negative reports
}
// Based on the age of the bridge and the max bridge-ips observed, do we
// have too few today?
fn too_few_bridge_ips(
&self,
days: u32,
max_bridge_ips: u32,
bridge_ips_today: u32,
positive_reports_today: u32,
) -> bool {
if days <= UNTRUSTED_INTERVAL {
// We expect 0-10 users
if self.harshness == 4 {
// In the most extreme case, mark any bridge with 0 connections
bridge_ips_today == 0
} else if self.harshness >= 2 {
// With medium harshness, mark any bridge that has had
// 9+ connections and now sees 0
max_bridge_ips > 8 && bridge_ips_today == 0
} else {
// With low harshness, we cannot make a judgement from
// bridge stats
false
}
} else if days <= UNTRUSTED_INTERVAL + LEVEL_INTERVAL[1] {
// We expect 1-30 users
let threshold = min(max_bridge_ips, 32);
// If we're at least 4 - self.harshness bins below the
// maximum value we've seen or 32 if we've seen a lot of
// connections, consider the bridge blocked.
bridge_ips_today < ssub(threshold, (4 - self.harshness) * 8)
// Examples:
// max 64 connections, harshness = 4
// Return true if today's count is less than 32
// max 32 connections, harshness = 4
// Return true if today's count is less than 32
// max 32 connections, harshness = 3
// Return true if today's count is less than 24
// max 32 connections, harshness = 2
// Return true if today's count is less than 16
// max 8 connections, harshness = 4
// Return true if today's count is less than 8
// max 8 connections, harshness = 3
// Return false
} else {
// Similar, but consider positive reports as well
let threshold = min(max_bridge_ips, 32);
// We allow positive reports to increase the threshold for
// considering the bridge blocked. 8 positive reports
// (rounded up) remove 1 level reduction from the threshold
let threshold = ssub(
threshold,
(4 - self.harshness + (positive_reports_today + 7) / 8) * 8,
);
// For example, suppose we've seen 32+ connections.
//
// If we have harshness 4, we mark the bridge blocked if it
// has fewer than 32 connections.
//
// If we have harshness 4 but have seen 1-7 positive
// reports, we mark the bridge blocked only if it has fewer
// than 24 connections.
//
// 25 positive reports reduce all thresholds to 0, meaning
// bridges will never be marked blocked from bridge stats
// if the adversary can submit 25 positive reports.
// Consider the bridge blocked if we have too few connections
bridge_ips_today < threshold
}
}
}

View File

@ -103,8 +103,7 @@ async fn update_daily_info(
update_positive_reports(db, distributors).await;
let new_blockages = guess_blockages(
db,
// Using max_threshold for convenience
&lox_analysis::LoxAnalyzer::new(max_threshold),
&analysis::NormalAnalyzer::new(max_threshold, scaling_factor),
confidence,
min_historical_days,
max_historical_days,