|
@@ -0,0 +1,443 @@
|
|
|
+/*! 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
|
|
|
+
|
|
|
+*/
|
|
|
+
|
|
|
+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};
|
|
|
+
|
|
|
+pub struct Request {
|
|
|
+ // Fields for blind showing the Lox credential
|
|
|
+ P: RistrettoPoint,
|
|
|
+ id: Scalar,
|
|
|
+ CBucket: RistrettoPoint,
|
|
|
+ CLevel: RistrettoPoint,
|
|
|
+ CSince: RistrettoPoint,
|
|
|
+ CInvRemain: RistrettoPoint,
|
|
|
+ CBlockages: RistrettoPoint,
|
|
|
+ CQ: RistrettoPoint,
|
|
|
+
|
|
|
+ // Fields for blind showing the Bucket Reachability credential
|
|
|
+ P_reach: RistrettoPoint,
|
|
|
+ CBucket_reach: RistrettoPoint,
|
|
|
+ CQ_reach: RistrettoPoint,
|
|
|
+
|
|
|
+ // Fields for user blinding of the Lox credential to be issued
|
|
|
+ D: RistrettoPoint,
|
|
|
+ EncIdClient: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncBucket: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncLevel: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncSince: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncInvRemain: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncBlockages: (RistrettoPoint, RistrettoPoint),
|
|
|
+
|
|
|
+ // Fields for user blinding of the Inivtation credential to be
|
|
|
+ // issued
|
|
|
+ EncInvIdClient: (RistrettoPoint, RistrettoPoint),
|
|
|
+ // The bucket and blockages attributes in the Invitation credential
|
|
|
+ // issuing protocol can just reuse the exact encryptions as for the
|
|
|
+ // Lox credential issuing protocol above.
|
|
|
+
|
|
|
+ // The combined ZKP
|
|
|
+ piUser: CompactProof,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct State {
|
|
|
+ d: Scalar,
|
|
|
+ D: RistrettoPoint,
|
|
|
+ EncIdClient: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncBucket: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncLevel: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncSince: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncInvRemain: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncBlockages: (RistrettoPoint, RistrettoPoint),
|
|
|
+ EncInvIdClient: (RistrettoPoint, RistrettoPoint),
|
|
|
+ id_client: Scalar,
|
|
|
+ bucket: Scalar,
|
|
|
+ level: Scalar,
|
|
|
+ since: Scalar,
|
|
|
+ invremain: Scalar,
|
|
|
+ blockages: Scalar,
|
|
|
+ inv_id_client: Scalar,
|
|
|
+}
|
|
|
+
|
|
|
+pub struct Response {
|
|
|
+ // The fields for the new Lox credential; the new invites_remaining
|
|
|
+ // is one less than the old value, so we don't have to include it
|
|
|
+ // here explicitly
|
|
|
+ P: RistrettoPoint,
|
|
|
+ EncQ: (RistrettoPoint, RistrettoPoint),
|
|
|
+ id_server: Scalar,
|
|
|
+ TId: RistrettoPoint,
|
|
|
+ TBucket: RistrettoPoint,
|
|
|
+ TLevel: RistrettoPoint,
|
|
|
+ TSince: RistrettoPoint,
|
|
|
+ TInvRemain: RistrettoPoint,
|
|
|
+ TBlockages: RistrettoPoint,
|
|
|
+ inv_id_server: Scalar,
|
|
|
+ TInvId: RistrettoPoint,
|
|
|
+
|
|
|
+ // The ZKP
|
|
|
+ piBlindIssue: CompactProof,
|
|
|
+}
|
|
|
+
|
|
|
+define_proof! {
|
|
|
+ requestproof,
|
|
|
+ "Issue Invite Request",
|
|
|
+ (bucket, level, since, invremain, blockages, zbucket, zlevel,
|
|
|
+ zsince, zinvremain, zblockages, negzQ,
|
|
|
+ zbucket_reach, negzQ_reach,
|
|
|
+ d, eid_client, ebucket, elevel, esince, einvremain, eblockages, id_client,
|
|
|
+ inv_id_client, einv_id_client,
|
|
|
+ invremain_inverse, zinvremain_inverse),
|
|
|
+ (P, CBucket, CLevel, CSince, CInvRemain, CBlockages, V, Xbucket,
|
|
|
+ Xlevel, Xsince, Xinvremain, Xblockages,
|
|
|
+ P_reach, CBucket_reach, V_reach, Xbucket_reach,
|
|
|
+ D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1,
|
|
|
+ EncLevel0, EncLevel1, EncSince0, EncSince1,
|
|
|
+ EncInvRemain0, EncInvRemain1_plus_B, EncBlockages0, EncBlockages1,
|
|
|
+ EncInvIdClient0, EncInvIdClient1),
|
|
|
+ (A, B):
|
|
|
+ // Blind showing of the Lox credential
|
|
|
+ CBucket = (bucket*P + zbucket*A),
|
|
|
+ CLevel = (level*P + zlevel*A),
|
|
|
+ CSince = (since*P + zsince*A),
|
|
|
+ CInvRemain = (invremain*P + zinvremain*A),
|
|
|
+ CBlockages = (blockages*P + zblockages*A),
|
|
|
+ // Proof that invremain is not 0
|
|
|
+ P = (invremain_inverse*CInvRemain + zinvremain_inverse*A),
|
|
|
+ // Blind showing of the Bucket Reachability credential; note the
|
|
|
+ // same bucket is used in the proof
|
|
|
+ CBucket_reach = (bucket*P_reach + zbucket_reach*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),
|
|
|
+ EncLevel0 = (elevel*B),
|
|
|
+ EncLevel1 = (level*B + elevel*D),
|
|
|
+ EncSince0 = (esince*B),
|
|
|
+ EncSince1 = (since*B + esince*D),
|
|
|
+ EncInvRemain0 = (einvremain*B),
|
|
|
+ EncInvRemain1_plus_B = (invremain*B + einvremain*D),
|
|
|
+ EncBlockages0 = (eblockages*B),
|
|
|
+ EncBlockages1 = (blockages*B + eblockages*D),
|
|
|
+ // User blinding of the Invitation to be issued
|
|
|
+ EncInvIdClient0 = (einv_id_client*B),
|
|
|
+ EncInvIdClient1 = (inv_id_client*B + einv_id_client*D)
|
|
|
+}
|
|
|
+
|
|
|
+pub fn request(
|
|
|
+ lox_cred: &cred::Lox,
|
|
|
+ reach_cred: &cred::BucketReachability,
|
|
|
+ lox_pub: &IssuerPubKey,
|
|
|
+ reach_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 invites_remaining not be 0
|
|
|
+ if lox_cred.invites_remaining == Scalar::zero() {
|
|
|
+ return Err(ProofError::VerificationFailure);
|
|
|
+ }
|
|
|
+ // The buckets in the Lox and Bucket Reachability credentials have
|
|
|
+ // to match
|
|
|
+ if lox_cred.bucket != reach_cred.bucket {
|
|
|
+ return Err(ProofError::VerificationFailure);
|
|
|
+ }
|
|
|
+ // The Bucket Reachability credential has to be dated today
|
|
|
+ let reach_date: u32 = match scalar_u32(&reach_cred.date) {
|
|
|
+ Some(v) => v,
|
|
|
+ None => return Err(ProofError::VerificationFailure),
|
|
|
+ };
|
|
|
+ if reach_date != today {
|
|
|
+ return Err(ProofError::VerificationFailure);
|
|
|
+ }
|
|
|
+ // The new invites_remaining
|
|
|
+ let new_invites_remaining = &lox_cred.invites_remaining - Scalar::one();
|
|
|
+
|
|
|
+ // Blind showing the Lox credential
|
|
|
+
|
|
|
+ // Reblind P and Q
|
|
|
+ let mut rng = rand::thread_rng();
|
|
|
+ let t = Scalar::random(&mut rng);
|
|
|
+ let P = t * lox_cred.P;
|
|
|
+ let Q = t * lox_cred.Q;
|
|
|
+
|
|
|
+ // Form Pedersen commitments to the blinded attributes
|
|
|
+ let zbucket = Scalar::random(&mut rng);
|
|
|
+ let zlevel = Scalar::random(&mut rng);
|
|
|
+ let zsince = Scalar::random(&mut rng);
|
|
|
+ let zinvremain = Scalar::random(&mut rng);
|
|
|
+ let zblockages = Scalar::random(&mut rng);
|
|
|
+ let CBucket = lox_cred.bucket * P + &zbucket * Atable;
|
|
|
+ let CLevel = lox_cred.bucket * P + &zlevel * Atable;
|
|
|
+ let CSince = lox_cred.level_since * P + &zsince * Atable;
|
|
|
+ let CInvRemain = lox_cred.invites_remaining * P + &zinvremain * Atable;
|
|
|
+ let CBlockages = lox_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 = zbucket * lox_pub.X[2]
|
|
|
+ + zlevel * lox_pub.X[3]
|
|
|
+ + zsince * lox_pub.X[4]
|
|
|
+ + zinvremain * lox_pub.X[5]
|
|
|
+ + zblockages * lox_pub.X[6]
|
|
|
+ + &negzQ * Atable;
|
|
|
+
|
|
|
+ // Blind showing the Bucket Reachability credential
|
|
|
+
|
|
|
+ // Reblind P and Q
|
|
|
+ let t_reach = Scalar::random(&mut rng);
|
|
|
+ let P_reach = t_reach * reach_cred.P;
|
|
|
+ let Q_reach = t_reach * reach_cred.Q;
|
|
|
+
|
|
|
+ // Form Pedersen commitments to the blinded attributes
|
|
|
+ let zbucket_reach = Scalar::random(&mut rng);
|
|
|
+ let CBucket_reach = reach_cred.bucket * P_reach + &zbucket_reach * 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_reach = Scalar::random(&mut rng);
|
|
|
+ let CQ_reach = Q_reach - &negzQ_reach * Atable;
|
|
|
+
|
|
|
+ // Compute the "error factor"
|
|
|
+ let V_reach = zbucket_reach * reach_pub.X[2] + &negzQ_reach * 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, &lox_cred.bucket * Btable + ebucket * D);
|
|
|
+ let elevel = Scalar::random(&mut rng);
|
|
|
+ let EncLevel = (
|
|
|
+ &elevel * Btable,
|
|
|
+ &lox_cred.trust_level * Btable + elevel * D,
|
|
|
+ );
|
|
|
+ let esince = Scalar::random(&mut rng);
|
|
|
+ let EncSince = (
|
|
|
+ &esince * Btable,
|
|
|
+ &lox_cred.level_since * Btable + esince * D,
|
|
|
+ );
|
|
|
+ let einvremain = Scalar::random(&mut rng);
|
|
|
+ let EncInvRemain = (
|
|
|
+ &einvremain * Btable,
|
|
|
+ &new_invites_remaining * Btable + einvremain * D,
|
|
|
+ );
|
|
|
+ let eblockages = Scalar::random(&mut rng);
|
|
|
+ let EncBlockages = (
|
|
|
+ &eblockages * Btable,
|
|
|
+ &lox_cred.blockages * Btable + eblockages * D,
|
|
|
+ );
|
|
|
+
|
|
|
+ // User blinding for the Invitation certificate to be issued
|
|
|
+
|
|
|
+ // Pick a random client component of the id
|
|
|
+ let inv_id_client = Scalar::random(&mut rng);
|
|
|
+
|
|
|
+ // Encrypt it (times the basepoint B) to the ElGamal public key D we
|
|
|
+ // just created
|
|
|
+ let einv_id_client = Scalar::random(&mut rng);
|
|
|
+ let EncInvIdClient = (
|
|
|
+ &einv_id_client * Btable,
|
|
|
+ &id_client * Btable + einv_id_client * D,
|
|
|
+ );
|
|
|
+
|
|
|
+ // The proof that invites_remaining is not zero. We prove this by
|
|
|
+ // demonstrating that we know its inverse.
|
|
|
+ let invremain_inverse = &lox_cred.invites_remaining.invert();
|
|
|
+
|
|
|
+ let zinvremain_inverse = -zinvremain * invremain_inverse;
|
|
|
+
|
|
|
+ // So now invremain_inverse * CInvRemain + zinvremain_inverse * A = P
|
|
|
+
|
|
|
+ // Construct the proof
|
|
|
+ let mut transcript = Transcript::new(b"issue invite request");
|
|
|
+ let piUser = requestproof::prove_compact(
|
|
|
+ &mut transcript,
|
|
|
+ requestproof::ProveAssignments {
|
|
|
+ A: &A,
|
|
|
+ B: &B,
|
|
|
+ P: &P,
|
|
|
+ CBucket: &CBucket,
|
|
|
+ CLevel: &CLevel,
|
|
|
+ CSince: &CSince,
|
|
|
+ CInvRemain: &CInvRemain,
|
|
|
+ CBlockages: &CBlockages,
|
|
|
+ V: &V,
|
|
|
+ Xbucket: &lox_pub.X[2],
|
|
|
+ Xlevel: &lox_pub.X[3],
|
|
|
+ Xsince: &lox_pub.X[4],
|
|
|
+ Xinvremain: &lox_pub.X[5],
|
|
|
+ Xblockages: &lox_pub.X[6],
|
|
|
+ P_reach: &P_reach,
|
|
|
+ CBucket_reach: &CBucket_reach,
|
|
|
+ V_reach: &V_reach,
|
|
|
+ Xbucket_reach: &reach_pub.X[2],
|
|
|
+ D: &D,
|
|
|
+ EncIdClient0: &EncIdClient.0,
|
|
|
+ EncIdClient1: &EncIdClient.1,
|
|
|
+ EncBucket0: &EncBucket.0,
|
|
|
+ EncBucket1: &EncBucket.1,
|
|
|
+ EncLevel0: &EncLevel.0,
|
|
|
+ EncLevel1: &EncLevel.1,
|
|
|
+ EncSince0: &EncSince.0,
|
|
|
+ EncSince1: &EncSince.1,
|
|
|
+ EncInvRemain0: &EncInvRemain.0,
|
|
|
+ EncInvRemain1_plus_B: &(EncInvRemain.1 + B),
|
|
|
+ EncBlockages0: &EncBlockages.0,
|
|
|
+ EncBlockages1: &EncBlockages.1,
|
|
|
+ EncInvIdClient0: &EncInvIdClient.0,
|
|
|
+ EncInvIdClient1: &EncInvIdClient.1,
|
|
|
+ bucket: &lox_cred.bucket,
|
|
|
+ level: &lox_cred.trust_level,
|
|
|
+ since: &lox_cred.level_since,
|
|
|
+ invremain: &lox_cred.invites_remaining,
|
|
|
+ blockages: &lox_cred.blockages,
|
|
|
+ zbucket: &zbucket,
|
|
|
+ zlevel: &zlevel,
|
|
|
+ zsince: &zsince,
|
|
|
+ zinvremain: &zinvremain,
|
|
|
+ zblockages: &zblockages,
|
|
|
+ negzQ: &negzQ,
|
|
|
+ zbucket_reach: &zbucket_reach,
|
|
|
+ negzQ_reach: &negzQ_reach,
|
|
|
+ d: &d,
|
|
|
+ eid_client: &eid_client,
|
|
|
+ ebucket: &ebucket,
|
|
|
+ elevel: &elevel,
|
|
|
+ esince: &esince,
|
|
|
+ einvremain: &einvremain,
|
|
|
+ eblockages: &eblockages,
|
|
|
+ id_client: &id_client,
|
|
|
+ inv_id_client: &inv_id_client,
|
|
|
+ einv_id_client: &einv_id_client,
|
|
|
+ invremain_inverse: &invremain_inverse,
|
|
|
+ zinvremain_inverse: &zinvremain_inverse,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ .0;
|
|
|
+
|
|
|
+ Ok((
|
|
|
+ Request {
|
|
|
+ P,
|
|
|
+ id: lox_cred.id,
|
|
|
+ CBucket,
|
|
|
+ CLevel,
|
|
|
+ CSince,
|
|
|
+ CInvRemain,
|
|
|
+ CBlockages,
|
|
|
+ CQ,
|
|
|
+ P_reach,
|
|
|
+ CBucket_reach,
|
|
|
+ CQ_reach,
|
|
|
+ D,
|
|
|
+ EncIdClient,
|
|
|
+ EncBucket,
|
|
|
+ EncLevel,
|
|
|
+ EncSince,
|
|
|
+ EncInvRemain,
|
|
|
+ EncBlockages,
|
|
|
+ EncInvIdClient,
|
|
|
+ piUser,
|
|
|
+ },
|
|
|
+ State {
|
|
|
+ d,
|
|
|
+ D,
|
|
|
+ EncIdClient,
|
|
|
+ EncBucket,
|
|
|
+ EncLevel,
|
|
|
+ EncSince,
|
|
|
+ EncInvRemain,
|
|
|
+ EncBlockages,
|
|
|
+ EncInvIdClient,
|
|
|
+ id_client,
|
|
|
+ bucket: lox_cred.bucket,
|
|
|
+ level: lox_cred.trust_level,
|
|
|
+ since: lox_cred.level_since,
|
|
|
+ invremain: new_invites_remaining,
|
|
|
+ blockages: lox_cred.blockages,
|
|
|
+ inv_id_client,
|
|
|
+ },
|
|
|
+ ))
|
|
|
+}
|