lox_library/
lib.rs

1/*! Implementation of a new style of bridge authority for Tor that
2allows users to invite other users, while protecting the social graph
3from the bridge authority itself.
4
5We use CMZ14 credentials (GGM version, which is more efficient, but
6makes a stronger security assumption): "Algebraic MACs and
7Keyed-Verification Anonymous Credentials" (Chase, Meiklejohn, and
8Zaverucha, CCS 2014)
9
10The notation follows that of the paper "Hyphae: Social Secret Sharing"
11(Lovecruft and de Valence, 2017), Section 4. */
12
13// We really want points to be capital letters and scalars to be
14// lowercase letters
15#![allow(non_snake_case)]
16
17#[macro_use]
18extern crate lox_zkp;
19
20pub mod bridge_table;
21pub mod bridge_verification_info;
22pub mod cred;
23pub mod dup_filter;
24pub mod migration_table;
25
26use bridge_verification_info::BridgeVerificationInfo;
27#[cfg(feature = "bridgeauth")]
28use chrono::{DateTime, Duration, Utc};
29#[cfg(feature = "blockage-detection")]
30use sha1::{Digest, Sha1};
31use sha2::Sha512;
32
33use curve25519_dalek::constants as dalek_constants;
34use curve25519_dalek::ristretto::RistrettoBasepointTable;
35use curve25519_dalek::ristretto::RistrettoPoint;
36use curve25519_dalek::scalar::Scalar;
37#[cfg(test)]
38use curve25519_dalek::traits::IsIdentity;
39#[allow(unused_imports)]
40use rand::rngs::OsRng;
41#[allow(unused_imports)]
42use rand::Rng;
43#[cfg(feature = "bridgeauth")]
44use std::collections::HashMap;
45#[cfg(feature = "bridgeauth")]
46use std::convert::TryFrom;
47use std::convert::TryInto;
48
49#[cfg(feature = "bridgeauth")]
50use ed25519_dalek::{Signature, SignatureError, Signer, SigningKey, Verifier, VerifyingKey};
51use subtle::ConstantTimeEq;
52
53#[cfg(feature = "bridgeauth")]
54use std::collections::HashSet;
55
56#[cfg(feature = "bridgeauth")]
57use bridge_table::{
58    BridgeLine, BridgeTable, EncryptedBucket, MAX_BRIDGES_PER_BUCKET, MIN_BUCKET_REACHABILITY,
59};
60#[cfg(feature = "bridgeauth")]
61use migration_table::{MigrationTable, MigrationType};
62
63use lazy_static::lazy_static;
64
65use serde::{Deserialize, Serialize};
66#[cfg(feature = "bridgeauth")]
67use thiserror::Error;
68
69lazy_static! {
70    pub static ref CMZ_A: RistrettoPoint =
71        RistrettoPoint::hash_from_bytes::<Sha512>(b"CMZ Generator A");
72    pub static ref CMZ_B: RistrettoPoint = dalek_constants::RISTRETTO_BASEPOINT_POINT;
73    pub static ref CMZ_A_TABLE: RistrettoBasepointTable = RistrettoBasepointTable::create(&CMZ_A);
74    pub static ref CMZ_B_TABLE: RistrettoBasepointTable =
75        dalek_constants::RISTRETTO_BASEPOINT_TABLE.clone();
76}
77
78// EXPIRY_DATE is set to EXPIRY_DATE days for open-entry and blocked buckets in order to match
79// the expiry date for Lox credentials.This particular value (EXPIRY_DATE) is chosen because
80// values that are 2^k − 1 make range proofs more efficient, but this can be changed to any value
81pub const EXPIRY_DATE: u32 = 511;
82
83/// ReplaceSuccess sends a signal to the lox-distributor to inform
84/// whether or not a bridge was successfully replaced
85#[derive(PartialEq, Eq)]
86#[cfg(feature = "bridgeauth")]
87pub enum ReplaceSuccess {
88    NotFound = 0,
89    NotReplaced = 1,
90    Replaced = 2,
91    Removed = 3,
92}
93
94/// This error is thrown if the number of buckets/keys in the bridge table
95/// exceeds u32 MAX.It is unlikely this error will ever occur.
96#[derive(Error, Debug)]
97#[cfg(feature = "bridgeauth")]
98pub enum NoAvailableIDError {
99    #[error("Find key exhausted with no available index found!")]
100    ExhaustedIndexer,
101}
102
103/// This error is thrown after the MAX_DAILY_BRIDGES threshold for bridges
104/// distributed in a day has been reached
105#[derive(Error, Debug)]
106#[cfg(feature = "bridgeauth")]
107pub enum OpenInvitationError {
108    #[error("The maximum number of bridges has already been distributed today, please try again tomorrow!")]
109    ExceededMaxBridges,
110
111    #[error("There are no bridges available for open invitations.")]
112    NoBridgesAvailable,
113}
114
115#[derive(Error, Debug)]
116#[cfg(feature = "bridgeauth")]
117pub enum BridgeTableError {
118    #[error("The bucket corresponding to key {0} was not in the bridge table")]
119    MissingBucket(u32),
120}
121
122/// Private Key of the Issuer
123#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
124pub struct IssuerPrivKey {
125    x0tilde: Scalar,
126    x: Vec<Scalar>,
127}
128
129impl IssuerPrivKey {
130    /// Create an IssuerPrivKey for credentials with the given number of
131    /// attributes.
132    pub fn new(n: u16) -> IssuerPrivKey {
133        let mut rng = rand::thread_rng();
134        let x0tilde = Scalar::random(&mut rng);
135        let mut x: Vec<Scalar> = Vec::with_capacity((n + 1) as usize);
136
137        // Set x to a vector of n+1 random Scalars
138        x.resize_with((n + 1) as usize, || Scalar::random(&mut rng));
139
140        IssuerPrivKey { x0tilde, x }
141    }
142}
143
144#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
145pub struct IssuerPubKey {
146    X: Vec<RistrettoPoint>,
147}
148
149/// Public Key of the Issuer
150impl IssuerPubKey {
151    /// Create an IssuerPubKey from the corresponding IssuerPrivKey
152    pub fn new(privkey: &IssuerPrivKey) -> IssuerPubKey {
153        let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
154        let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
155        let n_plus_one = privkey.x.len();
156        let mut X: Vec<RistrettoPoint> = Vec::with_capacity(n_plus_one);
157
158        // The first element is a special case; it is
159        // X[0] = x0tilde*A + x[0]*B
160        X.push(&privkey.x0tilde * Atable + &privkey.x[0] * Btable);
161
162        // The other elements (1 through n) are X[i] = x[i]*A
163        X.extend(privkey.x.iter().skip(1).map(|xi| xi * Atable));
164
165        IssuerPubKey { X }
166    }
167}
168
169/// Number of times a given invitation is ditributed
170pub const OPENINV_K: u32 = 10;
171/// TODO: Decide on maximum daily number of invitations to be distributed
172pub const MAX_DAILY_BRIDGES: u32 = 100;
173/// The BridgeDb. This will typically be a singleton object. The
174/// BridgeDb's role is simply to issue signed "open invitations" to
175/// people who are not yet part of the system.
176#[derive(Debug, Serialize, Deserialize)]
177#[cfg(feature = "bridgeauth")]
178pub struct BridgeDb {
179    /// The keypair for signing open invitations
180    keypair: SigningKey,
181    /// The public key for verifying open invitations
182    pub pubkey: VerifyingKey,
183    /// The set of open-invitation buckets
184    openinv_buckets: HashSet<u32>,
185    /// The set of open invitation buckets that have been distributed
186    distributed_buckets: Vec<u32>,
187    #[serde(skip)]
188    today: DateTime<Utc>,
189    pub current_k: u32,
190    pub daily_bridges_distributed: u32,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
194#[cfg(feature = "bridgeauth")]
195pub struct OldKeyStore {
196    /// Most recently outdated lox secret and private keys for verifying update_cred credentials
197    priv_key: IssuerPrivKey,
198    /// The public key for verifying update_cred credentials
199    pub pub_key: IssuerPubKey,
200}
201
202#[derive(Debug, Default, Clone, Serialize, Deserialize)]
203#[cfg(feature = "bridgeauth")]
204pub struct OldKeys {
205    /// Most recently outdated lox secret and private keys for verifying update_cred credentials
206    lox_keys: Vec<OldKeyStore>,
207    /// Most recently outdated open_invitation VerifyingKey for verifying update_openinv tokens
208    bridgedb_key: Vec<VerifyingKey>,
209    /// Most recently outdated invitation secret and private keys for verifying update_inv credentials
210    invitation_keys: Vec<OldKeyStore>,
211}
212
213#[derive(Debug, Default, Clone, Serialize, Deserialize)]
214#[cfg(feature = "bridgeauth")]
215pub struct OldFilters {
216    /// Most recently outdated lox id filter
217    lox_filter: Vec<dup_filter::DupFilter<Scalar>>,
218    /// Most recently outdated open invitation filter
219    openinv_filter: Vec<dup_filter::DupFilter<Scalar>>,
220    /// Most recently outdated invitation filter
221    invitation_filter: Vec<dup_filter::DupFilter<Scalar>>,
222}
223
224/// An open invitation is a [u8; OPENINV_LENGTH] where the first 32
225/// bytes are the serialization of a random Scalar (the invitation id),
226/// the next 4 bytes are a little-endian bucket number, and the last
227/// SIGNATURE_LENGTH bytes are the signature on the first 36 bytes.
228pub const OPENINV_LENGTH: usize = 32 // the length of the random
229                                     // invitation id (a Scalar)
230    + 4 // the length of the u32 for the bucket number
231    + ed25519_dalek::SIGNATURE_LENGTH; // the length of the signature
232
233#[cfg(feature = "bridgeauth")]
234impl BridgeDb {
235    /// Create the BridgeDb.
236    pub fn new() -> Self {
237        let mut csprng = OsRng {};
238        let keypair = SigningKey::generate(&mut csprng);
239        let pubkey = keypair.verifying_key();
240        Self {
241            keypair,
242            pubkey,
243            openinv_buckets: Default::default(),
244            distributed_buckets: Default::default(),
245            today: Utc::now(),
246            current_k: 0,
247            daily_bridges_distributed: 0,
248        }
249    }
250
251    pub fn openinv_length(&mut self) -> usize {
252        self.openinv_buckets.len()
253    }
254
255    /// Rotate Open Invitation keys
256    pub fn rotate_open_inv_keys(&mut self) -> VerifyingKey {
257        let mut csprng = OsRng {};
258        self.keypair = SigningKey::generate(&mut csprng);
259        self.pubkey = self.keypair.verifying_key();
260        self.pubkey
261    }
262
263    /// Insert an open-invitation bucket into the set
264    pub fn insert_openinv(&mut self, bucket: u32) {
265        self.openinv_buckets.insert(bucket);
266    }
267
268    /// Remove an open-invitation bucket from the set
269    pub fn remove_openinv(&mut self, bucket: &u32) {
270        self.openinv_buckets.remove(bucket);
271    }
272
273    /// Remove open invitation and/or otherwise distributed buckets that have
274    /// become blocked or are expired to free up the index for a new bucket
275    pub fn remove_blocked_or_expired_buckets(&mut self, bucket: &u32) {
276        if self.openinv_buckets.contains(bucket) {
277            println!("Removing a bucket that has not been distributed yet!");
278            self.openinv_buckets.remove(bucket);
279        } else if self.distributed_buckets.contains(bucket) {
280            self.distributed_buckets.retain(|&x| x != *bucket);
281        }
282    }
283
284    /// Mark a bucket as distributed
285    pub fn mark_distributed(&mut self, bucket: u32) {
286        self.distributed_buckets.push(bucket);
287    }
288
289    /// Produce an open invitation such that the next k users, where k is <
290    /// OPENINV_K, will receive the same open invitation bucket
291    /// chosen randomly from the set of open-invitation buckets.
292    pub fn invite(&mut self) -> Result<[u8; OPENINV_LENGTH], OpenInvitationError> {
293        let mut res: [u8; OPENINV_LENGTH] = [0; OPENINV_LENGTH];
294        let mut rng = rand::thread_rng();
295        // Choose a random invitation id (a Scalar) and serialize it
296        let id = Scalar::random(&mut rng);
297        res[0..32].copy_from_slice(&id.to_bytes());
298        let bucket_num: u32;
299        if Utc::now() >= (self.today + Duration::days(1)) {
300            self.today = Utc::now();
301            self.daily_bridges_distributed = 0;
302        }
303        if self.daily_bridges_distributed < MAX_DAILY_BRIDGES {
304            if self.current_k < OPENINV_K && !self.distributed_buckets.is_empty() {
305                bucket_num = *self.distributed_buckets.last().unwrap();
306                self.current_k += 1;
307            } else {
308                if self.openinv_buckets.is_empty() {
309                    return Err(OpenInvitationError::NoBridgesAvailable);
310                }
311                // Choose a random bucket number (from the set of open
312                // invitation buckets) and serialize it
313                let openinv_vec: Vec<&u32> = self.openinv_buckets.iter().collect();
314                bucket_num = *openinv_vec[rng.gen_range(0..openinv_vec.len())];
315                self.mark_distributed(bucket_num);
316                self.remove_openinv(&bucket_num);
317                self.current_k = 1;
318                self.daily_bridges_distributed += 1;
319            }
320            res[32..(32 + 4)].copy_from_slice(&bucket_num.to_le_bytes());
321            // Sign the first 36 bytes and serialize it
322            let sig = self.keypair.sign(&res[0..(32 + 4)]);
323            res[(32 + 4)..].copy_from_slice(&sig.to_bytes());
324            Ok(res)
325        } else {
326            Err(OpenInvitationError::ExceededMaxBridges)
327        }
328    }
329
330    /// Verify an open invitation. Returns the invitation id and the
331    /// bucket number if the signature checked out. It is up to the
332    /// caller to then check that the invitation id has not been used
333    /// before.
334    pub fn verify(
335        invitation: [u8; OPENINV_LENGTH],
336        pubkey: VerifyingKey,
337    ) -> Result<(Scalar, u32), SignatureError> {
338        // Pull out the signature and verify it
339        let sig = Signature::try_from(&invitation[(32 + 4)..])?;
340        pubkey.verify(&invitation[0..(32 + 4)], &sig)?;
341        // The signature passed. Pull out the bucket number and then
342        // the invitation id
343        let bucket = u32::from_le_bytes(invitation[32..(32 + 4)].try_into().unwrap());
344        let s = Scalar::from_canonical_bytes(invitation[0..32].try_into().unwrap());
345        if s.is_some().into() {
346            Ok((s.unwrap(), bucket))
347        } else {
348            // It should never happen that there's a valid signature on
349            // an invalid serialization of a Scalar, but check anyway.
350            Err(SignatureError::new())
351        }
352    }
353
354    ///#[cfg(test)]
355    /// For testing only: manually advance the day by the given number
356    /// of days
357    pub fn advance_days(&mut self, days: u16) {
358        if days > 0 {
359            self.today += Duration::days(days.into());
360            // Reset daily bridges distributed count
361            self.daily_bridges_distributed = 0;
362        }
363    }
364}
365
366#[cfg(feature = "bridgeauth")]
367impl Default for BridgeDb {
368    fn default() -> Self {
369        Self::new()
370    }
371}
372
373/// The bridge authority. This will typically be a singleton object.
374#[cfg(feature = "bridgeauth")]
375#[derive(Debug, Serialize, Deserialize)]
376pub struct BridgeAuth {
377    /// The private key for the main Lox credential
378    lox_priv: IssuerPrivKey,
379    /// The public key for the main Lox credential
380    pub lox_pub: IssuerPubKey,
381    /// The private key for migration credentials
382    migration_priv: IssuerPrivKey,
383    /// The public key for migration credentials
384    pub migration_pub: IssuerPubKey,
385    /// The private key for migration key credentials
386    migrationkey_priv: IssuerPrivKey,
387    /// The public key for migration key credentials
388    pub migrationkey_pub: IssuerPubKey,
389    /// The private key for bucket reachability credentials
390    reachability_priv: IssuerPrivKey,
391    /// The public key for bucket reachability credentials
392    pub reachability_pub: IssuerPubKey,
393    /// The private key for invitation credentials
394    invitation_priv: IssuerPrivKey,
395    /// The public key for invitation credentials
396    pub invitation_pub: IssuerPubKey,
397
398    /// The public key of the BridgeDb issuing open invitations
399    pub bridgedb_pub: VerifyingKey,
400
401    /// The bridge table
402    bridge_table: BridgeTable,
403
404    // Map of bridge fingerprint to values needed to verify TP reports
405    pub tp_bridge_infos: HashMap<String, BridgeVerificationInfo>,
406
407    /// The migration tables
408    trustup_migration_table: MigrationTable,
409    blockage_migration_table: MigrationTable,
410
411    /// Duplicate filter for open invitations
412    bridgedb_pub_filter: dup_filter::DupFilter<Scalar>,
413    /// Duplicate filter for Lox credential ids
414    id_filter: dup_filter::DupFilter<Scalar>,
415    /// Duplicate filter for Invitation credential ids
416    inv_id_filter: dup_filter::DupFilter<Scalar>,
417    /// Duplicate filter for trust promotions (from untrusted level 0 to
418    /// trusted level 1)
419    trust_promotion_filter: dup_filter::DupFilter<Scalar>,
420    // Outdated Lox Keys to be populated with the old Lox private and public keys
421    // after a key rotation
422    old_keys: OldKeys,
423    old_filters: OldFilters,
424
425    /// For testing only: offset of the true time to the simulated time
426    #[serde(skip)]
427    time_offset: time::Duration,
428}
429
430#[cfg(feature = "bridgeauth")]
431impl BridgeAuth {
432    pub fn new(bridgedb_pub: VerifyingKey) -> Self {
433        // Create the private and public keys for each of the types of
434        // credential, each with the appropriate number of attributes
435        let lox_priv = IssuerPrivKey::new(6);
436        let lox_pub = IssuerPubKey::new(&lox_priv);
437        let migration_priv = IssuerPrivKey::new(4);
438        let migration_pub = IssuerPubKey::new(&migration_priv);
439        let migrationkey_priv = IssuerPrivKey::new(2);
440        let migrationkey_pub = IssuerPubKey::new(&migrationkey_priv);
441        let reachability_priv = IssuerPrivKey::new(2);
442        let reachability_pub = IssuerPubKey::new(&reachability_priv);
443        let invitation_priv = IssuerPrivKey::new(4);
444        let invitation_pub = IssuerPubKey::new(&invitation_priv);
445        Self {
446            lox_priv,
447            lox_pub,
448            migration_priv,
449            migration_pub,
450            migrationkey_priv,
451            migrationkey_pub,
452            reachability_priv,
453            reachability_pub,
454            invitation_priv,
455            invitation_pub,
456            bridgedb_pub,
457            bridge_table: Default::default(),
458            tp_bridge_infos: HashMap::<String, BridgeVerificationInfo>::new(),
459            trustup_migration_table: MigrationTable::new(MigrationType::TrustUpgrade),
460            blockage_migration_table: MigrationTable::new(MigrationType::Blockage),
461            bridgedb_pub_filter: Default::default(),
462            id_filter: Default::default(),
463            inv_id_filter: Default::default(),
464            trust_promotion_filter: Default::default(),
465            time_offset: time::Duration::ZERO,
466            old_keys: Default::default(),
467            old_filters: Default::default(),
468        }
469    }
470
471    pub fn rotate_lox_keys(&mut self) {
472        let updated_lox_priv = IssuerPrivKey::new(6);
473        let updated_lox_pub = IssuerPubKey::new(&updated_lox_priv);
474        // Store the old keys until the next key rotation (this should happen no more than 511 days after the
475        // last rotation to ensure that all credentials issued with the old key can be updated
476        self.old_keys.lox_keys.push(OldKeyStore {
477            priv_key: self.lox_priv.clone(),
478            pub_key: self.lox_pub.clone(),
479        });
480        // Move the old lox id filter to the old_lox_id_filter
481        self.old_filters.lox_filter.push(self.id_filter.clone());
482        // TODO: Commit to the new keys and post the commitment somewhere public that can be verified
483        // by users, ideally
484        self.lox_priv = updated_lox_priv;
485        self.lox_pub = updated_lox_pub;
486        self.id_filter = Default::default();
487    }
488
489    pub fn rotate_invitation_keys(&mut self) {
490        let updated_invitation_priv = IssuerPrivKey::new(4);
491        let updated_invitation_pub = IssuerPubKey::new(&updated_invitation_priv);
492        // Store the old keys until the next key rotation (this should happen no more than 511 days after the
493        // last rotation to ensure that all credentials issued with the old key can be updated
494        self.old_keys.invitation_keys.push(OldKeyStore {
495            priv_key: self.invitation_priv.clone(),
496            pub_key: self.invitation_pub.clone(),
497        });
498        // Move the old invitation id filter to the old_invitation_id_filter
499        self.old_filters
500            .invitation_filter
501            .push(self.inv_id_filter.clone());
502        // TODO: Commit to the new keys and post the commitment somewhere public that can be verified
503        // by users, ideally
504        self.invitation_priv = updated_invitation_priv;
505        self.invitation_pub = updated_invitation_pub;
506        self.inv_id_filter = Default::default();
507    }
508
509    pub fn rotate_bridgedb_keys(&mut self, new_bridgedb_pub: VerifyingKey) {
510        // Store the old verifying key until the next key rotation (this should happen no more often than the
511        // we would reasonably expect a user to redeem an open invitation token to ensure that all invitations
512        // issued with the old key can be updated)
513        self.old_keys.bridgedb_key.push(self.bridgedb_pub);
514        // Move the old lox id filter to the old_lox_id_filter
515        self.old_filters
516            .openinv_filter
517            .push(self.bridgedb_pub_filter.clone());
518        // TODO: Commit to the new keys and post the commitment somewhere public that can be verified
519        // by users, ideally
520        self.bridgedb_pub = new_bridgedb_pub;
521        self.bridgedb_pub_filter = Default::default();
522    }
523
524    pub fn is_empty(&self) -> bool {
525        self.bridge_table.buckets.is_empty()
526    }
527
528    pub fn reachable_length(&self) -> usize {
529        self.bridge_table.reachable.len()
530    }
531
532    pub fn unallocated_length(&self) -> usize {
533        self.bridge_table.unallocated_bridges.len()
534    }
535
536    pub fn spares_length(&self) -> usize {
537        self.bridge_table.spares.len()
538    }
539
540    pub fn openinv_length(&self, bdb: &mut BridgeDb) -> usize {
541        bdb.openinv_length()
542    }
543
544    #[cfg(feature = "blockage-detection")]
545    pub fn fingerprint_hasher(&self, unhashed_fingerprint: [u8; 20]) -> String {
546        let mut hasher = Sha1::new();
547        hasher.update(unhashed_fingerprint);
548        let fingerprint: [u8; 20] = hasher.finalize().into();
549        array_bytes::bytes2hex("", fingerprint)
550    }
551
552    #[cfg(feature = "blockage-detection")]
553    pub fn get_tp_bucket_and_fingerprint(&self, bridge: &BridgeLine, id: &u32) -> (String, Scalar) {
554        // Get hashed fingerprint
555        let key = self.bridge_table.keys.get(id).unwrap();
556        let bucket = bridge_table::to_scalar(*id, key);
557        let fingerprint_str = self.fingerprint_hasher(bridge.unhashed_fingerprint);
558        (fingerprint_str, bucket)
559    }
560
561    #[cfg(feature = "blockage-detection")]
562    pub fn generate_bridge_infos(&mut self) {
563        // Go through all buckets and all bridges in buckets, map bridge to
564        // buckets containing it. Note that a bridge may be contained within
565        // multiple buckets (open invitation buckets and invite-only buckets)
566        let buckets = &self.bridge_table.buckets;
567        for id in buckets.keys() {
568            let bridges = buckets.get(id).unwrap();
569            let key = self.bridge_table.keys.get(id).unwrap();
570            let bucket = bridge_table::to_scalar(*id, key);
571            for bridge in bridges {
572                if bridge.unhashed_fingerprint == [0; 20] {
573                    continue;
574                }
575                let fingerprint_str = self.fingerprint_hasher(bridge.unhashed_fingerprint);
576
577                // Add bucket to existing entry or add new entry
578                match self.tp_bridge_infos.get_mut(&fingerprint_str) {
579                    Some(info) => {
580                        info.buckets.insert(bucket);
581                    }
582                    None => {
583                        let mut buckets = HashSet::<Scalar>::new();
584                        buckets.insert(bucket);
585                        self.tp_bridge_infos.insert(
586                            fingerprint_str,
587                            BridgeVerificationInfo {
588                                bridge_line: *bridge,
589                                buckets,
590                            },
591                        );
592                    }
593                };
594            }
595        }
596    }
597
598    /// Insert a set of open invitation bridges.
599    ///
600    /// Each of the bridges will be given its own open invitation
601    /// bucket, and the BridgeDb will be informed. A single bucket
602    /// containing all of the bridges will also be created, with a trust
603    /// upgrade migration from each of the single-bridge buckets.
604    pub fn add_openinv_bridges(
605        &mut self,
606        bridges: [BridgeLine; MAX_BRIDGES_PER_BUCKET],
607        bdb: &mut BridgeDb,
608    ) -> Result<(), NoAvailableIDError> {
609        let bindex = self.find_next_available_key(bdb)?;
610        self.bridge_table.new_bucket(bindex, &bridges);
611        let mut single = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET];
612        for b in bridges.iter() {
613            let sindex = self.find_next_available_key(bdb)?;
614            single[0] = *b;
615            self.bridge_table.new_bucket(sindex, &single);
616            self.bridge_table.open_inv_keys.push((sindex, self.today()));
617            bdb.insert_openinv(sindex);
618            self.trustup_migration_table.table.insert(sindex, bindex);
619        }
620        Ok(())
621    }
622
623    /// Insert a hot spare bucket of bridges
624    pub fn add_spare_bucket(
625        &mut self,
626        bucket: [BridgeLine; MAX_BRIDGES_PER_BUCKET],
627        bdb: &mut BridgeDb,
628    ) -> Result<(), NoAvailableIDError> {
629        let index = self.find_next_available_key(bdb)?;
630        self.bridge_table.new_bucket(index, &bucket);
631        self.bridge_table.spares.insert(index);
632        Ok(())
633    }
634
635    /// When syncing the Lox bridge table with rdsys, this function returns any bridges
636    /// that are found in the Lox bridge table that are not found in the Vector
637    /// of bridges received from rdsys through the Lox distributor.
638    pub fn find_and_remove_unaccounted_for_bridges(
639        &mut self,
640        accounted_for_bridges: Vec<u64>,
641    ) -> Vec<BridgeLine> {
642        let mut unaccounted_for: Vec<BridgeLine> = Vec::new();
643        for (k, _v) in self.bridge_table.reachable.clone() {
644            if !accounted_for_bridges.contains(&k.uid_fingerprint) {
645                unaccounted_for.push(k);
646            }
647        }
648        unaccounted_for
649    }
650
651    /// Allocate single left over bridges to an open invitation bucket
652    pub fn allocate_bridges(
653        &mut self,
654        distributor_bridges: &mut Vec<BridgeLine>,
655        bdb: &mut BridgeDb,
656    ) {
657        while let Some(bridge) = distributor_bridges.pop() {
658            self.bridge_table.unallocated_bridges.push(bridge);
659        }
660        while self.bridge_table.unallocated_bridges.len() >= MAX_BRIDGES_PER_BUCKET {
661            let mut bucket = [BridgeLine::default(); MAX_BRIDGES_PER_BUCKET];
662            for bridge in bucket.iter_mut() {
663                *bridge = self.bridge_table.unallocated_bridges.pop().unwrap();
664            }
665            match self.add_openinv_bridges(bucket, bdb) {
666                Ok(_) => continue,
667                Err(e) => {
668                    println!("Error: {:?}", e);
669                    for bridge in bucket {
670                        self.bridge_table.unallocated_bridges.push(bridge);
671                    }
672                }
673            }
674        }
675    }
676
677    // Update the details of a bridge in the bridge table. This assumes that the IP and Port
678    // of a given bridge remains the same and thus can be updated.
679    // First we must retrieve the list of reachable bridges, then we must search for any matching our partial key
680    // which will include the IP and Port. Finally we can replace the original bridge with the updated bridge.
681    // Returns true if the bridge has successfully updated
682    pub fn bridge_update(&mut self, bridge: &BridgeLine) -> bool {
683        let mut res: bool = false; //default False to assume that update failed
684        let reachable_bridges = self.bridge_table.reachable.clone();
685        for reachable_bridge in reachable_bridges {
686            if reachable_bridge.0.uid_fingerprint == bridge.uid_fingerprint {
687                // Now we must remove the old bridge from the table and insert the new bridge in its place
688                // i.e., in the same bucket and with the same permissions.
689                let positions = self.bridge_table.reachable.get(&reachable_bridge.0);
690                if let Some(v) = positions {
691                    for (bucketnum, offset) in v.iter() {
692                        let mut bridgelines = match self.bridge_table.buckets.get(bucketnum) {
693                            Some(bridgelines) => *bridgelines,
694                            None => return res,
695                        };
696                        assert!(bridgelines[*offset] == reachable_bridge.0);
697                        bridgelines[*offset] = *bridge;
698                        self.bridge_table.buckets.insert(*bucketnum, bridgelines);
699                        #[cfg(feature = "blockage-detection")]
700                        let (fingerprint_str, bucket) =
701                            self.get_tp_bucket_and_fingerprint(bridge, bucketnum);
702                        // Add bucket to existing entry or add new entry
703                        #[cfg(feature = "blockage-detection")]
704                        match self.tp_bridge_infos.get_mut(&fingerprint_str) {
705                            Some(info) => {
706                                info.buckets.insert(bucket);
707                            }
708                            None => {
709                                let mut buckets = HashSet::<Scalar>::new();
710                                buckets.insert(bucket);
711                                self.tp_bridge_infos.insert(
712                                    fingerprint_str,
713                                    BridgeVerificationInfo {
714                                        bridge_line: *bridge,
715                                        buckets,
716                                    },
717                                );
718                            }
719                        };
720                        if !self.bridge_table.buckets.contains_key(bucketnum) {
721                            return res;
722                        }
723                    }
724                    res = true;
725                } else {
726                    return res;
727                }
728                // We must also remove the old bridge from the reachable bridges table
729                // and add the new bridge
730                self.bridge_table.reachable.remove(&reachable_bridge.0);
731                self.bridge_table
732                    .reachable
733                    .insert(*bridge, reachable_bridge.1);
734                return res;
735            }
736        }
737        // Also check the unallocated bridges just in case there is a bridge that should be updated there
738        let unallocated_bridges = self.bridge_table.unallocated_bridges.clone();
739        for (i, unallocated_bridge) in unallocated_bridges.iter().enumerate() {
740            if unallocated_bridge.uid_fingerprint == bridge.uid_fingerprint {
741                // Now we must remove the old bridge from the unallocated bridges and insert the new bridge
742                // in its place
743                self.bridge_table.unallocated_bridges.remove(i);
744                self.bridge_table.unallocated_bridges.push(*bridge);
745                res = true;
746            }
747        }
748        // If this is returned, we assume that the bridge wasn't found in the bridge table
749        // and therefore should be treated as a "new bridge"
750        res
751    }
752
753    // Repurpose a bucket of spares into unallocated bridges
754    pub fn dissolve_spare_bucket(&mut self, key: u32) -> Result<(), BridgeTableError> {
755        self.bridge_table.spares.remove(&key);
756        // Get the actual bridges from the spare bucket
757        let spare_bucket = self
758            .bridge_table
759            .buckets
760            .remove(&key)
761            .ok_or(BridgeTableError::MissingBucket(key))?;
762        for bridge in spare_bucket.iter() {
763            self.bridge_table.unallocated_bridges.push(*bridge);
764            // Mark bucket as unreachable while it is unallocated
765            self.bridge_table.reachable.remove(bridge);
766        }
767        self.bridge_table.keys.remove(&key);
768        self.bridge_table.recycleable_keys.push(key);
769        Ok(())
770    }
771
772    // Removes an unallocated bridge and returns it if it was present
773    pub fn remove_unallocated(&mut self, bridge: &BridgeLine) -> Option<BridgeLine> {
774        #[cfg(feature = "blockage-detection")]
775        let fingerprint_str = self.fingerprint_hasher(bridge.unhashed_fingerprint);
776        match self
777            .bridge_table
778            .unallocated_bridges
779            .iter()
780            .position(|x| x == bridge)
781        {
782            Some(index) => Some({
783                #[cfg(feature = "blockage-detection")]
784                self.tp_bridge_infos.remove_entry(&fingerprint_str);
785                self.bridge_table.unallocated_bridges.swap_remove(index)
786            }),
787            None => None,
788        }
789    }
790
791    /// Attempt to remove a bridge that is failing tests and replace it with a bridge from
792    /// available_bridge or from a spare bucket
793    pub fn bridge_replace(
794        &mut self,
795        bridge: &BridgeLine,
796        available_bridge: Option<BridgeLine>,
797    ) -> ReplaceSuccess {
798        let reachable_bridges = &self.bridge_table.reachable.clone();
799        let Some(positions) = reachable_bridges.get(bridge) else {
800            match self.remove_unallocated(bridge) {
801                Some(_) => {
802                    return ReplaceSuccess::Removed;
803                }
804                None => {
805                    return ReplaceSuccess::NotFound;
806                }
807            }
808        };
809        // Check if the bridge is in a spare bucket first, if it is, dissolve the bucket
810        if let Some(spare) = self
811            .bridge_table
812            .spares
813            .iter()
814            .find(|x| positions.iter().any(|(bucketnum, _)| &bucketnum == x))
815            .cloned()
816        {
817            let Ok(_) = self.dissolve_spare_bucket(spare) else {
818                return ReplaceSuccess::NotReplaced;
819            };
820            // Next Check if the bridge is in the unallocated bridges and remove the bridge if so
821            // Bridges in spare buckets should have been moved to the unallocated bridges
822            match self.remove_unallocated(bridge) {
823                Some(_) => {
824                    return ReplaceSuccess::Removed;
825                }
826                None => {
827                    return ReplaceSuccess::NotFound;
828                }
829            }
830        }
831        // select replacement:
832        //   - first try the given bridge
833        //   - second try to pick one from the set of available bridges
834        //   - third dissolve a spare bucket to create more available bridges
835        let Some(replacement) = available_bridge.or_else(|| {
836            self.bridge_table.unallocated_bridges.pop().or_else(|| {
837                let spare = self
838                    .bridge_table
839                    .spares
840                    .iter()
841                    // in case bridge is a spare, avoid replacing it with itself
842                    .find(|x| !positions.iter().any(|(bucketnum, _)| &bucketnum == x))
843                    .cloned()?;
844                let Ok(_) = self.dissolve_spare_bucket(spare) else {
845                    return None;
846                };
847                self.bridge_table.unallocated_bridges.pop()
848            })
849        }) else {
850            // If there are no available bridges that can be assigned here, the only thing
851            // that can be done is return an indication that updating the gone bridge
852            // didn't work.
853            // In this case, we do not mark the bridge as unreachable or remove it from the
854            // reachable bridges so that we can still find it when a new bridge does become available
855            println!("No available bridges");
856            return ReplaceSuccess::NotReplaced;
857        };
858        for (bucketnum, offset) in positions.iter() {
859            let mut bridgelines = match self.bridge_table.buckets.get(bucketnum) {
860                Some(bridgelines) => *bridgelines,
861                None => return ReplaceSuccess::NotFound,
862            };
863            assert!(bridgelines[*offset] == *bridge);
864            bridgelines[*offset] = replacement;
865            self.bridge_table.buckets.insert(*bucketnum, bridgelines);
866            // Remove the bridge from the reachable bridges and add new bridge
867            self.bridge_table
868                .reachable
869                .insert(replacement, positions.clone());
870            // Remove the bridge from the bucket
871            self.bridge_table.reachable.remove(bridge);
872        }
873        ReplaceSuccess::Replaced
874    }
875
876    pub fn get_bridge_verification_info(
877        &mut self,
878        bridge_str: &String,
879    ) -> Option<&BridgeVerificationInfo> {
880        self.tp_bridge_infos.get(bridge_str)
881    }
882
883    // Remove Bridge Info for blocked bridge and return the bridgeline with the given fingerprint
884    pub fn block_by_string(&mut self, bridge_str: &String) -> Option<BridgeLine> {
885        if let Some(bridge_verification_info) = self.tp_bridge_infos.remove(bridge_str) {
886            return Some(bridge_verification_info.bridge_line);
887        }
888        None
889    }
890
891    /// Mark a bridge as blocked
892    ///
893    /// This bridge will be removed from each of the buckets that
894    /// contains it. If any of those are open-invitation buckets, the
895    /// trust upgrade migration for that bucket will be removed and the
896    /// BridgeDb will be informed to stop handing out that bridge. If
897    /// any of those are trusted buckets where the number of reachable
898    /// bridges has fallen below the threshold, a blockage migration
899    /// from that bucket to a spare bucket will be added, and the spare
900    /// bucket will be removed from the list of hot spares. In
901    /// addition, if the blocked bucket was the _target_ of a blockage
902    /// migration, change the target to the new (formerly spare) bucket.
903    /// Returns true if sucessful, or false if it needed a hot spare but
904    /// there was none available.
905    pub fn bridge_blocked(&mut self, bridge: &BridgeLine, bdb: &mut BridgeDb) -> bool {
906        let mut res: bool = true;
907        if self.remove_unallocated(bridge).is_some() {
908            return true;
909        }
910        if let Some(positions) = self.bridge_table.reachable.get(bridge) {
911            for (bucketnum, offset) in positions.iter() {
912                // Count how many bridges in this bucket are reachable
913                let mut bucket = match self.bridge_table.buckets.get(bucketnum) {
914                    Some(bridgelines) => *bridgelines,
915                    None => return false, // This should not happen
916                };
917                // Remove the bridge from the bucket
918                assert!(bucket[*offset] == *bridge);
919                bucket[*offset] = BridgeLine::default();
920
921                // If this is an open invitation bucket, there is only one bridge, remove bucket
922                if bdb.openinv_buckets.contains(bucketnum)
923                    || bdb.distributed_buckets.contains(bucketnum)
924                {
925                    bdb.remove_blocked_or_expired_buckets(bucketnum);
926                    self.trustup_migration_table.table.remove(bucketnum);
927                    continue;
928                }
929
930                // If this bucket still has an acceptable number of bridges, continue
931                let numreachable = bucket
932                    .iter()
933                    .filter(|br| self.bridge_table.reachable.contains_key(br))
934                    .count();
935                if numreachable != MIN_BUCKET_REACHABILITY {
936                    // No
937                    continue;
938                }
939
940                // Remove any trust upgrade migrations to this bucket
941                self.trustup_migration_table
942                    .table
943                    .retain(|_, &mut v| v != *bucketnum);
944
945                // If there are no spares, delete blockage migrations leading to this bucket
946                if self.bridge_table.spares.is_empty() {
947                    res = false;
948                    self.blockage_migration_table
949                        .table
950                        .retain(|_, &mut v| v != *bucketnum);
951                    continue;
952                }
953                // Get the first spare and remove it from the spares
954                // set.
955                let spare = *self.bridge_table.spares.iter().next().unwrap();
956                self.bridge_table.spares.remove(&spare);
957                self.bridge_table
958                    .blocked_keys
959                    .push((*bucketnum, self.today()));
960                // Add a blockage migration from this bucket to the spare
961                self.blockage_migration_table
962                    .table
963                    .insert(*bucketnum, spare);
964                // Change any blockage migrations with this bucket
965                // as the destination to the spare
966                for (_, v) in self.blockage_migration_table.table.iter_mut() {
967                    if *v == *bucketnum {
968                        *v = spare;
969                    }
970                }
971            }
972        }
973        self.bridge_table.reachable.remove(bridge);
974
975        res
976    }
977
978    // Since buckets are moved around in the bridge_table, finding a lookup key that
979    // does not overwrite existing bridges could become an issue.We keep a list
980    // of recycleable lookup keys from buckets that have been removed and prioritize
981    // this list before increasing the counter
982    fn find_next_available_key(&mut self, bdb: &mut BridgeDb) -> Result<u32, NoAvailableIDError> {
983        self.clean_up_expired_buckets(bdb);
984        if self.bridge_table.recycleable_keys.is_empty() {
985            let mut test_index = 1;
986            let mut test_counter = self.bridge_table.counter.wrapping_add(test_index);
987            let mut i = 0;
988            while self.bridge_table.buckets.contains_key(&test_counter) && i < 5000 {
989                test_index += 1;
990                test_counter = self.bridge_table.counter.wrapping_add(test_index);
991                i += 1;
992                if i == 5000 {
993                    return Err(NoAvailableIDError::ExhaustedIndexer);
994                }
995            }
996            self.bridge_table.counter = self.bridge_table.counter.wrapping_add(test_index);
997            Ok(self.bridge_table.counter)
998        } else {
999            Ok(self.bridge_table.recycleable_keys.pop().unwrap())
1000        }
1001    }
1002
1003    // This function looks for and removes buckets so their indexes can be reused
1004    // This should include buckets that have been blocked for a sufficiently long period
1005    // that we no longer want to allow migration to, or else, open-entry buckets that
1006    // have been unblocked long enough to become trusted and who's users' credentials
1007    // would have expired (after EXPIRY_DATE)
1008    pub fn clean_up_expired_buckets(&mut self, bdb: &mut BridgeDb) {
1009        // First check if there are any blocked indexes that are old enough to be replaced
1010        self.clean_up_blocked();
1011        // Next do the same for open_invitations buckets
1012        self.clean_up_open_entry(bdb);
1013    }
1014
1015    /// Cleans up exipred blocked buckets
1016    fn clean_up_blocked(&mut self) {
1017        // If there are expired blockages, separate them from the fresh blockages
1018        #[allow(clippy::type_complexity)]
1019        let (expired, fresh): (Vec<(u32, u32)>, Vec<(u32, u32)>) = self
1020            .bridge_table
1021            .blocked_keys
1022            .iter()
1023            .partition(|&x| x.1 + EXPIRY_DATE < self.today());
1024        for item in expired {
1025            let key = item.0;
1026            // check each single bridge line and ensure none are still marked as reachable.
1027            // if any are still reachable, remove from reachable bridges.
1028            // When syncing resources, we will likely have to reallocate this bridge but if it hasn't already been
1029            // blocked, this might be fine?
1030            let bridgelines = self.bridge_table.buckets.get(&key).unwrap();
1031            for bridgeline in bridgelines {
1032                // If the bridge hasn't been set to default, assume it's still reachable
1033                if bridgeline.port > 0 {
1034                    // Move to unallocated bridges
1035                    self.bridge_table.unallocated_bridges.push(*bridgeline);
1036                    // Make sure bridge is removed from reachable bridges
1037                    self.bridge_table.reachable.remove(bridgeline);
1038                }
1039            }
1040            // Then remove the bucket and keys at the specified index
1041            self.bridge_table.buckets.remove(&key);
1042            self.bridge_table.keys.remove(&key);
1043            //and add them to the recyclable keys
1044            self.bridge_table.recycleable_keys.push(key);
1045            // Remove the expired blocked bucket from the blockage migration table,
1046            // assuming that anyone that has still not attempted to migrate from their
1047            // blocked bridge after the EXPIRY_DATE probably doesn't still need to migrate.
1048            self.blockage_migration_table.table.retain(|&k, _| k != key);
1049        }
1050        // Finally, update the blocked_keys vector to only include the fresh keys
1051        self.bridge_table.blocked_keys = fresh
1052    }
1053
1054    /// Cleans up expired open invitation buckets
1055    fn clean_up_open_entry(&mut self, bdb: &mut BridgeDb) {
1056        // Separate exipred from fresh open invitation indexes
1057        #[allow(clippy::type_complexity)]
1058        let (expired, fresh): (Vec<(u32, u32)>, Vec<(u32, u32)>) = self
1059            .bridge_table
1060            .open_inv_keys
1061            .iter()
1062            .partition(|&x| x.1 + EXPIRY_DATE < self.today());
1063        for item in expired {
1064            let key = item.0;
1065            // We should check that the items were actually distributed before they are removed
1066            if !bdb.distributed_buckets.contains(&key) {
1067                // TODO: Add prometheus metric for this?
1068                println!("This bucket was not actually distributed!");
1069            }
1070            bdb.remove_blocked_or_expired_buckets(&key);
1071            // Remove any trust upgrade migrations from this
1072            // bucket
1073            self.trustup_migration_table.table.retain(|&k, _| k != key);
1074            self.bridge_table.buckets.remove(&key);
1075            self.bridge_table.keys.remove(&key);
1076            //and add them to the recyclable keys
1077            self.bridge_table.recycleable_keys.push(key);
1078        }
1079        // update the open_inv_keys vector to only include the fresh keys
1080        self.bridge_table.open_inv_keys = fresh
1081    }
1082
1083    #[cfg(test)]
1084    /// For testing only: manually advance the day by 1 day
1085    pub fn advance_day(&mut self) {
1086        self.time_offset += time::Duration::days(1);
1087    }
1088
1089    ///#[cfg(test)]
1090    /// For testing only: manually advance the day by the given number
1091    /// of days
1092    pub fn advance_days(&mut self, days: u16) {
1093        self.time_offset += time::Duration::days(days.into());
1094    }
1095
1096    /// Get today's (real or simulated) date as u32
1097    pub fn today(&self) -> u32 {
1098        // We will not encounter negative Julian dates (~6700 years ago)
1099        // or ones larger than 32 bits
1100        (time::OffsetDateTime::now_utc().date() + self.time_offset)
1101            .to_julian_day()
1102            .try_into()
1103            .unwrap()
1104    }
1105
1106    /// Get today's (real or simulated) date as a DateTime<Utc> value
1107    pub fn today_date(&self) -> DateTime<Utc> {
1108        Utc::now()
1109    }
1110
1111    /// Get a reference to the encrypted bridge table.
1112    ///
1113    /// Be sure to call this function when you want the latest version
1114    /// of the table, since it will put fresh Bucket Reachability
1115    /// credentials in the buckets each day.
1116    pub fn enc_bridge_table(&mut self) -> &HashMap<u32, EncryptedBucket> {
1117        let today = self.today();
1118        if self.bridge_table.date_last_enc != today {
1119            self.bridge_table
1120                .encrypt_table(today, &self.reachability_priv);
1121        }
1122        &self.bridge_table.encbuckets
1123    }
1124
1125    #[cfg(test)]
1126    /// Verify the two MACs on a Lox credential
1127    pub fn verify_lox(&self, cred: &cred::Lox) -> bool {
1128        if cred.P.is_identity() {
1129            return false;
1130        }
1131
1132        let Q = (self.lox_priv.x[0]
1133            + cred.id * self.lox_priv.x[1]
1134            + cred.bucket * self.lox_priv.x[2]
1135            + cred.trust_level * self.lox_priv.x[3]
1136            + cred.level_since * self.lox_priv.x[4]
1137            + cred.invites_remaining * self.lox_priv.x[5]
1138            + cred.blockages * self.lox_priv.x[6])
1139            * cred.P;
1140
1141        Q == cred.Q
1142    }
1143
1144    #[cfg(test)]
1145    /// Verify the MAC on a Migration credential
1146    pub fn verify_migration(&self, cred: &cred::Migration) -> bool {
1147        if cred.P.is_identity() {
1148            return false;
1149        }
1150
1151        let Q = (self.migration_priv.x[0]
1152            + cred.lox_id * self.migration_priv.x[1]
1153            + cred.from_bucket * self.migration_priv.x[2]
1154            + cred.to_bucket * self.migration_priv.x[3])
1155            * cred.P;
1156
1157        Q == cred.Q
1158    }
1159
1160    #[cfg(test)]
1161    /// Verify the MAC on a Bucket Reachability credential
1162    pub fn verify_reachability(&self, cred: &cred::BucketReachability) -> bool {
1163        if cred.P.is_identity() {
1164            return false;
1165        }
1166
1167        let Q = (self.reachability_priv.x[0]
1168            + cred.date * self.reachability_priv.x[1]
1169            + cred.bucket * self.reachability_priv.x[2])
1170            * cred.P;
1171
1172        Q == cred.Q
1173    }
1174
1175    #[cfg(test)]
1176    /// Verify the MAC on a Invitation credential
1177    pub fn verify_invitation(&self, cred: &cred::Invitation) -> bool {
1178        if cred.P.is_identity() {
1179            return false;
1180        }
1181
1182        let Q = (self.invitation_priv.x[0]
1183            + cred.inv_id * self.invitation_priv.x[1]
1184            + cred.date * self.invitation_priv.x[2]
1185            + cred.bucket * self.invitation_priv.x[3]
1186            + cred.blockages * self.invitation_priv.x[4])
1187            * cred.P;
1188
1189        Q == cred.Q
1190    }
1191}
1192
1193/// Try to extract a u64 from a Scalar
1194pub fn scalar_u64(s: &Scalar) -> Option<u64> {
1195    // Check that the top 24 bytes of the Scalar are 0
1196    let sbytes = s.as_bytes();
1197    if sbytes[8..].ct_eq(&[0u8; 24]).unwrap_u8() == 0 {
1198        return None;
1199    }
1200    Some(u64::from_le_bytes(sbytes[..8].try_into().unwrap()))
1201}
1202
1203/// Try to extract a u32 from a Scalar
1204pub fn scalar_u32(s: &Scalar) -> Option<u32> {
1205    // Check that the top 28 bytes of the Scalar are 0
1206    let sbytes = s.as_bytes();
1207    if sbytes[4..].ct_eq(&[0u8; 28]).unwrap_u8() == 0 {
1208        return None;
1209    }
1210    Some(u32::from_le_bytes(sbytes[..4].try_into().unwrap()))
1211}
1212
1213/// Double a Scalar
1214pub fn scalar_dbl(s: &Scalar) -> Scalar {
1215    s + s
1216}
1217
1218/// Double a RistrettoPoint
1219pub fn pt_dbl(P: &RistrettoPoint) -> RistrettoPoint {
1220    P + P
1221}
1222
1223/// The protocol modules.
1224///
1225/// Each protocol lives in a submodule. Each submodule defines structs
1226/// for Request (the message from the client to the bridge authority),
1227/// State (the state held by the client while waiting for the reply),
1228/// and Response (the message from the bridge authority to the client).
1229/// Each submodule defines functions request, which produces a (Request,
1230/// State) pair, and handle_response, which consumes a State and a
1231/// Response. It also adds a handle_* function to the BridgeAuth struct
1232/// that consumes a Request and produces a Result<Response, ProofError>.
1233pub mod proto {
1234    pub mod blockage_migration;
1235    pub mod check_blockage;
1236    pub mod errors;
1237    pub mod issue_invite;
1238    pub mod level_up;
1239    pub mod migration;
1240    pub mod open_invite;
1241    pub mod redeem_invite;
1242    pub mod trust_promotion;
1243    pub mod update_cred;
1244    pub mod update_invite;
1245}
1246
1247// Unit tests
1248#[cfg(test)]
1249mod tests;