onyinyang 10 месяцев назад
Родитель
Сommit
6386c4fa7a
2 измененных файлов с 193 добавлено и 0 удалено
  1. 1 0
      src/lib.rs
  2. 192 0
      src/proto/trust_promotion.rs

+ 1 - 0
src/lib.rs

@@ -42,6 +42,7 @@ pub mod proto {
     pub mod migration;
     pub mod open_invite;
     pub mod redeem_invite;
+    pub mod trust_promotion;
     pub mod update_cred;
     pub mod update_invite;
 }

+ 192 - 0
src/proto/trust_promotion.rs

@@ -0,0 +1,192 @@
+/*! A module for the protocol for the user to get promoted from
+untrusted (trust level 0) to trusted (trust level 1).
+
+They are allowed to do this as long as UNTRUSTED_INTERVAL days have
+passed since they obtained their level 0 Lox credential, and their
+bridge (level 0 users get put in a one-bridge bucket) has not been
+blocked.  (Blocked bridges in one-bridge buckets will have their entries
+removed from the bridge authority's trust_promotion table.)
+
+The user presents their current Lox credential:
+- id: revealed
+- bucket: blinded
+- trust_level: revealed to be 0
+- level_since: blinded, but proved in ZK that it's at least
+  UNTRUSTED_INTERVAL days ago
+- invites_remaining: revealed to be 0
+- blockages: revealed to be 0
+
+They will receive in return the encrypted MAC (Pk, EncQk) for their
+implicit Migration Key credential with attributes id and bucket,
+along with a HashMap of encrypted Migration credentials.  For each
+(from_i, to_i) in the BA's migration list, there will be an entry in
+the HashMap with key H1(id, from_attr_i, Qk_i) and value
+Enc_{H2(id, from_attr_i, Qk_i)}(to_attr_i, P_i, Q_i).  Here H1 and H2
+are the first 16 bytes and the second 16 bytes respectively of the
+SHA256 hash of the input, P_i and Q_i are a MAC on the Migration
+credential with attributes id, from_attr_i, and to_attr_i. Qk_i is the
+value EncQk would decrypt to if bucket were equal to from_attr_i. */
+
+#[cfg(feature = "bridgeauth")]
+use super::super::dup_filter::SeenType;
+use super::super::scalar_u32;
+#[cfg(feature = "bridgeauth")]
+use super::super::BridgeAuth;
+use super::errors::CredentialError;
+use crate::lox_creds::{Lox, Migration, MigrationKey};
+use crate::migration_table;
+use crate::migration_table::ENC_MIGRATION_BYTES;
+use cmz::*;
+#[cfg(feature = "bridgeauth")]
+use curve25519_dalek::ristretto::RistrettoBasepointTable;
+use curve25519_dalek::ristretto::RistrettoPoint as G;
+use group::Group;
+use rand_core::RngCore;
+use sha2::Sha512;
+use std::collections::HashMap;
+
+/// The minimum number of days a user has to be at trust level 0
+/// (untrusted) with their (single) bridge unblocked before they can
+/// move to level 1.
+///
+/// The implementation also puts an upper bound of UNTRUSTED_INTERVAL +
+/// 511 days, which is not unreasonable; we want users to be engaging
+/// with the system in order to move up trust levels.
+pub const UNTRUSTED_INTERVAL: u32 = 30;
+
+muCMZProtocol! { trust_promotion<credential_expiry, eligibility_max_age>,
+    L: Lox { id: R, bucket: H, trust_level: R, level_since: H, invites_remaining: R, blockages: R },
+    M: MigrationKey { lox_id: J, from_bucket: H} ,
+    L.bucket = M.from_bucket,
+   // credential_expiry <= L.level_since,
+   // L.level_since <= eligibility_max_age,
+}
+
+pub fn request(
+    L: Lox,
+    mig_pubkeys: CMZPubkey<G>,
+    today: u32,
+) -> Result<(trust_promotion::Request, trust_promotion::ClientState), CredentialError> {
+    let mut rng = rand::thread_rng();
+    cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
+
+    // Ensure that the credenials can be correctly shown; that is, the
+    // ids match and the Lox credential bucket matches the Migration
+    // credential from_bucket
+    if L.id.is_none() {
+        return Err(CredentialError::CredentialMismatch);
+    }
+
+    // This protocol only allows migrating from trust level 0 to trust
+    // level 1
+    if let Some(ls) = L.level_since {
+        let level_since = match scalar_u32(&ls) {
+            Some(v) => v,
+            None => {
+                return Err(CredentialError::InvalidField(
+                    String::from("level_since"),
+                    String::from("could not be converted to u32"),
+                ))
+            }
+        };
+        if level_since + UNTRUSTED_INTERVAL > today {
+            return Err(CredentialError::TimeThresholdNotMet(
+                level_since + UNTRUSTED_INTERVAL - today,
+            ));
+        }
+        let diffdays = today - (level_since + UNTRUSTED_INTERVAL);
+        if diffdays > 511 {
+            return Err(CredentialError::CredentialExpired);
+        }
+    }
+    let eligibility_max_age = today - UNTRUSTED_INTERVAL;
+
+    let M = MigrationKey::using_pubkey(&mig_pubkeys);
+    let params = trust_promotion::Params {
+        credential_expiry: (eligibility_max_age - 511).into(),
+        eligibility_max_age: eligibility_max_age.into(),
+    };
+
+    match trust_promotion::prepare(&mut rng, &L, M, &params) {
+        Ok(req_state) => Ok(req_state),
+        Err(e) => Err(CredentialError::CMZError(e)),
+    }
+}
+
+#[cfg(feature = "bridgeauth")]
+impl BridgeAuth {
+    pub fn handle_trust_promotion(
+        &mut self,
+        req: trust_promotion::Request,
+    ) -> Result<
+        (
+            trust_promotion::Reply,
+            HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]>,
+        ),
+        CredentialError,
+    > {
+        let mut rng = rand::thread_rng();
+        let reqbytes = req.as_bytes();
+        let recvreq = trust_promotion::Request::try_from(&reqbytes[..]).unwrap();
+
+        let today = self.today();
+        match trust_promotion::handle(
+            &mut rng,
+            recvreq,
+            |L: &mut Lox, M: &mut MigrationKey| {
+                L.set_privkey(&self.lox_priv);
+                M.set_privkey(&self.migrationkey_priv);
+                let eligibility_max_age = today - UNTRUSTED_INTERVAL;
+                Ok(trust_promotion::Params {
+                    credential_expiry: (eligibility_max_age - 511).into(),
+                    eligibility_max_age: eligibility_max_age.into(),
+                })
+            },
+            |L: &Lox, _M: &MigrationKey| {
+                if self.id_filter.filter(&L.id.unwrap()) == SeenType::Seen
+                    || self.trust_promotion_filter.filter(&L.id.unwrap()) == SeenType::Seen
+                {
+                    return Err(CMZError::RevealAttrMissing("id", "Credential Expired"));
+                }
+                Ok(())
+            },
+        ) {
+            Ok((response, (L_issuer, M_issuer))) => {
+                let Pktable: RistrettoBasepointTable =
+                    RistrettoBasepointTable::create(&M_issuer.MAC.P);
+                let enc_migration_table = self.trustup_migration_table.encrypt_table(
+                    L_issuer.id.unwrap(),
+                    &self.bridge_table,
+                    &Pktable,
+                    &self.migration_priv,
+                    &self.migrationkey_priv,
+                );
+                Ok((response, enc_migration_table))
+            }
+            Err(e) => Err(CredentialError::CMZError(e)),
+        }
+    }
+}
+
+pub fn handle_response(
+    migration_pubkey: CMZPubkey<G>,
+    state: trust_promotion::ClientState,
+    rep: trust_promotion::Reply,
+    enc_migration_table: HashMap<[u8; 16], [u8; ENC_MIGRATION_BYTES]>,
+) -> Result<Migration, CMZError> {
+    let replybytes = rep.as_bytes();
+    let recvreply = trust_promotion::Reply::try_from(&replybytes[..]).unwrap();
+    let migkey = match state.finalize(recvreply) {
+        Ok(cred) => cred,
+        Err(_e) => return Err(CMZError::Unknown),
+    };
+    match migration_table::decrypt_cred(
+        migkey,
+        migration_table::MigrationType::Blockage,
+        migration_pubkey,
+        &enc_migration_table,
+    ) {
+        Some(cred) => Ok(cred),
+        None => Err(CMZError::Unknown),
+    }
+}