Browse Source

Add basic Bridgeauth and Bridgedb

onyinyang 8 months ago
parent
commit
2c09bd4408
3 changed files with 397 additions and 3 deletions
  1. 156 1
      Cargo.lock
  2. 5 0
      Cargo.toml
  3. 236 2
      src/lib.rs

+ 156 - 1
Cargo.lock

@@ -2,6 +2,41 @@
 # It is not intended for manual editing.
 version = 4
 
+[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "ghash",
+ "subtle",
+]
+
 [[package]]
 name = "android-tzdata"
 version = "0.1.1"
@@ -23,6 +58,12 @@ version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
 [[package]]
 name = "base64"
 version = "0.22.1"
@@ -99,6 +140,16 @@ dependencies = [
  "windows-link",
 ]
 
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+]
+
 [[package]]
 name = "cmz"
 version = "0.1.0"
@@ -149,9 +200,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
 dependencies = [
  "generic-array",
+ "rand_core",
  "typenum",
 ]
 
+[[package]]
+name = "ctr"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+dependencies = [
+ "cipher",
+]
+
 [[package]]
 name = "curve25519-dalek"
 version = "4.1.3"
@@ -237,6 +298,30 @@ dependencies = [
  "crypto-common",
 ]
 
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "serde",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "rand_core",
+ "serde",
+ "sha2",
+ "subtle",
+]
+
 [[package]]
 name = "equivalent"
 version = "1.0.2"
@@ -302,6 +387,16 @@ dependencies = [
  "wasi",
 ]
 
+[[package]]
+name = "ghash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
+dependencies = [
+ "opaque-debug",
+ "polyval",
+]
+
 [[package]]
 name = "group"
 version = "0.13.0"
@@ -386,6 +481,15 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "inout"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
+dependencies = [
+ "generic-array",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.15"
@@ -424,9 +528,13 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 name = "lox-extensions"
 version = "0.1.0"
 dependencies = [
+ "aes-gcm",
+ "base64 0.21.7",
  "bincode",
+ "chrono",
  "cmz",
  "curve25519-dalek",
+ "ed25519-dalek",
  "ff",
  "group",
  "lazy_static",
@@ -434,9 +542,11 @@ dependencies = [
  "rand_core",
  "serde",
  "serde_with",
+ "sha1",
  "sha2",
  "subtle",
  "thiserror",
+ "time",
 ]
 
 [[package]]
@@ -466,6 +576,24 @@ version = "1.21.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
 
+[[package]]
+name = "opaque-debug"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+
+[[package]]
+name = "polyval"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "opaque-debug",
+ "universal-hash",
+]
+
 [[package]]
 name = "powerfmt"
 version = "0.2.0"
@@ -600,7 +728,7 @@ version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
 dependencies = [
- "base64",
+ "base64 0.22.1",
  "chrono",
  "hex",
  "indexmap 1.9.3",
@@ -624,6 +752,17 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
 [[package]]
 name = "sha2"
 version = "0.10.8"
@@ -641,6 +780,12 @@ version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+
 [[package]]
 name = "strsim"
 version = "0.11.1"
@@ -733,6 +878,16 @@ version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
 
+[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common",
+ "subtle",
+]
+
 [[package]]
 name = "version_check"
 version = "0.9.5"

+ 5 - 0
Cargo.toml

@@ -4,7 +4,11 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
+aes-gcm = { version = "0.10", features = ["aes"] }
+base64 = "0.21.0"
+chrono = { version = "0.4.38", default-features = false, features = ["now"], optional = true }
 curve25519-dalek = {version = "4.1.3", default-features = false, features = ["serde", "rand_core", "digest", "precomputed-tables"] }
+ed25519-dalek = { version = "2.1.1", default-features = false, features = ["serde", "rand_core"] }
 lazy_static = "1.5.0"
 rand = {version = "0.8.0", features = ["std_rng"] }
 serde = "1.0.217"
@@ -12,6 +16,7 @@ serde_with = { version = "3.0.0", features = ["json"] }
 sha1 = "0.10"
 sha2 = "0.10.8"
 subtle = "2.5"
+time = "0.3.36"
 cmz = {git = "ssh://gogs@git-crysp.uwaterloo.ca/SigmaProtocol/cmz.git"}
 group = "0.13"
 ff = "0.13.1"

+ 236 - 2
src/lib.rs

@@ -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;