12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697 |
- #![allow(non_snake_case)]
- use crate::{
- analysis::{blocked_in, Analyzer},
- bridge_verification_info::BridgeVerificationInfo,
- *,
- };
- use lox_library::{
- bridge_table::{self, BridgeLine, BridgeTable},
- cred::Lox,
- proto::*,
- scalar_u32, BridgeAuth, BridgeDb,
- };
- use base64::{engine::general_purpose, Engine as _};
- use curve25519_dalek::{ristretto::RistrettoBasepointTable, Scalar};
- use rand::RngCore;
- use sha1::{Digest, Sha1};
- use std::{
- collections::{BTreeMap, HashMap, HashSet},
- sync::{Arc, Mutex},
- };
- use x25519_dalek::{PublicKey, StaticSecret};
- struct TestHarness {
- bdb: BridgeDb,
- pub ba: BridgeAuth,
- }
- impl TestHarness {
- fn new() -> Self {
- TestHarness::new_buckets(5, 5)
- }
- fn new_buckets(num_buckets: u16, hot_spare: u16) -> Self {
- // Create a BridegDb
- let mut bdb = BridgeDb::new();
- // Create a BridgeAuth
- let mut ba = BridgeAuth::new(bdb.pubkey);
- // Make 3 x num_buckets open invitation bridges, in sets of 3
- for _ in 0..num_buckets {
- let bucket = [random(), random(), random()];
- let _ = ba.add_openinv_bridges(bucket, &mut bdb);
- }
- // Add hot_spare more hot spare buckets
- for _ in 0..hot_spare {
- let bucket = [random(), random(), random()];
- let _ = ba.add_spare_bucket(bucket, &mut bdb);
- }
- // Create the encrypted bridge table
- ba.enc_bridge_table();
- Self { bdb, ba }
- }
- fn advance_days(&mut self, days: u16) {
- self.ba.advance_days(days);
- }
- fn get_new_credential(&mut self) -> Lox {
- let inv = self.bdb.invite().unwrap();
- let (req, state) = open_invite::request(&inv);
- let resp = self.ba.handle_open_invite(req).unwrap();
- let (cred, _bridgeline) =
- open_invite::handle_response(state, resp, &self.ba.lox_pub).unwrap();
- cred
- }
- fn level_up(&mut self, cred: &Lox) -> Lox {
- let current_level = scalar_u32(&cred.trust_level).unwrap();
- if current_level == 0 {
- self.advance_days(trust_promotion::UNTRUSTED_INTERVAL.try_into().unwrap());
- let (promreq, promstate) =
- trust_promotion::request(cred, &self.ba.lox_pub, self.ba.today()).unwrap();
- let promresp = self.ba.handle_trust_promotion(promreq).unwrap();
- let migcred = trust_promotion::handle_response(promstate, promresp).unwrap();
- let (migreq, migstate) =
- migration::request(cred, &migcred, &self.ba.lox_pub, &self.ba.migration_pub)
- .unwrap();
- let migresp = self.ba.handle_migration(migreq).unwrap();
- let new_cred = migration::handle_response(migstate, migresp, &self.ba.lox_pub).unwrap();
- new_cred
- } else {
- self.advance_days(
- level_up::LEVEL_INTERVAL[usize::try_from(current_level).unwrap()]
- .try_into()
- .unwrap(),
- );
- let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
- let encbuckets = self.ba.enc_bridge_table();
- let bucket =
- bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
- .unwrap();
- let reachcred = bucket.1.unwrap();
- let (lvreq, lvstate) = level_up::request(
- cred,
- &reachcred,
- &self.ba.lox_pub,
- &self.ba.reachability_pub,
- self.ba.today(),
- )
- .unwrap();
- let lvresp = self.ba.handle_level_up(lvreq).unwrap();
- let new_cred = level_up::handle_response(lvstate, lvresp, &self.ba.lox_pub).unwrap();
- new_cred
- }
- }
- fn get_bucket(&mut self, cred: &Lox) -> [BridgeLine; bridge_table::MAX_BRIDGES_PER_BUCKET] {
- let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
- let encbuckets = self.ba.enc_bridge_table();
- let bucket =
- bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
- .unwrap();
- bucket.0
- }
- }
- 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();
- rng.fill_bytes(&mut res.fingerprint);
- let mut cert: [u8; 52] = [0; 52];
- rng.fill_bytes(&mut cert);
- let infostr: String = format!(
- "obfs4 cert={}, iat-mode=0",
- general_purpose::STANDARD_NO_PAD.encode(cert)
- );
- res.info[..infostr.len()].copy_from_slice(infostr.as_bytes());
- res
- }
- #[tokio::test]
- async fn test_extra_infos() {
- let bridge_to_test =
- array_bytes::hex2array("72E12B89136B45BBC81D1EF0AC7DDDBB91B148DB").unwrap();
- // Open test database
- let db: Db = sled::open("test_db_ei").unwrap();
- // Delete all data in test DB
- db.clear().unwrap();
- assert!(!db.contains_key("bridges").unwrap());
- assert!(!db.contains_key(bridge_to_test).unwrap());
- // Download and process recent extra-infos files
- update_extra_infos(
- &db,
- "https://collector.torproject.org/recent/bridge-descriptors/extra-infos/",
- )
- .await
- .unwrap();
- // Check that DB contains information on a bridge with high uptime
- assert!(db.contains_key("bridges").unwrap());
- let bridges: HashSet<[u8; 20]> =
- bincode::deserialize(&db.get("bridges").unwrap().unwrap()).unwrap();
- assert!(bridges.contains(&bridge_to_test));
- assert!(db.contains_key(bridge_to_test).unwrap());
- let _bridge_info: BridgeInfo =
- bincode::deserialize(&db.get(bridge_to_test).unwrap().unwrap()).unwrap();
- }
- #[test]
- fn test_negative_reports() {
- let mut th = TestHarness::new();
- // Get new level 1 credential
- let cred = th.get_new_credential();
- let cred = th.level_up(&cred);
- let bridges = th.get_bucket(&cred);
- // Create BridgeVerificationInfo for each bridge
- let mut buckets = HashSet::<Scalar>::new();
- buckets.insert(cred.bucket);
- let bridge_info_1 = BridgeVerificationInfo {
- bridge_line: bridges[0],
- buckets: buckets.clone(),
- pubkey: None,
- };
- let bridge_info_2 = BridgeVerificationInfo {
- bridge_line: bridges[1],
- buckets: buckets.clone(),
- pubkey: None,
- };
- let bridge_info_3 = BridgeVerificationInfo {
- bridge_line: bridges[2],
- buckets: buckets.clone(),
- pubkey: None,
- };
- // Create reports
- let report_1 =
- NegativeReport::from_bridgeline(bridges[0], "ru".to_string(), BridgeDistributor::Lox);
- let report_2 =
- NegativeReport::from_lox_bucket(bridges[1].fingerprint, cred.bucket, "ru".to_string());
- let report_3 =
- NegativeReport::from_lox_credential(bridges[2].fingerprint, &cred, "ru".to_string());
- // Backdated reports
- let date = get_date();
- let mut rng = rand::thread_rng();
- let mut nonce = [0; 32];
- rng.fill_bytes(&mut nonce);
- let report_4 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
- &bridges[0],
- date - 1,
- nonce,
- )),
- "ru".to_string(),
- date - 1,
- nonce,
- BridgeDistributor::Lox,
- );
- let mut nonce = [0; 32];
- rng.fill_bytes(&mut nonce);
- let report_5 = NegativeReport::new(
- bridges[1].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
- &bridges[1],
- date - 2,
- nonce,
- )),
- "ru".to_string(),
- date - 2,
- nonce,
- BridgeDistributor::Lox,
- );
- let mut nonce = [0; 32];
- rng.fill_bytes(&mut nonce);
- let report_6 = NegativeReport::new(
- bridges[2].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
- &bridges[2],
- date - 3,
- nonce,
- )),
- "ru".to_string(),
- date - 3,
- nonce,
- BridgeDistributor::Lox,
- );
- // Verify reports
- assert!(report_1.verify(&bridge_info_1));
- assert!(report_2.verify(&bridge_info_2));
- assert!(report_3.verify(&bridge_info_3));
- assert!(report_4.verify(&bridge_info_1));
- assert!(report_5.verify(&bridge_info_2));
- assert!(report_6.verify(&bridge_info_3));
- // Check that deserialization fails under invalid conditions
- // Date in the future
- let mut invalid_report_1 =
- NegativeReport::from_bridgeline(bridges[0], "ru".to_string(), BridgeDistributor::Lox)
- .to_serializable_report();
- invalid_report_1.date = invalid_report_1.date + 2;
- // Date too far in past
- let mut invalid_report_2 =
- NegativeReport::from_bridgeline(bridges[1], "ru".to_string(), BridgeDistributor::Lox)
- .to_serializable_report();
- invalid_report_2.date = invalid_report_2.date - MAX_BACKDATE - 1;
- // Invalid country code
- let invalid_report_3 =
- NegativeReport::from_bridgeline(bridges[2], "xx".to_string(), BridgeDistributor::Lox)
- .to_serializable_report();
- assert!(invalid_report_1.to_report().is_err());
- assert!(invalid_report_2.to_report().is_err());
- assert!(invalid_report_3.to_report().is_err());
- // Check that verification fails with incorrect data
- let date = get_date();
- let mut rng = rand::thread_rng();
- // Incorrect BridgeLine hash
- let mut nonce = [0; 32];
- rng.fill_bytes(&mut nonce);
- let invalid_report_4 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
- &BridgeLine::default(),
- date,
- nonce,
- )),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- // Incorrect bucket hash
- let mut nonce = [0; 32];
- rng.fill_bytes(&mut nonce);
- let invalid_report_5 = NegativeReport::new(
- bridges[1].fingerprint,
- ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&Scalar::ZERO, date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- assert!(!invalid_report_4.verify(&bridge_info_1));
- assert!(!invalid_report_5.verify(&bridge_info_2));
- // Test that reports with duplicate nonces are rejected
- // (Also test encryption and decryption.)
- // Open test database
- let db: Db = sled::open("test_db_nr").unwrap();
- // Delete all data in test DB
- db.clear().unwrap();
- assert!(!db.contains_key("nrs-to-process").unwrap());
- let mut nonce = [0; 32];
- rng.fill_bytes(&mut nonce);
- // A valid report
- let valid_report_1 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- let valid_report_1_copy_1 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- let valid_report_1_copy_2 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- // Report which reuses this nonce
- let invalid_report_1 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- // This is the same report
- assert_eq!(valid_report_1, invalid_report_1);
- // Report which reuses this nonce for a different bridge
- let invalid_report_2 = NegativeReport::new(
- bridges[1].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[1], date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- // Report which uses this nonce but on a different day
- let valid_report_2 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
- &bridges[0],
- date - 1,
- nonce,
- )),
- "ru".to_string(),
- date - 1,
- nonce,
- BridgeDistributor::Lox,
- );
- // Report with different nonce
- let mut nonce = [0; 32];
- rng.fill_bytes(&mut nonce);
- let valid_report_3 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- let map_key_1 = format!(
- "{}_{}_{}",
- array_bytes::bytes2hex("", valid_report_1.fingerprint),
- "ru".to_string(),
- date
- );
- // Generate key for today
- let secret = StaticSecret::random_from_rng(&mut rng);
- let public = PublicKey::from(&secret);
- let secret_yesterday = StaticSecret::random_from_rng(&mut rng);
- let public_yesterday = PublicKey::from(&secret_yesterday);
- assert!(!db.contains_key("nr-keys").unwrap());
- // Fail to add to database because we can't decrypt
- handle_encrypted_negative_report(&db, valid_report_1_copy_1.encrypt(&public));
- assert!(!db.contains_key("nrs-to-process").unwrap());
- // Store yesterday's key but not today's
- let mut nr_keys = BTreeMap::<u32, StaticSecret>::new();
- nr_keys.insert(date - 1, secret_yesterday);
- db.insert("nr-keys", bincode::serialize(&nr_keys).unwrap())
- .unwrap();
- // Fail to add to database because we still can't decrypt
- handle_encrypted_negative_report(&db, valid_report_1_copy_2.encrypt(&public));
- assert!(!db.contains_key("nrs-to-process").unwrap());
- // Store today's key
- nr_keys.insert(date, secret);
- db.insert("nr-keys", bincode::serialize(&nr_keys).unwrap())
- .unwrap();
- handle_encrypted_negative_report(&db, valid_report_1.encrypt(&public));
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
- assert_eq!(negative_reports.len(), 1);
- handle_encrypted_negative_report(&db, invalid_report_1.encrypt(&public)); // no change
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
- assert_eq!(negative_reports.len(), 1);
- let map_key_2 = format!(
- "{}_{}_{}",
- array_bytes::bytes2hex("", invalid_report_2.fingerprint),
- "ru".to_string(),
- date
- );
- handle_encrypted_negative_report(&db, invalid_report_2.encrypt(&public)); // no change
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- assert!(!nrs_to_process.contains_key(&map_key_2));
- let map_key_3 = format!(
- "{}_{}_{}",
- array_bytes::bytes2hex("", valid_report_2.fingerprint),
- "ru".to_string(),
- date - 1
- );
- handle_encrypted_negative_report(&db, valid_report_2.encrypt(&public_yesterday));
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
- assert_eq!(negative_reports.len(), 1);
- handle_encrypted_negative_report(&db, valid_report_3.encrypt(&public));
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
- assert_eq!(negative_reports.len(), 2);
- // Same tests, but use hash of bucket
- // Delete all data in test DB
- db.clear().unwrap();
- assert!(!db.contains_key("nrs-to-process").unwrap());
- // Re-generate keys and save in database
- let public = new_negative_report_key(&db, date).unwrap();
- let public_yesterday = new_negative_report_key(&db, date - 1).unwrap();
- let mut nonce = [0; 32];
- rng.fill_bytes(&mut nonce);
- // A valid report
- let valid_report_1 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- // Report which reuses this nonce
- let invalid_report_1 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- // This is the same report
- assert_eq!(valid_report_1, invalid_report_1);
- // Report which reuses this nonce for a different bridge
- let invalid_report_2 = NegativeReport::new(
- bridges[1].fingerprint,
- ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- // Report which uses this nonce but on a different day
- let valid_report_2 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date - 1, nonce)),
- "ru".to_string(),
- date - 1,
- nonce,
- BridgeDistributor::Lox,
- );
- // Report with different nonce
- let mut nonce = [0; 32];
- rng.fill_bytes(&mut nonce);
- let valid_report_3 = NegativeReport::new(
- bridges[0].fingerprint,
- ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
- "ru".to_string(),
- date,
- nonce,
- BridgeDistributor::Lox,
- );
- let map_key_1 = format!(
- "{}_{}_{}",
- array_bytes::bytes2hex("", valid_report_1.fingerprint),
- "ru".to_string(),
- date
- );
- handle_encrypted_negative_report(&db, valid_report_1.encrypt(&public));
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
- assert_eq!(negative_reports.len(), 1);
- handle_encrypted_negative_report(&db, invalid_report_1.encrypt(&public)); // no change
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
- assert_eq!(negative_reports.len(), 1);
- let map_key_2 = format!(
- "{}_{}_{}",
- array_bytes::bytes2hex("", invalid_report_2.fingerprint),
- "ru".to_string(),
- date
- );
- handle_encrypted_negative_report(&db, invalid_report_2.encrypt(&public)); // no change
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- assert!(!nrs_to_process.contains_key(&map_key_2));
- let map_key_3 = format!(
- "{}_{}_{}",
- array_bytes::bytes2hex("", valid_report_2.fingerprint),
- "ru".to_string(),
- date - 1
- );
- handle_encrypted_negative_report(&db, valid_report_2.encrypt(&public_yesterday));
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
- assert_eq!(negative_reports.len(), 1);
- handle_encrypted_negative_report(&db, valid_report_3.encrypt(&public));
- let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
- bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
- let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
- assert_eq!(negative_reports.len(), 2);
- }
- #[test]
- fn test_positive_reports() {
- let mut th = TestHarness::new();
- // Get new level 3 credential
- let cred = th.get_new_credential();
- let cred = th.level_up(&cred);
- let cred = th.level_up(&cred);
- let cred = th.level_up(&cred);
- let bridges = th.get_bucket(&cred);
- // Create BridgeVerificationInfo for each bridge
- let mut buckets = HashSet::<Scalar>::new();
- buckets.insert(cred.bucket);
- let bridge_info_1 = BridgeVerificationInfo {
- bridge_line: bridges[0],
- buckets: buckets.clone(),
- pubkey: None,
- };
- let bridge_info_2 = BridgeVerificationInfo {
- bridge_line: bridges[1],
- buckets: buckets.clone(),
- pubkey: None,
- };
- let bridge_info_3 = BridgeVerificationInfo {
- bridge_line: bridges[2],
- buckets: buckets.clone(),
- pubkey: None,
- };
- // Create reports
- let report_1 = PositiveReport::from_lox_credential(
- bridges[0].fingerprint,
- None,
- &cred,
- &th.ba.lox_pub,
- "ru".to_string(),
- )
- .unwrap();
- let report_2 = PositiveReport::from_lox_credential(
- bridges[1].fingerprint,
- None,
- &cred,
- &th.ba.lox_pub,
- "ru".to_string(),
- )
- .unwrap();
- let report_3 = PositiveReport::from_lox_credential(
- bridges[2].fingerprint,
- None,
- &cred,
- &th.ba.lox_pub,
- "ru".to_string(),
- )
- .unwrap();
- // Compute Htable
- let H = lox_library::proto::positive_report::compute_H(report_1.date);
- let Htable = RistrettoBasepointTable::create(&H);
- assert!(report_1.verify(&mut th.ba, &bridge_info_1, &Htable));
- assert!(report_2.verify(&mut th.ba, &bridge_info_2, &Htable));
- assert!(report_3.verify(&mut th.ba, &bridge_info_3, &Htable));
- // Check that user cannot use credential for other bridge
- // Get new credential
- let cred_2 = th.get_new_credential();
- let bridges_2 = th.get_bucket(&cred_2);
- let mut buckets_2 = HashSet::<Scalar>::new();
- buckets_2.insert(cred_2.bucket);
- let bridge_info_4 = BridgeVerificationInfo {
- bridge_line: bridges_2[0],
- buckets: buckets_2.clone(),
- pubkey: None,
- };
- // Use new credential to create positive report even we don't trust it
- let invalid_report_1 = PositiveReport::from_lox_credential(
- bridges_2[0].fingerprint,
- None,
- &cred_2,
- &th.ba.lox_pub,
- "ru".to_string(),
- );
- // Use first credential for bridge from second bucket
- let invalid_report_2 = PositiveReport::from_lox_credential(
- bridges_2[0].fingerprint,
- None,
- &cred,
- &th.ba.lox_pub,
- "ru".to_string(),
- );
- // Use second credential for bridge from first bucket
- let invalid_report_3 = PositiveReport::from_lox_credential(
- bridges[0].fingerprint,
- None,
- &cred_2,
- &th.ba.lox_pub,
- "ru".to_string(),
- );
- // Check that all of these fail
- assert!(invalid_report_1.is_err());
- assert!(!invalid_report_2
- .unwrap()
- .verify(&mut th.ba, &bridge_info_4, &Htable));
- assert!(invalid_report_3.is_err());
- // Check that deserialization fails under invalid conditions
- // Date in the future
- let mut invalid_report_4 = PositiveReport::from_lox_credential(
- bridges[0].fingerprint,
- None,
- &cred,
- &th.ba.lox_pub,
- "ru".to_string(),
- )
- .unwrap()
- .to_serializable_report();
- invalid_report_4.date = invalid_report_4.date + 2;
- // Invalid country code
- let invalid_report_5 = PositiveReport::from_lox_credential(
- bridges[0].fingerprint,
- None,
- &cred,
- &th.ba.lox_pub,
- "xx".to_string(),
- )
- .unwrap()
- .to_serializable_report();
- assert!(invalid_report_4.to_report().is_err());
- assert!(invalid_report_5.to_report().is_err());
- // Test storing to-be-processed positive reports to database
- // Create reports
- let report_1 = PositiveReport::from_lox_credential(
- bridges[0].fingerprint,
- None,
- &cred,
- &th.ba.lox_pub,
- "ru".to_string(),
- )
- .unwrap();
- let report_2 = PositiveReport::from_lox_credential(
- bridges[0].fingerprint,
- None,
- &cred,
- &th.ba.lox_pub,
- "ru".to_string(),
- )
- .unwrap();
- let report_3 = PositiveReport::from_lox_credential(
- bridges[1].fingerprint,
- None,
- &cred,
- &th.ba.lox_pub,
- "ru".to_string(),
- )
- .unwrap();
- // Open test database
- let db: Db = sled::open("test_db_pr").unwrap();
- // Delete all data in test DB
- db.clear().unwrap();
- assert!(!db.contains_key("prs-to-process").unwrap());
- let map_key_1 = format!(
- "{}_{}_{}",
- array_bytes::bytes2hex("", report_1.fingerprint),
- &report_1.country,
- &report_1.date
- );
- let map_key_2 = format!(
- "{}_{}_{}",
- array_bytes::bytes2hex("", report_3.fingerprint),
- &report_3.country,
- &report_3.date
- );
- save_positive_report_to_process(&db, report_1);
- let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
- bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
- let positive_reports = prs_to_process.get(&map_key_1).unwrap();
- assert_eq!(positive_reports.len(), 1);
- assert!(!prs_to_process.contains_key(&map_key_2));
- save_positive_report_to_process(&db, report_2);
- let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
- bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
- let positive_reports = prs_to_process.get(&map_key_1).unwrap();
- assert_eq!(positive_reports.len(), 2);
- assert!(!prs_to_process.contains_key(&map_key_2));
- save_positive_report_to_process(&db, report_3);
- let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
- bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
- // Check that this has not changed
- let positive_reports = prs_to_process.get(&map_key_1).unwrap();
- assert_eq!(positive_reports.len(), 2);
- // New report added to its own collection
- let positive_reports = prs_to_process.get(&map_key_2).unwrap();
- assert_eq!(positive_reports.len(), 1);
- }
- #[test]
- fn test_analysis() {
- // Test stage 1 analysis
- {
- let mut date = get_date();
- // New bridge info
- let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
- bridge_info
- .info_by_country
- .insert("ru".to_string(), BridgeCountryInfo::new(date));
- let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
- let confidence = 0.95;
- let mut blocking_countries = HashSet::<String>::new();
- // No data today
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // 1 connection, 0 negative reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // 0 connections, 0 negative reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 0,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // 0 connections, 1 negative report
- // (exceeds scaled threshold)
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 1,
- );
- blocking_countries.insert("ru".to_string());
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- {
- let mut date = get_date();
- // New bridge info
- let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
- bridge_info
- .info_by_country
- .insert("ru".to_string(), BridgeCountryInfo::new(date));
- let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
- let confidence = 0.95;
- let mut blocking_countries = HashSet::<String>::new();
- // No data today
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // 1 connection, 1 negative report
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 1,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // 8 connections, 2 negative reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 2,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // 8 connections, 3 negative reports
- // (exceeds scaled threshold)
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 3,
- );
- blocking_countries.insert("ru".to_string());
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- {
- let mut date = get_date();
- // New bridge info
- let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
- bridge_info
- .info_by_country
- .insert("ru".to_string(), BridgeCountryInfo::new(date));
- let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
- let confidence = 0.95;
- let mut blocking_countries = HashSet::<String>::new();
- // 24 connections, 5 negative reports
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 24,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 5,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // 24 connections, 6 negative reports
- // (exceeds max threshold)
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 24,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 6,
- );
- blocking_countries.insert("ru".to_string());
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- // Test stage 2 analysis
- {
- let mut date = get_date();
- // New bridge info
- let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
- bridge_info
- .info_by_country
- .insert("ru".to_string(), BridgeCountryInfo::new(date));
- let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
- let confidence = 0.95;
- let mut blocking_countries = HashSet::<String>::new();
- // No data today
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- for i in 1..30 {
- // 9-32 connections, 0-3 negative reports each day
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8 * (i % 3 + 2),
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- i % 4,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- // Data similar to previous days:
- // 24 connections, 2 negative reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 24,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 2,
- );
- // Should not be blocked because we have similar data.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 104 connections, 1 negative report
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 104,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 1,
- );
- // This should not be blocked even though it's very different because
- // it's different in the good direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 40 connections, 12 negative reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 40,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 12,
- );
- blocking_countries.insert("ru".to_string());
- // This should be blocked because it's different in the bad direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- {
- let mut date = get_date();
- // New bridge info
- let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
- bridge_info
- .info_by_country
- .insert("ru".to_string(), BridgeCountryInfo::new(date));
- let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
- let confidence = 0.95;
- let mut blocking_countries = HashSet::<String>::new();
- // No data today
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- for i in 1..30 {
- // 9-32 connections, 0-3 negative reports each day
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8 * (i % 3 + 2),
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- i % 4,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- // Data similar to previous days:
- // 24 connections, 2 negative reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 24,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 2,
- );
- // Should not be blocked because we have similar data.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 104 connections, 1 negative report
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 104,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 1,
- );
- // This should not be blocked even though it's very different because
- // it's different in the good direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 800 connections, 12 negative reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 800,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 12,
- );
- blocking_countries.insert("ru".to_string());
- // The censor artificially inflated bridge stats to prevent detection.
- // Ensure we still detect the censorship from negative reports.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- {
- let mut date = get_date();
- // New bridge info
- let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
- bridge_info
- .info_by_country
- .insert("ru".to_string(), BridgeCountryInfo::new(date));
- let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
- let confidence = 0.95;
- let mut blocking_countries = HashSet::<String>::new();
- // No data today
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- for i in 1..30 {
- // 9-32 connections, 0-3 negative reports each day
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8 * (i % 3 + 2),
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- i % 4,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- // Data similar to previous days:
- // 24 connections, 2 negative reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 24,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 2,
- );
- // Should not be blocked because we have similar data.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 104 connections, 1 negative report
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 104,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 1,
- );
- // This should not be blocked even though it's very different because
- // it's different in the good direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 0 connections, 0 negative reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 0,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 0,
- );
- blocking_countries.insert("ru".to_string());
- // This should be blocked because it's different in the bad direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- // Test stage 3 analysis
- {
- let mut date = get_date();
- // New bridge info
- let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
- bridge_info
- .info_by_country
- .insert("ru".to_string(), BridgeCountryInfo::new(date));
- let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
- let confidence = 0.95;
- let mut blocking_countries = HashSet::<String>::new();
- // No data today
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- for i in 1..30 {
- // 9-32 connections, 0-3 negative reports, 16-20 positive reports each day
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8 * (i % 3 + 2),
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- i % 4,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 16 + i % 5,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- // Data similar to previous days:
- // 24 connections, 2 negative reports, 17 positive reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 24,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 2,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 17,
- );
- // Should not be blocked because we have similar data.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 104 connections, 1 negative report, 100 positive reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 104,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 1,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 100,
- );
- // This should not be blocked even though it's very different because
- // it's different in the good direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 40 connections, 12 negative reports, 40 positive reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 40,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 12,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 40,
- );
- blocking_countries.insert("ru".to_string());
- // This should be blocked because it's different in the bad direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- {
- let mut date = get_date();
- // New bridge info
- let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
- bridge_info
- .info_by_country
- .insert("ru".to_string(), BridgeCountryInfo::new(date));
- let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
- let confidence = 0.95;
- let mut blocking_countries = HashSet::<String>::new();
- // No data today
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- for i in 1..30 {
- // 9-32 connections, 0-3 negative reports, 16-20 positive reports each day
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8 * (i % 3 + 2),
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- i % 4,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 16 + i % 5,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- // Data similar to previous days:
- // 24 connections, 2 negative reports, 17 positive reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 24,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 2,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 17,
- );
- // Should not be blocked because we have similar data.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 104 connections, 1 negative report, 85 positive reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 104,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 1,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 85,
- );
- // This should not be blocked even though it's very different because
- // it's different in the good direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 800 connections, 12 negative reports, 750 positive reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 800,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 12,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 750,
- );
- blocking_countries.insert("ru".to_string());
- // The censor artificially inflated bridge stats to prevent detection.
- // Ensure we still detect the censorship from negative reports.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- {
- let mut date = get_date();
- // New bridge info
- let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
- bridge_info
- .info_by_country
- .insert("ru".to_string(), BridgeCountryInfo::new(date));
- let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
- let confidence = 0.95;
- let mut blocking_countries = HashSet::<String>::new();
- // No data today
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- for i in 1..30 {
- // 9-32 connections, 0-3 negative reports, 16-20 positive reports each day
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 8 * (i % 3 + 2),
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- i % 4,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 16 + i % 5,
- );
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- // Data similar to previous days:
- // 24 connections, 2 negative reports, 17 positive reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 24,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 2,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 17,
- );
- // Should not be blocked because we have similar data.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 104 connections, 1 negative report, 100 positive reports
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 104,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 1,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 100,
- );
- // This should not be blocked even though it's very different because
- // it's different in the good direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- // Data different from previous days:
- // 24 connections, 1 negative report, 1 positive report
- date += 1;
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::BridgeIps,
- date,
- 24,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::NegativeReports,
- date,
- 1,
- );
- bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
- BridgeInfoType::PositiveReports,
- date,
- 1,
- );
- blocking_countries.insert("ru".to_string());
- // This should be blocked because it's different in the bad direction.
- assert_eq!(
- blocked_in(&analyzer, &bridge_info, confidence, date),
- blocking_countries
- );
- }
- }
|