#![allow(non_snake_case)]
#[macro_use]
extern crate lox_zkp;
pub mod bridge_table;
pub mod cred;
pub mod dup_filter;
pub mod migration_table;
#[cfg(feature = "bridgeauth")]
use chrono::{DateTime, Duration, Utc};
use sha2::Sha512;
use curve25519_dalek::constants as dalek_constants;
use curve25519_dalek::ristretto::RistrettoBasepointTable;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
#[cfg(test)]
use curve25519_dalek::traits::IsIdentity;
use rand::rngs::OsRng;
use rand::Rng;
#[cfg(feature = "bridgeauth")]
use std::collections::HashMap;
#[cfg(feature = "bridgeauth")]
use std::convert::TryFrom;
use std::convert::TryInto;
#[cfg(feature = "bridgeauth")]
use ed25519_dalek::{Signature, SignatureError, Signer, SigningKey, Verifier, VerifyingKey};
use subtle::ConstantTimeEq;
#[cfg(feature = "bridgeauth")]
use std::collections::HashSet;
#[cfg(feature = "bridgeauth")]
use bridge_table::{
BridgeLine, BridgeTable, EncryptedBucket, MAX_BRIDGES_PER_BUCKET, MIN_BUCKET_REACHABILITY,
};
#[cfg(feature = "bridgeauth")]
use migration_table::{MigrationTable, MigrationType};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
#[cfg(feature = "bridgeauth")]
use thiserror::Error;
lazy_static! {
pub static ref CMZ_A: RistrettoPoint =
RistrettoPoint::hash_from_bytes::<Sha512>(b"CMZ Generator A");
pub static ref CMZ_B: RistrettoPoint = dalek_constants::RISTRETTO_BASEPOINT_POINT;
pub static ref CMZ_A_TABLE: RistrettoBasepointTable = RistrettoBasepointTable::create(&CMZ_A);
pub static ref CMZ_B_TABLE: RistrettoBasepointTable =
dalek_constants::RISTRETTO_BASEPOINT_TABLE.clone();
}
pub const EXPIRY_DATE: u32 = 511;
#[derive(PartialEq, Eq)]
#[cfg(feature = "bridgeauth")]
pub enum ReplaceSuccess {
NotFound = 0,
NotReplaced = 1,
Replaced = 2,
Removed = 3,
}
#[derive(Error, Debug)]
#[cfg(feature = "bridgeauth")]
pub enum NoAvailableIDError {
#[error("Find key exhausted with no available index found!")]
ExhaustedIndexer,
}
#[derive(Error, Debug)]
#[cfg(feature = "bridgeauth")]
pub enum OpenInvitationError {
#[error("The maximum number of bridges has already been distributed today, please try again tomorrow!")]
ExceededMaxBridges,
#[error("There are no bridges available for open invitations.")]
NoBridgesAvailable,
}
#[derive(Error, Debug)]
#[cfg(feature = "bridgeauth")]
pub enum BridgeTableError {
#[error("The bucket corresponding to key {0} was not in the bridge table")]
MissingBucket(u32),
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct IssuerPrivKey {
x0tilde: Scalar,
x: Vec<Scalar>,
}
impl IssuerPrivKey {
pub fn new(n: u16) -> IssuerPrivKey {
let mut rng = rand::thread_rng();
let x0tilde = Scalar::random(&mut rng);
let mut x: Vec<Scalar> = Vec::with_capacity((n + 1) as usize);
x.resize_with((n + 1) as usize, || Scalar::random(&mut rng));
IssuerPrivKey { x0tilde, x }
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct IssuerPubKey {
X: Vec<RistrettoPoint>,
}
impl IssuerPubKey {
pub fn new(privkey: &IssuerPrivKey) -> IssuerPubKey {
let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
let n_plus_one = privkey.x.len();
let mut X: Vec<RistrettoPoint> = Vec::with_capacity(n_plus_one);
X.push(&privkey.x0tilde * Atable + &privkey.x[0] * Btable);
X.extend(privkey.x.iter().skip(1).map(|xi| xi * Atable));
IssuerPubKey { X }
}
}
pub const OPENINV_K: u32 = 10;
pub const MAX_DAILY_BRIDGES: u32 = 100;
#[derive(Debug, Serialize, Deserialize)]
#[cfg(feature = "bridgeauth")]
pub struct BridgeDb {
keypair: SigningKey,
pub pubkey: VerifyingKey,
openinv_buckets: HashSet<u32>,
distributed_buckets: Vec<u32>,
#[serde(skip)]
today: DateTime<Utc>,
pub current_k: u32,
pub daily_bridges_distributed: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg(feature = "bridgeauth")]
pub struct OldKeyStore {
priv_key: IssuerPrivKey,
pub pub_key: IssuerPubKey,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[cfg(feature = "bridgeauth")]
pub struct OldKeys {
lox_keys: Vec<OldKeyStore>,
bridgedb_key: Vec<VerifyingKey>,
invitation_keys: Vec<OldKeyStore>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[cfg(feature = "bridgeauth")]
pub struct OldFilters {
lox_filter: Vec<dup_filter::DupFilter<Scalar>>,
openinv_filter: Vec<dup_filter::DupFilter<Scalar>>,
invitation_filter: Vec<dup_filter::DupFilter<Scalar>>,
}
pub const OPENINV_LENGTH: usize = 32 + 4 + ed25519_dalek::SIGNATURE_LENGTH;
#[cfg(feature = "bridgeauth")]
impl BridgeDb {
pub fn new() -> Self {
let mut csprng = OsRng {};
let keypair = SigningKey::generate(&mut csprng);
let pubkey = keypair.verifying_key();
Self {
keypair,
pubkey,
openinv_buckets: Default::default(),
distributed_buckets: Default::default(),
today: Utc::now(),
current_k: 0,
daily_bridges_distributed: 0,
}
}
pub fn openinv_length(&mut self) -> usize {
self.openinv_buckets.len()
}
pub fn rotate_open_inv_keys(&mut self) -> VerifyingKey {
let mut csprng = OsRng {};
self.keypair = SigningKey::generate(&mut csprng);
self.pubkey = self.keypair.verifying_key();
self.pubkey
}
pub fn insert_openinv(&mut self, bucket: u32) {
self.openinv_buckets.insert(bucket);
}
pub fn remove_openinv(&mut self, bucket: &u32) {
self.openinv_buckets.remove(bucket);
}
pub fn remove_blocked_or_expired_buckets(&mut self, bucket: &u32) {
if self.openinv_buckets.contains(bucket) {
println!("Removing a bucket that has not been distributed yet!");
self.openinv_buckets.remove(bucket);
} else if self.distributed_buckets.contains(bucket) {
self.distributed_buckets.retain(|&x| x != *bucket);
}
}
pub fn mark_distributed(&mut self, bucket: u32) {
self.distributed_buckets.push(bucket);
}
pub fn invite(&mut self) -> Result<[u8; OPENINV_LENGTH], OpenInvitationError> {
let mut res: [u8; OPENINV_LENGTH] = [0; OPENINV_LENGTH];
let mut rng = rand::thread_rng();
let id = Scalar::random(&mut rng);
res[0..32].copy_from_slice(&id.to_bytes());
let bucket_num: u32;
if Utc::now() >= (self.today + Duration::days(1)) {
self.today = Utc::now();
self.daily_bridges_distributed = 0;
}
if self.daily_bridges_distributed < MAX_DAILY_BRIDGES {
if self.current_k < OPENINV_K && !self.distributed_buckets.is_empty() {
bucket_num = *self.distributed_buckets.last().unwrap();
self.current_k += 1;
} else {
if self.openinv_buckets.is_empty() {
return Err(OpenInvitationError::NoBridgesAvailable);
}
let openinv_vec: Vec<&u32> = self.openinv_buckets.iter().collect();
bucket_num = *openinv_vec[rng.gen_range(0..openinv_vec.len())];
self.mark_distributed(bucket_num);
self.remove_openinv(&bucket_num);
self.current_k = 1;
self.daily_bridges_distributed += 1;
}
res[32..(32 + 4)].copy_from_slice(&bucket_num.to_le_bytes());
let sig = self.keypair.sign(&res[0..(32 + 4)]);
res[(32 + 4)..].copy_from_slice(&sig.to_bytes());
Ok(res)
} else {
Err(OpenInvitationError::ExceededMaxBridges)
}
}
pub fn verify(
invitation: [u8; OPENINV_LENGTH],
pubkey: VerifyingKey,
) -> Result<(Scalar, u32), SignatureError> {
let sig = Signature::try_from(&invitation[(32 + 4)..])?;
pubkey.verify(&invitation[0..(32 + 4)], &sig)?;
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());
if s.is_some().into() {
Ok((s.unwrap(), bucket))
} else {
Err(SignatureError::new())
}
}
}
#[cfg(feature = "bridgeauth")]
impl Default for BridgeDb {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "bridgeauth")]
#[derive(Debug, Serialize, Deserialize)]
pub struct BridgeAuth {
lox_priv: IssuerPrivKey,
pub lox_pub: IssuerPubKey,
migration_priv: IssuerPrivKey,
pub migration_pub: IssuerPubKey,
migrationkey_priv: IssuerPrivKey,
pub migrationkey_pub: IssuerPubKey,
reachability_priv: IssuerPrivKey,
pub reachability_pub: IssuerPubKey,
invitation_priv: IssuerPrivKey,
pub invitation_pub: IssuerPubKey,
pub bridgedb_pub: VerifyingKey,
bridge_table: BridgeTable,
trustup_migration_table: MigrationTable,
blockage_migration_table: MigrationTable,
bridgedb_pub_filter: dup_filter::DupFilter<Scalar>,
id_filter: dup_filter::DupFilter<Scalar>,
inv_id_filter: dup_filter::DupFilter<Scalar>,
trust_promotion_filter: dup_filter::DupFilter<Scalar>,
old_keys: OldKeys,
old_filters: OldFilters,
#[serde(skip)]
time_offset: time::Duration,
}
#[cfg(feature = "bridgeauth")]
impl BridgeAuth {
pub fn new(bridgedb_pub: VerifyingKey) -> Self {
let lox_priv = IssuerPrivKey::new(6);
let lox_pub = IssuerPubKey::new(&lox_priv);
let migration_priv = IssuerPrivKey::new(4);
let migration_pub = IssuerPubKey::new(&migration_priv);
let migrationkey_priv = IssuerPrivKey::new(2);
let migrationkey_pub = IssuerPubKey::new(&migrationkey_priv);
let reachability_priv = IssuerPrivKey::new(2);
let reachability_pub = IssuerPubKey::new(&reachability_priv);
let invitation_priv = IssuerPrivKey::new(4);
let invitation_pub = IssuerPubKey::new(&invitation_priv);
Self {
lox_priv,
lox_pub,
migration_priv,
migration_pub,
migrationkey_priv,
migrationkey_pub,
reachability_priv,
reachability_pub,
invitation_priv,
invitation_pub,
bridgedb_pub,
bridge_table: Default::default(),
trustup_migration_table: MigrationTable::new(MigrationType::TrustUpgrade),
blockage_migration_table: MigrationTable::new(MigrationType::Blockage),
bridgedb_pub_filter: Default::default(),
id_filter: Default::default(),
inv_id_filter: Default::default(),
trust_promotion_filter: Default::default(),
time_offset: time::Duration::ZERO,
old_keys: Default::default(),
old_filters: Default::default(),
}
}
pub fn rotate_lox_keys(&mut self) {
let updated_lox_priv = IssuerPrivKey::new(6);
let updated_lox_pub = IssuerPubKey::new(&updated_lox_priv);
self.old_keys.lox_keys.push(OldKeyStore {
priv_key: self.lox_priv.clone(),
pub_key: self.lox_pub.clone(),
});
self.old_filters.lox_filter.push(self.id_filter.clone());
self.lox_priv = updated_lox_priv;
self.lox_pub = updated_lox_pub;
self.id_filter = Default::default();
}
pub fn rotate_invitation_keys(&mut self) {
let updated_invitation_priv = IssuerPrivKey::new(4);
let updated_invitation_pub = IssuerPubKey::new(&updated_invitation_priv);
self.old_keys.invitation_keys.push(OldKeyStore {
priv_key: self.invitation_priv.clone(),
pub_key: self.invitation_pub.clone(),
});
self.old_filters
.invitation_filter
.push(self.inv_id_filter.clone());
self.invitation_priv = updated_invitation_priv;
self.invitation_pub = updated_invitation_pub;
self.inv_id_filter = Default::default();
}
pub fn rotate_bridgedb_keys(&mut self, new_bridgedb_pub: VerifyingKey) {
self.old_keys.bridgedb_key.push(self.bridgedb_pub);
self.old_filters
.openinv_filter
.push(self.bridgedb_pub_filter.clone());
self.bridgedb_pub = new_bridgedb_pub;
self.bridgedb_pub_filter = Default::default();
}
pub fn is_empty(&self) -> bool {
self.bridge_table.buckets.is_empty()
}
pub fn reachable_length(&self) -> usize {
self.bridge_table.reachable.len()
}
pub fn unallocated_length(&self) -> usize {
self.bridge_table.unallocated_bridges.len()
}
pub fn spares_length(&self) -> usize {
self.bridge_table.spares.len()
}
pub fn openinv_length(&self, bdb: &mut BridgeDb) -> usize {
bdb.openinv_length()
}
pub fn add_openinv_bridges(
&mut self,
bridges: [BridgeLine; MAX_BRIDGES_PER_BUCKET],
bdb: &mut BridgeDb,
) -> Result<(), NoAvailableIDError> {
let bindex = match self.find_next_available_key(bdb) {
Ok(sindex) => sindex,
Err(e) => return Err(e),
};
self.bridge_table.new_bucket(bindex, &bridges);
let mut single = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET];
for b in bridges.iter() {
let sindex = match self.find_next_available_key(bdb) {
Ok(sindex) => sindex,
Err(e) => return Err(e),
};
single[0] = *b;
self.bridge_table.new_bucket(sindex, &single);
self.bridge_table.open_inv_keys.push((sindex, self.today()));
bdb.insert_openinv(sindex);
self.trustup_migration_table.table.insert(sindex, bindex);
}
Ok(())
}
pub fn add_spare_bucket(
&mut self,
bucket: [BridgeLine; MAX_BRIDGES_PER_BUCKET],
bdb: &mut BridgeDb,
) -> Result<(), NoAvailableIDError> {
let index = match self.find_next_available_key(bdb) {
Ok(index) => index,
Err(e) => return Err(e),
};
self.bridge_table.new_bucket(index, &bucket);
self.bridge_table.spares.insert(index);
Ok(())
}
pub fn find_and_remove_unaccounted_for_bridges(
&mut self,
accounted_for_bridges: Vec<u64>,
) -> Vec<BridgeLine> {
let mut unaccounted_for: Vec<BridgeLine> = Vec::new();
for (k, _v) in self.bridge_table.reachable.clone() {
if !accounted_for_bridges.contains(&k.uid_fingerprint) {
unaccounted_for.push(k);
}
}
unaccounted_for
}
pub fn allocate_bridges(
&mut self,
distributor_bridges: &mut Vec<BridgeLine>,
bdb: &mut BridgeDb,
) {
while let Some(bridge) = distributor_bridges.pop() {
self.bridge_table.unallocated_bridges.push(bridge);
}
while self.bridge_table.unallocated_bridges.len() >= MAX_BRIDGES_PER_BUCKET {
let mut bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET];
for bridge in bucket.iter_mut() {
*bridge = self.bridge_table.unallocated_bridges.pop().unwrap();
}
match self.add_openinv_bridges(bucket, bdb) {
Ok(_) => continue,
Err(e) => {
println!("Error: {:?}", e);
for bridge in bucket {
self.bridge_table.unallocated_bridges.push(bridge);
}
}
}
}
}
pub fn bridge_update(&mut self, bridge: &BridgeLine) -> bool {
let mut res: bool = false; let reachable_bridges = self.bridge_table.reachable.clone();
for reachable_bridge in reachable_bridges {
if reachable_bridge.0.uid_fingerprint == bridge.uid_fingerprint {
let positions = self.bridge_table.reachable.get(&reachable_bridge.0);
if let Some(v) = positions {
for (bucketnum, offset) in v.iter() {
let mut bridgelines = match self.bridge_table.buckets.get(bucketnum) {
Some(bridgelines) => *bridgelines,
None => return res,
};
assert!(bridgelines[*offset] == reachable_bridge.0);
bridgelines[*offset] = *bridge;
self.bridge_table.buckets.insert(*bucketnum, bridgelines);
if !self.bridge_table.buckets.contains_key(bucketnum) {
return res;
}
}
res = true;
} else {
return res;
}
self.bridge_table.reachable.remove(&reachable_bridge.0);
self.bridge_table
.reachable
.insert(*bridge, reachable_bridge.1);
return res;
}
}
let unallocated_bridges = self.bridge_table.unallocated_bridges.clone();
for (i, unallocated_bridge) in unallocated_bridges.iter().enumerate() {
if unallocated_bridge.uid_fingerprint == bridge.uid_fingerprint {
self.bridge_table.unallocated_bridges.remove(i);
self.bridge_table.unallocated_bridges.push(*bridge);
res = true;
}
}
res
}
pub fn dissolve_spare_bucket(&mut self, key: u32) -> Result<(), BridgeTableError> {
self.bridge_table.spares.remove(&key);
let spare_bucket = self
.bridge_table
.buckets
.remove(&key)
.ok_or(BridgeTableError::MissingBucket(key))?;
for bridge in spare_bucket.iter() {
self.bridge_table.unallocated_bridges.push(*bridge);
self.bridge_table.reachable.remove(bridge);
}
self.bridge_table.keys.remove(&key);
self.bridge_table.recycleable_keys.push(key);
Ok(())
}
pub fn remove_unallocated(&mut self, bridge: &BridgeLine) -> Option<BridgeLine> {
match self
.bridge_table
.unallocated_bridges
.iter()
.position(|x| x == bridge)
{
Some(index) => Some(self.bridge_table.unallocated_bridges.swap_remove(index)),
None => None,
}
}
pub fn bridge_replace(
&mut self,
bridge: &BridgeLine,
available_bridge: Option<BridgeLine>,
) -> ReplaceSuccess {
let reachable_bridges = &self.bridge_table.reachable.clone();
let Some(positions) = reachable_bridges.get(bridge) else {
match self.remove_unallocated(bridge) {
Some(_) => {
return ReplaceSuccess::Removed;
}
None => {
return ReplaceSuccess::NotFound;
}
}
};
if let Some(spare) = self
.bridge_table
.spares
.iter()
.find(|x| positions.iter().any(|(bucketnum, _)| &bucketnum == x))
.cloned()
{
let Ok(_) = self.dissolve_spare_bucket(spare) else {
return ReplaceSuccess::NotReplaced;
};
match self.remove_unallocated(bridge) {
Some(_) => {
return ReplaceSuccess::Removed;
}
None => {
return ReplaceSuccess::NotFound;
}
}
}
let Some(replacement) = available_bridge.or_else(|| {
self.bridge_table.unallocated_bridges.pop().or_else(|| {
let spare = self
.bridge_table
.spares
.iter()
.find(|x| !positions.iter().any(|(bucketnum, _)| &bucketnum == x))
.cloned()?;
let Ok(_) = self.dissolve_spare_bucket(spare) else {
return None;
};
self.bridge_table.unallocated_bridges.pop()
})
}) else {
println!("No available bridges");
return ReplaceSuccess::NotReplaced;
};
for (bucketnum, offset) in positions.iter() {
let mut bridgelines = match self.bridge_table.buckets.get(bucketnum) {
Some(bridgelines) => *bridgelines,
None => return ReplaceSuccess::NotFound,
};
assert!(bridgelines[*offset] == *bridge);
bridgelines[*offset] = replacement;
self.bridge_table.buckets.insert(*bucketnum, bridgelines);
self.bridge_table
.reachable
.insert(replacement, positions.clone());
self.bridge_table.reachable.remove(bridge);
}
ReplaceSuccess::Replaced
}
pub fn bridge_blocked(&mut self, bridge: &BridgeLine, bdb: &mut BridgeDb) -> bool {
let mut res: bool = true;
if self.remove_unallocated(bridge).is_some() {
return true;
}
if let Some(positions) = self.bridge_table.reachable.get(bridge) {
for (bucketnum, offset) in positions.iter() {
let mut bucket = match self.bridge_table.buckets.get(bucketnum) {
Some(bridgelines) => *bridgelines,
None => return false, };
assert!(bucket[*offset] == *bridge);
bucket[*offset] = BridgeLine::default();
if bdb.openinv_buckets.contains(bucketnum)
|| bdb.distributed_buckets.contains(bucketnum)
{
bdb.remove_blocked_or_expired_buckets(bucketnum);
self.trustup_migration_table.table.remove(bucketnum);
continue;
}
let numreachable = bucket
.iter()
.filter(|br| self.bridge_table.reachable.contains_key(br))
.count();
if numreachable != MIN_BUCKET_REACHABILITY {
continue;
}
self.trustup_migration_table
.table
.retain(|_, &mut v| v != *bucketnum);
if self.bridge_table.spares.is_empty() {
res = false;
self.blockage_migration_table
.table
.retain(|_, &mut v| v != *bucketnum);
continue;
}
let spare = *self.bridge_table.spares.iter().next().unwrap();
self.bridge_table.spares.remove(&spare);
self.bridge_table
.blocked_keys
.push((*bucketnum, self.today()));
self.blockage_migration_table
.table
.insert(*bucketnum, spare);
for (_, v) in self.blockage_migration_table.table.iter_mut() {
if *v == *bucketnum {
*v = spare;
}
}
}
}
self.bridge_table.reachable.remove(bridge);
res
}
fn find_next_available_key(&mut self, bdb: &mut BridgeDb) -> Result<u32, NoAvailableIDError> {
self.clean_up_expired_buckets(bdb);
if self.bridge_table.recycleable_keys.is_empty() {
let mut test_index = 1;
let mut test_counter = self.bridge_table.counter.wrapping_add(test_index);
let mut i = 0;
while self.bridge_table.buckets.contains_key(&test_counter) && i < 5000 {
test_index += 1;
test_counter = self.bridge_table.counter.wrapping_add(test_index);
i += 1;
if i == 5000 {
return Err(NoAvailableIDError::ExhaustedIndexer);
}
}
self.bridge_table.counter = self.bridge_table.counter.wrapping_add(test_index);
Ok(self.bridge_table.counter)
} else {
Ok(self.bridge_table.recycleable_keys.pop().unwrap())
}
}
pub fn clean_up_expired_buckets(&mut self, bdb: &mut BridgeDb) {
self.clean_up_blocked();
self.clean_up_open_entry(bdb);
}
fn clean_up_blocked(&mut self) {
#[allow(clippy::type_complexity)]
let (expired, fresh): (Vec<(u32, u32)>, Vec<(u32, u32)>) = self
.bridge_table
.blocked_keys
.iter()
.partition(|&x| x.1 + EXPIRY_DATE < self.today());
for item in expired {
let key = item.0;
let bridgelines = self.bridge_table.buckets.get(&key).unwrap();
for bridgeline in bridgelines {
if bridgeline.port > 0 {
self.bridge_table.unallocated_bridges.push(*bridgeline);
self.bridge_table.reachable.remove(bridgeline);
}
}
self.bridge_table.buckets.remove(&key);
self.bridge_table.keys.remove(&key);
self.bridge_table.recycleable_keys.push(key);
self.blockage_migration_table.table.retain(|&k, _| k != key);
}
self.bridge_table.blocked_keys = fresh
}
fn clean_up_open_entry(&mut self, bdb: &mut BridgeDb) {
#[allow(clippy::type_complexity)]
let (expired, fresh): (Vec<(u32, u32)>, Vec<(u32, u32)>) = self
.bridge_table
.open_inv_keys
.iter()
.partition(|&x| x.1 + EXPIRY_DATE < self.today());
for item in expired {
let key = item.0;
if !bdb.distributed_buckets.contains(&key) {
println!("This bucket was not actually distributed!");
}
bdb.remove_blocked_or_expired_buckets(&key);
self.trustup_migration_table.table.retain(|&k, _| k != key);
self.bridge_table.buckets.remove(&key);
self.bridge_table.keys.remove(&key);
self.bridge_table.recycleable_keys.push(key);
}
self.bridge_table.open_inv_keys = fresh
}
#[cfg(test)]
pub fn advance_day(&mut self) {
self.time_offset += time::Duration::days(1);
}
pub fn advance_days(&mut self, days: u16) {
self.time_offset += time::Duration::days(days.into());
}
pub fn today(&self) -> u32 {
(time::OffsetDateTime::now_utc().date() + self.time_offset)
.to_julian_day()
.try_into()
.unwrap()
}
pub fn today_date(&self) -> DateTime<Utc> {
Utc::now()
}
pub fn enc_bridge_table(&mut self) -> &HashMap<u32, EncryptedBucket> {
let today = self.today();
if self.bridge_table.date_last_enc != today {
self.bridge_table
.encrypt_table(today, &self.reachability_priv);
}
&self.bridge_table.encbuckets
}
#[cfg(test)]
pub fn verify_lox(&self, cred: &cred::Lox) -> bool {
if cred.P.is_identity() {
return false;
}
let Q = (self.lox_priv.x[0]
+ cred.id * self.lox_priv.x[1]
+ cred.bucket * self.lox_priv.x[2]
+ cred.trust_level * self.lox_priv.x[3]
+ cred.level_since * self.lox_priv.x[4]
+ cred.invites_remaining * self.lox_priv.x[5]
+ cred.blockages * self.lox_priv.x[6])
* cred.P;
Q == cred.Q
}
#[cfg(test)]
pub fn verify_migration(&self, cred: &cred::Migration) -> bool {
if cred.P.is_identity() {
return false;
}
let Q = (self.migration_priv.x[0]
+ cred.lox_id * self.migration_priv.x[1]
+ cred.from_bucket * self.migration_priv.x[2]
+ cred.to_bucket * self.migration_priv.x[3])
* cred.P;
Q == cred.Q
}
#[cfg(test)]
pub fn verify_reachability(&self, cred: &cred::BucketReachability) -> bool {
if cred.P.is_identity() {
return false;
}
let Q = (self.reachability_priv.x[0]
+ cred.date * self.reachability_priv.x[1]
+ cred.bucket * self.reachability_priv.x[2])
* cred.P;
Q == cred.Q
}
#[cfg(test)]
pub fn verify_invitation(&self, cred: &cred::Invitation) -> bool {
if cred.P.is_identity() {
return false;
}
let Q = (self.invitation_priv.x[0]
+ cred.inv_id * self.invitation_priv.x[1]
+ cred.date * self.invitation_priv.x[2]
+ cred.bucket * self.invitation_priv.x[3]
+ cred.blockages * self.invitation_priv.x[4])
* cred.P;
Q == cred.Q
}
}
pub fn scalar_u64(s: &Scalar) -> Option<u64> {
let sbytes = s.as_bytes();
if sbytes[8..].ct_eq(&[0u8; 24]).unwrap_u8() == 0 {
return None;
}
Some(u64::from_le_bytes(sbytes[..8].try_into().unwrap()))
}
pub fn scalar_u32(s: &Scalar) -> Option<u32> {
let sbytes = s.as_bytes();
if sbytes[4..].ct_eq(&[0u8; 28]).unwrap_u8() == 0 {
return None;
}
Some(u32::from_le_bytes(sbytes[..4].try_into().unwrap()))
}
pub fn scalar_dbl(s: &Scalar) -> Scalar {
s + s
}
pub fn pt_dbl(P: &RistrettoPoint) -> RistrettoPoint {
P + P
}
pub mod proto {
pub mod blockage_migration;
pub mod check_blockage;
pub mod errors;
pub mod issue_invite;
pub mod level_up;
pub mod migration;
pub mod open_invite;
pub mod redeem_invite;
pub mod trust_promotion;
pub mod update_cred;
pub mod update_invite;
}
#[cfg(test)]
mod tests;