Browse Source

Add issue_invite extension

onyinyang 8 months ago
parent
commit
9891f3f482
2 changed files with 198 additions and 0 deletions
  1. 1 0
      src/lib.rs
  2. 197 0
      src/proto/issue_invite.rs

+ 1 - 0
src/lib.rs

@@ -37,6 +37,7 @@ pub mod migration_table;
 pub mod proto {
     pub mod blockage_migration;
     pub mod errors;
+    pub mod issue_invite;
     pub mod level_up;
     pub mod open_invite;
     pub mod redeem_invite;

+ 197 - 0
src/proto/issue_invite.rs

@@ -0,0 +1,197 @@
+/*! A module for the protocol for a user to request the issuing of an
+Invitation credential they can pass to someone they know.
+
+They are allowed to do this as long as their current Lox credentials has
+a non-zero "invites_remaining" attribute (which will be decreased by
+one), and they have a Bucket Reachability credential for their current
+bucket and today's date.  (Such credentials are placed daily in the
+encrypted bridge table.)
+
+The user presents their current Lox credential:
+- id: revealed
+- bucket: blinded
+- trust_level: blinded
+- level_since: blinded
+- invites_remaining: blinded, but proved in ZK that it's not zero
+- blockages: blinded
+
+and a Bucket Reachability credential:
+- date: revealed to be today
+- bucket: blinded, but proved in ZK that it's the same as in the Lox
+  credential above
+
+and a new Lox credential to be issued:
+
+- id: jointly chosen by the user and BA
+- bucket: blinded, but proved in ZK that it's the same as in the Lox
+  credential above
+- trust_level: blinded, but proved in ZK that it's the same as in the
+  Lox credential above
+- level_since: blinded, but proved in ZK that it's the same as in the
+  Lox credential above
+- invites_remaining: blinded, but proved in ZK that it's one less than
+  the number in the Lox credential above
+- blockages: blinded, but proved in ZK that it's the same as in the
+  Lox credential above
+
+and a new Invitation credential to be issued:
+
+- inv_id: jointly chosen by the user and BA
+- date: revealed to be today
+- bucket: blinded, but proved in ZK that it's the same as in the Lox
+  credential above
+- blockages: blinded, but proved in ZK that it's the same as in the Lox
+  credential above
+
+*/
+
+#[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::{BucketReachability, Invitation, Lox};
+use cmz::*;
+use curve25519_dalek::ristretto::RistrettoPoint as G;
+use curve25519_dalek::scalar::Scalar;
+use group::Group;
+use rand_core::RngCore;
+use sha2::Sha512;
+
+/// Invitations must be used within this many days of being issued.
+/// Note that if you change this number to be larger than 15, you must
+/// also add bits to the zero knowledge proof.
+pub const INVITATION_EXPIRY: u32 = 15;
+
+muCMZProtocol! { issue_invite,
+    [L: Lox {id: R, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H}, B: BucketReachability { date: R, bucket: H } ],
+    [ I: Invitation { inv_id: J, date: S, bucket: H, blockages: H }, N: Lox {id: J, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H }],
+    L.bucket = B.bucket,
+    //L.invites_remaining > 0,
+    N.bucket = L.bucket,
+    N.trust_level = L.trust_level,
+    N.level_since = L.level_since,
+    N.invites_remaining = L.invites_remaining - Scalar::ONE,
+    N.blockages = L.blockages,
+    I. bucket = L.bucket,
+    I.blockages = L.blockages
+}
+
+pub fn request(
+    L: Lox,
+    lox_pubkeys: CMZPubkey<G>,
+    B: BucketReachability,
+    inv_pub: CMZPubkey<G>,
+    today: u32,
+) -> Result<(issue_invite::Request, issue_invite::ClientState), CredentialError> {
+    let mut rng = rand::thread_rng();
+    cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
+
+    // Ensure the credential can be correctly shown: it must be the case
+    // that invites_remaining not be 0
+    if let Some(invites_remaining) = L.invites_remaining {
+        if invites_remaining == Scalar::ZERO {
+            return Err(CredentialError::NoInvitationsRemaining);
+        }
+    } else {
+        return Err(CredentialError::InvalidField(
+            String::from("invites_remaining"),
+            String::from("None"),
+        ));
+    }
+
+    // The buckets in the Lox and Bucket Reachability credentials have
+    // to match
+    if L.bucket.is_some_and(|b| b != B.bucket.unwrap()) {
+        return Err(CredentialError::CredentialMismatch);
+    }
+    // The Bucket Reachability credential has to be dated today
+    let reach_date: u32 = match scalar_u32(&B.date.unwrap()) {
+        Some(v) => v,
+        None => {
+            return Err(CredentialError::InvalidField(
+                String::from("date"),
+                String::from("could not be converted to u32"),
+            ))
+        }
+    };
+    if reach_date != today {
+        return Err(CredentialError::InvalidField(
+            String::from("date"),
+            String::from("reachability credential must be generated today"),
+        ));
+    }
+
+    //TODO check all values are not None
+    let mut I = Invitation::using_pubkey(&inv_pub);
+    let mut N = Lox::using_pubkey(&lox_pubkeys);
+    N.bucket = L.bucket;
+    N.trust_level = L.trust_level;
+    N.level_since = L.level_since;
+    N.invites_remaining = Some(L.invites_remaining.unwrap() - Scalar::ONE);
+    N.blockages = L.blockages;
+    I.bucket = L.bucket;
+    I.blockages = L.blockages;
+
+    match issue_invite::prepare(&mut rng, &L, &B, I, N) {
+        Ok(req_state) => Ok(req_state),
+        Err(e) => Err(CredentialError::CMZError(e)),
+    }
+}
+
+#[cfg(feature = "bridgeauth")]
+impl BridgeAuth {
+    pub fn handle_issue_invite(
+        &mut self,
+        req: issue_invite::Request,
+    ) -> Result<issue_invite::Reply, CredentialError> {
+        let mut rng = rand::thread_rng();
+        let reqbytes = req.as_bytes();
+        let recvreq = issue_invite::Request::try_from(&reqbytes[..]).unwrap();
+        let today = self.today();
+        match issue_invite::handle(
+            &mut rng,
+            recvreq,
+            |L: &mut Lox, B: &mut BucketReachability, I: &mut Invitation, N: &mut Lox| {
+                L.set_privkey(&self.lox_priv);
+                B.set_privkey(&self.reachability_priv);
+                I.set_privkey(&self.invitation_priv);
+                N.set_privkey(&self.lox_priv);
+                if B.date.is_some_and(|b| b != today.into()) {
+                    return Err(CMZError::RevealAttrMissing("date", "not today"));
+                }
+                L.bucket = B.bucket;
+                N.bucket = L.bucket;
+                N.trust_level = L.trust_level;
+                N.level_since = Some(today.into());
+                N.invites_remaining = Some(L.invites_remaining.unwrap() - Scalar::ONE);
+                N.blockages = L.blockages;
+                I.bucket = N.bucket;
+                I.blockages = L.blockages;
+                Ok(())
+            },
+            |_L: &Lox, _B: &BucketReachability, I: &Invitation, _N: &Lox| {
+                if self.inv_id_filter.filter(&I.inv_id.unwrap()) == SeenType::Seen {
+                    return Err(CMZError::RevealAttrMissing("id", "Credential Expired"));
+                }
+                Ok(())
+            },
+        ) {
+            Ok((response, (_L_issuer, _B_issuer, _I_issuer, _N_issuer))) => Ok(response),
+            Err(e) => Err(CredentialError::CMZError(e)),
+        }
+    }
+}
+
+pub fn handle_response(
+    state: issue_invite::ClientState,
+    rep: issue_invite::Reply,
+) -> Result<(Invitation, Lox), CMZError> {
+    let replybytes = rep.as_bytes();
+    let recvreply = issue_invite::Reply::try_from(&replybytes[..]).unwrap();
+    match state.finalize(recvreply) {
+        Ok(creds) => Ok(creds),
+        Err(_e) => Err(CMZError::Unknown),
+    }
+}