|
@@ -25,7 +25,7 @@ pub mod migration_table;
|
|
use sha2::Sha512;
|
|
use sha2::Sha512;
|
|
|
|
|
|
use rand::rngs::OsRng;
|
|
use rand::rngs::OsRng;
|
|
-use rand::RngCore;
|
|
+use rand::Rng;
|
|
use std::convert::{TryFrom, TryInto};
|
|
use std::convert::{TryFrom, TryInto};
|
|
|
|
|
|
use curve25519_dalek::constants as dalek_constants;
|
|
use curve25519_dalek::constants as dalek_constants;
|
|
@@ -38,6 +38,13 @@ use curve25519_dalek::traits::IsIdentity;
|
|
use ed25519_dalek::{Keypair, PublicKey, Signature, SignatureError, Signer, Verifier};
|
|
use ed25519_dalek::{Keypair, PublicKey, Signature, SignatureError, Signer, Verifier};
|
|
use subtle::ConstantTimeEq;
|
|
use subtle::ConstantTimeEq;
|
|
|
|
|
|
|
|
+use std::collections::HashSet;
|
|
|
|
+
|
|
|
|
+use bridge_table::{
|
|
|
|
+ BridgeLine, BridgeTable, ENC_BUCKET_BYTES, MAX_BRIDGES_PER_BUCKET, MIN_BUCKET_REACHABILITY,
|
|
|
|
+};
|
|
|
|
+use migration_table::{MigrationTable, MigrationType};
|
|
|
|
+
|
|
use lazy_static::lazy_static;
|
|
use lazy_static::lazy_static;
|
|
|
|
|
|
lazy_static! {
|
|
lazy_static! {
|
|
@@ -104,8 +111,8 @@ pub struct BridgeDb {
|
|
keypair: Keypair,
|
|
keypair: Keypair,
|
|
/// The public key for verifying open invitations
|
|
/// The public key for verifying open invitations
|
|
pub pubkey: PublicKey,
|
|
pub pubkey: PublicKey,
|
|
- /// The number of open-invitation buckets
|
|
+ /// The set of open-invitation buckets
|
|
- num_openinv_buckets: u32,
|
|
+ openinv_buckets: HashSet<u32>,
|
|
}
|
|
}
|
|
|
|
|
|
/// An open invitation is a [u8; OPENINV_LENGTH] where the first 32
|
|
/// An open invitation is a [u8; OPENINV_LENGTH] where the first 32
|
|
@@ -119,17 +126,27 @@ pub const OPENINV_LENGTH: usize = 32 // the length of the random
|
|
|
|
|
|
impl BridgeDb {
|
|
impl BridgeDb {
|
|
/// Create the BridgeDb.
|
|
/// Create the BridgeDb.
|
|
- pub fn new(num_openinv_buckets: u32) -> Self {
|
|
+ pub fn new() -> Self {
|
|
let mut csprng = OsRng {};
|
|
let mut csprng = OsRng {};
|
|
let keypair = Keypair::generate(&mut csprng);
|
|
let keypair = Keypair::generate(&mut csprng);
|
|
let pubkey = keypair.public;
|
|
let pubkey = keypair.public;
|
|
Self {
|
|
Self {
|
|
keypair,
|
|
keypair,
|
|
pubkey,
|
|
pubkey,
|
|
- num_openinv_buckets,
|
|
+ openinv_buckets: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// Insert an open-invitation bucket into the set
|
|
|
|
+ pub fn insert_openinv(&mut self, bucket: u32) {
|
|
|
|
+ self.openinv_buckets.insert(bucket);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Remove an open-invitation bucket from the set
|
|
|
|
+ pub fn remove_openinv(&mut self, bucket: u32) {
|
|
|
|
+ self.openinv_buckets.remove(&bucket);
|
|
|
|
+ }
|
|
|
|
+
|
|
/// Produce an open invitation. In this example code, we just
|
|
/// Produce an open invitation. In this example code, we just
|
|
/// choose a random open-invitation bucket.
|
|
/// choose a random open-invitation bucket.
|
|
pub fn invite(&self) -> [u8; OPENINV_LENGTH] {
|
|
pub fn invite(&self) -> [u8; OPENINV_LENGTH] {
|
|
@@ -138,9 +155,10 @@ impl BridgeDb {
|
|
// Choose a random invitation id (a Scalar) and serialize it
|
|
// Choose a random invitation id (a Scalar) and serialize it
|
|
let id = Scalar::random(&mut rng);
|
|
let id = Scalar::random(&mut rng);
|
|
res[0..32].copy_from_slice(&id.to_bytes());
|
|
res[0..32].copy_from_slice(&id.to_bytes());
|
|
- // Choose a random bucket number (mod num_openinv_buckets) and
|
|
+ // Choose a random bucket number (from the set of open
|
|
- // serialize it
|
|
+ // invitation buckets) and serialize it
|
|
- let bucket_num = rng.next_u32() % self.num_openinv_buckets;
|
|
+ let openinv_vec: Vec<&u32> = self.openinv_buckets.iter().collect();
|
|
|
|
+ let bucket_num = *openinv_vec[rng.gen_range(0, openinv_vec.len())];
|
|
res[32..(32 + 4)].copy_from_slice(&bucket_num.to_le_bytes());
|
|
res[32..(32 + 4)].copy_from_slice(&bucket_num.to_le_bytes());
|
|
// Sign the first 36 bytes and serialize it
|
|
// Sign the first 36 bytes and serialize it
|
|
let sig = self.keypair.sign(&res[0..(32 + 4)]);
|
|
let sig = self.keypair.sign(&res[0..(32 + 4)]);
|
|
@@ -171,6 +189,12 @@ impl BridgeDb {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+impl Default for BridgeDb {
|
|
|
|
+ fn default() -> Self {
|
|
|
|
+ Self::new()
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/// The bridge authority. This will typically be a singleton object.
|
|
/// The bridge authority. This will typically be a singleton object.
|
|
#[derive(Debug)]
|
|
#[derive(Debug)]
|
|
pub struct BridgeAuth {
|
|
pub struct BridgeAuth {
|
|
@@ -199,10 +223,11 @@ pub struct BridgeAuth {
|
|
pub bridgedb_pub: PublicKey,
|
|
pub bridgedb_pub: PublicKey,
|
|
|
|
|
|
/// The bridge table
|
|
/// The bridge table
|
|
- bridge_table: bridge_table::BridgeTable,
|
|
+ bridge_table: BridgeTable,
|
|
|
|
|
|
- /// The migration table
|
|
+ /// The migration tables
|
|
- migration_table: migration_table::MigrationTable,
|
|
+ trustup_migration_table: MigrationTable,
|
|
|
|
+ blockage_migration_table: MigrationTable,
|
|
|
|
|
|
/// Duplicate filter for open invitations
|
|
/// Duplicate filter for open invitations
|
|
openinv_filter: dup_filter::DupFilter<Scalar>,
|
|
openinv_filter: dup_filter::DupFilter<Scalar>,
|
|
@@ -245,7 +270,8 @@ impl BridgeAuth {
|
|
invitation_pub,
|
|
invitation_pub,
|
|
bridgedb_pub,
|
|
bridgedb_pub,
|
|
bridge_table: Default::default(),
|
|
bridge_table: Default::default(),
|
|
- migration_table: Default::default(),
|
|
+ trustup_migration_table: MigrationTable::new(MigrationType::TrustUpgrade),
|
|
|
|
+ blockage_migration_table: MigrationTable::new(MigrationType::Blockage),
|
|
openinv_filter: Default::default(),
|
|
openinv_filter: Default::default(),
|
|
id_filter: Default::default(),
|
|
id_filter: Default::default(),
|
|
inv_id_filter: Default::default(),
|
|
inv_id_filter: Default::default(),
|
|
@@ -254,6 +280,117 @@ impl BridgeAuth {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// Insert a set of open invitation bridges.
|
|
|
|
+ ///
|
|
|
|
+ /// Each of the bridges will be given its own open invitation
|
|
|
|
+ /// bucket, and the BridgeDb will be informed. A single bucket
|
|
|
|
+ /// containing all of the bridges will also be created, with a trust
|
|
|
|
+ /// upgrade migration from each of the single-bridge buckets.
|
|
|
|
+ pub fn add_openinv_bridges(
|
|
|
|
+ &mut self,
|
|
|
|
+ bridges: [BridgeLine; MAX_BRIDGES_PER_BUCKET],
|
|
|
|
+ bdb: &mut BridgeDb,
|
|
|
|
+ ) {
|
|
|
|
+ let bnum = self.bridge_table.new_bucket(&bridges);
|
|
|
|
+ let mut single = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET];
|
|
|
|
+ for b in bridges.iter() {
|
|
|
|
+ single[0] = *b;
|
|
|
|
+ let snum = self.bridge_table.new_bucket(&single);
|
|
|
|
+ bdb.insert_openinv(snum);
|
|
|
|
+ println!("Adding {} -> {}", snum, bnum);
|
|
|
|
+ self.trustup_migration_table.table.insert(snum, bnum);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Insert a hot spare bucket of bridges
|
|
|
|
+ pub fn add_spare_bucket(&mut self, bucket: [BridgeLine; MAX_BRIDGES_PER_BUCKET]) {
|
|
|
|
+ let bnum = self.bridge_table.new_bucket(&bucket);
|
|
|
|
+ self.bridge_table.spares.insert(bnum);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// Mark a bridge as unreachable
|
|
|
|
+ ///
|
|
|
|
+ /// This bridge will be removed from each of the buckets that
|
|
|
|
+ /// contains it. If any of those are open-invitation buckets, the
|
|
|
|
+ /// trust upgrade migration for that bucket will be removed and the
|
|
|
|
+ /// BridgeDb will be informed to stop handing out that bridge. If
|
|
|
|
+ /// any of those are trusted buckets where the number of reachable
|
|
|
|
+ /// bridges has fallen below the threshold, a blockage migration
|
|
|
|
+ /// from that bucket to a spare bucket will be added, and the spare
|
|
|
|
+ /// bucket will be removed from the list of hot spares. In
|
|
|
|
+ /// addition, if the blocked bucket was the _target_ of a blockage
|
|
|
|
+ /// migration, change the target to the new (formerly spare) bucket.
|
|
|
|
+ /// Returns true if sucessful, or false if it needed a hot spare but
|
|
|
|
+ /// there was none available.
|
|
|
|
+ pub fn bridge_unreachable(&mut self, bridge: &BridgeLine, bdb: &mut BridgeDb) -> bool {
|
|
|
|
+ let mut res: bool = true;
|
|
|
|
+ let positions = self.bridge_table.reachable.get(bridge);
|
|
|
|
+ if let Some(v) = positions {
|
|
|
|
+ for (bucketnum, offset) in v.iter() {
|
|
|
|
+ // Count how many bridges in this bucket are reachable
|
|
|
|
+ let numreachable = self.bridge_table.buckets[*bucketnum as usize]
|
|
|
|
+ .iter()
|
|
|
|
+ .filter(|br| self.bridge_table.reachable.get(br).is_some())
|
|
|
|
+ .count();
|
|
|
|
+
|
|
|
|
+ // Remove the bridge from the bucket
|
|
|
|
+ assert!(self.bridge_table.buckets[*bucketnum as usize][*offset] == *bridge);
|
|
|
|
+ self.bridge_table.buckets[*bucketnum as usize][*offset] = BridgeLine::default();
|
|
|
|
+
|
|
|
|
+ // Is this bucket an open-invitation bucket?
|
|
|
|
+ if bdb.openinv_buckets.contains(bucketnum) {
|
|
|
|
+ bdb.openinv_buckets.remove(bucketnum);
|
|
|
|
+ self.trustup_migration_table.table.remove(bucketnum);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Does this removal cause the bucket to go below the
|
|
|
|
+ // threshold?
|
|
|
|
+ if numreachable != MIN_BUCKET_REACHABILITY {
|
|
|
|
+ // No
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // This bucket is now unreachable. Get a spare bucket
|
|
|
|
+ if self.bridge_table.spares.is_empty() {
|
|
|
|
+ // Uh, oh. No spares available. Just delete any
|
|
|
|
+ // migrations leading to this bucket.
|
|
|
|
+ res = false;
|
|
|
|
+ self.trustup_migration_table
|
|
|
|
+ .table
|
|
|
|
+ .retain(|_, &mut v| v != *bucketnum);
|
|
|
|
+ self.blockage_migration_table
|
|
|
|
+ .table
|
|
|
|
+ .retain(|_, &mut v| v != *bucketnum);
|
|
|
|
+ } else {
|
|
|
|
+ // Get the first spare and remove it from the spares
|
|
|
|
+ // set.
|
|
|
|
+ let spare = *self.bridge_table.spares.iter().next().unwrap();
|
|
|
|
+ self.bridge_table.spares.remove(&spare);
|
|
|
|
+ // Add a blockage migration from this bucket to the spare
|
|
|
|
+ self.blockage_migration_table
|
|
|
|
+ .table
|
|
|
|
+ .insert(*bucketnum, spare);
|
|
|
|
+ // Remove any trust upgrade migrations to this
|
|
|
|
+ // bucket
|
|
|
|
+ self.trustup_migration_table
|
|
|
|
+ .table
|
|
|
|
+ .retain(|_, &mut v| v != *bucketnum);
|
|
|
|
+ // Change any blockage migrations with this bucket
|
|
|
|
+ // as the destination to the spare
|
|
|
|
+ for (_, v) in self.blockage_migration_table.table.iter_mut() {
|
|
|
|
+ if *v == *bucketnum {
|
|
|
|
+ *v = spare;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ self.bridge_table.reachable.remove(bridge);
|
|
|
|
+
|
|
|
|
+ res
|
|
|
|
+ }
|
|
|
|
+
|
|
#[cfg(test)]
|
|
#[cfg(test)]
|
|
/// For testing only: manually advance the day by 1 day
|
|
/// For testing only: manually advance the day by 1 day
|
|
pub fn advance_day(&mut self) {
|
|
pub fn advance_day(&mut self) {
|
|
@@ -282,7 +419,7 @@ impl BridgeAuth {
|
|
/// Be sure to call this function when you want the latest version
|
|
/// Be sure to call this function when you want the latest version
|
|
/// of the table, since it will put fresh Bucket Reachability
|
|
/// of the table, since it will put fresh Bucket Reachability
|
|
/// credentials in the buckets each day.
|
|
/// credentials in the buckets each day.
|
|
- pub fn enc_bridge_table(&mut self) -> &Vec<[u8; bridge_table::ENC_BUCKET_BYTES]> {
|
|
+ pub fn enc_bridge_table(&mut self) -> &Vec<[u8; ENC_BUCKET_BYTES]> {
|
|
let today = self.today();
|
|
let today = self.today();
|
|
if self.bridge_table.date_last_enc != today {
|
|
if self.bridge_table.date_last_enc != today {
|
|
self.bridge_table
|
|
self.bridge_table
|