Browse Source

The request message of the redeem invitation protocol

Ian Goldberg 3 years ago
parent
commit
bfb8f24a2f
3 changed files with 400 additions and 18 deletions
  1. 1 0
      src/lib.rs
  2. 350 0
      src/proto/redeem_invite.rs
  3. 49 18
      src/tests.rs

+ 1 - 0
src/lib.rs

@@ -401,6 +401,7 @@ pub mod proto {
     pub mod level_up;
     pub mod migration;
     pub mod open_invite;
+    pub mod redeem_invite;
     pub mod trust_promotion;
 }
 

+ 350 - 0
src/proto/redeem_invite.rs

@@ -0,0 +1,350 @@
+/*! A module for the protocol for a new user to redeem an Invitation
+credential.  The user will start at trust level 1 (instead of 0 for
+untrusted uninvited users).
+
+The user presents the Invitation credential:
+- id: revealed
+- date: blinded, but proved in ZK to be at most INVITATION_EXPIRY days ago
+- bucket: blinded
+- blockages: blinded
+
+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
+  Invitation credential above
+- trust_level: revealed to be 1
+- level_since: today
+- invites_remaining: revealed to be 0
+- blockages: blinded, but proved in ZK that it's the same as in the
+  Invitations credential above
+
+*/
+
+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 super::super::cred;
+use super::super::dup_filter::SeenType;
+use super::super::{pt_dbl, scalar_dbl, scalar_u32};
+use super::super::{BridgeAuth, IssuerPubKey};
+use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
+
+/// 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;
+
+pub struct Request {
+    // Fields for showing the Invitation credential
+    P: RistrettoPoint,
+    id: Scalar,
+    CDate: RistrettoPoint,
+    CBucket: RistrettoPoint,
+    CBlockages: RistrettoPoint,
+    CQ: RistrettoPoint,
+
+    // Fields for the inequality proof
+    // date + INVITATION_EXPIRY >= today
+    CG1: RistrettoPoint,
+    CG2: RistrettoPoint,
+    CG3: RistrettoPoint,
+    CG0sq: RistrettoPoint,
+    CG1sq: RistrettoPoint,
+    CG2sq: RistrettoPoint,
+    CG3sq: RistrettoPoint,
+
+    // Fields for user blinding of the Lox credential to be issued
+    D: RistrettoPoint,
+    EncIdClient: (RistrettoPoint, RistrettoPoint),
+    EncBucket: (RistrettoPoint, RistrettoPoint),
+    EncBlockages: (RistrettoPoint, RistrettoPoint),
+
+    // The combined ZKP
+    piUser: CompactProof,
+}
+
+#[derive(Debug)]
+pub struct State {
+    d: Scalar,
+    D: RistrettoPoint,
+    EncIdClient: (RistrettoPoint, RistrettoPoint),
+    EncBucket: (RistrettoPoint, RistrettoPoint),
+    EncBlockages: (RistrettoPoint, RistrettoPoint),
+    id_client: Scalar,
+    bucket: Scalar,
+    blockages: Scalar,
+}
+
+pub struct Response {
+    // The fields for the new Lox credential; the new trust level is 1
+    // and the new invites_remaining is 0, so we don't have to include
+    // them here explicitly
+    P: RistrettoPoint,
+    EncQ: (RistrettoPoint, RistrettoPoint),
+    id_server: Scalar,
+    level_since: Scalar,
+    TId: RistrettoPoint,
+    TBucket: RistrettoPoint,
+    TBlockages: RistrettoPoint,
+
+    // The ZKP
+    piBlindIssue: CompactProof,
+}
+
+define_proof! {
+    requestproof,
+    "Redeem Invite Request",
+    (date, bucket, blockages, zdate, zbucket, zblockages, negzQ,
+     d, eid_client, ebucket, eblockages, id_client,
+     g0, g1, g2, g3,
+     zg0, zg1, zg2, zg3,
+     wg0, wg1, wg2, wg3,
+     yg0, yg1, yg2, yg3),
+    (P, CDate, CBucket, CBlockages, V, Xdate, Xbucket, Xblockages,
+     D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1,
+     EncBlockages0, EncBlockages1,
+     CG0, CG1, CG2, CG3,
+     CG0sq, CG1sq, CG2sq, CG3sq),
+    (A, B):
+    // Blind showing of the Invitation credential
+    CDate = (date*P + zdate*A),
+    CBucket = (bucket*P + zbucket*A),
+    CBlockages = (blockages*P + zblockages*A),
+    // User blinding of the Lox credential to be issued
+    D = (d*B),
+    EncIdClient0 = (eid_client*B),
+    EncIdClient1 = (id_client*B + eid_client*D),
+    EncBucket0 = (ebucket*B),
+    EncBucket1 = (bucket*B + ebucket*D),
+    EncBlockages0 = (eblockages*B),
+    EncBlockages1 = (blockages*B + eblockages*D),
+    // Prove CDate encodes a value at most INVITATION_EXPIRY
+    // days ago: first prove each of g0, ..., g3 is a bit by
+    // proving that gi = gi^2
+    CG0 = (g0*P + zg0*A), CG0sq = (g0*CG0 + wg0*A), CG0sq = (g0*P + yg0*A),
+    CG1 = (g1*P + zg1*A), CG1sq = (g1*CG1 + wg1*A), CG1sq = (g1*P + yg1*A),
+    CG2 = (g2*P + zg2*A), CG2sq = (g2*CG2 + wg2*A), CG2sq = (g2*P + yg2*A),
+    CG3 = (g3*P + zg3*A), CG3sq = (g3*CG3 + wg3*A), CG3sq = (g3*P + yg3*A)
+    // Then we'll check that today*P + CG0 + 2*CG1 + 4*CG2 + 8*CG3 =
+    // CDate + INVITATION_EXPIRY*P by having the verifier
+    // plug in CDate + INVITATION_EXPIRY*P - (today*P + 2*CG1 + 4*CG2
+    // + 8*CG3) as its value of CG0.
+}
+
+pub fn request(
+    inv_cred: &cred::Invitation,
+    invitation_pub: &IssuerPubKey,
+    today: u32,
+) -> Result<(Request, State), ProofError> {
+    let A: &RistrettoPoint = &CMZ_A;
+    let B: &RistrettoPoint = &CMZ_B;
+    let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
+    let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
+
+    // Ensure the credential can be correctly shown: it must be the case
+    // that date + INVITATION_EXPIRY >= today.
+    let date: u32 = match scalar_u32(&inv_cred.date) {
+        Some(v) => v,
+        None => return Err(ProofError::VerificationFailure),
+    };
+    if date + INVITATION_EXPIRY < today {
+        return Err(ProofError::VerificationFailure);
+    }
+    let diffdays = date + INVITATION_EXPIRY - today;
+    // If diffdays > 15, then since INVITATION_EXPIRY <= 15, then date
+    // must be in the future.  Reject.
+    if diffdays > 15 {
+        return Err(ProofError::VerificationFailure);
+    }
+
+    // Blind showing the Invitation credential
+
+    // Reblind P and Q
+    let mut rng = rand::thread_rng();
+    let t = Scalar::random(&mut rng);
+    let P = t * inv_cred.P;
+    let Q = t * inv_cred.Q;
+
+    // Form Pedersen commitments to the blinded attributes
+    let zdate = Scalar::random(&mut rng);
+    let zbucket = Scalar::random(&mut rng);
+    let zblockages = Scalar::random(&mut rng);
+    let CDate = inv_cred.date * P + &zdate * Atable;
+    let CBucket = inv_cred.bucket * P + &zbucket * Atable;
+    let CBlockages = inv_cred.blockages * P + &zblockages * Atable;
+
+    // Form a Pedersen commitment to the MAC Q
+    // We flip the sign of zQ from that of the Hyphae paper so that
+    // the ZKP has a "+" instead of a "-", as that's what the zkp
+    // macro supports.
+    let negzQ = Scalar::random(&mut rng);
+    let CQ = Q - &negzQ * Atable;
+
+    // Compute the "error factor"
+    let V = zdate * invitation_pub.X[2]
+        + zbucket * invitation_pub.X[3]
+        + zblockages * invitation_pub.X[4]
+        + &negzQ * Atable;
+
+    // User blinding for the Lox certificate to be issued
+
+    // Pick an ElGamal keypair
+    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);
+
+    // Encrypt the other blinded fields (times B) to D as well
+    let ebucket = Scalar::random(&mut rng);
+    let EncBucket = (&ebucket * Btable, &inv_cred.bucket * Btable + ebucket * D);
+    let eblockages = Scalar::random(&mut rng);
+    let EncBlockages = (
+        &eblockages * Btable,
+        &inv_cred.blockages * Btable + eblockages * D,
+    );
+
+    // The range proof that 0 <= diffdays <= 15
+
+    // Extract the 4 bits from diffdays
+    let g0: Scalar = (diffdays & 1).into();
+    let g1: Scalar = ((diffdays >> 1) & 1).into();
+    let g2: Scalar = ((diffdays >> 2) & 1).into();
+    let g3: Scalar = ((diffdays >> 3) & 1).into();
+
+    // Pick random factors for the Pedersen commitments
+    let wg0 = Scalar::random(&mut rng);
+    let zg1 = Scalar::random(&mut rng);
+    let wg1 = Scalar::random(&mut rng);
+    let zg2 = Scalar::random(&mut rng);
+    let wg2 = Scalar::random(&mut rng);
+    let zg3 = Scalar::random(&mut rng);
+    let wg3 = Scalar::random(&mut rng);
+
+    // Compute zg0 to cancel things out as
+    // zg0 = -(zdate + 2*zg1 + 4*zg2 + 8*zg3)
+    // but use Horner's method
+    let zg0 = -(scalar_dbl(&(scalar_dbl(&(scalar_dbl(&zg3) + zg2)) + zg1)) + zdate);
+
+    let yg0 = wg0 + g0 * zg0;
+    let yg1 = wg1 + g1 * zg1;
+    let yg2 = wg2 + g2 * zg2;
+    let yg3 = wg3 + g3 * zg3;
+
+    let CG0 = g0 * P + &zg0 * Atable;
+    let CG1 = g1 * P + &zg1 * Atable;
+    let CG2 = g2 * P + &zg2 * Atable;
+    let CG3 = g3 * P + &zg3 * Atable;
+
+    let CG0sq = g0 * P + &yg0 * Atable;
+    let CG1sq = g1 * P + &yg1 * Atable;
+    let CG2sq = g2 * P + &yg2 * Atable;
+    let CG3sq = g3 * P + &yg3 * Atable;
+
+    // Construct the proof
+    let mut transcript = Transcript::new(b"redeem invite request");
+    let piUser = requestproof::prove_compact(
+        &mut transcript,
+        requestproof::ProveAssignments {
+            A: &A,
+            B: &B,
+            P: &P,
+            CDate: &CDate,
+            CBucket: &CBucket,
+            CBlockages: &CBlockages,
+            V: &V,
+            Xdate: &invitation_pub.X[2],
+            Xbucket: &invitation_pub.X[3],
+            Xblockages: &invitation_pub.X[4],
+            D: &D,
+            EncIdClient0: &EncIdClient.0,
+            EncIdClient1: &EncIdClient.1,
+            EncBucket0: &EncBucket.0,
+            EncBucket1: &EncBucket.1,
+            EncBlockages0: &EncBlockages.0,
+            EncBlockages1: &EncBlockages.1,
+            CG0: &CG0,
+            CG1: &CG1,
+            CG2: &CG2,
+            CG3: &CG3,
+            CG0sq: &CG0sq,
+            CG1sq: &CG1sq,
+            CG2sq: &CG2sq,
+            CG3sq: &CG3sq,
+            date: &inv_cred.date,
+            bucket: &inv_cred.bucket,
+            blockages: &inv_cred.blockages,
+            zdate: &zdate,
+            zbucket: &zbucket,
+            zblockages: &zblockages,
+            negzQ: &negzQ,
+            d: &d,
+            eid_client: &eid_client,
+            ebucket: &ebucket,
+            eblockages: &eblockages,
+            id_client: &id_client,
+            g0: &g0,
+            g1: &g1,
+            g2: &g2,
+            g3: &g3,
+            zg0: &zg0,
+            zg1: &zg1,
+            zg2: &zg2,
+            zg3: &zg3,
+            wg0: &wg0,
+            wg1: &wg1,
+            wg2: &wg2,
+            wg3: &wg3,
+            yg0: &yg0,
+            yg1: &yg1,
+            yg2: &yg2,
+            yg3: &yg3,
+        },
+    )
+    .0;
+
+    Ok((
+        Request {
+            P,
+            id: inv_cred.inv_id,
+            CDate,
+            CBucket,
+            CBlockages,
+            CQ,
+            D,
+            EncIdClient,
+            EncBucket,
+            EncBlockages,
+            CG1,
+            CG2,
+            CG3,
+            CG0sq,
+            CG1sq,
+            CG2sq,
+            CG3sq,
+            piUser,
+        },
+        State {
+            d,
+            D,
+            EncIdClient,
+            EncBucket,
+            EncBlockages,
+            id_client,
+            bucket: inv_cred.bucket,
+            blockages: inv_cred.blockages,
+        },
+    ))
+}

+ 49 - 18
src/tests.rs

@@ -201,30 +201,17 @@ fn test_level_up() {
     assert!(ba.verify_lox(&cred4));
 }
 
-#[test]
-fn test_issue_inv() {
-    let (bdb, mut ba) = setup();
-    let cred1 = level0_migration(&bdb, &mut ba);
-    assert!(scalar_u32(&cred1.trust_level).unwrap() == 1);
-
-    // Time passes
-    ba.advance_days(20);
-
-    let cred2 = level_up(&mut ba, &cred1);
-    assert!(scalar_u32(&cred2.trust_level).unwrap() == 2);
-    println!("cred2 = {:?}", cred2);
-    assert!(ba.verify_lox(&cred2));
-
+fn issue_invite(ba: &mut BridgeAuth, cred: &cred::Lox) -> (cred::Lox, cred::Invitation) {
     // Read the bucket in the credential to get today's Bucket
     // Reachability credential
-    let (id, key) = bridge_table::from_scalar(cred2.bucket).unwrap();
+    let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
     let encbuckets = ba.enc_bridge_table();
     let bucket =
         bridge_table::BridgeTable::decrypt_bucket(id, &key, &encbuckets[id as usize]).unwrap();
     let reachcred = bucket.1.unwrap();
 
     let (req, state) = issue_invite::request(
-        &cred2,
+        &cred,
         &reachcred,
         &ba.lox_pub,
         &ba.reachability_pub,
@@ -232,10 +219,54 @@ fn test_issue_inv() {
     )
     .unwrap();
     let resp = ba.handle_issue_invite(req).unwrap();
-    let (cred3, invite) =
-        issue_invite::handle_response(state, resp, &ba.lox_pub, &ba.invitation_pub).unwrap();
+    issue_invite::handle_response(state, resp, &ba.lox_pub, &ba.invitation_pub).unwrap()
+}
+
+#[test]
+fn test_issue_invite() {
+    let (bdb, mut ba) = setup();
+    let cred1 = level0_migration(&bdb, &mut ba);
+    assert!(scalar_u32(&cred1.trust_level).unwrap() == 1);
+
+    // Time passes
+    ba.advance_days(20);
+
+    let cred2 = level_up(&mut ba, &cred1);
+    assert!(scalar_u32(&cred2.trust_level).unwrap() == 2);
+    println!("cred2 = {:?}", cred2);
+    assert!(ba.verify_lox(&cred2));
+
+    let (cred3, invite) = issue_invite(&mut ba, &cred2);
     assert!(ba.verify_lox(&cred3));
     assert!(ba.verify_invitation(&invite));
     println!("cred3 = {:?}", cred3);
     println!("invite = {:?}", invite);
 }
+
+fn redeem_invite(ba: &mut BridgeAuth, inv: &cred::Invitation) -> cred::Lox {
+    let (req, state) = redeem_invite::request(&inv, &ba.invitation_pub, ba.today()).unwrap();
+    panic!("Not finished")
+}
+
+#[test]
+fn test_redeem_invite() {
+    let (bdb, mut ba) = setup();
+    let cred1 = level0_migration(&bdb, &mut ba);
+    assert!(scalar_u32(&cred1.trust_level).unwrap() == 1);
+
+    // Time passes
+    ba.advance_days(20);
+
+    let cred2 = level_up(&mut ba, &cred1);
+    assert!(scalar_u32(&cred2.trust_level).unwrap() == 2);
+    println!("cred2 = {:?}", cred2);
+    assert!(ba.verify_lox(&cred2));
+
+    let (cred3, invite) = issue_invite(&mut ba, &cred2);
+    println!("cred3 = {:?}", cred3);
+    println!("invite = {:?}", invite);
+
+    let cred4 = redeem_invite(&mut ba, &invite);
+    assert!(ba.verify_lox(&cred4));
+    println!("cred4 = {:?}", cred4);
+}