Fixup and add comments, README to Lox Library

This commit is contained in:
onyinyang 2023-11-07 18:44:25 -05:00
parent 73b04b9a19
commit e356034dae
No known key found for this signature in database
GPG Key ID: 156A6435430C2036
8 changed files with 112 additions and 59 deletions

View File

@ -9,10 +9,10 @@
}, },
"rtype": { "rtype": {
"endpoint": "http://127.0.0.1:7100/resources", "endpoint": "http://127.0.0.1:7100/resources",
"name": "https", "name": "lox",
"token": "HttpsApiTokenPlaceholder", "token": "LoxApiTokenPlaceholder",
"types": [ "types": [
"obfs2", "obfs4",
"scramblesuit" "scramblesuit"
] ]
} }

View File

@ -1,8 +1,40 @@
# Lox # Lox
Lox is a reputation-based bridge distribution system that provides privacy protection to users and their social graph and is open to all users. Lox is a reputation-based bridge distribution system that provides privacy protection to users and their social graph and is open to all users.
Lox is written in rust and requires `cargo` to test. [Install Rust](https://www.rust-lang.org/tools/install). We used Rust version 1.56.0.
Note that this implementation is coded such that the reachability certificate expires at 00:00 UTC. In reality, if the bucket is still reachable, a user could simply request a new reachability token if their request fails for this reason (a new certificate should be available prior to the outdated certificate expiring). The protocols in the Lox-library are consistent with the Lox system described
in [Tulloch and Goldberg](https://petsymposium.org/popets/2023/popets-2023-0029.php) (and in greater detail [here](https://uwspace.uwaterloo.ca/handle/10012/18333)). However, this implementation may diverge from the theory over time as the system is deployed and its limitations are better illuminated. The [original version of this library](https://git-crysp.uwaterloo.ca/iang/lox) will remain a more precise implementation of the theory.
Lox is written in rust and requires `cargo` to test. [Install Rust](https://www.rust-lang.org/tools/install). We used Rust version 1.65.0.
## Notable Changes from the original repository
Some changes have been made to integrate the existing Lox protocols with Tor's
bridge distributor [rdsys](https://gitlab.torproject.org/tpo/anti-censorship/rdsys),
but so far, these have not affected the Lox protocols themselves.
These changes are necessary to keep the consistentcy of bridges in buckets that Lox requires while working with the reality of how rdsys/Tor currently receives and distributes information about bridges. The changes to Lox are:
1. Add a `uid_fingerprint` field to the BridgeLine which helps with bridge lookup and
corresponds (roughly) to the unique fingerprint rdsys gives to each bridge
(made up of a hash of the IP and pluggable transport type)
2. Allow for the details of a bridge to be updated. This has been added to
[`crates/lox-library/src/lib.rs`](https://gitlab.torproject.org/tpo/anti-censorship/lox-rs/-/blob/main/crates/lox-library/src/lib.rs) and accounts for the fact that some details
of an existing bridge (i.e., that has a matching fingerprint) may be updated
from time to time.
3. Allow for a bridge to be replaced without penalty. This has also been added to
[`crates/lox-library/src/lib.rs`](https://gitlab.torproject.org/tpo/anti-censorship/lox-rs/-/blob/main/crates/lox-library/src/lib.rs)
and accounts for the fact that Tor currently does not have a robust way of
[knowing that a bridge is blocked](https://gitlab.torproject.org/tpo/anti-censorship/censorship-analysis/-/issues/40035), but does have some tests (namely,
[bridgestrap](https://gitlab.torproject.org/tpo/anti-censorship/bridgestrap) and [onbasca](https://gitlab.torproject.org/tpo/network-health/onbasca)) that help to determine if a
bridge should not be distributed. Since we do not know if the results of
these tests indicate a blocking event, we are allowing for bridges that
rdsys marks as unsuitable for distribution to be updated without penalty in the Lox library.
4. The vectors within `bridge_table.rs` have been refactored into HashMaps that use a unique `u32` for lookup. This has led to a
number of changes around how bridges are inserted/removed from the bridge table but does not impact the overall functionality of the Lox system.
5. The `DupFilter` has been changed from a `HashMap` to a `HashSet`, primarily because this is easier to Serialize/Deserialize when storing the state of the Lox system to recover from failure or to be able to roll back to a previous state.
6. The [`dalek-cryptography`](https://dalek.rs/) libraries have been updated to their most recent versions and the `zkp` library has been forked (until/unless this is fixed in one of the existing upstream repos) to fix a bug that appears when a public attribute is set to 0 (previously impacting only the blockage migration protocol when a user's invitations are set to 0 after migrating). The fork of `zkp` also includes similar updates to `dalek-cryptography` dependencies and some others such as `rand`.
7. Many tests that were used to create the Lox paper/thesis and measure the performance of the system were removed from this repository as they are unnecessary in a deployment scenario. They are still available in the [original repository](https://git-crysp.uwaterloo.ca/iang/lox).
### Other important Notes
As with the original implementation, this implementation is coded such that the reachability certificate expires at 00:00 UTC. Therefore, if an unlucky user requests a reachability certificate just before the 00:00 UTC and tries to use it just after, the request will fail. If the bucket is still reachable, a user can simply request a new reachability token if their request fails for this reason (a new certificate should be available prior to the outdated certificate expiring).

View File

@ -240,41 +240,46 @@ struct K {
/// A BridgeTable is the internal structure holding the buckets /// A BridgeTable is the internal structure holding the buckets
/// containing the bridges, the keys used to encrypt the buckets, and /// containing the bridges, the keys used to encrypt the buckets, and
/// the encrypted buckets. The encrypted buckets will be exposed to the /// the encrypted buckets. The encrypted buckets will be exposed to the
/// users of the system, and each user credential will contain the /// users of the system, and each user credential will contain the
/// decryption key for one bucket. /// decryption key for one bucket.
#[serde_as] #[serde_as]
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
pub struct BridgeTable { pub struct BridgeTable {
// All structures in the bridgetable are indexed by counter /// All structures in the bridgetable are indexed by counter
pub counter: u32, pub counter: u32,
/// The keys of all buckets, indexed by counter, that are still part of the bridge table.
pub keys: HashMap<u32, [u8; 16]>, pub keys: HashMap<u32, [u8; 16]>,
/// All buckets, indexed by counter corresponding to the key above, that are
/// part of the bridge table.
pub buckets: HashMap<u32, [BridgeLine; MAX_BRIDGES_PER_BUCKET]>, pub buckets: HashMap<u32, [BridgeLine; MAX_BRIDGES_PER_BUCKET]>,
pub encbuckets: HashMap<u32, EncryptedBucket>, pub encbuckets: HashMap<u32, EncryptedBucket>,
/// Individual bridges that are reachable /// Individual bridges that are reachable.
#[serde_as(as = "HashMap<serde_with::json::JsonString, _>")] #[serde_as(as = "HashMap<serde_with::json::JsonString, _>")]
pub reachable: HashMap<BridgeLine, Vec<(u32, usize)>>, pub reachable: HashMap<BridgeLine, Vec<(u32, usize)>>,
/// bucket ids of "hot spare" buckets. These buckets are not handed /// Bucket ids of "hot spare" buckets. These buckets are not handed
/// to users, nor do they have any Migration credentials pointing to /// to users, nor do they have any Migration credentials pointing to
/// them. When a new Migration credential is needed, a bucket is /// them. When a new Migration credential is needed, a bucket is
/// removed from this set and used for that purpose. /// removed from this set and used for that purpose.
pub spares: HashSet<u32>, pub spares: HashSet<u32>,
/// In some instances a single bridge may need to be added to a bucket /// In some instances a single bridge may need to be added to a bucket as a replacement
/// In that case, a spare bucket will be removed from the set of spare bridges. One /// or otherwise. In that case, a spare bucket will be removed from the set of spares, one
/// bridge will be used as the replacement and the left over bridges will be appended to
/// unallocated_bridges.
pub unallocated_bridges: Vec<BridgeLine>, pub unallocated_bridges: Vec<BridgeLine>,
// To prevent issues with a counter for the hashmap keys, we keep a list of keys that // To prevent issues with the counter for the hashmap keys, keep a list of keys that
// no longer match any buckets that can be used before increasing the counter // no longer match any buckets that can be used before increasing the counter.
pub recycleable_keys: Vec<u32>, pub recycleable_keys: Vec<u32>,
// We maintain a list of keys that have been blocked (bucket_id: u32), as well as the // A list of keys that have been blocked (bucket_id: u32), as well as the
// time (julian_date: u32) of their blocking so that they can be repurposed with new // time (julian_date: u32) of their blocking so that they can be repurposed with new
// buckets after the EXPIRY_DATE // buckets after the EXPIRY_DATE.
pub blocked_keys: Vec<(u32, u32)>, pub blocked_keys: Vec<(u32, u32)>,
// Similarly, we maintain a list of open entry buckets (bucket_id: u32) and the time they were // Similarly, a list of open entry buckets (bucket_id: u32) and the time they were
// created (julian_date: u32) so they will be listed as expired after the EXPIRY_DATE // created (julian_date: u32) so they will be listed as expired after the EXPIRY_DATE.
// TODO: add open entry buckets to the open_inv_keys only once they have been distributed // TODO: add open entry buckets to the open_inv_keys only once they have been distributed
pub open_inv_keys: Vec<(u32, u32)>, pub open_inv_keys: Vec<(u32, u32)>,
/// The date the buckets were last encrypted to make the encbucket. /// The date the buckets were last encrypted to make the encbucket.
/// The encbucket must be rebuilt each day so that the Bucket /// The encbucket must be rebuilt at least each day so that the Bucket
/// Reachability credentials in the buckets can be refreshed. /// Reachability credentials in the buckets can be refreshed.
pub date_last_enc: u32, pub date_last_enc: u32,
} }
@ -288,7 +293,7 @@ impl BridgeTable {
self.buckets.len() self.buckets.len()
} }
/// Append a new bucket to the bridge table, returning its index /// Insert a new bucket into the bridge table, returning its index
pub fn new_bucket(&mut self, index: u32, bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET]) { pub fn new_bucket(&mut self, index: u32, bucket: &[BridgeLine; MAX_BRIDGES_PER_BUCKET]) {
// Pick a random key to encrypt this bucket // Pick a random key to encrypt this bucket
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
@ -311,7 +316,7 @@ impl BridgeTable {
} }
/// Create the vector of encrypted buckets from the keys and buckets /// Create the vector of encrypted buckets from the keys and buckets
/// in the BridgeTable. All of the entries will be (randomly) /// in the BridgeTable. All of the entries will be (randomly)
/// re-encrypted, so it will be hidden whether any individual bucket /// re-encrypted, so it will be hidden whether any individual bucket
/// has changed (except for entirely new buckets, of course). /// has changed (except for entirely new buckets, of course).
/// Bucket Reachability credentials are added to the buckets when /// Bucket Reachability credentials are added to the buckets when

View File

@ -1,8 +1,8 @@
/*! The various credentials used by the system. /*! The various credentials used by the system.
In each case, (P,Q) forms the MAC on the credential. This MAC is In each case, (P,Q) forms the MAC on the credential. This MAC is
verifiable only by the issuing party, or if the issuing party issues a verifiable only by the issuing party, or if the issuing party issues a
zero-knowledge proof of its correctness (as it does at issuing time). */ zero-knowledge proof of its correctness (as it does at issuing time).*/
use curve25519_dalek::ristretto::RistrettoPoint; use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar; use curve25519_dalek::scalar::Scalar;
@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
/// A migration credential. /// A migration credential.
/// ///
/// This credential authorizes the holder of the Lox credential with the /// This credential authorizes the holder of the Lox credential with the
/// given id to switch from bucket from_bucket to bucket to_bucket. The /// given id to switch from bucket from_bucket to bucket to_bucket. The
/// migration_type attribute is 0 for trust upgrade migrations (moving /// migration_type attribute is 0 for trust upgrade migrations (moving
/// from a 1-bridge untrusted bucket to a 3-bridge trusted bucket) and 1 /// from a 1-bridge untrusted bucket to a 3-bridge trusted bucket) and 1
/// for blockage migrations (moving buckets because the from_bucket has /// for blockage migrations (moving buckets because the from_bucket has
@ -29,7 +29,7 @@ pub struct Migration {
/// The main user credential in the Lox system. /// The main user credential in the Lox system.
/// ///
/// Its id is jointly generated by the user and the BA (bridge /// Its id is jointly generated by the user and the BA (bridge
/// authority), but known only to the user. The level_since date is the /// authority), but known only to the user. The level_since date is the
/// Julian date of when this user was changed to the current trust /// Julian date of when this user was changed to the current trust
/// level. /// level.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -46,13 +46,13 @@ pub struct Lox {
/// The migration key credential. /// The migration key credential.
/// ///
/// This credential is never actually instantiated. It is an implicit /// This credential is never actually instantiated. It is an implicit
/// credential on attributes lox_id and from_bucket. This credential /// credential on attributes lox_id and from_bucket. This credential
/// type does have an associated private and public key, however. The /// type does have an associated private and public key, however. The
/// idea is that if a user proves (in zero knowledge) that their Lox /// idea is that if a user proves (in zero knowledge) that their Lox
/// credential entitles them to migrate from one bucket to another, the /// credential entitles them to migrate from one bucket to another, the
/// BA will issue a (blinded, so the BA will not know the values of the /// BA will issue a (blinded, so the BA will not know the values of the
/// attributes or of Q) MAC on this implicit credential. The Q value /// attributes or of Q) MAC on this implicit credential. The Q value
/// will then be used (actually, a hash of lox_id, from_bucket, and Q) /// will then be used (actually, a hash of lox_id, from_bucket, and Q)
/// to encrypt the to_bucket, P, and Q fields of a Migration credential. /// to encrypt the to_bucket, P, and Q fields of a Migration credential.
/// That way, people entitled to migrate buckets can receive a Migration /// That way, people entitled to migrate buckets can receive a Migration
@ -70,7 +70,7 @@ pub struct MigrationKey {
/// ///
/// Each day, a credential of this type is put in each bucket that has /// Each day, a credential of this type is put in each bucket that has
/// at least a (configurable) threshold number of bridges that have not /// at least a (configurable) threshold number of bridges that have not
/// been blocked as of the given date. Users can present this /// been blocked as of the given date. Users can present this
/// credential (in zero knowledge) with today's date to prove that the /// credential (in zero knowledge) with today's date to prove that the
/// bridges in their bucket have not been blocked, in order to gain a /// bridges in their bucket have not been blocked, in order to gain a
/// trust level. /// trust level.
@ -86,7 +86,7 @@ pub struct BucketReachability {
/// ///
/// These credentials allow a Lox user (the inviter) of sufficient trust /// These credentials allow a Lox user (the inviter) of sufficient trust
/// (level 2 or higher) to invite someone else (the invitee) to join the /// (level 2 or higher) to invite someone else (the invitee) to join the
/// system. The invitee ends up at trust level 1, in the _same bucket_ /// system. The invitee ends up at trust level 1, in the _same bucket_
/// as the inviter, and inherits the inviter's blockages count (so that /// as the inviter, and inherits the inviter's blockages count (so that
/// you can't clear your blockages count simply by inviting yourself). /// you can't clear your blockages count simply by inviting yourself).
/// Invitations expire after some amount of time. /// Invitations expire after some amount of time.

View File

@ -10,7 +10,7 @@ use std::hash::Hash;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Each instance of DupFilter maintains its own independent table of /// Each instance of DupFilter maintains its own independent table of
/// seen ids. IdType will typically be Scalar. /// seen ids. IdType will typically be Scalar.
#[derive(Default, Debug, Serialize, Deserialize)] #[derive(Default, Debug, Serialize, Deserialize)]
pub struct DupFilter<IdType: Hash + Eq + Copy + Serialize> { pub struct DupFilter<IdType: Hash + Eq + Copy + Serialize> {
seen_table: HashSet<IdType>, seen_table: HashSet<IdType>,

View File

@ -8,7 +8,7 @@ Keyed-Verification Anonymous Credentials" (Chase, Meiklejohn, and
Zaverucha, CCS 2014) Zaverucha, CCS 2014)
The notation follows that of the paper "Hyphae: Social Secret Sharing" The notation follows that of the paper "Hyphae: Social Secret Sharing"
(Lovecruft and de Valence, 2017), Section 4. */ (Lovecruft and de Valence, 2017), Section 4. */
// We really want points to be capital letters and scalars to be // We really want points to be capital letters and scalars to be
// lowercase letters // lowercase letters
@ -62,10 +62,12 @@ lazy_static! {
} }
// EXPIRY_DATE is set to EXPIRY_DATE days for open-entry and blocked buckets in order to match // EXPIRY_DATE is set to EXPIRY_DATE days for open-entry and blocked buckets in order to match
// the expiry date for Lox credentials. This particular value (EXPIRY_DATE) is chosen because // the expiry date for Lox credentials.This particular value (EXPIRY_DATE) is chosen because
// values that are 2^k 1 make range proofs more efficient, but this can be changed to any value // values that are 2^k 1 make range proofs more efficient, but this can be changed to any value
pub const EXPIRY_DATE: u32 = 511; pub const EXPIRY_DATE: u32 = 511;
/// ReplaceSuccess sends a signal to the lox-distributor to inform
/// whether or not a bridge was successfully replaced
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub enum ReplaceSuccess { pub enum ReplaceSuccess {
NotFound = 0, NotFound = 0,
@ -73,18 +75,23 @@ pub enum ReplaceSuccess {
Replaced = 2, Replaced = 2,
} }
/// This error is thrown if the number of buckets/keys in the bridge table
/// exceeds u32 MAX.It is unlikely this error will ever occur.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum NoAvailableIDError { pub enum NoAvailableIDError {
#[error("Find key exhausted with no available index found!")] #[error("Find key exhausted with no available index found!")]
ExhaustedIndexer, ExhaustedIndexer,
} }
/// This error is thrown after the MAX_DAILY_BRIDGES threshold for bridges
/// distributed in a day has been reached
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum ExceededMaxBridgesError { pub enum ExceededMaxBridgesError {
#[error("The maximum number of bridges has already been distributed today, please try again tomorrow!")] #[error("The maximum number of bridges has already been distributed today, please try again tomorrow!")]
ExceededMaxBridges, ExceededMaxBridges,
} }
/// Private Key of the Issuer
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IssuerPrivKey { pub struct IssuerPrivKey {
x0tilde: Scalar, x0tilde: Scalar,
@ -106,11 +113,13 @@ impl IssuerPrivKey {
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct IssuerPubKey { pub struct IssuerPubKey {
X: Vec<RistrettoPoint>, X: Vec<RistrettoPoint>,
} }
/// Public Key of the Issuer
impl IssuerPubKey { impl IssuerPubKey {
/// Create an IssuerPubKey from the corresponding IssuerPrivKey /// Create an IssuerPubKey from the corresponding IssuerPrivKey
pub fn new(privkey: &IssuerPrivKey) -> IssuerPubKey { pub fn new(privkey: &IssuerPrivKey) -> IssuerPubKey {
@ -130,11 +139,11 @@ impl IssuerPubKey {
} }
} }
// Number of times a given invitation is ditributed /// Number of times a given invitation is ditributed
pub const OPENINV_K: u32 = 10; pub const OPENINV_K: u32 = 10;
// TODO: Decide on maximum daily number of invitations to be distributed /// TODO: Decide on maximum daily number of invitations to be distributed
pub const MAX_DAILY_BRIDGES: u32 = 100; pub const MAX_DAILY_BRIDGES: u32 = 100;
/// The BridgeDb. This will typically be a singleton object. The /// The BridgeDb. This will typically be a singleton object. The
/// BridgeDb's role is simply to issue signed "open invitations" to /// BridgeDb's role is simply to issue signed "open invitations" to
/// people who are not yet part of the system. /// people who are not yet part of the system.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -188,6 +197,8 @@ impl BridgeDb {
self.openinv_buckets.remove(bucket); self.openinv_buckets.remove(bucket);
} }
/// Remove open invitation and/or otherwise distributed buckets that have
/// become blocked or are expired to free up the index for a new bucket
pub fn remove_blocked_or_expired_buckets(&mut self, bucket: &u32) { pub fn remove_blocked_or_expired_buckets(&mut self, bucket: &u32) {
if self.openinv_buckets.contains(bucket) { if self.openinv_buckets.contains(bucket) {
println!("Removing a bucket that has not been distributed yet!"); println!("Removing a bucket that has not been distributed yet!");
@ -197,6 +208,7 @@ impl BridgeDb {
} }
} }
/// Mark a bucket as distributed
pub fn mark_distributed(&mut self, bucket: u32) { pub fn mark_distributed(&mut self, bucket: u32) {
self.distributed_buckets.push(bucket); self.distributed_buckets.push(bucket);
} }
@ -239,8 +251,8 @@ impl BridgeDb {
} }
} }
/// Verify an open invitation. Returns the invitation id and the /// Verify an open invitation. Returns the invitation id and the
/// bucket number if the signature checked out. It is up to the /// bucket number if the signature checked out. It is up to the
/// caller to then check that the invitation id has not been used /// caller to then check that the invitation id has not been used
/// before. /// before.
pub fn verify( pub fn verify(
@ -250,7 +262,7 @@ impl BridgeDb {
// Pull out the signature and verify it // Pull out the signature and verify it
let sig = Signature::try_from(&invitation[(32 + 4)..])?; let sig = Signature::try_from(&invitation[(32 + 4)..])?;
pubkey.verify(&invitation[0..(32 + 4)], &sig)?; pubkey.verify(&invitation[0..(32 + 4)], &sig)?;
// The signature passed. Pull out the bucket number and then // The signature passed. Pull out the bucket number and then
// the invitation id // the invitation id
let bucket = u32::from_le_bytes(invitation[32..(32 + 4)].try_into().unwrap()); let bucket = u32::from_le_bytes(invitation[32..(32 + 4)].try_into().unwrap());
let s = Scalar::from_canonical_bytes(invitation[0..32].try_into().unwrap()); let s = Scalar::from_canonical_bytes(invitation[0..32].try_into().unwrap());
@ -270,7 +282,7 @@ impl Default for BridgeDb {
} }
} }
/// The bridge authority. This will typically be a singleton object. /// The bridge authority. This will typically be a singleton object.
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct BridgeAuth { pub struct BridgeAuth {
/// The private key for the main Lox credential /// The private key for the main Lox credential
@ -363,7 +375,7 @@ impl BridgeAuth {
/// Insert a set of open invitation bridges. /// Insert a set of open invitation bridges.
/// ///
/// Each of the bridges will be given its own open invitation /// Each of the bridges will be given its own open invitation
/// bucket, and the BridgeDb will be informed. A single bucket /// bucket, and the BridgeDb will be informed. A single bucket
/// containing all of the bridges will also be created, with a trust /// containing all of the bridges will also be created, with a trust
/// upgrade migration from each of the single-bridge buckets. /// upgrade migration from each of the single-bridge buckets.
pub fn add_openinv_bridges( pub fn add_openinv_bridges(
@ -406,6 +418,9 @@ impl BridgeAuth {
Ok(()) Ok(())
} }
/// When syncing the Lox bridge table with rdsys, this function returns any bridges
/// that are found in the Lox bridge table that are not found in the Vector
/// of bridges received from rdsys through the Lox distributor.
pub fn find_and_remove_unaccounted_for_bridges( pub fn find_and_remove_unaccounted_for_bridges(
&mut self, &mut self,
accounted_for_bridges: Vec<u64>, accounted_for_bridges: Vec<u64>,
@ -419,6 +434,7 @@ impl BridgeAuth {
unaccounted_for unaccounted_for
} }
/// Allocate single left over bridges to an open invitation bucket
pub fn allocate_bridges( pub fn allocate_bridges(
&mut self, &mut self,
distributor_bridges: &mut Vec<BridgeLine>, distributor_bridges: &mut Vec<BridgeLine>,
@ -447,12 +463,10 @@ impl BridgeAuth {
// Update the details of a bridge in the bridge table. This assumes that the IP and Port // Update the details of a bridge in the bridge table. This assumes that the IP and Port
// of a given bridge remains the same and thus can be updated. // of a given bridge remains the same and thus can be updated.
// First we must retrieve the list of reachable bridges, then we must search for any matching our partial key // First we must retrieve the list of reachable bridges, then we must search for any matching our partial key
// which will include the IP and Port. Then we can replace the original bridge with the updated bridge // which will include the IP and Port. Finally we can replace the original bridge with the updated bridge.
// Returns true if the bridge has successfully updated // Returns true if the bridge has successfully updated
pub fn bridge_update(&mut self, bridge: &BridgeLine) -> bool { pub fn bridge_update(&mut self, bridge: &BridgeLine) -> bool {
let mut res: bool = false; //default False to assume that update failed let mut res: bool = false; //default False to assume that update failed
//Needs to be updated since bridge will only match on some fields.
let reachable_bridges = self.bridge_table.reachable.clone(); let reachable_bridges = self.bridge_table.reachable.clone();
for reachable_bridge in reachable_bridges { for reachable_bridge in reachable_bridges {
if reachable_bridge.0.uid_fingerprint == bridge.uid_fingerprint { if reachable_bridge.0.uid_fingerprint == bridge.uid_fingerprint {
@ -490,6 +504,8 @@ impl BridgeAuth {
res res
} }
/// Attempt to remove a bridge that is failing tests and replace it with a bridge from
/// available_bridge or from a spare bucket
pub fn bridge_replace( pub fn bridge_replace(
&mut self, &mut self,
bridge: &BridgeLine, bridge: &BridgeLine,
@ -591,13 +607,13 @@ impl BridgeAuth {
/// Mark a bridge as unreachable /// Mark a bridge as unreachable
/// ///
/// This bridge will be removed from each of the buckets that /// This bridge will be removed from each of the buckets that
/// contains it. If any of those are open-invitation buckets, the /// contains it. If any of those are open-invitation buckets, the
/// trust upgrade migration for that bucket will be removed and the /// trust upgrade migration for that bucket will be removed and the
/// BridgeDb will be informed to stop handing out that bridge. If /// BridgeDb will be informed to stop handing out that bridge. If
/// any of those are trusted buckets where the number of reachable /// any of those are trusted buckets where the number of reachable
/// bridges has fallen below the threshold, a blockage migration /// bridges has fallen below the threshold, a blockage migration
/// from that bucket to a spare bucket will be added, and the spare /// from that bucket to a spare bucket will be added, and the spare
/// bucket will be removed from the list of hot spares. In /// bucket will be removed from the list of hot spares. In
/// addition, if the blocked bucket was the _target_ of a blockage /// addition, if the blocked bucket was the _target_ of a blockage
/// migration, change the target to the new (formerly spare) bucket. /// migration, change the target to the new (formerly spare) bucket.
/// Returns true if sucessful, or false if it needed a hot spare but /// Returns true if sucessful, or false if it needed a hot spare but
@ -647,9 +663,9 @@ impl BridgeAuth {
continue; continue;
} }
// This bucket is now unreachable. Get a spare bucket // This bucket is now unreachable. Get a spare bucket
if self.bridge_table.spares.is_empty() { if self.bridge_table.spares.is_empty() {
// Uh, oh. No spares available. Just delete any // Uh, oh. No spares available. Just delete any
// migrations leading to this bucket. // migrations leading to this bucket.
res = false; res = false;
self.trustup_migration_table self.trustup_migration_table
@ -692,7 +708,7 @@ impl BridgeAuth {
} }
// Since buckets are moved around in the bridge_table, finding a lookup key that // Since buckets are moved around in the bridge_table, finding a lookup key that
// does not overwrite existing bridges could become an issue. We keep a list // does not overwrite existing bridges could become an issue.We keep a list
// of recycleable lookup keys from buckets that have been removed and prioritize // of recycleable lookup keys from buckets that have been removed and prioritize
// this list before increasing the counter // this list before increasing the counter
fn find_next_available_key(&mut self, bdb: &mut BridgeDb) -> Result<u32, NoAvailableIDError> { fn find_next_available_key(&mut self, bdb: &mut BridgeDb) -> Result<u32, NoAvailableIDError> {
@ -728,6 +744,7 @@ impl BridgeAuth {
self.clean_up_open_entry(bdb); self.clean_up_open_entry(bdb);
} }
/// Cleans up exipred blocked buckets
fn clean_up_blocked(&mut self) { fn clean_up_blocked(&mut self) {
if !self.bridge_table.blocked_keys.is_empty() if !self.bridge_table.blocked_keys.is_empty()
&& self && self
@ -754,7 +771,7 @@ impl BridgeAuth {
if bridgeline.port > 0 { if bridgeline.port > 0 {
// Move to unallocated bridges // Move to unallocated bridges
self.bridge_table.unallocated_bridges.push(*bridgeline); self.bridge_table.unallocated_bridges.push(*bridgeline);
// Check if it's still in the reachable bridges. It should be if we've gotten this far. // Check if it's still in the reachable bridges.It should be if we've gotten this far.
if let Some(_reachable_indexes_for_bridgeline) = if let Some(_reachable_indexes_for_bridgeline) =
self.bridge_table.reachable.get(bridgeline) self.bridge_table.reachable.get(bridgeline)
{ {
@ -780,6 +797,7 @@ impl BridgeAuth {
} }
} }
/// Cleans up expired open invitation buckets
fn clean_up_open_entry(&mut self, bdb: &mut BridgeDb) { fn clean_up_open_entry(&mut self, bdb: &mut BridgeDb) {
// First check if there are any open invitation indexes that are old enough to be replaced // First check if there are any open invitation indexes that are old enough to be replaced
if !self.bridge_table.open_inv_keys.is_empty() if !self.bridge_table.open_inv_keys.is_empty()
@ -825,14 +843,14 @@ impl BridgeAuth {
self.time_offset += time::Duration::days(1); self.time_offset += time::Duration::days(1);
} }
//#[cfg(test)] ///#[cfg(test)]
/// For testing only: manually advance the day by the given number /// For testing only: manually advance the day by the given number
/// of days /// of days
pub fn advance_days(&mut self, days: u16) { pub fn advance_days(&mut self, days: u16) {
self.time_offset += time::Duration::days(days.into()); self.time_offset += time::Duration::days(days.into());
} }
/// Get today's (real or simulated) date /// Get today's (real or simulated) date as u32
pub fn today(&self) -> u32 { pub fn today(&self) -> u32 {
// We will not encounter negative Julian dates (~6700 years ago) // We will not encounter negative Julian dates (~6700 years ago)
// or ones larger than 32 bits // or ones larger than 32 bits
@ -842,7 +860,7 @@ impl BridgeAuth {
.unwrap() .unwrap()
} }
/// Get today's (real or simulated) date /// Get today's (real or simulated) date as a DateTime<Utc> value
pub fn today_date(&self) -> DateTime<Utc> { pub fn today_date(&self) -> DateTime<Utc> {
Utc::now() Utc::now()
} }
@ -961,13 +979,13 @@ pub fn pt_dbl(P: &RistrettoPoint) -> RistrettoPoint {
/// The protocol modules. /// The protocol modules.
/// ///
/// Each protocol lives in a submodule. Each submodule defines structs /// Each protocol lives in a submodule. Each submodule defines structs
/// for Request (the message from the client to the bridge authority), /// for Request (the message from the client to the bridge authority),
/// State (the state held by the client while waiting for the reply), /// State (the state held by the client while waiting for the reply),
/// and Response (the message from the bridge authority to the client). /// and Response (the message from the bridge authority to the client).
/// Each submodule defines functions request, which produces a (Request, /// Each submodule defines functions request, which produces a (Request,
/// State) pair, and handle_response, which consumes a State and a /// State) pair, and handle_response, which consumes a State and a
/// Response. It also adds a handle_* function to the BridgeAuth struct /// Response. It also adds a handle_* function to the BridgeAuth struct
/// that consumes a Request and produces a Result<Response, ProofError>. /// that consumes a Request and produces a Result<Response, ProofError>.
pub mod proto { pub mod proto {
pub mod blockage_migration; pub mod blockage_migration;

View File

@ -7,7 +7,6 @@ rust-version = "1.65"
homepage = "https://gitlab.torproject.org/tpo/anti-censorship/lox/-/wikis/home" homepage = "https://gitlab.torproject.org/tpo/anti-censorship/lox/-/wikis/home"
description = "General helpers used by Lox" description = "General helpers used by Lox"
keywords = ["tor", "lox"] keywords = ["tor", "lox"]
# We must put *something* here and this will do
categories = ["rust-patterns"] categories = ["rust-patterns"]
repository = "https://gitlab.torproject.org/tpo/anti-censorship/lox.git/" repository = "https://gitlab.torproject.org/tpo/anti-censorship/lox.git/"

View File

@ -10,7 +10,6 @@ categories = ["api-bindings", "encoding"]
repository = "https://gitlab.torproject.org/tpo/anti-censorship/lox/-/tree/main/crates/rdsys-backend-api" repository = "https://gitlab.torproject.org/tpo/anti-censorship/lox/-/tree/main/crates/rdsys-backend-api"
readme = "https://gitlab.torproject.org/tpo/anti-censorship/lox/-/blob/main/crates/rdsys-backend-api/README.md" readme = "https://gitlab.torproject.org/tpo/anti-censorship/lox/-/blob/main/crates/rdsys-backend-api/README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
serde_json = "1" serde_json = "1"