Browse Source

Add open_invitation extension

onyinyang 9 months ago
parent
commit
6011246671
2 changed files with 156 additions and 147 deletions
  1. 0 147
      src/proto/open_invite.md
  2. 156 0
      src/proto/open_invite.rs

+ 0 - 147
src/proto/open_invite.md

@@ -1,147 +0,0 @@
-Open Invitation Credential
-
-`use super::super::cred;`
-`use super::super::cmz::{IssueType, ShowType};`
-
-```
-#[serde_as]
-#[derive(Serialize, Deserialize)]
-pub struct Request {
-    #[serde_as(as = "[_; OPENINV_LENGTH]")]
-    invite: [u8; OPENINV_LENGTH],
-    // Lox credential with each attribute annotated with the IssueType
-    issue_cred: AnnotatedIssueCredential,
-    // Statement relating the annotated_credentials, in this case: the
-    // commitment to the Lox ID
-    open_issue_statement: Statement,
-}
-```
-
-The client state for this protocol
-```
-#[derive(Debug, Serialize, Deserialize)]
-pub struct State {
-    // Randomized attributes could be stored in annotated credential as
-    // part of the state, that can be used to process the Issued credential
-    // later
-    annotated_cred: AnnotatedIssueCredential,
-    s: Scalar,
-}
-```
-
-The response message for this protocol: (This was copied from the exisiting
-protocol and still needs to be updated)
-
-```
-#[derive(Serialize, Deserialize)]
-pub struct Response {
-    P: RistrettoPoint,
-    BlindLoxQ: RistrettoPoint,
-    server_id: Scalar,
-    bucket: Scalar,
-    level_since: Scalar,
-    piBlindIssue: CompactProof,
-    bridge_line: BridgeLine,
-}
-```
-
-Client Request:
-```
-pub fn request(invite: &[u8; OPENINV_LENGTH], lox_pub: &IssuerPubKey) -> (Request, State) {
-
-// There is no call to "show" for open invitation but the open invite must be verified  
-let open_invite_cred_directive =  cred::Lox {
-       id: Scalar::random(),
-       bucket: Scalar::ZERO, // Placeholder value
-       trust_level: Scalar::ZERO, // Placeholder value 
-       level_since: Scalar::ZERO, // Placeholder value 
-       invites_remaining: Scalar::ZERO, // Placeholder value
-       blockages: Scalar::ZERO, // Placeholder value
-    }
-// Create new Lox credential with mapped fields(String) -> Scalar
-let open_invite_cred = cred.Lox.annotate(open_invite_cred_directive, lox_pub);
-
-// Create the open invitation issue directive
-let open_invite_issue_directive = HashMap::from([
-       ("id", IssueType::Joint),
-       ("bucket", IssueType::Select),
-       ("trust_level", IssueType::Select), // Constant: 0
-       ("level_since", IssueType::Select), // Constant: 0
-       ("invites_remaining", IssueType::Select), // Constant: 0
-       ("blockages", IssueType::Select), // Constant: 0
-    ]);
-
-// Annotate the credential with the IssueType for each attribute, returning the 
-// AnnotatedIssueCredential
-let issue_cred: AnnotatedIssueCredential = open_invite_cred.issue_annotation(open_invite_issue_directive);
-
-// Open Invite doesn't have a statement comparing issue and show annotated
-// credentials, but needs to commit to the joint issue lox id
-let (state, open_invite_issue_statement) =
-let (request, state) = open_invite_cred.issue_request(None, vec![issue_cred], Statement::Empty); 
-(
-        Request {
-            invite: *invite,
-            request.issue_cred,
-            request.statement
-        },
-        state
-    )
-
-}
-```
-
-
-Server Response:
-Receive an open invitation issued by the BridgeDb and if it is
-valid and fresh, issue a Lox credential at trust level 0.
-
-
-
-```
-impl BridgeAuth {
-
-pub fn issue_response(&self, Option<Vec<AnnotatedShowCredential>>, Vec<AnnotatedIssueCredential>, Statement) -> Statement, Credential;
-
-pub fn handle_open_invite(&mut self, req: Request) -> Result<Response, ProofError> {
-
-    // verify Invite from request: check signature and age are correct
-
-    // Create new Lox credential with mapped fields(String) -> Scalar
-    // Including both the private and public keys
-    let issue_statement, open_invite_issuecred = self.issue_response(None, req.issue_cred,
-    req.open_invite_issue_statement);
-
-    Response {
-      // This still needs work
-      // Yet another annotated credential with all of the attributes
-      // from the server + P and blinded Q
-      // ZKProof
-      // BridgeLine
-      open_invite_issuecred,
-      issue_statement,
-      BridgeLine,
-    }
-
-
-}
-```
-
-
-Handle the response to the request, producing the desired Lox
-credential if successful.
-```
-pub fn handle_response(
-    state: State,
-    resp: Response,
-    lox_pub: &IssuerPubKey,
-) -> Result<(cred::Lox, BridgeLine), ProofError> {
-
-// Pass the credential from the response to the annotated credential from the
-// client's state and call handle_response to form the final credential
-let lox_cred = state.annotated_cred.handle_response(resp.BlindedCredential, resp.Statement)
-
-Ok( lox_cred, resp.BridgeLine)
-}
-
-```

+ 156 - 0
src/proto/open_invite.rs

@@ -0,0 +1,156 @@
+/*! A module for the protocol for the user to redeem an open invitation
+with the BA (bridge authority) to receive their initial Lox
+credential.
+
+The credential will have attributes:
+
+- id: jointly chosen by the user and BA
+- bucket: set by the BA
+- trust_level: 0
+- level_since: today
+- invites_remaining: 0
+- blockages: 0
+
+*/
+
+#[cfg(feature = "bridgeauth")]
+use super::super::bridge_table;
+#[cfg(feature = "bridgeauth")]
+use super::super::bridge_table::BridgeLine;
+#[cfg(feature = "bridgeauth")]
+use super::super::dup_filter::SeenType;
+#[cfg(feature = "bridgeauth")]
+use super::super::OPENINV_LENGTH;
+#[cfg(feature = "bridgeauth")]
+use super::super::{BridgeAuth, BridgeDb};
+use super::errors::CredentialError;
+use crate::lox_creds::Lox;
+use cmz::*;
+use curve25519_dalek::ristretto::RistrettoPoint as G;
+#[cfg(feature = "bridgeauth")]
+use curve25519_dalek::scalar::Scalar;
+use group::Group;
+use rand_core::RngCore;
+use sha2::Sha512;
+
+muCMZProtocol! { open_invitation,
+    ,
+    L: Lox {id: J, bucket: S, trust_level: I, level_since: S, invites_remaining: I, blockages: I },
+}
+
+/// Prepare the open invitation request to send to the Lox Authority
+/// Note that preparing the request does not require an open invitation, but an invitation
+/// must be sent along with the prepared open_inivtation::Request to the Lox authority
+pub fn request(
+    pubkeys: CMZPubkey<G>,
+) -> Result<(open_invitation::Request, open_invitation::ClientState), CredentialError> {
+    let mut rng = rand::thread_rng();
+    cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
+
+    let L = Lox::using_pubkey(&pubkeys);
+
+    match open_invitation::prepare(&mut rng, L) {
+        Ok(req_state) => Ok(req_state),
+        Err(e) => Err(CredentialError::CMZError(e)),
+    }
+}
+
+#[cfg(feature = "bridgeauth")]
+impl BridgeAuth {
+    pub fn open_invitation(
+        &mut self,
+        req: open_invitation::Request,
+        invite: &[u8; OPENINV_LENGTH],
+    ) -> Result<(open_invitation::Reply, BridgeLine), CredentialError> {
+        // Check the signature on the open_invite, first with the old key, then with the new key.
+        // We manually match here because we're changing the Err type from SignatureError
+        // to ProofError
+        let mut old_token: Option<((Scalar, u32), usize)> = Default::default();
+        let invite_id: Scalar;
+        let bucket_id: u32;
+        // If there are old openinv keys, check them first
+        for (i, old_openinv_key) in self.old_keys.bridgedb_key.iter().enumerate() {
+            old_token = match BridgeDb::verify(*invite, *old_openinv_key) {
+                Ok(res) => Some((res, i)),
+                Err(_) => None,
+            };
+        }
+
+        // Check if verifying with the old key succeeded, if it did, check if it has been seen
+        if old_token.is_some() {
+            // Only proceed if the invite_id is fresh
+            (invite_id, bucket_id) = old_token.unwrap().0;
+            if self
+                .old_filters
+                .openinv_filter
+                .get_mut(old_token.unwrap().1)
+                .unwrap()
+                .filter(&invite_id)
+                == SeenType::Seen
+            {
+                return Err(CredentialError::CredentialExpired);
+            }
+        // If it didn't, try verifying with the new key
+        } else {
+            (invite_id, bucket_id) = match BridgeDb::verify(*invite, self.bridgedb_pub) {
+                Ok(res) => res,
+                // Also verify that the request doesn't match with an old openinv_key
+                Err(_) => {
+                    return Err(CredentialError::InvalidField(
+                        "invitation".to_string(),
+                        "pubkey".to_string(),
+                    ))
+                }
+            };
+            // Only proceed if the invite_id is fresh
+            if self.bridgedb_pub_filter.filter(&invite_id) == SeenType::Seen {
+                return Err(CredentialError::CredentialExpired);
+            }
+        }
+
+        // And also check that the bucket id is valid
+        if !self.bridge_table.buckets.contains_key(&bucket_id) {
+            return Err(CredentialError::InvalidField(
+                "invitation".to_string(),
+                "bucket".to_string(),
+            ));
+        }
+
+        let mut rng = rand::thread_rng();
+        let reqbytes = req.as_bytes();
+        // Create the bucket attribute (Scalar), which is a combination
+        // of the bucket id (u32) and the bucket's decryption key ([u8; 16])
+        let bucket_key = self.bridge_table.keys.get(&bucket_id).unwrap();
+        let bucket: Scalar = bridge_table::to_scalar(bucket_id, bucket_key);
+        let bridge_lines = self.bridge_table.buckets.get(&bucket_id).unwrap();
+        let bridge_line = bridge_lines[0];
+
+        let recvreq = open_invitation::Request::try_from(&reqbytes[..]).unwrap();
+        match open_invitation::handle(
+            &mut rng,
+            recvreq,
+            |L: &mut Lox| {
+                L.set_privkey(&self.lox_priv);
+                L.bucket = Some(bucket);
+                L.level_since = Some(self.today().into());
+                Ok(())
+            },
+            |_L: &Lox| Ok(()),
+        ) {
+            Ok((response, _L_issuer)) => Ok((response, bridge_line)),
+            Err(e) => Err(CredentialError::CMZError(e)),
+        }
+    }
+}
+
+pub fn handle_response(
+    state: open_invitation::ClientState,
+    rep: open_invitation::Reply,
+) -> Result<Lox, CMZError> {
+    let replybytes = rep.as_bytes();
+    let recvreply = open_invitation::Reply::try_from(&replybytes[..]).unwrap();
+    match state.finalize(recvreply) {
+        Ok(cred) => Ok(cred),
+        Err(_e) => Err(CMZError::Unknown),
+    }
+}