123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*! 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
- */
- use curve25519_dalek::ristretto::RistrettoBasepointTable;
- use curve25519_dalek::ristretto::RistrettoPoint;
- use curve25519_dalek::scalar::Scalar;
- use curve25519_dalek::traits::IsIdentity;
- use zkp::CompactProof;
- use zkp::ProofError;
- use zkp::Transcript;
- use serde::{Deserialize, Serialize};
- use serde_with::serde_as;
- use super::super::bridge_table;
- use super::super::bridge_table::BridgeLine;
- use super::super::cred;
- use super::super::dup_filter::SeenType;
- use super::super::OPENINV_LENGTH;
- use super::super::{BridgeAuth, BridgeDb, IssuerPubKey};
- use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
- /// The request message for this protocol
- #[serde_as]
- #[derive(Serialize, Deserialize)]
- pub struct Request {
- #[serde_as(as = "[_; OPENINV_LENGTH]")]
- invite: [u8; OPENINV_LENGTH],
- D: RistrettoPoint,
- EncIdClient: (RistrettoPoint, RistrettoPoint),
- piUserBlinding: CompactProof,
- }
- /// The client state for this protocol
- #[derive(Debug)]
- pub struct State {
- d: Scalar,
- D: RistrettoPoint,
- EncIdClient: (RistrettoPoint, RistrettoPoint),
- id_client: Scalar,
- }
- /// The response message for this protocol
- #[derive(Serialize, Deserialize)]
- pub struct Response {
- P: RistrettoPoint,
- EncQ: (RistrettoPoint, RistrettoPoint),
- id_server: Scalar,
- TId: RistrettoPoint,
- bucket: Scalar,
- level_since: Scalar,
- piBlindIssue: CompactProof,
- bridge_line: BridgeLine,
- }
- // The userblinding ZKP
- define_proof! {
- userblinding,
- "Open Invitation User Blinding",
- (d, eid_client, id_client),
- (D, EncIdClient0, EncIdClient1),
- (B) :
- D = (d*B),
- EncIdClient0 = (eid_client*B),
- EncIdClient1 = (id_client*B + eid_client*D)
- }
- // The issuing ZKP
- define_proof! {
- blindissue,
- "Open Invitation Blind Issuing",
- (x0, x0tilde, xid, xbucket, xsince, s, b, tid),
- (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xsince, Pbucket, Psince, TId,
- D, EncId0, EncId1),
- (A, B) :
- Xid = (xid*A),
- Xbucket = (xbucket*A),
- Xsince = (xsince*A),
- X0 = (x0*B + x0tilde*A),
- P = (b*B),
- TId = (b*Xid),
- TId = (tid*A),
- EncQ0 = (s*B + tid*EncId0),
- EncQ1 = (s*D + tid*EncId1 + x0*P + xbucket*Pbucket + xsince*Psince)
- }
- /// Submit an open invitation issued by the BridgeDb to receive your
- /// first Lox credential
- pub fn request(invite: &[u8; OPENINV_LENGTH]) -> (Request, State) {
- let B: &RistrettoPoint = &CMZ_B;
- let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
- // Pick an ElGamal keypair
- let mut rng = rand::thread_rng();
- let d = Scalar::random(&mut rng);
- let D = &d * Btable;
- // Pick a random client component of the id
- let id_client = Scalar::random(&mut rng);
- // Encrypt it (times the basepoint B) to the ElGamal public key D we
- // just created
- let eid_client = Scalar::random(&mut rng);
- let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
- // Construct the proof of correct user blinding
- let mut transcript = Transcript::new(b"open invite user blinding");
- let piUserBlinding = userblinding::prove_compact(
- &mut transcript,
- userblinding::ProveAssignments {
- B: &B,
- D: &D,
- EncIdClient0: &EncIdClient.0,
- EncIdClient1: &EncIdClient.1,
- d: &d,
- eid_client: &eid_client,
- id_client: &id_client,
- },
- )
- .0;
- (
- Request {
- invite: *invite,
- D,
- EncIdClient,
- piUserBlinding,
- },
- State {
- d,
- D,
- EncIdClient,
- id_client,
- },
- )
- }
- impl BridgeAuth {
- /// Receive an open invitation issued by the BridgeDb and if it is
- /// valid and fresh, issue a Lox credential at trust level 0.
- pub fn handle_open_invite(&mut self, req: Request) -> Result<Response, ProofError> {
- // Check the signature on the open_invite. We manually match
- // here because we're changing the Err type from SignatureError
- // to ProofError
- let (invite_id, bucket_id_u32) = match BridgeDb::verify(req.invite, self.bridgedb_pub) {
- Ok(res) => res,
- Err(_) => return Err(ProofError::VerificationFailure),
- };
- let bucket_id: usize = bucket_id_u32 as usize;
- // Only proceed if the invite_id is fresh
- if self.openinv_filter.filter(&invite_id) == SeenType::Seen {
- return Err(ProofError::VerificationFailure);
- }
- // And also check that the bucket id is valid
- if bucket_id >= self.bridge_table.num_buckets() {
- return Err(ProofError::VerificationFailure);
- }
- let A: &RistrettoPoint = &CMZ_A;
- let B: &RistrettoPoint = &CMZ_B;
- let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
- let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
- // Next check the proof in the request
- let mut transcript = Transcript::new(b"open invite user blinding");
- userblinding::verify_compact(
- &req.piUserBlinding,
- &mut transcript,
- userblinding::VerifyAssignments {
- B: &B.compress(),
- EncIdClient0: &req.EncIdClient.0.compress(),
- EncIdClient1: &req.EncIdClient.1.compress(),
- D: &req.D.compress(),
- },
- )?;
- // Choose a random server id component to add to the client's
- // (blinded) id component
- let mut rng = rand::thread_rng();
- let id_server = Scalar::random(&mut rng);
- let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable);
- // 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[bucket_id];
- let bucket: Scalar = bridge_table::to_scalar(bucket_id_u32, &bucket_key);
- let bridge_line = self.bridge_table.buckets[bucket_id][0];
- // Create the level_since attribute (Scalar), which is today's
- // Julian date
- let level_since: Scalar = self.today().into();
- // Compute the MAC on the visible attributes
- let b = Scalar::random(&mut rng);
- let P = &b * Btable;
- // trust_level = invites_remaining = blockages = 0
- let QHc =
- (self.lox_priv.x[0] + self.lox_priv.x[2] * bucket + self.lox_priv.x[4] * level_since)
- * P;
- // El Gamal encrypt it to the public key req.D
- let s = Scalar::random(&mut rng);
- let EncQHc = (&s * Btable, QHc + s * req.D);
- // Homomorphically compute the part of the MAC corresponding to
- // the blinded id attribute
- let tid = self.lox_priv.x[1] * b;
- let TId = &tid * Atable;
- let EncQId = (tid * EncId.0, tid * EncId.1);
- let EncQ = (EncQHc.0 + EncQId.0, EncQHc.1 + EncQId.1);
- let mut transcript = Transcript::new(b"open invite issuing");
- let piBlindIssue = blindissue::prove_compact(
- &mut transcript,
- blindissue::ProveAssignments {
- A: &A,
- B: &B,
- P: &P,
- EncQ0: &EncQ.0,
- EncQ1: &EncQ.1,
- X0: &self.lox_pub.X[0],
- Xid: &self.lox_pub.X[1],
- Xbucket: &self.lox_pub.X[2],
- Xsince: &self.lox_pub.X[4],
- Pbucket: &(bucket * P),
- Psince: &(level_since * P),
- TId: &TId,
- D: &req.D,
- EncId0: &EncId.0,
- EncId1: &EncId.1,
- x0: &self.lox_priv.x[0],
- x0tilde: &self.lox_priv.x0tilde,
- xid: &self.lox_priv.x[1],
- xbucket: &self.lox_priv.x[2],
- xsince: &self.lox_priv.x[4],
- s: &s,
- b: &b,
- tid: &tid,
- },
- )
- .0;
- Ok(Response {
- P,
- EncQ,
- id_server,
- TId,
- bucket,
- level_since,
- piBlindIssue,
- bridge_line,
- })
- }
- }
- /// Handle the reponse 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> {
- let A: &RistrettoPoint = &CMZ_A;
- let B: &RistrettoPoint = &CMZ_B;
- let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
- if resp.P.is_identity() {
- return Err(ProofError::VerificationFailure);
- }
- // Add the server's contribution to the id to our own, both in plain
- // and encrypted form
- let id = state.id_client + resp.id_server;
- let EncId = (
- state.EncIdClient.0,
- state.EncIdClient.1 + &resp.id_server * Btable,
- );
- // Verify the proof
- let mut transcript = Transcript::new(b"open invite issuing");
- blindissue::verify_compact(
- &resp.piBlindIssue,
- &mut transcript,
- blindissue::VerifyAssignments {
- A: &A.compress(),
- B: &B.compress(),
- P: &resp.P.compress(),
- EncQ0: &resp.EncQ.0.compress(),
- EncQ1: &resp.EncQ.1.compress(),
- X0: &lox_pub.X[0].compress(),
- Xid: &lox_pub.X[1].compress(),
- Xbucket: &lox_pub.X[2].compress(),
- Xsince: &lox_pub.X[4].compress(),
- Pbucket: &(resp.bucket * resp.P).compress(),
- Psince: &(resp.level_since * resp.P).compress(),
- TId: &resp.TId.compress(),
- D: &state.D.compress(),
- EncId0: &EncId.0.compress(),
- EncId1: &EncId.1.compress(),
- },
- )?;
- // Decrypt EncQ
- let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
- Ok((
- cred::Lox {
- P: resp.P,
- Q,
- id,
- bucket: resp.bucket,
- trust_level: Scalar::zero(),
- level_since: resp.level_since,
- invites_remaining: Scalar::zero(),
- blockages: Scalar::zero(),
- },
- resp.bridge_line,
- ))
- }
|