lox/crates/lox-library/src/proto/issue_invite.rs

444 lines
15 KiB
Rust
Raw Normal View History

/*! 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,
},
))
}