Fixup and add comments, README to Lox Library
This commit is contained in:
parent
73b04b9a19
commit
e356034dae
|
@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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/"
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue