tests.rs 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697
  1. #![allow(non_snake_case)]
  2. use crate::{
  3. analysis::{blocked_in, Analyzer},
  4. bridge_verification_info::BridgeVerificationInfo,
  5. *,
  6. };
  7. use lox_library::{
  8. bridge_table::{self, BridgeLine, BridgeTable},
  9. cred::Lox,
  10. proto::*,
  11. scalar_u32, BridgeAuth, BridgeDb,
  12. };
  13. use base64::{engine::general_purpose, Engine as _};
  14. use curve25519_dalek::{ristretto::RistrettoBasepointTable, Scalar};
  15. use rand::RngCore;
  16. use sha1::{Digest, Sha1};
  17. use std::{
  18. collections::{BTreeMap, HashMap, HashSet},
  19. sync::{Arc, Mutex},
  20. };
  21. use x25519_dalek::{PublicKey, StaticSecret};
  22. struct TestHarness {
  23. bdb: BridgeDb,
  24. pub ba: BridgeAuth,
  25. }
  26. impl TestHarness {
  27. fn new() -> Self {
  28. TestHarness::new_buckets(5, 5)
  29. }
  30. fn new_buckets(num_buckets: u16, hot_spare: u16) -> Self {
  31. // Create a BridegDb
  32. let mut bdb = BridgeDb::new();
  33. // Create a BridgeAuth
  34. let mut ba = BridgeAuth::new(bdb.pubkey);
  35. // Make 3 x num_buckets open invitation bridges, in sets of 3
  36. for _ in 0..num_buckets {
  37. let bucket = [random(), random(), random()];
  38. let _ = ba.add_openinv_bridges(bucket, &mut bdb);
  39. }
  40. // Add hot_spare more hot spare buckets
  41. for _ in 0..hot_spare {
  42. let bucket = [random(), random(), random()];
  43. let _ = ba.add_spare_bucket(bucket, &mut bdb);
  44. }
  45. // Create the encrypted bridge table
  46. ba.enc_bridge_table();
  47. Self { bdb, ba }
  48. }
  49. fn advance_days(&mut self, days: u16) {
  50. self.ba.advance_days(days);
  51. }
  52. fn get_new_credential(&mut self) -> Lox {
  53. let inv = self.bdb.invite().unwrap();
  54. let (req, state) = open_invite::request(&inv);
  55. let resp = self.ba.handle_open_invite(req).unwrap();
  56. let (cred, _bridgeline) =
  57. open_invite::handle_response(state, resp, &self.ba.lox_pub).unwrap();
  58. cred
  59. }
  60. fn level_up(&mut self, cred: &Lox) -> Lox {
  61. let current_level = scalar_u32(&cred.trust_level).unwrap();
  62. if current_level == 0 {
  63. self.advance_days(trust_promotion::UNTRUSTED_INTERVAL.try_into().unwrap());
  64. let (promreq, promstate) =
  65. trust_promotion::request(cred, &self.ba.lox_pub, self.ba.today()).unwrap();
  66. let promresp = self.ba.handle_trust_promotion(promreq).unwrap();
  67. let migcred = trust_promotion::handle_response(promstate, promresp).unwrap();
  68. let (migreq, migstate) =
  69. migration::request(cred, &migcred, &self.ba.lox_pub, &self.ba.migration_pub)
  70. .unwrap();
  71. let migresp = self.ba.handle_migration(migreq).unwrap();
  72. let new_cred = migration::handle_response(migstate, migresp, &self.ba.lox_pub).unwrap();
  73. new_cred
  74. } else {
  75. self.advance_days(
  76. level_up::LEVEL_INTERVAL[usize::try_from(current_level).unwrap()]
  77. .try_into()
  78. .unwrap(),
  79. );
  80. let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
  81. let encbuckets = self.ba.enc_bridge_table();
  82. let bucket =
  83. bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
  84. .unwrap();
  85. let reachcred = bucket.1.unwrap();
  86. let (lvreq, lvstate) = level_up::request(
  87. cred,
  88. &reachcred,
  89. &self.ba.lox_pub,
  90. &self.ba.reachability_pub,
  91. self.ba.today(),
  92. )
  93. .unwrap();
  94. let lvresp = self.ba.handle_level_up(lvreq).unwrap();
  95. let new_cred = level_up::handle_response(lvstate, lvresp, &self.ba.lox_pub).unwrap();
  96. new_cred
  97. }
  98. }
  99. fn get_bucket(&mut self, cred: &Lox) -> [BridgeLine; bridge_table::MAX_BRIDGES_PER_BUCKET] {
  100. let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
  101. let encbuckets = self.ba.enc_bridge_table();
  102. let bucket =
  103. bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
  104. .unwrap();
  105. bucket.0
  106. }
  107. }
  108. pub fn random() -> BridgeLine {
  109. let mut rng = rand::thread_rng();
  110. let mut res: BridgeLine = BridgeLine::default();
  111. // Pick a random 4-byte address
  112. let mut addr: [u8; 4] = [0; 4];
  113. rng.fill_bytes(&mut addr);
  114. // If the leading byte is 224 or more, that's not a valid IPv4
  115. // address. Choose an IPv6 address instead (but don't worry too
  116. // much about it being well formed).
  117. if addr[0] >= 224 {
  118. rng.fill_bytes(&mut res.addr);
  119. } else {
  120. // Store an IPv4 address as a v4-mapped IPv6 address
  121. res.addr[10] = 255;
  122. res.addr[11] = 255;
  123. res.addr[12..16].copy_from_slice(&addr);
  124. };
  125. let ports: [u16; 4] = [443, 4433, 8080, 43079];
  126. let portidx = (rng.next_u32() % 4) as usize;
  127. res.port = ports[portidx];
  128. res.uid_fingerprint = rng.next_u64();
  129. rng.fill_bytes(&mut res.fingerprint);
  130. let mut cert: [u8; 52] = [0; 52];
  131. rng.fill_bytes(&mut cert);
  132. let infostr: String = format!(
  133. "obfs4 cert={}, iat-mode=0",
  134. general_purpose::STANDARD_NO_PAD.encode(cert)
  135. );
  136. res.info[..infostr.len()].copy_from_slice(infostr.as_bytes());
  137. res
  138. }
  139. #[tokio::test]
  140. async fn test_extra_infos() {
  141. let bridge_to_test =
  142. array_bytes::hex2array("72E12B89136B45BBC81D1EF0AC7DDDBB91B148DB").unwrap();
  143. // Open test database
  144. let db: Db = sled::open("test_db_ei").unwrap();
  145. // Delete all data in test DB
  146. db.clear().unwrap();
  147. assert!(!db.contains_key("bridges").unwrap());
  148. assert!(!db.contains_key(bridge_to_test).unwrap());
  149. // Download and process recent extra-infos files
  150. update_extra_infos(
  151. &db,
  152. "https://collector.torproject.org/recent/bridge-descriptors/extra-infos/",
  153. )
  154. .await
  155. .unwrap();
  156. // Check that DB contains information on a bridge with high uptime
  157. assert!(db.contains_key("bridges").unwrap());
  158. let bridges: HashSet<[u8; 20]> =
  159. bincode::deserialize(&db.get("bridges").unwrap().unwrap()).unwrap();
  160. assert!(bridges.contains(&bridge_to_test));
  161. assert!(db.contains_key(bridge_to_test).unwrap());
  162. let _bridge_info: BridgeInfo =
  163. bincode::deserialize(&db.get(bridge_to_test).unwrap().unwrap()).unwrap();
  164. }
  165. #[test]
  166. fn test_negative_reports() {
  167. let mut th = TestHarness::new();
  168. // Get new level 1 credential
  169. let cred = th.get_new_credential();
  170. let cred = th.level_up(&cred);
  171. let bridges = th.get_bucket(&cred);
  172. // Create BridgeVerificationInfo for each bridge
  173. let mut buckets = HashSet::<Scalar>::new();
  174. buckets.insert(cred.bucket);
  175. let bridge_info_1 = BridgeVerificationInfo {
  176. bridge_line: bridges[0],
  177. buckets: buckets.clone(),
  178. pubkey: None,
  179. };
  180. let bridge_info_2 = BridgeVerificationInfo {
  181. bridge_line: bridges[1],
  182. buckets: buckets.clone(),
  183. pubkey: None,
  184. };
  185. let bridge_info_3 = BridgeVerificationInfo {
  186. bridge_line: bridges[2],
  187. buckets: buckets.clone(),
  188. pubkey: None,
  189. };
  190. // Create reports
  191. let report_1 =
  192. NegativeReport::from_bridgeline(bridges[0], "ru".to_string(), BridgeDistributor::Lox);
  193. let report_2 =
  194. NegativeReport::from_lox_bucket(bridges[1].fingerprint, cred.bucket, "ru".to_string());
  195. let report_3 =
  196. NegativeReport::from_lox_credential(bridges[2].fingerprint, &cred, "ru".to_string());
  197. // Backdated reports
  198. let date = get_date();
  199. let mut rng = rand::thread_rng();
  200. let mut nonce = [0; 32];
  201. rng.fill_bytes(&mut nonce);
  202. let report_4 = NegativeReport::new(
  203. bridges[0].fingerprint,
  204. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  205. &bridges[0],
  206. date - 1,
  207. nonce,
  208. )),
  209. "ru".to_string(),
  210. date - 1,
  211. nonce,
  212. BridgeDistributor::Lox,
  213. );
  214. let mut nonce = [0; 32];
  215. rng.fill_bytes(&mut nonce);
  216. let report_5 = NegativeReport::new(
  217. bridges[1].fingerprint,
  218. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  219. &bridges[1],
  220. date - 2,
  221. nonce,
  222. )),
  223. "ru".to_string(),
  224. date - 2,
  225. nonce,
  226. BridgeDistributor::Lox,
  227. );
  228. let mut nonce = [0; 32];
  229. rng.fill_bytes(&mut nonce);
  230. let report_6 = NegativeReport::new(
  231. bridges[2].fingerprint,
  232. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  233. &bridges[2],
  234. date - 3,
  235. nonce,
  236. )),
  237. "ru".to_string(),
  238. date - 3,
  239. nonce,
  240. BridgeDistributor::Lox,
  241. );
  242. // Verify reports
  243. assert!(report_1.verify(&bridge_info_1));
  244. assert!(report_2.verify(&bridge_info_2));
  245. assert!(report_3.verify(&bridge_info_3));
  246. assert!(report_4.verify(&bridge_info_1));
  247. assert!(report_5.verify(&bridge_info_2));
  248. assert!(report_6.verify(&bridge_info_3));
  249. // Check that deserialization fails under invalid conditions
  250. // Date in the future
  251. let mut invalid_report_1 =
  252. NegativeReport::from_bridgeline(bridges[0], "ru".to_string(), BridgeDistributor::Lox)
  253. .to_serializable_report();
  254. invalid_report_1.date = invalid_report_1.date + 2;
  255. // Date too far in past
  256. let mut invalid_report_2 =
  257. NegativeReport::from_bridgeline(bridges[1], "ru".to_string(), BridgeDistributor::Lox)
  258. .to_serializable_report();
  259. invalid_report_2.date = invalid_report_2.date - MAX_BACKDATE - 1;
  260. // Invalid country code
  261. let invalid_report_3 =
  262. NegativeReport::from_bridgeline(bridges[2], "xx".to_string(), BridgeDistributor::Lox)
  263. .to_serializable_report();
  264. assert!(invalid_report_1.to_report().is_err());
  265. assert!(invalid_report_2.to_report().is_err());
  266. assert!(invalid_report_3.to_report().is_err());
  267. // Check that verification fails with incorrect data
  268. let date = get_date();
  269. let mut rng = rand::thread_rng();
  270. // Incorrect BridgeLine hash
  271. let mut nonce = [0; 32];
  272. rng.fill_bytes(&mut nonce);
  273. let invalid_report_4 = NegativeReport::new(
  274. bridges[0].fingerprint,
  275. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  276. &BridgeLine::default(),
  277. date,
  278. nonce,
  279. )),
  280. "ru".to_string(),
  281. date,
  282. nonce,
  283. BridgeDistributor::Lox,
  284. );
  285. // Incorrect bucket hash
  286. let mut nonce = [0; 32];
  287. rng.fill_bytes(&mut nonce);
  288. let invalid_report_5 = NegativeReport::new(
  289. bridges[1].fingerprint,
  290. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&Scalar::ZERO, date, nonce)),
  291. "ru".to_string(),
  292. date,
  293. nonce,
  294. BridgeDistributor::Lox,
  295. );
  296. assert!(!invalid_report_4.verify(&bridge_info_1));
  297. assert!(!invalid_report_5.verify(&bridge_info_2));
  298. // Test that reports with duplicate nonces are rejected
  299. // (Also test encryption and decryption.)
  300. // Open test database
  301. let db: Db = sled::open("test_db_nr").unwrap();
  302. // Delete all data in test DB
  303. db.clear().unwrap();
  304. assert!(!db.contains_key("nrs-to-process").unwrap());
  305. let mut nonce = [0; 32];
  306. rng.fill_bytes(&mut nonce);
  307. // A valid report
  308. let valid_report_1 = NegativeReport::new(
  309. bridges[0].fingerprint,
  310. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  311. "ru".to_string(),
  312. date,
  313. nonce,
  314. BridgeDistributor::Lox,
  315. );
  316. let valid_report_1_copy_1 = NegativeReport::new(
  317. bridges[0].fingerprint,
  318. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  319. "ru".to_string(),
  320. date,
  321. nonce,
  322. BridgeDistributor::Lox,
  323. );
  324. let valid_report_1_copy_2 = NegativeReport::new(
  325. bridges[0].fingerprint,
  326. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  327. "ru".to_string(),
  328. date,
  329. nonce,
  330. BridgeDistributor::Lox,
  331. );
  332. // Report which reuses this nonce
  333. let invalid_report_1 = NegativeReport::new(
  334. bridges[0].fingerprint,
  335. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  336. "ru".to_string(),
  337. date,
  338. nonce,
  339. BridgeDistributor::Lox,
  340. );
  341. // This is the same report
  342. assert_eq!(valid_report_1, invalid_report_1);
  343. // Report which reuses this nonce for a different bridge
  344. let invalid_report_2 = NegativeReport::new(
  345. bridges[1].fingerprint,
  346. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[1], date, nonce)),
  347. "ru".to_string(),
  348. date,
  349. nonce,
  350. BridgeDistributor::Lox,
  351. );
  352. // Report which uses this nonce but on a different day
  353. let valid_report_2 = NegativeReport::new(
  354. bridges[0].fingerprint,
  355. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  356. &bridges[0],
  357. date - 1,
  358. nonce,
  359. )),
  360. "ru".to_string(),
  361. date - 1,
  362. nonce,
  363. BridgeDistributor::Lox,
  364. );
  365. // Report with different nonce
  366. let mut nonce = [0; 32];
  367. rng.fill_bytes(&mut nonce);
  368. let valid_report_3 = NegativeReport::new(
  369. bridges[0].fingerprint,
  370. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  371. "ru".to_string(),
  372. date,
  373. nonce,
  374. BridgeDistributor::Lox,
  375. );
  376. let map_key_1 = format!(
  377. "{}_{}_{}",
  378. array_bytes::bytes2hex("", valid_report_1.fingerprint),
  379. "ru".to_string(),
  380. date
  381. );
  382. // Generate key for today
  383. let secret = StaticSecret::random_from_rng(&mut rng);
  384. let public = PublicKey::from(&secret);
  385. let secret_yesterday = StaticSecret::random_from_rng(&mut rng);
  386. let public_yesterday = PublicKey::from(&secret_yesterday);
  387. assert!(!db.contains_key("nr-keys").unwrap());
  388. // Fail to add to database because we can't decrypt
  389. handle_encrypted_negative_report(&db, valid_report_1_copy_1.encrypt(&public));
  390. assert!(!db.contains_key("nrs-to-process").unwrap());
  391. // Store yesterday's key but not today's
  392. let mut nr_keys = BTreeMap::<u32, StaticSecret>::new();
  393. nr_keys.insert(date - 1, secret_yesterday);
  394. db.insert("nr-keys", bincode::serialize(&nr_keys).unwrap())
  395. .unwrap();
  396. // Fail to add to database because we still can't decrypt
  397. handle_encrypted_negative_report(&db, valid_report_1_copy_2.encrypt(&public));
  398. assert!(!db.contains_key("nrs-to-process").unwrap());
  399. // Store today's key
  400. nr_keys.insert(date, secret);
  401. db.insert("nr-keys", bincode::serialize(&nr_keys).unwrap())
  402. .unwrap();
  403. handle_encrypted_negative_report(&db, valid_report_1.encrypt(&public));
  404. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  405. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  406. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  407. assert_eq!(negative_reports.len(), 1);
  408. handle_encrypted_negative_report(&db, invalid_report_1.encrypt(&public)); // no change
  409. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  410. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  411. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  412. assert_eq!(negative_reports.len(), 1);
  413. let map_key_2 = format!(
  414. "{}_{}_{}",
  415. array_bytes::bytes2hex("", invalid_report_2.fingerprint),
  416. "ru".to_string(),
  417. date
  418. );
  419. handle_encrypted_negative_report(&db, invalid_report_2.encrypt(&public)); // no change
  420. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  421. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  422. assert!(!nrs_to_process.contains_key(&map_key_2));
  423. let map_key_3 = format!(
  424. "{}_{}_{}",
  425. array_bytes::bytes2hex("", valid_report_2.fingerprint),
  426. "ru".to_string(),
  427. date - 1
  428. );
  429. handle_encrypted_negative_report(&db, valid_report_2.encrypt(&public_yesterday));
  430. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  431. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  432. let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
  433. assert_eq!(negative_reports.len(), 1);
  434. handle_encrypted_negative_report(&db, valid_report_3.encrypt(&public));
  435. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  436. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  437. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  438. assert_eq!(negative_reports.len(), 2);
  439. // Same tests, but use hash of bucket
  440. // Delete all data in test DB
  441. db.clear().unwrap();
  442. assert!(!db.contains_key("nrs-to-process").unwrap());
  443. // Re-generate keys and save in database
  444. let public = new_negative_report_key(&db, date).unwrap();
  445. let public_yesterday = new_negative_report_key(&db, date - 1).unwrap();
  446. let mut nonce = [0; 32];
  447. rng.fill_bytes(&mut nonce);
  448. // A valid report
  449. let valid_report_1 = NegativeReport::new(
  450. bridges[0].fingerprint,
  451. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
  452. "ru".to_string(),
  453. date,
  454. nonce,
  455. BridgeDistributor::Lox,
  456. );
  457. // Report which reuses this nonce
  458. let invalid_report_1 = NegativeReport::new(
  459. bridges[0].fingerprint,
  460. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
  461. "ru".to_string(),
  462. date,
  463. nonce,
  464. BridgeDistributor::Lox,
  465. );
  466. // This is the same report
  467. assert_eq!(valid_report_1, invalid_report_1);
  468. // Report which reuses this nonce for a different bridge
  469. let invalid_report_2 = NegativeReport::new(
  470. bridges[1].fingerprint,
  471. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
  472. "ru".to_string(),
  473. date,
  474. nonce,
  475. BridgeDistributor::Lox,
  476. );
  477. // Report which uses this nonce but on a different day
  478. let valid_report_2 = NegativeReport::new(
  479. bridges[0].fingerprint,
  480. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date - 1, nonce)),
  481. "ru".to_string(),
  482. date - 1,
  483. nonce,
  484. BridgeDistributor::Lox,
  485. );
  486. // Report with different nonce
  487. let mut nonce = [0; 32];
  488. rng.fill_bytes(&mut nonce);
  489. let valid_report_3 = NegativeReport::new(
  490. bridges[0].fingerprint,
  491. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
  492. "ru".to_string(),
  493. date,
  494. nonce,
  495. BridgeDistributor::Lox,
  496. );
  497. let map_key_1 = format!(
  498. "{}_{}_{}",
  499. array_bytes::bytes2hex("", valid_report_1.fingerprint),
  500. "ru".to_string(),
  501. date
  502. );
  503. handle_encrypted_negative_report(&db, valid_report_1.encrypt(&public));
  504. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  505. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  506. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  507. assert_eq!(negative_reports.len(), 1);
  508. handle_encrypted_negative_report(&db, invalid_report_1.encrypt(&public)); // no change
  509. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  510. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  511. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  512. assert_eq!(negative_reports.len(), 1);
  513. let map_key_2 = format!(
  514. "{}_{}_{}",
  515. array_bytes::bytes2hex("", invalid_report_2.fingerprint),
  516. "ru".to_string(),
  517. date
  518. );
  519. handle_encrypted_negative_report(&db, invalid_report_2.encrypt(&public)); // no change
  520. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  521. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  522. assert!(!nrs_to_process.contains_key(&map_key_2));
  523. let map_key_3 = format!(
  524. "{}_{}_{}",
  525. array_bytes::bytes2hex("", valid_report_2.fingerprint),
  526. "ru".to_string(),
  527. date - 1
  528. );
  529. handle_encrypted_negative_report(&db, valid_report_2.encrypt(&public_yesterday));
  530. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  531. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  532. let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
  533. assert_eq!(negative_reports.len(), 1);
  534. handle_encrypted_negative_report(&db, valid_report_3.encrypt(&public));
  535. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  536. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  537. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  538. assert_eq!(negative_reports.len(), 2);
  539. }
  540. #[test]
  541. fn test_positive_reports() {
  542. let mut th = TestHarness::new();
  543. // Get new level 3 credential
  544. let cred = th.get_new_credential();
  545. let cred = th.level_up(&cred);
  546. let cred = th.level_up(&cred);
  547. let cred = th.level_up(&cred);
  548. let bridges = th.get_bucket(&cred);
  549. // Create BridgeVerificationInfo for each bridge
  550. let mut buckets = HashSet::<Scalar>::new();
  551. buckets.insert(cred.bucket);
  552. let bridge_info_1 = BridgeVerificationInfo {
  553. bridge_line: bridges[0],
  554. buckets: buckets.clone(),
  555. pubkey: None,
  556. };
  557. let bridge_info_2 = BridgeVerificationInfo {
  558. bridge_line: bridges[1],
  559. buckets: buckets.clone(),
  560. pubkey: None,
  561. };
  562. let bridge_info_3 = BridgeVerificationInfo {
  563. bridge_line: bridges[2],
  564. buckets: buckets.clone(),
  565. pubkey: None,
  566. };
  567. // Create reports
  568. let report_1 = PositiveReport::from_lox_credential(
  569. bridges[0].fingerprint,
  570. None,
  571. &cred,
  572. &th.ba.lox_pub,
  573. "ru".to_string(),
  574. )
  575. .unwrap();
  576. let report_2 = PositiveReport::from_lox_credential(
  577. bridges[1].fingerprint,
  578. None,
  579. &cred,
  580. &th.ba.lox_pub,
  581. "ru".to_string(),
  582. )
  583. .unwrap();
  584. let report_3 = PositiveReport::from_lox_credential(
  585. bridges[2].fingerprint,
  586. None,
  587. &cred,
  588. &th.ba.lox_pub,
  589. "ru".to_string(),
  590. )
  591. .unwrap();
  592. // Compute Htable
  593. let H = lox_library::proto::positive_report::compute_H(report_1.date);
  594. let Htable = RistrettoBasepointTable::create(&H);
  595. assert!(report_1.verify(&mut th.ba, &bridge_info_1, &Htable));
  596. assert!(report_2.verify(&mut th.ba, &bridge_info_2, &Htable));
  597. assert!(report_3.verify(&mut th.ba, &bridge_info_3, &Htable));
  598. // Check that user cannot use credential for other bridge
  599. // Get new credential
  600. let cred_2 = th.get_new_credential();
  601. let bridges_2 = th.get_bucket(&cred_2);
  602. let mut buckets_2 = HashSet::<Scalar>::new();
  603. buckets_2.insert(cred_2.bucket);
  604. let bridge_info_4 = BridgeVerificationInfo {
  605. bridge_line: bridges_2[0],
  606. buckets: buckets_2.clone(),
  607. pubkey: None,
  608. };
  609. // Use new credential to create positive report even we don't trust it
  610. let invalid_report_1 = PositiveReport::from_lox_credential(
  611. bridges_2[0].fingerprint,
  612. None,
  613. &cred_2,
  614. &th.ba.lox_pub,
  615. "ru".to_string(),
  616. );
  617. // Use first credential for bridge from second bucket
  618. let invalid_report_2 = PositiveReport::from_lox_credential(
  619. bridges_2[0].fingerprint,
  620. None,
  621. &cred,
  622. &th.ba.lox_pub,
  623. "ru".to_string(),
  624. );
  625. // Use second credential for bridge from first bucket
  626. let invalid_report_3 = PositiveReport::from_lox_credential(
  627. bridges[0].fingerprint,
  628. None,
  629. &cred_2,
  630. &th.ba.lox_pub,
  631. "ru".to_string(),
  632. );
  633. // Check that all of these fail
  634. assert!(invalid_report_1.is_err());
  635. assert!(!invalid_report_2
  636. .unwrap()
  637. .verify(&mut th.ba, &bridge_info_4, &Htable));
  638. assert!(invalid_report_3.is_err());
  639. // Check that deserialization fails under invalid conditions
  640. // Date in the future
  641. let mut invalid_report_4 = PositiveReport::from_lox_credential(
  642. bridges[0].fingerprint,
  643. None,
  644. &cred,
  645. &th.ba.lox_pub,
  646. "ru".to_string(),
  647. )
  648. .unwrap()
  649. .to_serializable_report();
  650. invalid_report_4.date = invalid_report_4.date + 2;
  651. // Invalid country code
  652. let invalid_report_5 = PositiveReport::from_lox_credential(
  653. bridges[0].fingerprint,
  654. None,
  655. &cred,
  656. &th.ba.lox_pub,
  657. "xx".to_string(),
  658. )
  659. .unwrap()
  660. .to_serializable_report();
  661. assert!(invalid_report_4.to_report().is_err());
  662. assert!(invalid_report_5.to_report().is_err());
  663. // Test storing to-be-processed positive reports to database
  664. // Create reports
  665. let report_1 = PositiveReport::from_lox_credential(
  666. bridges[0].fingerprint,
  667. None,
  668. &cred,
  669. &th.ba.lox_pub,
  670. "ru".to_string(),
  671. )
  672. .unwrap();
  673. let report_2 = PositiveReport::from_lox_credential(
  674. bridges[0].fingerprint,
  675. None,
  676. &cred,
  677. &th.ba.lox_pub,
  678. "ru".to_string(),
  679. )
  680. .unwrap();
  681. let report_3 = PositiveReport::from_lox_credential(
  682. bridges[1].fingerprint,
  683. None,
  684. &cred,
  685. &th.ba.lox_pub,
  686. "ru".to_string(),
  687. )
  688. .unwrap();
  689. // Open test database
  690. let db: Db = sled::open("test_db_pr").unwrap();
  691. // Delete all data in test DB
  692. db.clear().unwrap();
  693. assert!(!db.contains_key("prs-to-process").unwrap());
  694. let map_key_1 = format!(
  695. "{}_{}_{}",
  696. array_bytes::bytes2hex("", report_1.fingerprint),
  697. &report_1.country,
  698. &report_1.date
  699. );
  700. let map_key_2 = format!(
  701. "{}_{}_{}",
  702. array_bytes::bytes2hex("", report_3.fingerprint),
  703. &report_3.country,
  704. &report_3.date
  705. );
  706. save_positive_report_to_process(&db, report_1);
  707. let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
  708. bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
  709. let positive_reports = prs_to_process.get(&map_key_1).unwrap();
  710. assert_eq!(positive_reports.len(), 1);
  711. assert!(!prs_to_process.contains_key(&map_key_2));
  712. save_positive_report_to_process(&db, report_2);
  713. let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
  714. bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
  715. let positive_reports = prs_to_process.get(&map_key_1).unwrap();
  716. assert_eq!(positive_reports.len(), 2);
  717. assert!(!prs_to_process.contains_key(&map_key_2));
  718. save_positive_report_to_process(&db, report_3);
  719. let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
  720. bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
  721. // Check that this has not changed
  722. let positive_reports = prs_to_process.get(&map_key_1).unwrap();
  723. assert_eq!(positive_reports.len(), 2);
  724. // New report added to its own collection
  725. let positive_reports = prs_to_process.get(&map_key_2).unwrap();
  726. assert_eq!(positive_reports.len(), 1);
  727. }
  728. #[test]
  729. fn test_analysis() {
  730. // Test stage 1 analysis
  731. {
  732. let mut date = get_date();
  733. // New bridge info
  734. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  735. bridge_info
  736. .info_by_country
  737. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  738. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  739. let confidence = 0.95;
  740. let mut blocking_countries = HashSet::<String>::new();
  741. // No data today
  742. assert_eq!(
  743. blocked_in(&analyzer, &bridge_info, confidence, date),
  744. blocking_countries
  745. );
  746. // 1 connection, 0 negative reports
  747. date += 1;
  748. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  749. BridgeInfoType::BridgeIps,
  750. date,
  751. 8,
  752. );
  753. assert_eq!(
  754. blocked_in(&analyzer, &bridge_info, confidence, date),
  755. blocking_countries
  756. );
  757. // 0 connections, 0 negative reports
  758. date += 1;
  759. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  760. BridgeInfoType::BridgeIps,
  761. date,
  762. 0,
  763. );
  764. assert_eq!(
  765. blocked_in(&analyzer, &bridge_info, confidence, date),
  766. blocking_countries
  767. );
  768. // 0 connections, 1 negative report
  769. // (exceeds scaled threshold)
  770. date += 1;
  771. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  772. BridgeInfoType::NegativeReports,
  773. date,
  774. 1,
  775. );
  776. blocking_countries.insert("ru".to_string());
  777. assert_eq!(
  778. blocked_in(&analyzer, &bridge_info, confidence, date),
  779. blocking_countries
  780. );
  781. }
  782. {
  783. let mut date = get_date();
  784. // New bridge info
  785. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  786. bridge_info
  787. .info_by_country
  788. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  789. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  790. let confidence = 0.95;
  791. let mut blocking_countries = HashSet::<String>::new();
  792. // No data today
  793. assert_eq!(
  794. blocked_in(&analyzer, &bridge_info, confidence, date),
  795. blocking_countries
  796. );
  797. // 1 connection, 1 negative report
  798. date += 1;
  799. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  800. BridgeInfoType::BridgeIps,
  801. date,
  802. 8,
  803. );
  804. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  805. BridgeInfoType::NegativeReports,
  806. date,
  807. 1,
  808. );
  809. assert_eq!(
  810. blocked_in(&analyzer, &bridge_info, confidence, date),
  811. blocking_countries
  812. );
  813. // 8 connections, 2 negative reports
  814. date += 1;
  815. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  816. BridgeInfoType::BridgeIps,
  817. date,
  818. 8,
  819. );
  820. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  821. BridgeInfoType::NegativeReports,
  822. date,
  823. 2,
  824. );
  825. assert_eq!(
  826. blocked_in(&analyzer, &bridge_info, confidence, date),
  827. blocking_countries
  828. );
  829. // 8 connections, 3 negative reports
  830. // (exceeds scaled threshold)
  831. date += 1;
  832. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  833. BridgeInfoType::BridgeIps,
  834. date,
  835. 8,
  836. );
  837. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  838. BridgeInfoType::NegativeReports,
  839. date,
  840. 3,
  841. );
  842. blocking_countries.insert("ru".to_string());
  843. assert_eq!(
  844. blocked_in(&analyzer, &bridge_info, confidence, date),
  845. blocking_countries
  846. );
  847. }
  848. {
  849. let mut date = get_date();
  850. // New bridge info
  851. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  852. bridge_info
  853. .info_by_country
  854. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  855. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  856. let confidence = 0.95;
  857. let mut blocking_countries = HashSet::<String>::new();
  858. // 24 connections, 5 negative reports
  859. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  860. BridgeInfoType::BridgeIps,
  861. date,
  862. 24,
  863. );
  864. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  865. BridgeInfoType::NegativeReports,
  866. date,
  867. 5,
  868. );
  869. assert_eq!(
  870. blocked_in(&analyzer, &bridge_info, confidence, date),
  871. blocking_countries
  872. );
  873. // 24 connections, 6 negative reports
  874. // (exceeds max threshold)
  875. date += 1;
  876. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  877. BridgeInfoType::BridgeIps,
  878. date,
  879. 24,
  880. );
  881. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  882. BridgeInfoType::NegativeReports,
  883. date,
  884. 6,
  885. );
  886. blocking_countries.insert("ru".to_string());
  887. assert_eq!(
  888. blocked_in(&analyzer, &bridge_info, confidence, date),
  889. blocking_countries
  890. );
  891. }
  892. // Test stage 2 analysis
  893. {
  894. let mut date = get_date();
  895. // New bridge info
  896. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  897. bridge_info
  898. .info_by_country
  899. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  900. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  901. let confidence = 0.95;
  902. let mut blocking_countries = HashSet::<String>::new();
  903. // No data today
  904. assert_eq!(
  905. blocked_in(&analyzer, &bridge_info, confidence, date),
  906. blocking_countries
  907. );
  908. for i in 1..30 {
  909. // 9-32 connections, 0-3 negative reports each day
  910. date += 1;
  911. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  912. BridgeInfoType::BridgeIps,
  913. date,
  914. 8 * (i % 3 + 2),
  915. );
  916. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  917. BridgeInfoType::NegativeReports,
  918. date,
  919. i % 4,
  920. );
  921. assert_eq!(
  922. blocked_in(&analyzer, &bridge_info, confidence, date),
  923. blocking_countries
  924. );
  925. }
  926. // Data similar to previous days:
  927. // 24 connections, 2 negative reports
  928. date += 1;
  929. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  930. BridgeInfoType::BridgeIps,
  931. date,
  932. 24,
  933. );
  934. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  935. BridgeInfoType::NegativeReports,
  936. date,
  937. 2,
  938. );
  939. // Should not be blocked because we have similar data.
  940. assert_eq!(
  941. blocked_in(&analyzer, &bridge_info, confidence, date),
  942. blocking_countries
  943. );
  944. // Data different from previous days:
  945. // 104 connections, 1 negative report
  946. date += 1;
  947. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  948. BridgeInfoType::BridgeIps,
  949. date,
  950. 104,
  951. );
  952. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  953. BridgeInfoType::NegativeReports,
  954. date,
  955. 1,
  956. );
  957. // This should not be blocked even though it's very different because
  958. // it's different in the good direction.
  959. assert_eq!(
  960. blocked_in(&analyzer, &bridge_info, confidence, date),
  961. blocking_countries
  962. );
  963. // Data different from previous days:
  964. // 40 connections, 12 negative reports
  965. date += 1;
  966. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  967. BridgeInfoType::BridgeIps,
  968. date,
  969. 40,
  970. );
  971. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  972. BridgeInfoType::NegativeReports,
  973. date,
  974. 12,
  975. );
  976. blocking_countries.insert("ru".to_string());
  977. // This should be blocked because it's different in the bad direction.
  978. assert_eq!(
  979. blocked_in(&analyzer, &bridge_info, confidence, date),
  980. blocking_countries
  981. );
  982. }
  983. {
  984. let mut date = get_date();
  985. // New bridge info
  986. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  987. bridge_info
  988. .info_by_country
  989. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  990. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  991. let confidence = 0.95;
  992. let mut blocking_countries = HashSet::<String>::new();
  993. // No data today
  994. assert_eq!(
  995. blocked_in(&analyzer, &bridge_info, confidence, date),
  996. blocking_countries
  997. );
  998. for i in 1..30 {
  999. // 9-32 connections, 0-3 negative reports each day
  1000. date += 1;
  1001. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1002. BridgeInfoType::BridgeIps,
  1003. date,
  1004. 8 * (i % 3 + 2),
  1005. );
  1006. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1007. BridgeInfoType::NegativeReports,
  1008. date,
  1009. i % 4,
  1010. );
  1011. assert_eq!(
  1012. blocked_in(&analyzer, &bridge_info, confidence, date),
  1013. blocking_countries
  1014. );
  1015. }
  1016. // Data similar to previous days:
  1017. // 24 connections, 2 negative reports
  1018. date += 1;
  1019. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1020. BridgeInfoType::BridgeIps,
  1021. date,
  1022. 24,
  1023. );
  1024. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1025. BridgeInfoType::NegativeReports,
  1026. date,
  1027. 2,
  1028. );
  1029. // Should not be blocked because we have similar data.
  1030. assert_eq!(
  1031. blocked_in(&analyzer, &bridge_info, confidence, date),
  1032. blocking_countries
  1033. );
  1034. // Data different from previous days:
  1035. // 104 connections, 1 negative report
  1036. date += 1;
  1037. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1038. BridgeInfoType::BridgeIps,
  1039. date,
  1040. 104,
  1041. );
  1042. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1043. BridgeInfoType::NegativeReports,
  1044. date,
  1045. 1,
  1046. );
  1047. // This should not be blocked even though it's very different because
  1048. // it's different in the good direction.
  1049. assert_eq!(
  1050. blocked_in(&analyzer, &bridge_info, confidence, date),
  1051. blocking_countries
  1052. );
  1053. // Data different from previous days:
  1054. // 800 connections, 12 negative reports
  1055. date += 1;
  1056. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1057. BridgeInfoType::BridgeIps,
  1058. date,
  1059. 800,
  1060. );
  1061. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1062. BridgeInfoType::NegativeReports,
  1063. date,
  1064. 12,
  1065. );
  1066. blocking_countries.insert("ru".to_string());
  1067. // The censor artificially inflated bridge stats to prevent detection.
  1068. // Ensure we still detect the censorship from negative reports.
  1069. assert_eq!(
  1070. blocked_in(&analyzer, &bridge_info, confidence, date),
  1071. blocking_countries
  1072. );
  1073. }
  1074. {
  1075. let mut date = get_date();
  1076. // New bridge info
  1077. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  1078. bridge_info
  1079. .info_by_country
  1080. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  1081. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  1082. let confidence = 0.95;
  1083. let mut blocking_countries = HashSet::<String>::new();
  1084. // No data today
  1085. assert_eq!(
  1086. blocked_in(&analyzer, &bridge_info, confidence, date),
  1087. blocking_countries
  1088. );
  1089. for i in 1..30 {
  1090. // 9-32 connections, 0-3 negative reports each day
  1091. date += 1;
  1092. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1093. BridgeInfoType::BridgeIps,
  1094. date,
  1095. 8 * (i % 3 + 2),
  1096. );
  1097. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1098. BridgeInfoType::NegativeReports,
  1099. date,
  1100. i % 4,
  1101. );
  1102. assert_eq!(
  1103. blocked_in(&analyzer, &bridge_info, confidence, date),
  1104. blocking_countries
  1105. );
  1106. }
  1107. // Data similar to previous days:
  1108. // 24 connections, 2 negative reports
  1109. date += 1;
  1110. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1111. BridgeInfoType::BridgeIps,
  1112. date,
  1113. 24,
  1114. );
  1115. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1116. BridgeInfoType::NegativeReports,
  1117. date,
  1118. 2,
  1119. );
  1120. // Should not be blocked because we have similar data.
  1121. assert_eq!(
  1122. blocked_in(&analyzer, &bridge_info, confidence, date),
  1123. blocking_countries
  1124. );
  1125. // Data different from previous days:
  1126. // 104 connections, 1 negative report
  1127. date += 1;
  1128. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1129. BridgeInfoType::BridgeIps,
  1130. date,
  1131. 104,
  1132. );
  1133. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1134. BridgeInfoType::NegativeReports,
  1135. date,
  1136. 1,
  1137. );
  1138. // This should not be blocked even though it's very different because
  1139. // it's different in the good direction.
  1140. assert_eq!(
  1141. blocked_in(&analyzer, &bridge_info, confidence, date),
  1142. blocking_countries
  1143. );
  1144. // Data different from previous days:
  1145. // 0 connections, 0 negative reports
  1146. date += 1;
  1147. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1148. BridgeInfoType::BridgeIps,
  1149. date,
  1150. 0,
  1151. );
  1152. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1153. BridgeInfoType::NegativeReports,
  1154. date,
  1155. 0,
  1156. );
  1157. blocking_countries.insert("ru".to_string());
  1158. // This should be blocked because it's different in the bad direction.
  1159. assert_eq!(
  1160. blocked_in(&analyzer, &bridge_info, confidence, date),
  1161. blocking_countries
  1162. );
  1163. }
  1164. // Test stage 3 analysis
  1165. {
  1166. let mut date = get_date();
  1167. // New bridge info
  1168. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  1169. bridge_info
  1170. .info_by_country
  1171. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  1172. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  1173. let confidence = 0.95;
  1174. let mut blocking_countries = HashSet::<String>::new();
  1175. // No data today
  1176. assert_eq!(
  1177. blocked_in(&analyzer, &bridge_info, confidence, date),
  1178. blocking_countries
  1179. );
  1180. for i in 1..30 {
  1181. // 9-32 connections, 0-3 negative reports, 16-20 positive reports each day
  1182. date += 1;
  1183. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1184. BridgeInfoType::BridgeIps,
  1185. date,
  1186. 8 * (i % 3 + 2),
  1187. );
  1188. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1189. BridgeInfoType::NegativeReports,
  1190. date,
  1191. i % 4,
  1192. );
  1193. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1194. BridgeInfoType::PositiveReports,
  1195. date,
  1196. 16 + i % 5,
  1197. );
  1198. assert_eq!(
  1199. blocked_in(&analyzer, &bridge_info, confidence, date),
  1200. blocking_countries
  1201. );
  1202. }
  1203. // Data similar to previous days:
  1204. // 24 connections, 2 negative reports, 17 positive reports
  1205. date += 1;
  1206. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1207. BridgeInfoType::BridgeIps,
  1208. date,
  1209. 24,
  1210. );
  1211. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1212. BridgeInfoType::NegativeReports,
  1213. date,
  1214. 2,
  1215. );
  1216. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1217. BridgeInfoType::PositiveReports,
  1218. date,
  1219. 17,
  1220. );
  1221. // Should not be blocked because we have similar data.
  1222. assert_eq!(
  1223. blocked_in(&analyzer, &bridge_info, confidence, date),
  1224. blocking_countries
  1225. );
  1226. // Data different from previous days:
  1227. // 104 connections, 1 negative report, 100 positive reports
  1228. date += 1;
  1229. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1230. BridgeInfoType::BridgeIps,
  1231. date,
  1232. 104,
  1233. );
  1234. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1235. BridgeInfoType::NegativeReports,
  1236. date,
  1237. 1,
  1238. );
  1239. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1240. BridgeInfoType::PositiveReports,
  1241. date,
  1242. 100,
  1243. );
  1244. // This should not be blocked even though it's very different because
  1245. // it's different in the good direction.
  1246. assert_eq!(
  1247. blocked_in(&analyzer, &bridge_info, confidence, date),
  1248. blocking_countries
  1249. );
  1250. // Data different from previous days:
  1251. // 40 connections, 12 negative reports, 40 positive reports
  1252. date += 1;
  1253. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1254. BridgeInfoType::BridgeIps,
  1255. date,
  1256. 40,
  1257. );
  1258. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1259. BridgeInfoType::NegativeReports,
  1260. date,
  1261. 12,
  1262. );
  1263. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1264. BridgeInfoType::PositiveReports,
  1265. date,
  1266. 40,
  1267. );
  1268. blocking_countries.insert("ru".to_string());
  1269. // This should be blocked because it's different in the bad direction.
  1270. assert_eq!(
  1271. blocked_in(&analyzer, &bridge_info, confidence, date),
  1272. blocking_countries
  1273. );
  1274. }
  1275. {
  1276. let mut date = get_date();
  1277. // New bridge info
  1278. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  1279. bridge_info
  1280. .info_by_country
  1281. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  1282. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  1283. let confidence = 0.95;
  1284. let mut blocking_countries = HashSet::<String>::new();
  1285. // No data today
  1286. assert_eq!(
  1287. blocked_in(&analyzer, &bridge_info, confidence, date),
  1288. blocking_countries
  1289. );
  1290. for i in 1..30 {
  1291. // 9-32 connections, 0-3 negative reports, 16-20 positive reports each day
  1292. date += 1;
  1293. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1294. BridgeInfoType::BridgeIps,
  1295. date,
  1296. 8 * (i % 3 + 2),
  1297. );
  1298. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1299. BridgeInfoType::NegativeReports,
  1300. date,
  1301. i % 4,
  1302. );
  1303. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1304. BridgeInfoType::PositiveReports,
  1305. date,
  1306. 16 + i % 5,
  1307. );
  1308. assert_eq!(
  1309. blocked_in(&analyzer, &bridge_info, confidence, date),
  1310. blocking_countries
  1311. );
  1312. }
  1313. // Data similar to previous days:
  1314. // 24 connections, 2 negative reports, 17 positive reports
  1315. date += 1;
  1316. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1317. BridgeInfoType::BridgeIps,
  1318. date,
  1319. 24,
  1320. );
  1321. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1322. BridgeInfoType::NegativeReports,
  1323. date,
  1324. 2,
  1325. );
  1326. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1327. BridgeInfoType::PositiveReports,
  1328. date,
  1329. 17,
  1330. );
  1331. // Should not be blocked because we have similar data.
  1332. assert_eq!(
  1333. blocked_in(&analyzer, &bridge_info, confidence, date),
  1334. blocking_countries
  1335. );
  1336. // Data different from previous days:
  1337. // 104 connections, 1 negative report, 85 positive reports
  1338. date += 1;
  1339. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1340. BridgeInfoType::BridgeIps,
  1341. date,
  1342. 104,
  1343. );
  1344. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1345. BridgeInfoType::NegativeReports,
  1346. date,
  1347. 1,
  1348. );
  1349. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1350. BridgeInfoType::PositiveReports,
  1351. date,
  1352. 85,
  1353. );
  1354. // This should not be blocked even though it's very different because
  1355. // it's different in the good direction.
  1356. assert_eq!(
  1357. blocked_in(&analyzer, &bridge_info, confidence, date),
  1358. blocking_countries
  1359. );
  1360. // Data different from previous days:
  1361. // 800 connections, 12 negative reports, 750 positive reports
  1362. date += 1;
  1363. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1364. BridgeInfoType::BridgeIps,
  1365. date,
  1366. 800,
  1367. );
  1368. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1369. BridgeInfoType::NegativeReports,
  1370. date,
  1371. 12,
  1372. );
  1373. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1374. BridgeInfoType::PositiveReports,
  1375. date,
  1376. 750,
  1377. );
  1378. blocking_countries.insert("ru".to_string());
  1379. // The censor artificially inflated bridge stats to prevent detection.
  1380. // Ensure we still detect the censorship from negative reports.
  1381. assert_eq!(
  1382. blocked_in(&analyzer, &bridge_info, confidence, date),
  1383. blocking_countries
  1384. );
  1385. }
  1386. {
  1387. let mut date = get_date();
  1388. // New bridge info
  1389. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  1390. bridge_info
  1391. .info_by_country
  1392. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  1393. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  1394. let confidence = 0.95;
  1395. let mut blocking_countries = HashSet::<String>::new();
  1396. // No data today
  1397. assert_eq!(
  1398. blocked_in(&analyzer, &bridge_info, confidence, date),
  1399. blocking_countries
  1400. );
  1401. for i in 1..30 {
  1402. // 9-32 connections, 0-3 negative reports, 16-20 positive reports each day
  1403. date += 1;
  1404. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1405. BridgeInfoType::BridgeIps,
  1406. date,
  1407. 8 * (i % 3 + 2),
  1408. );
  1409. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1410. BridgeInfoType::NegativeReports,
  1411. date,
  1412. i % 4,
  1413. );
  1414. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1415. BridgeInfoType::PositiveReports,
  1416. date,
  1417. 16 + i % 5,
  1418. );
  1419. assert_eq!(
  1420. blocked_in(&analyzer, &bridge_info, confidence, date),
  1421. blocking_countries
  1422. );
  1423. }
  1424. // Data similar to previous days:
  1425. // 24 connections, 2 negative reports, 17 positive reports
  1426. date += 1;
  1427. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1428. BridgeInfoType::BridgeIps,
  1429. date,
  1430. 24,
  1431. );
  1432. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1433. BridgeInfoType::NegativeReports,
  1434. date,
  1435. 2,
  1436. );
  1437. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1438. BridgeInfoType::PositiveReports,
  1439. date,
  1440. 17,
  1441. );
  1442. // Should not be blocked because we have similar data.
  1443. assert_eq!(
  1444. blocked_in(&analyzer, &bridge_info, confidence, date),
  1445. blocking_countries
  1446. );
  1447. // Data different from previous days:
  1448. // 104 connections, 1 negative report, 100 positive reports
  1449. date += 1;
  1450. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1451. BridgeInfoType::BridgeIps,
  1452. date,
  1453. 104,
  1454. );
  1455. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1456. BridgeInfoType::NegativeReports,
  1457. date,
  1458. 1,
  1459. );
  1460. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1461. BridgeInfoType::PositiveReports,
  1462. date,
  1463. 100,
  1464. );
  1465. // This should not be blocked even though it's very different because
  1466. // it's different in the good direction.
  1467. assert_eq!(
  1468. blocked_in(&analyzer, &bridge_info, confidence, date),
  1469. blocking_countries
  1470. );
  1471. // Data different from previous days:
  1472. // 24 connections, 1 negative report, 1 positive report
  1473. date += 1;
  1474. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1475. BridgeInfoType::BridgeIps,
  1476. date,
  1477. 24,
  1478. );
  1479. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1480. BridgeInfoType::NegativeReports,
  1481. date,
  1482. 1,
  1483. );
  1484. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1485. BridgeInfoType::PositiveReports,
  1486. date,
  1487. 1,
  1488. );
  1489. blocking_countries.insert("ru".to_string());
  1490. // This should be blocked because it's different in the bad direction.
  1491. assert_eq!(
  1492. blocked_in(&analyzer, &bridge_info, confidence, date),
  1493. blocking_countries
  1494. );
  1495. }
  1496. }