123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /*! Unit tests. Note that these require
- https://gitlab.torproject.org/vecna/lox/-/tree/main/crates/lox-distributor
- to be running. That fork adds an endpoint which allows for artificially
- increasing the number of days that have passed, which allows us to test
- trust migration and level up functions. */
- // TODO:
- // - unit test file save/read functions
- // - unit test migration when possible
- // Note: We can't run multiple time-changing tests simultaneously because
- // they will invalidate invites that haven't been redeemed yet.
- use crate::{networking::*, *};
- use lox_library::{
- bridge_table::{self, from_scalar, BridgeLine, BridgeTable},
- cred::Lox,
- proto::{
- level_up::{LEVEL_INTERVAL, LEVEL_INVITATIONS},
- trust_promotion::UNTRUSTED_INTERVAL,
- },
- scalar_u32,
- };
- use array_bytes;
- use sha1::{Digest, Sha1};
- use std::{
- cmp::min,
- collections::{HashMap, HashSet},
- };
- // Advance days on server
- pub async fn advance_days(net: &dyn Networking, days: u16) -> u32 {
- let resp = net
- .request(
- "/advancedays".to_string(),
- serde_json::to_vec(&days).unwrap(),
- )
- .await
- .unwrap();
- let today: u32 = serde_json::from_slice(&resp).unwrap();
- today
- }
- // Advance days and level up
- pub async fn test_level_up(
- net: &dyn Networking,
- net_test: &dyn Networking,
- cred: &Lox,
- la_pubkeys: &Vec<IssuerPubKey>,
- ) -> Lox {
- let level = scalar_u32(&cred.trust_level).unwrap();
- advance_days(
- net_test,
- u16::try_from(LEVEL_INTERVAL[usize::try_from(level).unwrap()]).unwrap(),
- )
- .await;
- assert!(eligible_for_level_up(net, cred).await);
- let (bucket, reachcred) = get_bucket(net, cred).await.unwrap();
- println!("Leveling up from level {} to {}", level, min(4, level + 1));
- let new_cred = level_up(
- net,
- cred,
- &reachcred.unwrap(),
- get_lox_pub(la_pubkeys),
- get_reachability_pub(la_pubkeys),
- )
- .await
- .unwrap();
- new_cred
- }
- // These are all combined into the same test because otherwise we run into
- // issues with server state due to asynchronicity.
- #[tokio::test]
- async fn test_credential_operations() {
- let net = HyperNet {
- hostname: "http://localhost:8001".to_string(),
- };
- let net_test = HyperNet {
- hostname: "http://localhost:8005".to_string(),
- };
- let la_pubkeys = get_lox_auth_keys(&net).await.unwrap();
- // Get new Lox credential
- println!("Getting new open-entry Lox credential");
- let open_inv = get_open_invitation(&net).await.unwrap();
- let (mut cred, bridgeline) = get_lox_credential(&net, &open_inv, get_lox_pub(&la_pubkeys))
- .await
- .unwrap();
- let bucket = get_bucket(&net, &cred).await.unwrap().0;
- //assert_eq!(bucket[0], bridgeline); // For some reason, this sometimes fails.
- assert_eq!(bucket[1], BridgeLine::default());
- assert_eq!(bucket[2], BridgeLine::default());
- // Level up Lox Credential
- println!("Leveling up to level 1");
- assert_eq!(scalar_u32(&cred.trust_level).unwrap(), 0);
- // Advance server time and trust migrate
- advance_days(&net_test, u16::try_from(UNTRUSTED_INTERVAL).unwrap()).await;
- assert!(eligible_for_trust_promotion(&net, &cred).await);
- let migration_cred = trust_promotion(&net, &cred, get_lox_pub(&la_pubkeys))
- .await
- .unwrap();
- cred = trust_migration(
- &net,
- &cred,
- &migration_cred,
- get_lox_pub(&la_pubkeys),
- get_migration_pub(&la_pubkeys),
- )
- .await
- .unwrap();
- assert_eq!(scalar_u32(&cred.trust_level).unwrap(), 1);
- // Advance server time and level up
- for i in 1..LEVEL_INTERVAL.len() {
- println!("Leveling up to level {}", min(i, LEVEL_INTERVAL.len() - 1));
- assert_eq!(
- scalar_u32(&cred.trust_level).unwrap(),
- u32::try_from(i).unwrap()
- );
- cred = test_level_up(&net, &net_test, &cred, &la_pubkeys).await;
- // Assert that we increased level by 1 or stayed at 4
- assert_eq!(
- scalar_u32(&cred.trust_level).unwrap(),
- u32::try_from(min(i + 1, LEVEL_INTERVAL.len() - 1)).unwrap()
- );
- assert_eq!(
- scalar_u32(&cred.invites_remaining).unwrap(),
- LEVEL_INVITATIONS[i]
- );
- // Invite as many friends as possible
- for j in 0..LEVEL_INVITATIONS[i] {
- println!("Inviting friend {}", j);
- let encbuckets = get_reachability_credential(&net).await.unwrap();
- let (new_cred, invite) = issue_invite(
- &net,
- &cred,
- &encbuckets,
- get_lox_pub(&la_pubkeys),
- get_reachability_pub(&la_pubkeys),
- get_invitation_pub(&la_pubkeys),
- )
- .await
- .unwrap();
- let (friend_cred, friend_bucket) = redeem_invite(
- &net,
- &invite,
- get_lox_pub(&la_pubkeys),
- get_invitation_pub(&la_pubkeys),
- )
- .await
- .unwrap();
- cred = new_cred;
- assert_eq!(
- scalar_u32(&cred.invites_remaining).unwrap(),
- LEVEL_INVITATIONS[i] - j - 1
- );
- assert_eq!(scalar_u32(&friend_cred.trust_level).unwrap(), 1);
- assert_eq!(&cred.bucket, &friend_cred.bucket);
- }
- }
- let net_tp = HyperNet {
- hostname: "http://localhost:8002".to_string(),
- };
- // helper function to create map of bridges from bucket to mark blocked
- fn bridges_to_block(
- bucket: [BridgeLine; bridge_table::MAX_BRIDGES_PER_BUCKET],
- num_bridges_to_block: usize,
- ) -> HashMap<String, HashSet<String>> {
- let mut blocked_bridges = HashMap::<String, HashSet<String>>::new();
- for i in 0..num_bridges_to_block {
- let mut hasher = Sha1::new();
- hasher.update(bucket[i].fingerprint);
- let mut countries = HashSet::<String>::new();
- countries.insert("RU".to_string());
- blocked_bridges.insert(array_bytes::bytes2hex("", hasher.finalize()), countries);
- }
- blocked_bridges
- }
- // Block 1 bridge
- println!("Marking one bridge blocked");
- let bridges = get_bucket(&net, &cred).await.unwrap().0;
- let blocked_bridges = bridges_to_block(bridges, 1);
- let response = net_tp
- .request(
- "/reportblocked".to_string(),
- serde_json::to_string(&blocked_bridges).unwrap().into(),
- )
- .await
- .unwrap();
- assert_eq!(String::from_utf8(response).unwrap(), "OK");
- // Time passes...
- advance_days(&net_test, 1).await;
- // Check that we still have a Bucket Reachability credential
- let (bucket, reachcred) = get_bucket(&net, &cred).await.unwrap();
- assert!(reachcred.is_some());
- println!("Can still obtain bucket reachability credential");
- // Block 2 bridges
- println!("Marking two bridges blocked");
- let bridges = get_bucket(&net, &cred).await.unwrap().0;
- let blocked_bridges = bridges_to_block(bridges, 2);
- let response = net_tp
- .request(
- "/reportblocked".to_string(),
- serde_json::to_string(&blocked_bridges).unwrap().into(),
- )
- .await
- .unwrap();
- assert_eq!(String::from_utf8(response).unwrap(), "OK");
- // Time passes...
- advance_days(&net_test, 1).await;
- // Check that we don't have a Bucket Reachability credential
- let (bucket, reachcred) = get_bucket(&net, &cred).await.unwrap();
- assert!(reachcred.is_none());
- println!("Cannot obtain bucket reachability credential");
- // Migrate to a new bucket
- println!("Migrating to a new bucket");
- let migration_cred = check_blockage(&net, &cred, get_lox_pub(&la_pubkeys))
- .await
- .unwrap();
- cred = blockage_migration(
- &net,
- &cred,
- &migration_cred,
- get_lox_pub(&la_pubkeys),
- get_migration_pub(&la_pubkeys),
- )
- .await
- .unwrap();
- assert_eq!(scalar_u32(&cred.trust_level).unwrap(), 2);
- assert_eq!(scalar_u32(&cred.blockages).unwrap(), 1);
- // Level up to level 3
- cred = test_level_up(&net, &net_test, &cred, &la_pubkeys).await;
- assert_eq!(scalar_u32(&cred.trust_level).unwrap(), 3);
- assert_eq!(scalar_u32(&cred.blockages).unwrap(), 1);
- // Another blockage happens
- println!("Marking three bridges blocked");
- let bridges = get_bucket(&net, &cred).await.unwrap().0;
- let blocked_bridges = bridges_to_block(bridges, 3);
- let response = net_tp
- .request(
- "/reportblocked".to_string(),
- serde_json::to_string(&blocked_bridges).unwrap().into(),
- )
- .await
- .unwrap();
- assert_eq!(String::from_utf8(response).unwrap(), "OK");
- // Time passes...
- advance_days(&net_test, 1).await;
- // Migrate again
- println!("Migrating to a new bucket");
- let migration_cred = check_blockage(&net, &cred, get_lox_pub(&la_pubkeys))
- .await
- .unwrap();
- cred = blockage_migration(
- &net,
- &cred,
- &migration_cred,
- get_lox_pub(&la_pubkeys),
- get_migration_pub(&la_pubkeys),
- )
- .await
- .unwrap();
- assert_eq!(scalar_u32(&cred.trust_level).unwrap(), 1);
- // Level up to level 2
- cred = test_level_up(&net, &net_test, &cred, &la_pubkeys).await;
- // Can't level up to level 3
- assert!(!eligible_for_level_up(&net, &cred).await);
- }
|