tests.rs 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770
  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_download_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_dei").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_simulate_extra_infos() {
  167. let extra_info_str = r#"@type bridge-extra-info 1.3
  168. extra-info ElephantBridgeDE2 72E12B89136B45BBC81D1EF0AC7DDDBB91B148DB
  169. master-key-ed25519 eWxjRwAWW7n8BGG9fNa6rApmBFbe3f0xcD7dqwOICW8
  170. published 2024-04-06 03:51:04
  171. transport obfs4
  172. write-history 2024-04-05 04:55:22 (86400 s) 31665735680,14918491136,15423603712,36168353792,40396827648
  173. read-history 2024-04-05 04:55:22 (86400 s) 31799622656,15229917184,15479115776,36317251584,40444155904
  174. ipv6-write-history 2024-04-05 04:55:22 (86400 s) 5972127744,610078720,516897792,327949312,640708608
  175. ipv6-read-history 2024-04-05 04:55:22 (86400 s) 4156158976,4040448000,2935756800,4263080960,6513532928
  176. dirreq-write-history 2024-04-05 04:55:22 (86400 s) 625217536,646188032,618014720,584386560,600778752
  177. dirreq-read-history 2024-04-05 04:55:22 (86400 s) 18816000,19000320,18484224,17364992,18443264
  178. geoip-db-digest 44073997E1ED63E183B79DE2A1757E9997A834E3
  179. geoip6-db-digest C0BF46880C6C132D746683279CC90DD4B2D31786
  180. dirreq-stats-end 2024-04-05 06:51:23 (86400 s)
  181. dirreq-v3-ips ru=16,au=8,by=8,cn=8,gb=8,ir=8,mt=8,nl=8,pl=8,tn=8,tr=8,us=8
  182. dirreq-v3-reqs ru=72,gb=64,pl=32,cn=16,ir=16,us=16,au=8,by=8,mt=8,nl=8,tn=8,tr=8
  183. dirreq-v3-resp ok=216,not-enough-sigs=0,unavailable=0,not-found=0,not-modified=328,busy=0
  184. dirreq-v3-direct-dl complete=0,timeout=0,running=0
  185. dirreq-v3-tunneled-dl complete=212,timeout=4,running=0,min=21595,d1=293347,d2=1624137,q1=1911800,d3=2066929,d4=2415000,md=2888500,d6=3264000,d7=3851333,q3=41>
  186. hidserv-stats-end 2024-04-05 06:51:23 (86400 s)
  187. hidserv-rend-relayed-cells 7924 delta_f=2048 epsilon=0.30 bin_size=1024
  188. hidserv-dir-onions-seen -12 delta_f=8 epsilon=0.30 bin_size=8
  189. hidserv-v3-stats-end 2024-04-05 12:00:00 (86400 s)
  190. hidserv-rend-v3-relayed-cells -4785 delta_f=2048 epsilon=0.30 bin_size=1024
  191. hidserv-dir-v3-onions-seen 5 delta_f=8 epsilon=0.30 bin_size=8
  192. padding-counts 2024-04-05 06:51:42 (86400 s) bin-size=10000 write-drop=0 write-pad=80000 write-total=79980000 read-drop=0 read-pad=1110000 read-total=7989000>
  193. bridge-stats-end 2024-04-05 06:51:44 (86400 s)
  194. bridge-ips ru=40,us=32,??=8,au=8,br=8,by=8,cn=8,de=8,eg=8,eu=8,gb=8,ge=8,hr=8,ie=8,ir=8,kp=8,lt=8,mt=8,nl=8,pl=8,ro=8,sg=8,tn=8,tr=8,vn=8
  195. bridge-ip-versions v4=104,v6=8
  196. bridge-ip-transports <OR>=56,obfs4=56
  197. router-digest-sha256 zK0VMl3i0B2eaeQTR03e2hZ0i8ytkuhK/psgD2J1/lQ
  198. router-digest F30B38390C375E1EE74BFED844177804442569E0"#;
  199. let extra_info_set = ExtraInfo::parse_file(&extra_info_str);
  200. assert_eq!(extra_info_set.len(), 1);
  201. let extra_info = extra_info_set.iter().next().unwrap().clone();
  202. let extra_info_str = extra_info.to_string();
  203. let extra_info_2 = ExtraInfo::parse_file(&extra_info_str)
  204. .into_iter()
  205. .next()
  206. .unwrap()
  207. .clone();
  208. assert_eq!(extra_info, extra_info_2);
  209. let bridge_to_test: [u8; 20] =
  210. array_bytes::hex2array("72E12B89136B45BBC81D1EF0AC7DDDBB91B148DB").unwrap();
  211. // Open test database
  212. let db: Db = sled::open("test_db_sei").unwrap();
  213. // Delete all data in test DB
  214. db.clear().unwrap();
  215. assert!(!db.contains_key("bridges").unwrap());
  216. assert!(!db.contains_key(bridge_to_test).unwrap());
  217. // TODO: Run local web server and change this to update_extra_infos
  218. add_extra_info_to_db(&db, extra_info_2);
  219. // Check that DB contains information on a bridge with high uptime
  220. assert!(db.contains_key("bridges").unwrap());
  221. let bridges: HashSet<[u8; 20]> =
  222. bincode::deserialize(&db.get("bridges").unwrap().unwrap()).unwrap();
  223. assert!(bridges.contains(&bridge_to_test));
  224. assert!(db.contains_key(bridge_to_test).unwrap());
  225. let _bridge_info: BridgeInfo =
  226. bincode::deserialize(&db.get(bridge_to_test).unwrap().unwrap()).unwrap();
  227. }
  228. #[test]
  229. fn test_negative_reports() {
  230. let mut th = TestHarness::new();
  231. // Get new level 1 credential
  232. let cred = th.get_new_credential();
  233. let cred = th.level_up(&cred);
  234. let bridges = th.get_bucket(&cred);
  235. // Create BridgeVerificationInfo for each bridge
  236. let mut buckets = HashSet::<Scalar>::new();
  237. buckets.insert(cred.bucket);
  238. let bridge_info_1 = BridgeVerificationInfo {
  239. bridge_line: bridges[0],
  240. buckets: buckets.clone(),
  241. pubkey: None,
  242. };
  243. let bridge_info_2 = BridgeVerificationInfo {
  244. bridge_line: bridges[1],
  245. buckets: buckets.clone(),
  246. pubkey: None,
  247. };
  248. let bridge_info_3 = BridgeVerificationInfo {
  249. bridge_line: bridges[2],
  250. buckets: buckets.clone(),
  251. pubkey: None,
  252. };
  253. // Create reports
  254. let report_1 =
  255. NegativeReport::from_bridgeline(bridges[0], "ru".to_string(), BridgeDistributor::Lox);
  256. let report_2 =
  257. NegativeReport::from_lox_bucket(bridges[1].fingerprint, cred.bucket, "ru".to_string());
  258. let report_3 =
  259. NegativeReport::from_lox_credential(bridges[2].fingerprint, &cred, "ru".to_string());
  260. // Backdated reports
  261. let date = get_date();
  262. let mut rng = rand::thread_rng();
  263. let mut nonce = [0; 32];
  264. rng.fill_bytes(&mut nonce);
  265. let report_4 = NegativeReport::new(
  266. bridges[0].fingerprint,
  267. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  268. &bridges[0],
  269. date - 1,
  270. nonce,
  271. )),
  272. "ru".to_string(),
  273. date - 1,
  274. nonce,
  275. BridgeDistributor::Lox,
  276. );
  277. let mut nonce = [0; 32];
  278. rng.fill_bytes(&mut nonce);
  279. let report_5 = NegativeReport::new(
  280. bridges[1].fingerprint,
  281. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  282. &bridges[1],
  283. date - 2,
  284. nonce,
  285. )),
  286. "ru".to_string(),
  287. date - 2,
  288. nonce,
  289. BridgeDistributor::Lox,
  290. );
  291. let mut nonce = [0; 32];
  292. rng.fill_bytes(&mut nonce);
  293. let report_6 = NegativeReport::new(
  294. bridges[2].fingerprint,
  295. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  296. &bridges[2],
  297. date - 3,
  298. nonce,
  299. )),
  300. "ru".to_string(),
  301. date - 3,
  302. nonce,
  303. BridgeDistributor::Lox,
  304. );
  305. // Verify reports
  306. assert!(report_1.verify(&bridge_info_1));
  307. assert!(report_2.verify(&bridge_info_2));
  308. assert!(report_3.verify(&bridge_info_3));
  309. assert!(report_4.verify(&bridge_info_1));
  310. assert!(report_5.verify(&bridge_info_2));
  311. assert!(report_6.verify(&bridge_info_3));
  312. // Check that deserialization fails under invalid conditions
  313. // Date in the future
  314. let mut invalid_report_1 =
  315. NegativeReport::from_bridgeline(bridges[0], "ru".to_string(), BridgeDistributor::Lox)
  316. .to_serializable_report();
  317. invalid_report_1.date = invalid_report_1.date + 2;
  318. // Date too far in past
  319. let mut invalid_report_2 =
  320. NegativeReport::from_bridgeline(bridges[1], "ru".to_string(), BridgeDistributor::Lox)
  321. .to_serializable_report();
  322. invalid_report_2.date = invalid_report_2.date - MAX_BACKDATE - 1;
  323. // Invalid country code
  324. let invalid_report_3 =
  325. NegativeReport::from_bridgeline(bridges[2], "xx".to_string(), BridgeDistributor::Lox)
  326. .to_serializable_report();
  327. assert!(invalid_report_1.to_report().is_err());
  328. assert!(invalid_report_2.to_report().is_err());
  329. assert!(invalid_report_3.to_report().is_err());
  330. // Check that verification fails with incorrect data
  331. let date = get_date();
  332. let mut rng = rand::thread_rng();
  333. // Incorrect BridgeLine hash
  334. let mut nonce = [0; 32];
  335. rng.fill_bytes(&mut nonce);
  336. let invalid_report_4 = NegativeReport::new(
  337. bridges[0].fingerprint,
  338. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  339. &BridgeLine::default(),
  340. date,
  341. nonce,
  342. )),
  343. "ru".to_string(),
  344. date,
  345. nonce,
  346. BridgeDistributor::Lox,
  347. );
  348. // Incorrect bucket hash
  349. let mut nonce = [0; 32];
  350. rng.fill_bytes(&mut nonce);
  351. let invalid_report_5 = NegativeReport::new(
  352. bridges[1].fingerprint,
  353. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&Scalar::ZERO, date, nonce)),
  354. "ru".to_string(),
  355. date,
  356. nonce,
  357. BridgeDistributor::Lox,
  358. );
  359. assert!(!invalid_report_4.verify(&bridge_info_1));
  360. assert!(!invalid_report_5.verify(&bridge_info_2));
  361. // Test that reports with duplicate nonces are rejected
  362. // (Also test encryption and decryption.)
  363. // Open test database
  364. let db: Db = sled::open("test_db_nr").unwrap();
  365. // Delete all data in test DB
  366. db.clear().unwrap();
  367. assert!(!db.contains_key("nrs-to-process").unwrap());
  368. let mut nonce = [0; 32];
  369. rng.fill_bytes(&mut nonce);
  370. // A valid report
  371. let valid_report_1 = NegativeReport::new(
  372. bridges[0].fingerprint,
  373. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  374. "ru".to_string(),
  375. date,
  376. nonce,
  377. BridgeDistributor::Lox,
  378. );
  379. let valid_report_1_copy_1 = NegativeReport::new(
  380. bridges[0].fingerprint,
  381. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  382. "ru".to_string(),
  383. date,
  384. nonce,
  385. BridgeDistributor::Lox,
  386. );
  387. let valid_report_1_copy_2 = NegativeReport::new(
  388. bridges[0].fingerprint,
  389. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  390. "ru".to_string(),
  391. date,
  392. nonce,
  393. BridgeDistributor::Lox,
  394. );
  395. // Report which reuses this nonce
  396. let invalid_report_1 = NegativeReport::new(
  397. bridges[0].fingerprint,
  398. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  399. "ru".to_string(),
  400. date,
  401. nonce,
  402. BridgeDistributor::Lox,
  403. );
  404. // This is the same report
  405. assert_eq!(valid_report_1, invalid_report_1);
  406. // Report which reuses this nonce for a different bridge
  407. let invalid_report_2 = NegativeReport::new(
  408. bridges[1].fingerprint,
  409. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[1], date, nonce)),
  410. "ru".to_string(),
  411. date,
  412. nonce,
  413. BridgeDistributor::Lox,
  414. );
  415. // Report which uses this nonce but on a different day
  416. let valid_report_2 = NegativeReport::new(
  417. bridges[0].fingerprint,
  418. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
  419. &bridges[0],
  420. date - 1,
  421. nonce,
  422. )),
  423. "ru".to_string(),
  424. date - 1,
  425. nonce,
  426. BridgeDistributor::Lox,
  427. );
  428. // Report with different nonce
  429. let mut nonce = [0; 32];
  430. rng.fill_bytes(&mut nonce);
  431. let valid_report_3 = NegativeReport::new(
  432. bridges[0].fingerprint,
  433. ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
  434. "ru".to_string(),
  435. date,
  436. nonce,
  437. BridgeDistributor::Lox,
  438. );
  439. let map_key_1 = format!(
  440. "{}_{}_{}",
  441. array_bytes::bytes2hex("", valid_report_1.fingerprint),
  442. "ru".to_string(),
  443. date
  444. );
  445. // Generate key for today
  446. let secret = StaticSecret::random_from_rng(&mut rng);
  447. let public = PublicKey::from(&secret);
  448. let secret_yesterday = StaticSecret::random_from_rng(&mut rng);
  449. let public_yesterday = PublicKey::from(&secret_yesterday);
  450. assert!(!db.contains_key("nr-keys").unwrap());
  451. // Fail to add to database because we can't decrypt
  452. handle_encrypted_negative_report(&db, valid_report_1_copy_1.encrypt(&public));
  453. assert!(!db.contains_key("nrs-to-process").unwrap());
  454. // Store yesterday's key but not today's
  455. let mut nr_keys = BTreeMap::<u32, StaticSecret>::new();
  456. nr_keys.insert(date - 1, secret_yesterday);
  457. db.insert("nr-keys", bincode::serialize(&nr_keys).unwrap())
  458. .unwrap();
  459. // Fail to add to database because we still can't decrypt
  460. handle_encrypted_negative_report(&db, valid_report_1_copy_2.encrypt(&public));
  461. assert!(!db.contains_key("nrs-to-process").unwrap());
  462. // Store today's key
  463. nr_keys.insert(date, secret);
  464. db.insert("nr-keys", bincode::serialize(&nr_keys).unwrap())
  465. .unwrap();
  466. handle_encrypted_negative_report(&db, valid_report_1.encrypt(&public));
  467. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  468. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  469. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  470. assert_eq!(negative_reports.len(), 1);
  471. handle_encrypted_negative_report(&db, invalid_report_1.encrypt(&public)); // no change
  472. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  473. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  474. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  475. assert_eq!(negative_reports.len(), 1);
  476. let map_key_2 = format!(
  477. "{}_{}_{}",
  478. array_bytes::bytes2hex("", invalid_report_2.fingerprint),
  479. "ru".to_string(),
  480. date
  481. );
  482. handle_encrypted_negative_report(&db, invalid_report_2.encrypt(&public)); // no change
  483. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  484. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  485. assert!(!nrs_to_process.contains_key(&map_key_2));
  486. let map_key_3 = format!(
  487. "{}_{}_{}",
  488. array_bytes::bytes2hex("", valid_report_2.fingerprint),
  489. "ru".to_string(),
  490. date - 1
  491. );
  492. handle_encrypted_negative_report(&db, valid_report_2.encrypt(&public_yesterday));
  493. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  494. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  495. let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
  496. assert_eq!(negative_reports.len(), 1);
  497. handle_encrypted_negative_report(&db, valid_report_3.encrypt(&public));
  498. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  499. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  500. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  501. assert_eq!(negative_reports.len(), 2);
  502. // Same tests, but use hash of bucket
  503. // Delete all data in test DB
  504. db.clear().unwrap();
  505. assert!(!db.contains_key("nrs-to-process").unwrap());
  506. // Re-generate keys and save in database
  507. let public = new_negative_report_key(&db, date).unwrap();
  508. let public_yesterday = new_negative_report_key(&db, date - 1).unwrap();
  509. let mut nonce = [0; 32];
  510. rng.fill_bytes(&mut nonce);
  511. // A valid report
  512. let valid_report_1 = NegativeReport::new(
  513. bridges[0].fingerprint,
  514. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
  515. "ru".to_string(),
  516. date,
  517. nonce,
  518. BridgeDistributor::Lox,
  519. );
  520. // Report which reuses this nonce
  521. let invalid_report_1 = NegativeReport::new(
  522. bridges[0].fingerprint,
  523. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
  524. "ru".to_string(),
  525. date,
  526. nonce,
  527. BridgeDistributor::Lox,
  528. );
  529. // This is the same report
  530. assert_eq!(valid_report_1, invalid_report_1);
  531. // Report which reuses this nonce for a different bridge
  532. let invalid_report_2 = NegativeReport::new(
  533. bridges[1].fingerprint,
  534. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
  535. "ru".to_string(),
  536. date,
  537. nonce,
  538. BridgeDistributor::Lox,
  539. );
  540. // Report which uses this nonce but on a different day
  541. let valid_report_2 = NegativeReport::new(
  542. bridges[0].fingerprint,
  543. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date - 1, nonce)),
  544. "ru".to_string(),
  545. date - 1,
  546. nonce,
  547. BridgeDistributor::Lox,
  548. );
  549. // Report with different nonce
  550. let mut nonce = [0; 32];
  551. rng.fill_bytes(&mut nonce);
  552. let valid_report_3 = NegativeReport::new(
  553. bridges[0].fingerprint,
  554. ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
  555. "ru".to_string(),
  556. date,
  557. nonce,
  558. BridgeDistributor::Lox,
  559. );
  560. let map_key_1 = format!(
  561. "{}_{}_{}",
  562. array_bytes::bytes2hex("", valid_report_1.fingerprint),
  563. "ru".to_string(),
  564. date
  565. );
  566. handle_encrypted_negative_report(&db, valid_report_1.encrypt(&public));
  567. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  568. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  569. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  570. assert_eq!(negative_reports.len(), 1);
  571. handle_encrypted_negative_report(&db, invalid_report_1.encrypt(&public)); // no change
  572. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  573. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  574. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  575. assert_eq!(negative_reports.len(), 1);
  576. let map_key_2 = format!(
  577. "{}_{}_{}",
  578. array_bytes::bytes2hex("", invalid_report_2.fingerprint),
  579. "ru".to_string(),
  580. date
  581. );
  582. handle_encrypted_negative_report(&db, invalid_report_2.encrypt(&public)); // no change
  583. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  584. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  585. assert!(!nrs_to_process.contains_key(&map_key_2));
  586. let map_key_3 = format!(
  587. "{}_{}_{}",
  588. array_bytes::bytes2hex("", valid_report_2.fingerprint),
  589. "ru".to_string(),
  590. date - 1
  591. );
  592. handle_encrypted_negative_report(&db, valid_report_2.encrypt(&public_yesterday));
  593. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  594. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  595. let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
  596. assert_eq!(negative_reports.len(), 1);
  597. handle_encrypted_negative_report(&db, valid_report_3.encrypt(&public));
  598. let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
  599. bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
  600. let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
  601. assert_eq!(negative_reports.len(), 2);
  602. }
  603. #[test]
  604. fn test_positive_reports() {
  605. let mut th = TestHarness::new();
  606. // Get new level 3 credential
  607. let cred = th.get_new_credential();
  608. let cred = th.level_up(&cred);
  609. let cred = th.level_up(&cred);
  610. let cred = th.level_up(&cred);
  611. let bridges = th.get_bucket(&cred);
  612. // Create BridgeVerificationInfo for each bridge
  613. let mut buckets = HashSet::<Scalar>::new();
  614. buckets.insert(cred.bucket);
  615. let bridge_info_1 = BridgeVerificationInfo {
  616. bridge_line: bridges[0],
  617. buckets: buckets.clone(),
  618. pubkey: None,
  619. };
  620. let bridge_info_2 = BridgeVerificationInfo {
  621. bridge_line: bridges[1],
  622. buckets: buckets.clone(),
  623. pubkey: None,
  624. };
  625. let bridge_info_3 = BridgeVerificationInfo {
  626. bridge_line: bridges[2],
  627. buckets: buckets.clone(),
  628. pubkey: None,
  629. };
  630. // Create reports
  631. let report_1 = PositiveReport::from_lox_credential(
  632. bridges[0].fingerprint,
  633. None,
  634. &cred,
  635. &th.ba.lox_pub,
  636. "ru".to_string(),
  637. )
  638. .unwrap();
  639. let report_2 = PositiveReport::from_lox_credential(
  640. bridges[1].fingerprint,
  641. None,
  642. &cred,
  643. &th.ba.lox_pub,
  644. "ru".to_string(),
  645. )
  646. .unwrap();
  647. let report_3 = PositiveReport::from_lox_credential(
  648. bridges[2].fingerprint,
  649. None,
  650. &cred,
  651. &th.ba.lox_pub,
  652. "ru".to_string(),
  653. )
  654. .unwrap();
  655. // Compute Htable
  656. let H = lox_library::proto::positive_report::compute_H(report_1.date);
  657. let Htable = RistrettoBasepointTable::create(&H);
  658. assert!(report_1.verify(&mut th.ba, &bridge_info_1, &Htable));
  659. assert!(report_2.verify(&mut th.ba, &bridge_info_2, &Htable));
  660. assert!(report_3.verify(&mut th.ba, &bridge_info_3, &Htable));
  661. // Check that user cannot use credential for other bridge
  662. // Get new credential
  663. let cred_2 = th.get_new_credential();
  664. let bridges_2 = th.get_bucket(&cred_2);
  665. let mut buckets_2 = HashSet::<Scalar>::new();
  666. buckets_2.insert(cred_2.bucket);
  667. let bridge_info_4 = BridgeVerificationInfo {
  668. bridge_line: bridges_2[0],
  669. buckets: buckets_2.clone(),
  670. pubkey: None,
  671. };
  672. // Use new credential to create positive report even we don't trust it
  673. let invalid_report_1 = PositiveReport::from_lox_credential(
  674. bridges_2[0].fingerprint,
  675. None,
  676. &cred_2,
  677. &th.ba.lox_pub,
  678. "ru".to_string(),
  679. );
  680. // Use first credential for bridge from second bucket
  681. let invalid_report_2 = PositiveReport::from_lox_credential(
  682. bridges_2[0].fingerprint,
  683. None,
  684. &cred,
  685. &th.ba.lox_pub,
  686. "ru".to_string(),
  687. );
  688. // Use second credential for bridge from first bucket
  689. let invalid_report_3 = PositiveReport::from_lox_credential(
  690. bridges[0].fingerprint,
  691. None,
  692. &cred_2,
  693. &th.ba.lox_pub,
  694. "ru".to_string(),
  695. );
  696. // Check that all of these fail
  697. assert!(invalid_report_1.is_err());
  698. assert!(!invalid_report_2
  699. .unwrap()
  700. .verify(&mut th.ba, &bridge_info_4, &Htable));
  701. assert!(invalid_report_3.is_err());
  702. // Check that deserialization fails under invalid conditions
  703. // Date in the future
  704. let mut invalid_report_4 = PositiveReport::from_lox_credential(
  705. bridges[0].fingerprint,
  706. None,
  707. &cred,
  708. &th.ba.lox_pub,
  709. "ru".to_string(),
  710. )
  711. .unwrap()
  712. .to_serializable_report();
  713. invalid_report_4.date = invalid_report_4.date + 2;
  714. // Invalid country code
  715. let invalid_report_5 = PositiveReport::from_lox_credential(
  716. bridges[0].fingerprint,
  717. None,
  718. &cred,
  719. &th.ba.lox_pub,
  720. "xx".to_string(),
  721. )
  722. .unwrap()
  723. .to_serializable_report();
  724. assert!(invalid_report_4.to_report().is_err());
  725. assert!(invalid_report_5.to_report().is_err());
  726. // Test storing to-be-processed positive reports to database
  727. // Create reports
  728. let report_1 = PositiveReport::from_lox_credential(
  729. bridges[0].fingerprint,
  730. None,
  731. &cred,
  732. &th.ba.lox_pub,
  733. "ru".to_string(),
  734. )
  735. .unwrap();
  736. let report_2 = PositiveReport::from_lox_credential(
  737. bridges[0].fingerprint,
  738. None,
  739. &cred,
  740. &th.ba.lox_pub,
  741. "ru".to_string(),
  742. )
  743. .unwrap();
  744. let report_3 = PositiveReport::from_lox_credential(
  745. bridges[1].fingerprint,
  746. None,
  747. &cred,
  748. &th.ba.lox_pub,
  749. "ru".to_string(),
  750. )
  751. .unwrap();
  752. // Open test database
  753. let db: Db = sled::open("test_db_pr").unwrap();
  754. // Delete all data in test DB
  755. db.clear().unwrap();
  756. assert!(!db.contains_key("prs-to-process").unwrap());
  757. let map_key_1 = format!(
  758. "{}_{}_{}",
  759. array_bytes::bytes2hex("", report_1.fingerprint),
  760. &report_1.country,
  761. &report_1.date
  762. );
  763. let map_key_2 = format!(
  764. "{}_{}_{}",
  765. array_bytes::bytes2hex("", report_3.fingerprint),
  766. &report_3.country,
  767. &report_3.date
  768. );
  769. save_positive_report_to_process(&db, report_1);
  770. let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
  771. bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
  772. let positive_reports = prs_to_process.get(&map_key_1).unwrap();
  773. assert_eq!(positive_reports.len(), 1);
  774. assert!(!prs_to_process.contains_key(&map_key_2));
  775. save_positive_report_to_process(&db, report_2);
  776. let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
  777. bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
  778. let positive_reports = prs_to_process.get(&map_key_1).unwrap();
  779. assert_eq!(positive_reports.len(), 2);
  780. assert!(!prs_to_process.contains_key(&map_key_2));
  781. save_positive_report_to_process(&db, report_3);
  782. let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
  783. bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
  784. // Check that this has not changed
  785. let positive_reports = prs_to_process.get(&map_key_1).unwrap();
  786. assert_eq!(positive_reports.len(), 2);
  787. // New report added to its own collection
  788. let positive_reports = prs_to_process.get(&map_key_2).unwrap();
  789. assert_eq!(positive_reports.len(), 1);
  790. }
  791. #[test]
  792. fn test_analysis() {
  793. // Test stage 1 analysis
  794. {
  795. let mut date = get_date();
  796. // New bridge info
  797. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  798. bridge_info
  799. .info_by_country
  800. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  801. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  802. let confidence = 0.95;
  803. let mut blocking_countries = HashSet::<String>::new();
  804. // No data today
  805. assert_eq!(
  806. blocked_in(&analyzer, &bridge_info, confidence, date),
  807. blocking_countries
  808. );
  809. // 1 connection, 0 negative reports
  810. date += 1;
  811. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  812. BridgeInfoType::BridgeIps,
  813. date,
  814. 8,
  815. );
  816. assert_eq!(
  817. blocked_in(&analyzer, &bridge_info, confidence, date),
  818. blocking_countries
  819. );
  820. // 0 connections, 0 negative reports
  821. date += 1;
  822. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  823. BridgeInfoType::BridgeIps,
  824. date,
  825. 0,
  826. );
  827. assert_eq!(
  828. blocked_in(&analyzer, &bridge_info, confidence, date),
  829. blocking_countries
  830. );
  831. // 0 connections, 1 negative report
  832. // (exceeds scaled threshold)
  833. date += 1;
  834. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  835. BridgeInfoType::NegativeReports,
  836. date,
  837. 1,
  838. );
  839. blocking_countries.insert("ru".to_string());
  840. assert_eq!(
  841. blocked_in(&analyzer, &bridge_info, confidence, date),
  842. blocking_countries
  843. );
  844. }
  845. {
  846. let mut date = get_date();
  847. // New bridge info
  848. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  849. bridge_info
  850. .info_by_country
  851. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  852. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  853. let confidence = 0.95;
  854. let mut blocking_countries = HashSet::<String>::new();
  855. // No data today
  856. assert_eq!(
  857. blocked_in(&analyzer, &bridge_info, confidence, date),
  858. blocking_countries
  859. );
  860. // 1 connection, 1 negative report
  861. date += 1;
  862. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  863. BridgeInfoType::BridgeIps,
  864. date,
  865. 8,
  866. );
  867. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  868. BridgeInfoType::NegativeReports,
  869. date,
  870. 1,
  871. );
  872. assert_eq!(
  873. blocked_in(&analyzer, &bridge_info, confidence, date),
  874. blocking_countries
  875. );
  876. // 8 connections, 2 negative reports
  877. date += 1;
  878. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  879. BridgeInfoType::BridgeIps,
  880. date,
  881. 8,
  882. );
  883. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  884. BridgeInfoType::NegativeReports,
  885. date,
  886. 2,
  887. );
  888. assert_eq!(
  889. blocked_in(&analyzer, &bridge_info, confidence, date),
  890. blocking_countries
  891. );
  892. // 8 connections, 3 negative reports
  893. // (exceeds scaled threshold)
  894. date += 1;
  895. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  896. BridgeInfoType::BridgeIps,
  897. date,
  898. 8,
  899. );
  900. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  901. BridgeInfoType::NegativeReports,
  902. date,
  903. 3,
  904. );
  905. blocking_countries.insert("ru".to_string());
  906. assert_eq!(
  907. blocked_in(&analyzer, &bridge_info, confidence, date),
  908. blocking_countries
  909. );
  910. }
  911. {
  912. let mut date = get_date();
  913. // New bridge info
  914. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  915. bridge_info
  916. .info_by_country
  917. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  918. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  919. let confidence = 0.95;
  920. let mut blocking_countries = HashSet::<String>::new();
  921. // 24 connections, 5 negative reports
  922. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  923. BridgeInfoType::BridgeIps,
  924. date,
  925. 24,
  926. );
  927. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  928. BridgeInfoType::NegativeReports,
  929. date,
  930. 5,
  931. );
  932. assert_eq!(
  933. blocked_in(&analyzer, &bridge_info, confidence, date),
  934. blocking_countries
  935. );
  936. // 24 connections, 6 negative reports
  937. // (exceeds max threshold)
  938. date += 1;
  939. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  940. BridgeInfoType::BridgeIps,
  941. date,
  942. 24,
  943. );
  944. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  945. BridgeInfoType::NegativeReports,
  946. date,
  947. 6,
  948. );
  949. blocking_countries.insert("ru".to_string());
  950. assert_eq!(
  951. blocked_in(&analyzer, &bridge_info, confidence, date),
  952. blocking_countries
  953. );
  954. }
  955. // Test stage 2 analysis
  956. {
  957. let mut date = get_date();
  958. // New bridge info
  959. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  960. bridge_info
  961. .info_by_country
  962. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  963. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  964. let confidence = 0.95;
  965. let mut blocking_countries = HashSet::<String>::new();
  966. // No data today
  967. assert_eq!(
  968. blocked_in(&analyzer, &bridge_info, confidence, date),
  969. blocking_countries
  970. );
  971. for i in 1..30 {
  972. // 9-32 connections, 0-3 negative reports each day
  973. date += 1;
  974. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  975. BridgeInfoType::BridgeIps,
  976. date,
  977. 8 * (i % 3 + 2),
  978. );
  979. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  980. BridgeInfoType::NegativeReports,
  981. date,
  982. i % 4,
  983. );
  984. assert_eq!(
  985. blocked_in(&analyzer, &bridge_info, confidence, date),
  986. blocking_countries
  987. );
  988. }
  989. // Data similar to previous days:
  990. // 24 connections, 2 negative reports
  991. date += 1;
  992. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  993. BridgeInfoType::BridgeIps,
  994. date,
  995. 24,
  996. );
  997. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  998. BridgeInfoType::NegativeReports,
  999. date,
  1000. 2,
  1001. );
  1002. // Should not be blocked because we have similar data.
  1003. assert_eq!(
  1004. blocked_in(&analyzer, &bridge_info, confidence, date),
  1005. blocking_countries
  1006. );
  1007. // Data different from previous days:
  1008. // 104 connections, 1 negative report
  1009. date += 1;
  1010. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1011. BridgeInfoType::BridgeIps,
  1012. date,
  1013. 104,
  1014. );
  1015. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1016. BridgeInfoType::NegativeReports,
  1017. date,
  1018. 1,
  1019. );
  1020. // This should not be blocked even though it's very different because
  1021. // it's different in the good direction.
  1022. assert_eq!(
  1023. blocked_in(&analyzer, &bridge_info, confidence, date),
  1024. blocking_countries
  1025. );
  1026. // Data different from previous days:
  1027. // 40 connections, 12 negative reports
  1028. date += 1;
  1029. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1030. BridgeInfoType::BridgeIps,
  1031. date,
  1032. 40,
  1033. );
  1034. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1035. BridgeInfoType::NegativeReports,
  1036. date,
  1037. 12,
  1038. );
  1039. blocking_countries.insert("ru".to_string());
  1040. // This should be blocked because it's different in the bad direction.
  1041. assert_eq!(
  1042. blocked_in(&analyzer, &bridge_info, confidence, date),
  1043. blocking_countries
  1044. );
  1045. }
  1046. {
  1047. let mut date = get_date();
  1048. // New bridge info
  1049. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  1050. bridge_info
  1051. .info_by_country
  1052. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  1053. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  1054. let confidence = 0.95;
  1055. let mut blocking_countries = HashSet::<String>::new();
  1056. // No data today
  1057. assert_eq!(
  1058. blocked_in(&analyzer, &bridge_info, confidence, date),
  1059. blocking_countries
  1060. );
  1061. for i in 1..30 {
  1062. // 9-32 connections, 0-3 negative reports each day
  1063. date += 1;
  1064. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1065. BridgeInfoType::BridgeIps,
  1066. date,
  1067. 8 * (i % 3 + 2),
  1068. );
  1069. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1070. BridgeInfoType::NegativeReports,
  1071. date,
  1072. i % 4,
  1073. );
  1074. assert_eq!(
  1075. blocked_in(&analyzer, &bridge_info, confidence, date),
  1076. blocking_countries
  1077. );
  1078. }
  1079. // Data similar to previous days:
  1080. // 24 connections, 2 negative reports
  1081. date += 1;
  1082. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1083. BridgeInfoType::BridgeIps,
  1084. date,
  1085. 24,
  1086. );
  1087. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1088. BridgeInfoType::NegativeReports,
  1089. date,
  1090. 2,
  1091. );
  1092. // Should not be blocked because we have similar data.
  1093. assert_eq!(
  1094. blocked_in(&analyzer, &bridge_info, confidence, date),
  1095. blocking_countries
  1096. );
  1097. // Data different from previous days:
  1098. // 104 connections, 1 negative report
  1099. date += 1;
  1100. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1101. BridgeInfoType::BridgeIps,
  1102. date,
  1103. 104,
  1104. );
  1105. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1106. BridgeInfoType::NegativeReports,
  1107. date,
  1108. 1,
  1109. );
  1110. // This should not be blocked even though it's very different because
  1111. // it's different in the good direction.
  1112. assert_eq!(
  1113. blocked_in(&analyzer, &bridge_info, confidence, date),
  1114. blocking_countries
  1115. );
  1116. // Data different from previous days:
  1117. // 800 connections, 12 negative reports
  1118. date += 1;
  1119. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1120. BridgeInfoType::BridgeIps,
  1121. date,
  1122. 800,
  1123. );
  1124. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1125. BridgeInfoType::NegativeReports,
  1126. date,
  1127. 12,
  1128. );
  1129. blocking_countries.insert("ru".to_string());
  1130. // The censor artificially inflated bridge stats to prevent detection.
  1131. // Ensure we still detect the censorship from negative reports.
  1132. assert_eq!(
  1133. blocked_in(&analyzer, &bridge_info, confidence, date),
  1134. blocking_countries
  1135. );
  1136. }
  1137. {
  1138. let mut date = get_date();
  1139. // New bridge info
  1140. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  1141. bridge_info
  1142. .info_by_country
  1143. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  1144. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  1145. let confidence = 0.95;
  1146. let mut blocking_countries = HashSet::<String>::new();
  1147. // No data today
  1148. assert_eq!(
  1149. blocked_in(&analyzer, &bridge_info, confidence, date),
  1150. blocking_countries
  1151. );
  1152. for i in 1..30 {
  1153. // 9-32 connections, 0-3 negative reports each day
  1154. date += 1;
  1155. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1156. BridgeInfoType::BridgeIps,
  1157. date,
  1158. 8 * (i % 3 + 2),
  1159. );
  1160. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1161. BridgeInfoType::NegativeReports,
  1162. date,
  1163. i % 4,
  1164. );
  1165. assert_eq!(
  1166. blocked_in(&analyzer, &bridge_info, confidence, date),
  1167. blocking_countries
  1168. );
  1169. }
  1170. // Data similar to previous days:
  1171. // 24 connections, 2 negative reports
  1172. date += 1;
  1173. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1174. BridgeInfoType::BridgeIps,
  1175. date,
  1176. 24,
  1177. );
  1178. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1179. BridgeInfoType::NegativeReports,
  1180. date,
  1181. 2,
  1182. );
  1183. // Should not be blocked because we have similar data.
  1184. assert_eq!(
  1185. blocked_in(&analyzer, &bridge_info, confidence, date),
  1186. blocking_countries
  1187. );
  1188. // Data different from previous days:
  1189. // 104 connections, 1 negative report
  1190. date += 1;
  1191. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1192. BridgeInfoType::BridgeIps,
  1193. date,
  1194. 104,
  1195. );
  1196. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1197. BridgeInfoType::NegativeReports,
  1198. date,
  1199. 1,
  1200. );
  1201. // This should not be blocked even though it's very different because
  1202. // it's different in the good direction.
  1203. assert_eq!(
  1204. blocked_in(&analyzer, &bridge_info, confidence, date),
  1205. blocking_countries
  1206. );
  1207. // Data different from previous days:
  1208. // 0 connections, 0 negative reports
  1209. date += 1;
  1210. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1211. BridgeInfoType::BridgeIps,
  1212. date,
  1213. 0,
  1214. );
  1215. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1216. BridgeInfoType::NegativeReports,
  1217. date,
  1218. 0,
  1219. );
  1220. blocking_countries.insert("ru".to_string());
  1221. // This should be blocked because it's different in the bad direction.
  1222. assert_eq!(
  1223. blocked_in(&analyzer, &bridge_info, confidence, date),
  1224. blocking_countries
  1225. );
  1226. }
  1227. // Test stage 3 analysis
  1228. {
  1229. let mut date = get_date();
  1230. // New bridge info
  1231. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  1232. bridge_info
  1233. .info_by_country
  1234. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  1235. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  1236. let confidence = 0.95;
  1237. let mut blocking_countries = HashSet::<String>::new();
  1238. // No data today
  1239. assert_eq!(
  1240. blocked_in(&analyzer, &bridge_info, confidence, date),
  1241. blocking_countries
  1242. );
  1243. for i in 1..30 {
  1244. // 9-32 connections, 0-3 negative reports, 16-20 positive reports each day
  1245. date += 1;
  1246. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1247. BridgeInfoType::BridgeIps,
  1248. date,
  1249. 8 * (i % 3 + 2),
  1250. );
  1251. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1252. BridgeInfoType::NegativeReports,
  1253. date,
  1254. i % 4,
  1255. );
  1256. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1257. BridgeInfoType::PositiveReports,
  1258. date,
  1259. 16 + i % 5,
  1260. );
  1261. assert_eq!(
  1262. blocked_in(&analyzer, &bridge_info, confidence, date),
  1263. blocking_countries
  1264. );
  1265. }
  1266. // Data similar to previous days:
  1267. // 24 connections, 2 negative reports, 17 positive reports
  1268. date += 1;
  1269. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1270. BridgeInfoType::BridgeIps,
  1271. date,
  1272. 24,
  1273. );
  1274. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1275. BridgeInfoType::NegativeReports,
  1276. date,
  1277. 2,
  1278. );
  1279. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1280. BridgeInfoType::PositiveReports,
  1281. date,
  1282. 17,
  1283. );
  1284. // Should not be blocked because we have similar data.
  1285. assert_eq!(
  1286. blocked_in(&analyzer, &bridge_info, confidence, date),
  1287. blocking_countries
  1288. );
  1289. // Data different from previous days:
  1290. // 104 connections, 1 negative report, 100 positive reports
  1291. date += 1;
  1292. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1293. BridgeInfoType::BridgeIps,
  1294. date,
  1295. 104,
  1296. );
  1297. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1298. BridgeInfoType::NegativeReports,
  1299. date,
  1300. 1,
  1301. );
  1302. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1303. BridgeInfoType::PositiveReports,
  1304. date,
  1305. 100,
  1306. );
  1307. // This should not be blocked even though it's very different because
  1308. // it's different in the good direction.
  1309. assert_eq!(
  1310. blocked_in(&analyzer, &bridge_info, confidence, date),
  1311. blocking_countries
  1312. );
  1313. // Data different from previous days:
  1314. // 40 connections, 12 negative reports, 40 positive reports
  1315. date += 1;
  1316. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1317. BridgeInfoType::BridgeIps,
  1318. date,
  1319. 40,
  1320. );
  1321. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1322. BridgeInfoType::NegativeReports,
  1323. date,
  1324. 12,
  1325. );
  1326. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1327. BridgeInfoType::PositiveReports,
  1328. date,
  1329. 40,
  1330. );
  1331. blocking_countries.insert("ru".to_string());
  1332. // This should be blocked because it's different in the bad direction.
  1333. assert_eq!(
  1334. blocked_in(&analyzer, &bridge_info, confidence, date),
  1335. blocking_countries
  1336. );
  1337. }
  1338. {
  1339. let mut date = get_date();
  1340. // New bridge info
  1341. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  1342. bridge_info
  1343. .info_by_country
  1344. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  1345. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  1346. let confidence = 0.95;
  1347. let mut blocking_countries = HashSet::<String>::new();
  1348. // No data today
  1349. assert_eq!(
  1350. blocked_in(&analyzer, &bridge_info, confidence, date),
  1351. blocking_countries
  1352. );
  1353. for i in 1..30 {
  1354. // 9-32 connections, 0-3 negative reports, 16-20 positive reports each day
  1355. date += 1;
  1356. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1357. BridgeInfoType::BridgeIps,
  1358. date,
  1359. 8 * (i % 3 + 2),
  1360. );
  1361. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1362. BridgeInfoType::NegativeReports,
  1363. date,
  1364. i % 4,
  1365. );
  1366. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1367. BridgeInfoType::PositiveReports,
  1368. date,
  1369. 16 + i % 5,
  1370. );
  1371. assert_eq!(
  1372. blocked_in(&analyzer, &bridge_info, confidence, date),
  1373. blocking_countries
  1374. );
  1375. }
  1376. // Data similar to previous days:
  1377. // 24 connections, 2 negative reports, 17 positive reports
  1378. date += 1;
  1379. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1380. BridgeInfoType::BridgeIps,
  1381. date,
  1382. 24,
  1383. );
  1384. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1385. BridgeInfoType::NegativeReports,
  1386. date,
  1387. 2,
  1388. );
  1389. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1390. BridgeInfoType::PositiveReports,
  1391. date,
  1392. 17,
  1393. );
  1394. // Should not be blocked because we have similar data.
  1395. assert_eq!(
  1396. blocked_in(&analyzer, &bridge_info, confidence, date),
  1397. blocking_countries
  1398. );
  1399. // Data different from previous days:
  1400. // 104 connections, 1 negative report, 85 positive reports
  1401. date += 1;
  1402. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1403. BridgeInfoType::BridgeIps,
  1404. date,
  1405. 104,
  1406. );
  1407. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1408. BridgeInfoType::NegativeReports,
  1409. date,
  1410. 1,
  1411. );
  1412. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1413. BridgeInfoType::PositiveReports,
  1414. date,
  1415. 85,
  1416. );
  1417. // This should not be blocked even though it's very different because
  1418. // it's different in the good direction.
  1419. assert_eq!(
  1420. blocked_in(&analyzer, &bridge_info, confidence, date),
  1421. blocking_countries
  1422. );
  1423. // Data different from previous days:
  1424. // 800 connections, 12 negative reports, 750 positive reports
  1425. date += 1;
  1426. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1427. BridgeInfoType::BridgeIps,
  1428. date,
  1429. 800,
  1430. );
  1431. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1432. BridgeInfoType::NegativeReports,
  1433. date,
  1434. 12,
  1435. );
  1436. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1437. BridgeInfoType::PositiveReports,
  1438. date,
  1439. 750,
  1440. );
  1441. blocking_countries.insert("ru".to_string());
  1442. // The censor artificially inflated bridge stats to prevent detection.
  1443. // Ensure we still detect the censorship from negative reports.
  1444. assert_eq!(
  1445. blocked_in(&analyzer, &bridge_info, confidence, date),
  1446. blocking_countries
  1447. );
  1448. }
  1449. {
  1450. let mut date = get_date();
  1451. // New bridge info
  1452. let mut bridge_info = BridgeInfo::new([0; 20], &String::default());
  1453. bridge_info
  1454. .info_by_country
  1455. .insert("ru".to_string(), BridgeCountryInfo::new(date));
  1456. let analyzer = analysis::NormalAnalyzer::new(5, 0.25);
  1457. let confidence = 0.95;
  1458. let mut blocking_countries = HashSet::<String>::new();
  1459. // No data today
  1460. assert_eq!(
  1461. blocked_in(&analyzer, &bridge_info, confidence, date),
  1462. blocking_countries
  1463. );
  1464. for i in 1..30 {
  1465. // 9-32 connections, 0-3 negative reports, 16-20 positive reports each day
  1466. date += 1;
  1467. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1468. BridgeInfoType::BridgeIps,
  1469. date,
  1470. 8 * (i % 3 + 2),
  1471. );
  1472. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1473. BridgeInfoType::NegativeReports,
  1474. date,
  1475. i % 4,
  1476. );
  1477. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1478. BridgeInfoType::PositiveReports,
  1479. date,
  1480. 16 + i % 5,
  1481. );
  1482. assert_eq!(
  1483. blocked_in(&analyzer, &bridge_info, confidence, date),
  1484. blocking_countries
  1485. );
  1486. }
  1487. // Data similar to previous days:
  1488. // 24 connections, 2 negative reports, 17 positive reports
  1489. date += 1;
  1490. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1491. BridgeInfoType::BridgeIps,
  1492. date,
  1493. 24,
  1494. );
  1495. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1496. BridgeInfoType::NegativeReports,
  1497. date,
  1498. 2,
  1499. );
  1500. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1501. BridgeInfoType::PositiveReports,
  1502. date,
  1503. 17,
  1504. );
  1505. // Should not be blocked because we have similar data.
  1506. assert_eq!(
  1507. blocked_in(&analyzer, &bridge_info, confidence, date),
  1508. blocking_countries
  1509. );
  1510. // Data different from previous days:
  1511. // 104 connections, 1 negative report, 100 positive reports
  1512. date += 1;
  1513. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1514. BridgeInfoType::BridgeIps,
  1515. date,
  1516. 104,
  1517. );
  1518. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1519. BridgeInfoType::NegativeReports,
  1520. date,
  1521. 1,
  1522. );
  1523. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1524. BridgeInfoType::PositiveReports,
  1525. date,
  1526. 100,
  1527. );
  1528. // This should not be blocked even though it's very different because
  1529. // it's different in the good direction.
  1530. assert_eq!(
  1531. blocked_in(&analyzer, &bridge_info, confidence, date),
  1532. blocking_countries
  1533. );
  1534. // Data different from previous days:
  1535. // 24 connections, 1 negative report, 1 positive report
  1536. date += 1;
  1537. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1538. BridgeInfoType::BridgeIps,
  1539. date,
  1540. 24,
  1541. );
  1542. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1543. BridgeInfoType::NegativeReports,
  1544. date,
  1545. 1,
  1546. );
  1547. bridge_info.info_by_country.get_mut("ru").unwrap().add_info(
  1548. BridgeInfoType::PositiveReports,
  1549. date,
  1550. 1,
  1551. );
  1552. blocking_countries.insert("ru".to_string());
  1553. // This should be blocked because it's different in the bad direction.
  1554. assert_eq!(
  1555. blocked_in(&analyzer, &bridge_info, confidence, date),
  1556. blocking_countries
  1557. );
  1558. }
  1559. }