|
|
@@ -1,10 +1,246 @@
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+use chrono::{DateTime, Utc};
|
|
|
use curve25519_dalek::scalar::Scalar;
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+use ed25519_dalek::{Signature, SignatureError, SigningKey, Verifier, VerifyingKey};
|
|
|
+#[allow(unused_imports)]
|
|
|
+use rand::rngs::OsRng;
|
|
|
use subtle::ConstantTimeEq;
|
|
|
|
|
|
+pub mod bridge_table;
|
|
|
+pub mod dup_filter;
|
|
|
pub mod lox_creds;
|
|
|
pub mod proto {
|
|
|
pub mod errors;
|
|
|
pub mod level_up;
|
|
|
+ pub mod open_invite;
|
|
|
+}
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+use bridge_table::BridgeTable;
|
|
|
+// BridgeLine, EncryptedBucket, MAX_BRIDGES_PER_BUCKET, MIN_BUCKET_REACHABILITY,
|
|
|
+//};
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+use serde::{Deserialize, Serialize};
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+use std::collections::HashSet;
|
|
|
+
|
|
|
+/// Number of times a given invitation is ditributed
|
|
|
+pub const OPENINV_K: u32 = 10;
|
|
|
+/// TODO: Decide on maximum daily number of invitations to be distributed
|
|
|
+pub const MAX_DAILY_BRIDGES: u32 = 100;
|
|
|
+/// The BridgeDb. This will typically be a singleton object. The
|
|
|
+/// BridgeDb's role is simply to issue signed "open invitations" to
|
|
|
+/// people who are not yet part of the system.
|
|
|
+#[derive(Debug, Serialize, Deserialize)]
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+pub struct BridgeDb {
|
|
|
+ /// The keypair for signing open invitations
|
|
|
+ keypair: SigningKey,
|
|
|
+ /// The public key for verifying open invitations
|
|
|
+ pub pubkey: VerifyingKey,
|
|
|
+ /// The set of open-invitation buckets
|
|
|
+ openinv_buckets: HashSet<u32>,
|
|
|
+ /// The set of open invitation buckets that have been distributed
|
|
|
+ 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 {
|
|
|
+ // /// Most recently outdated lox secret and private keys for verifying update_cred credentials
|
|
|
+ // priv_key: IssuerPrivKey,
|
|
|
+ // /// The public key for verifying update_cred credentials
|
|
|
+ // pub pub_key: IssuerPubKey,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+pub struct OldKeys {
|
|
|
+ /// Most recently outdated lox secret and private keys for verifying update_cred credentials
|
|
|
+ lox_keys: Vec<OldKeyStore>,
|
|
|
+ /// Most recently outdated open_invitation VerifyingKey for verifying update_openinv tokens
|
|
|
+ bridgedb_key: Vec<VerifyingKey>,
|
|
|
+ /// Most recently outdated invitation secret and private keys for verifying update_inv credentials
|
|
|
+ invitation_keys: Vec<OldKeyStore>,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+pub struct OldFilters {
|
|
|
+ /// Most recently outdated lox id filter
|
|
|
+ lox_filter: Vec<dup_filter::DupFilter<Scalar>>,
|
|
|
+ /// Most recently outdated open invitation filter
|
|
|
+ openinv_filter: Vec<dup_filter::DupFilter<Scalar>>,
|
|
|
+ /// Most recently outdated invitation filter
|
|
|
+ invitation_filter: Vec<dup_filter::DupFilter<Scalar>>,
|
|
|
+}
|
|
|
+
|
|
|
+/// An open invitation is a [u8; OPENINV_LENGTH] where the first 32
|
|
|
+/// bytes are the serialization of a random Scalar (the invitation id),
|
|
|
+/// the next 4 bytes are a little-endian bucket number, and the last
|
|
|
+/// SIGNATURE_LENGTH bytes are the signature on the first 36 bytes.
|
|
|
+pub const OPENINV_LENGTH: usize = 32 // the length of the random
|
|
|
+ // invitation id (a Scalar)
|
|
|
+ + 4 // the length of the u32 for the bucket number
|
|
|
+ + ed25519_dalek::SIGNATURE_LENGTH; // the length of the signature
|
|
|
+
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+impl BridgeDb {
|
|
|
+ /// Create the 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,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /// Verify an open invitation. Returns the invitation id and 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
|
|
|
+ /// before.
|
|
|
+ pub fn verify(
|
|
|
+ invitation: [u8; OPENINV_LENGTH],
|
|
|
+ pubkey: VerifyingKey,
|
|
|
+ ) -> Result<(Scalar, u32), SignatureError> {
|
|
|
+ // Pull out the signature and verify it
|
|
|
+ let sig = Signature::try_from(&invitation[(32 + 4)..])?;
|
|
|
+ pubkey.verify(&invitation[0..(32 + 4)], &sig)?;
|
|
|
+ // The signature passed. Pull out the bucket number and then
|
|
|
+ // the invitation id
|
|
|
+ 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 {
|
|
|
+ // It should never happen that there's a valid signature on
|
|
|
+ // an invalid serialization of a Scalar, but check anyway.
|
|
|
+ Err(SignatureError::new())
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// The bridge authority. This will typically be a singleton object.
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+#[derive(Debug, Serialize, Deserialize)]
|
|
|
+pub struct BridgeAuth {
|
|
|
+ /// The private key for the main Lox credential
|
|
|
+ // lox_priv: IssuerPrivKey,
|
|
|
+ /// The public key for the main Lox credential
|
|
|
+ // pub lox_pub: IssuerPubKey,
|
|
|
+ /// The private key for migration credentials
|
|
|
+ // migration_priv: IssuerPrivKey,
|
|
|
+ /// The public key for migration credentials
|
|
|
+ // pub migration_pub: IssuerPubKey,
|
|
|
+ /// The private key for migration key credentials
|
|
|
+ // migrationkey_priv: IssuerPrivKey,
|
|
|
+ /// The public key for migration key credentials
|
|
|
+ // pub migrationkey_pub: IssuerPubKey,
|
|
|
+ /// The private key for bucket reachability credentials
|
|
|
+ // reachability_priv: IssuerPrivKey,
|
|
|
+ /// The public key for bucket reachability credentials
|
|
|
+ // pub reachability_pub: IssuerPubKey,
|
|
|
+ /// The private key for invitation credentials
|
|
|
+ // invitation_priv: IssuerPrivKey,
|
|
|
+ /// The public key for invitation credentials
|
|
|
+ // pub invitation_pub: IssuerPubKey,
|
|
|
+
|
|
|
+ /// The public key of the BridgeDb issuing open invitations
|
|
|
+ pub bridgedb_pub: VerifyingKey,
|
|
|
+
|
|
|
+ /// The bridge table
|
|
|
+ bridge_table: BridgeTable,
|
|
|
+
|
|
|
+ // Map of bridge fingerprint to values needed to verify TP reports
|
|
|
+ // pub tp_bridge_infos: HashMap<String, BridgeVerificationInfo>,
|
|
|
+ /// The migration tables
|
|
|
+ // trustup_migration_table: MigrationTable,
|
|
|
+ // blockage_migration_table: MigrationTable,
|
|
|
+
|
|
|
+ /// Duplicate filter for open invitations
|
|
|
+ bridgedb_pub_filter: dup_filter::DupFilter<Scalar>,
|
|
|
+ /// Duplicate filter for Lox credential ids
|
|
|
+ id_filter: dup_filter::DupFilter<Scalar>,
|
|
|
+ /// Duplicate filter for Invitation credential ids
|
|
|
+ inv_id_filter: dup_filter::DupFilter<Scalar>,
|
|
|
+ /// Duplicate filter for trust promotions (from untrusted level 0 to
|
|
|
+ /// trusted level 1)
|
|
|
+ trust_promotion_filter: dup_filter::DupFilter<Scalar>,
|
|
|
+ // Outdated Lox Keys to be populated with the old Lox private and public keys
|
|
|
+ // after a key rotation
|
|
|
+ old_keys: OldKeys,
|
|
|
+ old_filters: OldFilters,
|
|
|
+
|
|
|
+ /// For testing only: offset of the true time to the simulated time
|
|
|
+ #[serde(skip)]
|
|
|
+ time_offset: time::Duration,
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(feature = "bridgeauth")]
|
|
|
+impl BridgeAuth {
|
|
|
+ pub fn new(bridgedb_pub: VerifyingKey) -> Self {
|
|
|
+ // Create the private and public keys for each of the types of
|
|
|
+ // credential, each with the appropriate number of attributes
|
|
|
+ // 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(),
|
|
|
+ // tp_bridge_infos: HashMap::<String, BridgeVerificationInfo>::new(),
|
|
|
+ // 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(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Get today's (real or simulated) date as u32
|
|
|
+ pub fn today(&self) -> u32 {
|
|
|
+ // We will not encounter negative Julian dates (~6700 years ago)
|
|
|
+ // or ones larger than 32 bits
|
|
|
+ (time::OffsetDateTime::now_utc().date() + self.time_offset)
|
|
|
+ .to_julian_day()
|
|
|
+ .try_into()
|
|
|
+ .unwrap()
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Get today's (real or simulated) date as a DateTime<Utc> value
|
|
|
+ pub fn today_date(&self) -> DateTime<Utc> {
|
|
|
+ Utc::now()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Try to extract a u32 from a Scalar
|
|
|
@@ -16,5 +252,3 @@ pub fn scalar_u32(s: &Scalar) -> Option<u32> {
|
|
|
}
|
|
|
Some(u32::from_le_bytes(sbytes[..4].try_into().unwrap()))
|
|
|
}
|
|
|
-
|
|
|
-//pub mod open_invite;
|