Compare commits

...

2 Commits

Author SHA1 Message Date
Vecna 498d6b4cee Change Hides to Secrecy, have censor submit all false reports at end
If the censor submits a report each time it blocks a connection, this number may exceed the number of Lox credentials the censor can actually use to submit these reports. This is an issue if we restrict these reports to one per credential.
2024-06-08 15:52:35 -04:00
Vecna bc834c329d Better error handling, update for lox_cli 2024-06-08 15:27:22 -04:00
6 changed files with 119 additions and 68 deletions

View File

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
aes-gcm = "0.10" aes-gcm = "0.10"
anyhow = "1.0"
array-bytes = "6.2.0" array-bytes = "6.2.0"
bincode = "1" bincode = "1"
chrono = "0.4" chrono = "0.4"

View File

@ -44,7 +44,7 @@ pub struct Config {
pub la_test_port: u16, pub la_test_port: u16,
pub tp_port: u16, pub tp_port: u16,
pub tp_test_port: u16, pub tp_test_port: u16,
pub censor_hides: censor::Hides, pub censor_secrecy: censor::Secrecy,
pub censor_speed: censor::Speed, pub censor_speed: censor::Speed,
pub censor_event_duration: u32, pub censor_event_duration: u32,
pub censor_totality: censor::Totality, pub censor_totality: censor::Totality,
@ -96,7 +96,7 @@ pub async fn main() {
la_pubkeys, la_pubkeys,
la_net, la_net,
tp_net, tp_net,
censor_hides: config.censor_hides, censor_secrecy: config.censor_secrecy,
censor_speed: config.censor_speed, censor_speed: config.censor_speed,
censor_event_duration: config.censor_event_duration, censor_event_duration: config.censor_event_duration,
censor_totality: config.censor_totality, censor_totality: config.censor_totality,
@ -141,7 +141,8 @@ pub async fn main() {
.unwrap() .unwrap()
.into(), .into(),
) )
.await; .await
.unwrap();
// Advance simulated time // Advance simulated time
set_simulated_date(get_date() + UNTRUSTED_INTERVAL); set_simulated_date(get_date() + UNTRUSTED_INTERVAL);
@ -176,7 +177,8 @@ pub async fn main() {
.unwrap() .unwrap()
.into(), .into(),
) )
.await; .await
.unwrap();
// Advance simulated time // Advance simulated time
set_simulated_date(get_date() + LEVEL_INTERVAL[i]); set_simulated_date(get_date() + LEVEL_INTERVAL[i]);
@ -207,7 +209,8 @@ pub async fn main() {
"/advancedays".to_string(), "/advancedays".to_string(),
serde_json::to_string(&(1 as u16)).unwrap().into(), serde_json::to_string(&(1 as u16)).unwrap().into(),
) )
.await; .await
.unwrap();
// Advance simulated time to tomorrow // Advance simulated time to tomorrow
increment_simulated_date(); increment_simulated_date();
@ -366,10 +369,14 @@ pub async fn main() {
"/add".to_string(), "/add".to_string(),
serde_json::to_string(&new_extra_infos).unwrap().into(), serde_json::to_string(&new_extra_infos).unwrap().into(),
) )
.await; .await
.unwrap();
// TROLL PATROL TASKS // TROLL PATROL TASKS
let new_blockages_resp = tp_net_test.request("/update".to_string(), vec![]).await; let new_blockages_resp = tp_net_test
.request("/update".to_string(), vec![])
.await
.unwrap();
let new_blockages: HashMap<String, HashSet<String>> = let new_blockages: HashMap<String, HashSet<String>> =
serde_json::from_slice(&new_blockages_resp).unwrap(); serde_json::from_slice(&new_blockages_resp).unwrap();
@ -415,7 +422,8 @@ pub async fn main() {
"/advancedays".to_string(), "/advancedays".to_string(),
serde_json::to_string(&(1 as u16)).unwrap().into(), serde_json::to_string(&(1 as u16)).unwrap().into(),
) )
.await; .await
.unwrap();
// SIMULATION TASKS // SIMULATION TASKS

View File

@ -26,8 +26,8 @@ pub struct Bridge {
// bridge (for identifying stage three) // bridge (for identifying stage three)
pub first_positive_report: u32, pub first_positive_report: u32,
real_connections: u32, pub real_connections: u32,
total_connections: u32, pub total_connections: u32,
} }
impl Bridge { impl Bridge {

View File

@ -8,7 +8,10 @@ use lox_cli::{get_lox_pub, networking::Networking};
use lox_library::{cred::Lox, scalar_u32}; use lox_library::{cred::Lox, scalar_u32};
use rand::Rng; use rand::Rng;
use serde::Deserialize; use serde::Deserialize;
use std::collections::{HashMap, HashSet}; use std::{
cmp::min,
collections::{HashMap, HashSet},
};
pub struct Censor { pub struct Censor {
pub known_bridges: HashSet<[u8; 20]>, pub known_bridges: HashSet<[u8; 20]>,
@ -112,17 +115,22 @@ impl Censor {
config.country.clone(), config.country.clone(),
) )
.unwrap(); .unwrap();
config if config
.tp_net .tp_net
.request("/positivereport".to_string(), pr.to_json().into_bytes()) .request("/positivereport".to_string(), pr.to_json().into_bytes())
.await; .await
.is_err()
{
// failed to send positive report
return false;
}
true true
} }
// Make a bunch of connections and submit positive reports if possible // Make a bunch of connections and submit positive reports if possible
async fn flood(&self, config: &Config, bridges: &mut HashMap<[u8; 20], Bridge>) { async fn flood(&self, config: &Config, bridges: &mut HashMap<[u8; 20], Bridge>) {
// Only do this if Flooding censor // Only do this if Flooding censor
if config.censor_hides == Hides::Flooding { if config.censor_secrecy == Secrecy::Flooding {
for fingerprint in &self.known_bridges { for fingerprint in &self.known_bridges {
// Only do this if we're blocking the bridge // Only do this if we're blocking the bridge
if config.censor_speed == Speed::Fast if config.censor_speed == Speed::Fast
@ -156,6 +164,38 @@ impl Censor {
} }
} }
// Send one positive report per connection we blocked
async fn send_positive_reports(
&self,
config: &Config,
bridges: &mut HashMap<[u8; 20], Bridge>,
) {
// Only do this if Hiding censor. Flooding censors should use
// flood() instead.
if config.censor_secrecy == Secrecy::Hiding {
for fingerprint in &self.known_bridges {
// Only do this if we're blocking the bridge
if self.blocks_bridge(config, fingerprint) && self.has_lox_cred(fingerprint) {
let bridge = bridges.get_mut(fingerprint).unwrap();
// We may be restricted to one positive report per
// credential
let num_reports_to_send = if config.one_positive_report_per_cred {
min(
bridge.total_connections - bridge.real_connections,
self.lox_credentials.get(fingerprint).unwrap().1,
)
} else {
bridge.total_connections - bridge.real_connections
};
for _ in 0..num_reports_to_send {
self.send_positive_report(config, fingerprint).await;
}
}
}
}
}
fn recompute_delay(&mut self, config: &Config) { fn recompute_delay(&mut self, config: &Config) {
// Only do this if Random censor // Only do this if Random censor
if config.censor_speed == Speed::Random if config.censor_speed == Speed::Random
@ -175,10 +215,14 @@ impl Censor {
config: &Config, config: &Config,
bridges: &mut HashMap<[u8; 20], Bridge>, bridges: &mut HashMap<[u8; 20], Bridge>,
) { ) {
if config.censor_hides == Hides::Flooding if config.censor_secrecy == Secrecy::Flooding
&& !(config.censor_speed == Speed::Random && self.delay_date <= get_date()) && !(config.censor_speed == Speed::Random && self.delay_date <= get_date())
{ {
self.flood(config, bridges).await; self.flood(config, bridges).await;
} else if config.censor_secrecy == Secrecy::Hiding
&& !(config.censor_speed == Speed::Random && self.delay_date <= get_date())
{
self.send_positive_reports(config, bridges).await;
} }
self.recompute_delay(config); self.recompute_delay(config);
@ -193,7 +237,7 @@ pub enum Speed {
} }
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)] #[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
pub enum Hides { pub enum Secrecy {
Overt, Overt,
Hiding, Hiding,
Flooding, Flooding,

View File

@ -8,7 +8,7 @@ pub struct Config {
pub la_net: HyperNet, pub la_net: HyperNet,
pub tp_net: HyperNet, pub tp_net: HyperNet,
// Define censor behavior // Define censor behavior
pub censor_hides: censor::Hides, pub censor_secrecy: censor::Secrecy,
pub censor_speed: censor::Speed, pub censor_speed: censor::Speed,
pub censor_event_duration: u32, pub censor_event_duration: u32,
pub censor_totality: censor::Totality, pub censor_totality: censor::Totality,

View File

@ -6,17 +6,17 @@ use crate::{
positive_report::PositiveReport, positive_report::PositiveReport,
simulation::{ simulation::{
bridge::Bridge, bridge::Bridge,
censor::{Censor, Hides::*, Totality::*}, censor::{Censor, Secrecy::*, Totality::*},
config::Config, config::Config,
}, },
BridgeDistributor, BridgeDistributor,
}; };
use anyhow::{anyhow, Result};
use lox_cli::{networking::*, *}; use lox_cli::{networking::*, *};
use lox_library::{ use lox_library::{
bridge_table::BridgeLine, cred::Lox, proto::check_blockage::MIN_TRUST_LEVEL, scalar_u32, bridge_table::BridgeLine, cred::Lox, proto::check_blockage::MIN_TRUST_LEVEL, scalar_u32,
}; };
use rand::Rng; use rand::Rng;
use serde_json::error::Error;
use std::{cmp::min, collections::HashMap}; use std::{cmp::min, collections::HashMap};
use x25519_dalek::PublicKey; use x25519_dalek::PublicKey;
@ -45,7 +45,7 @@ pub struct User {
} }
impl User { impl User {
pub async fn new(config: &Config, is_censor: bool) -> Result<Self, Error> { pub async fn new(config: &Config, is_censor: bool) -> Result<Self> {
let cred = get_lox_credential( let cred = get_lox_credential(
&config.la_net, &config.la_net,
&get_open_invitation(&config.la_net).await?, &get_open_invitation(&config.la_net).await?,
@ -75,7 +75,7 @@ impl User {
}) })
} }
pub async fn trusted_user(config: &Config) -> Result<Self, Error> { pub async fn trusted_user(config: &Config) -> Result<Self> {
let cred = get_lox_credential( let cred = get_lox_credential(
&config.la_net, &config.la_net,
&get_open_invitation(&config.la_net).await?, &get_open_invitation(&config.la_net).await?,
@ -98,7 +98,7 @@ impl User {
config: &Config, config: &Config,
censor: &mut Censor, censor: &mut Censor,
invited_user_is_censor: bool, invited_user_is_censor: bool,
) -> Result<Self, Error> { ) -> Result<Self> {
let etable = get_reachability_credential(&config.la_net).await?; let etable = get_reachability_credential(&config.la_net).await?;
let (new_cred, invite) = issue_invite( let (new_cred, invite) = issue_invite(
&config.la_net, &config.la_net,
@ -157,7 +157,7 @@ impl User {
// Note that this does not involve making a real connection to a // Note that this does not involve making a real connection to a
// real bridge. The function is async because the *censor* might // real bridge. The function is async because the *censor* might
// submit a positive report during this function. // submit a positive report during this function.
pub async fn connect(&self, config: &Config, bridge: &mut Bridge, censor: &Censor) -> bool { pub fn connect(&self, config: &Config, bridge: &mut Bridge, censor: &Censor) -> bool {
if censor.blocks_bridge(config, &bridge.fingerprint) { if censor.blocks_bridge(config, &bridge.fingerprint) {
if config.censor_totality == Full if config.censor_totality == Full
|| config.censor_totality == Partial || config.censor_totality == Partial
@ -165,7 +165,7 @@ impl User {
{ {
// If censor tries to hide its censorship, record a // If censor tries to hide its censorship, record a
// false connection // false connection
if config.censor_hides == Hiding { if config.censor_secrecy == Hiding {
bridge.connect_total(); bridge.connect_total();
} }
@ -179,15 +179,6 @@ impl User {
if event_happens(config.prob_user_treats_throttling_as_blocking) { if event_happens(config.prob_user_treats_throttling_as_blocking) {
bridge.connect_total(); bridge.connect_total();
// A Hiding censor does not make an additional
// connection here, but it will make a false
// positive report if possible.
if config.censor_hides == Hiding && censor.has_lox_cred(&bridge.fingerprint) {
censor
.send_positive_report(config, &bridge.fingerprint)
.await;
}
// Return false because there was interference // Return false because there was interference
// detected in the connection // detected in the connection
return false; return false;
@ -205,7 +196,7 @@ impl User {
true true
} }
pub async fn get_new_credential(config: &Config) -> Result<(Lox, BridgeLine), Error> { pub async fn get_new_credential(config: &Config) -> Result<(Lox, BridgeLine)> {
get_lox_credential( get_lox_credential(
&config.la_net, &config.la_net,
&get_open_invitation(&config.la_net).await?, &get_open_invitation(&config.la_net).await?,
@ -214,7 +205,10 @@ impl User {
.await .await
} }
pub async fn send_negative_reports(config: &Config, reports: Vec<NegativeReport>) { pub async fn send_negative_reports(
config: &Config,
reports: Vec<NegativeReport>,
) -> Result<()> {
let date = get_date(); let date = get_date();
let pubkey = serde_json::from_slice::<Option<PublicKey>>( let pubkey = serde_json::from_slice::<Option<PublicKey>>(
&config &config
@ -223,9 +217,8 @@ impl User {
"/nrkey".to_string(), "/nrkey".to_string(),
serde_json::to_string(&date).unwrap().into(), serde_json::to_string(&date).unwrap().into(),
) )
.await, .await?,
) )?
.unwrap()
.unwrap(); .unwrap();
for report in reports { for report in reports {
config config
@ -234,17 +227,22 @@ impl User {
"/negativereport".to_string(), "/negativereport".to_string(),
bincode::serialize(&report.encrypt(&pubkey)).unwrap(), bincode::serialize(&report.encrypt(&pubkey)).unwrap(),
) )
.await; .await?;
} }
Ok(())
} }
pub async fn send_positive_reports(config: &Config, reports: Vec<PositiveReport>) { pub async fn send_positive_reports(
config: &Config,
reports: Vec<PositiveReport>,
) -> Result<()> {
for report in reports { for report in reports {
config config
.tp_net .tp_net
.request("/positivereport".to_string(), report.to_json().into_bytes()) .request("/positivereport".to_string(), report.to_json().into_bytes())
.await; .await?;
} }
Ok(())
} }
pub async fn daily_tasks( pub async fn daily_tasks(
@ -254,7 +252,7 @@ impl User {
num_censor_invites: u32, num_censor_invites: u32,
bridges: &mut HashMap<[u8; 20], Bridge>, bridges: &mut HashMap<[u8; 20], Bridge>,
censor: &mut Censor, censor: &mut Censor,
) -> Result<Vec<User>, Error> { ) -> Result<Vec<User>> {
if self.is_censor { if self.is_censor {
self.daily_tasks_censor(config, bridges, censor).await self.daily_tasks_censor(config, bridges, censor).await
} else { } else {
@ -280,14 +278,17 @@ impl User {
num_censor_invites: u32, num_censor_invites: u32,
bridges: &mut HashMap<[u8; 20], Bridge>, bridges: &mut HashMap<[u8; 20], Bridge>,
censor: &mut Censor, censor: &mut Censor,
) -> Result<Vec<User>, Error> { ) -> Result<Vec<User>> {
// Probabilistically decide if the user should use bridges today // Probabilistically decide if the user should use bridges today
if event_happens(self.prob_use_bridges) { if event_happens(self.prob_use_bridges) {
// Download bucket to see if bridge is still reachable. (We // Download bucket to see if bridge is still reachable. (We
// assume that this step can be done even if the user can't // assume that this step can be done even if the user can't
// actually talk to the LA.) // actually talk to the LA.)
let (bucket, reachcred) = get_bucket(&config.la_net, &self.primary_cred).await?; let (bucket, reachcred) = get_bucket(&config.la_net, &self.primary_cred).await?;
let level = scalar_u32(&self.primary_cred.trust_level).unwrap(); let level = match scalar_u32(&self.primary_cred.trust_level) {
Some(v) => v,
None => return Err(anyhow!("Failed to get trust level from credential")),
};
// Make sure each bridge in bucket is in the global bridges set // Make sure each bridge in bucket is in the global bridges set
for bridgeline in bucket { for bridgeline in bucket {
@ -318,16 +319,13 @@ impl User {
for i in 0..bucket.len() { for i in 0..bucket.len() {
// At level 0, we only have 1 bridge // At level 0, we only have 1 bridge
if bucket[i] != BridgeLine::default() { if bucket[i] != BridgeLine::default() {
if self if self.connect(
.connect( &config,
&config, bridges
bridges .get_mut(&bucket[i].get_hashed_fingerprint())
.get_mut(&bucket[i].get_hashed_fingerprint()) .unwrap(),
.unwrap(), &censor,
&censor, ) {
)
.await
{
succeeded.push(bucket[i]); succeeded.push(bucket[i]);
} else { } else {
failed.push(bucket[i]); failed.push(bucket[i]);
@ -368,16 +366,13 @@ impl User {
); );
} }
// Attempt to connect to second cred's bridge // Attempt to connect to second cred's bridge
if self if self.connect(
.connect( &config,
&config, bridges
bridges .get_mut(&bridgeline.get_hashed_fingerprint())
.get_mut(&bridgeline.get_hashed_fingerprint()) .unwrap(),
.unwrap(), censor,
censor, ) {
)
.await
{
succeeded.push(bridgeline); succeeded.push(bridgeline);
if second_reachcred.is_some() if second_reachcred.is_some()
&& eligible_for_trust_promotion(&config.la_net, &second_cred).await && eligible_for_trust_promotion(&config.la_net, &second_cred).await
@ -506,14 +501,17 @@ impl User {
} }
if negative_reports.len() > 0 { if negative_reports.len() > 0 {
Self::send_negative_reports(&config, negative_reports).await; Self::send_negative_reports(&config, negative_reports).await?;
} }
if positive_reports.len() > 0 { if positive_reports.len() > 0 {
Self::send_positive_reports(&config, positive_reports).await; Self::send_positive_reports(&config, positive_reports).await?;
} }
// Invite friends if applicable // Invite friends if applicable
let invitations = scalar_u32(&self.primary_cred.invites_remaining).unwrap(); let invitations = match scalar_u32(&self.primary_cred.invites_remaining) {
Some(v) => v,
None => 0, // This is probably an error case that should not happen
};
let mut new_friends = Vec::<User>::new(); let mut new_friends = Vec::<User>::new();
for _i in 0..min(invitations, num_users_requesting_invites) { for _i in 0..min(invitations, num_users_requesting_invites) {
if event_happens(config.prob_user_invites_friend) { if event_happens(config.prob_user_invites_friend) {
@ -561,7 +559,7 @@ impl User {
config: &Config, config: &Config,
bridges: &mut HashMap<[u8; 20], Bridge>, bridges: &mut HashMap<[u8; 20], Bridge>,
censor: &mut Censor, censor: &mut Censor,
) -> Result<Vec<User>, Error> { ) -> Result<Vec<User>> {
// Download bucket to see if bridge is still reachable and if we // Download bucket to see if bridge is still reachable and if we
// have any new bridges // have any new bridges
let (bucket, reachcred) = get_bucket(&config.la_net, &self.primary_cred).await?; let (bucket, reachcred) = get_bucket(&config.la_net, &self.primary_cred).await?;