|
@@ -57,20 +57,26 @@ use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
|
|
|
/// upgrade protocol when they're already at the max level; they will
|
|
|
/// get a fresh invites_remaining batch, and reset their level_since
|
|
|
/// field to today's date, but will remain in the max level.
|
|
|
-pub const MAX_LEVEL: usize = 3;
|
|
|
+pub const MAX_LEVEL: usize = 4;
|
|
|
|
|
|
/// LEVEL_INTERVAL\[i\] for i >= 1 is the minimum number of days a user
|
|
|
/// must be at trust level i before advancing to level i+1 (or as above,
|
|
|
/// remain at level i if i == MAX_LEVEL). Note that the
|
|
|
/// LEVEL_INTERVAL\[0\] entry is a dummy; the trust_promotion protocol
|
|
|
/// is used instead of this one to move from level 0 to level 1.
|
|
|
-pub const LEVEL_INTERVAL: [u32; MAX_LEVEL + 1] = [0, 14, 28, 56];
|
|
|
+pub const LEVEL_INTERVAL: [u32; MAX_LEVEL + 1] = [0, 14, 28, 56, 84];
|
|
|
|
|
|
/// LEVEL_INVITATIONS\[i\] for i >= 1 is the number of invitations a
|
|
|
/// user will be eligible to issue upon advancing from level i to level
|
|
|
/// i+1. Again the LEVEL_INVITATIONS\[0\] entry is a dummy, as for
|
|
|
/// LEVEL_INTERVAL.
|
|
|
-pub const LEVEL_INVITATIONS: [u32; MAX_LEVEL + 1] = [0, 2, 4, 6];
|
|
|
+pub const LEVEL_INVITATIONS: [u32; MAX_LEVEL + 1] = [0, 2, 4, 6, 8];
|
|
|
+
|
|
|
+/// MAX_BLOCKAGES\[i\] for i >= 1 is the maximum number of bucket
|
|
|
+/// blockages this credential is allowed to have recorded in order to
|
|
|
+/// advance from level i to level i+1. Again the LEVEL_INVITATIONS\[0\]
|
|
|
+/// entry is a dummy, as for LEVEL_INTERVAL.
|
|
|
+pub const MAX_BLOCKAGES: [u32; MAX_LEVEL + 1] = [0, 4, 3, 2, 2];
|
|
|
|
|
|
pub struct Request {
|
|
|
// Fields for blind showing the Lox credential
|
|
@@ -88,8 +94,8 @@ pub struct Request {
|
|
|
CBucket_reach: RistrettoPoint,
|
|
|
CQ_reach: RistrettoPoint,
|
|
|
|
|
|
- // Fields for the inequality proof (level_since +
|
|
|
- // LEVEL_INTERVAL[level] <= today)
|
|
|
+ // Fields for the inequality proof
|
|
|
+ // level_since + LEVEL_INTERVAL[level] <= today
|
|
|
CG1: RistrettoPoint,
|
|
|
CG2: RistrettoPoint,
|
|
|
CG3: RistrettoPoint,
|
|
@@ -108,6 +114,14 @@ pub struct Request {
|
|
|
CG7sq: RistrettoPoint,
|
|
|
CG8sq: RistrettoPoint,
|
|
|
|
|
|
+ // Fields for the inequality proof
|
|
|
+ // blockages <= MAX_BLOCKAGES[level]
|
|
|
+ CH1: RistrettoPoint,
|
|
|
+ CH2: RistrettoPoint,
|
|
|
+ CH0sq: RistrettoPoint,
|
|
|
+ CH1sq: RistrettoPoint,
|
|
|
+ CH2sq: RistrettoPoint,
|
|
|
+
|
|
|
// Fields for user blinding of the Lox credential to be issued
|
|
|
D: RistrettoPoint,
|
|
|
EncIdClient: (RistrettoPoint, RistrettoPoint),
|
|
@@ -158,14 +172,20 @@ define_proof! {
|
|
|
g0, g1, g2, g3, g4, g5, g6, g7, g8,
|
|
|
zg0, zg1, zg2, zg3, zg4, zg5, zg6, zg7, zg8,
|
|
|
wg0, wg1, wg2, wg3, wg4, wg5, wg6, wg7, wg8,
|
|
|
- yg0, yg1, yg2, yg3, yg4, yg5, yg6, yg7, yg8),
|
|
|
+ yg0, yg1, yg2, yg3, yg4, yg5, yg6, yg7, yg8,
|
|
|
+ h0, h1, h2,
|
|
|
+ zh0, zh1, zh2,
|
|
|
+ wh0, wh1, wh2,
|
|
|
+ yh0, yh1, yh2),
|
|
|
(P, CBucket, CSince, CInvRemain, CBlockages, V, Xbucket, Xsince,
|
|
|
Xinvremain, Xblockages,
|
|
|
P_reach, CBucket_reach, V_reach, Xbucket_reach,
|
|
|
D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1,
|
|
|
EncBlockages0, EncBlockages1,
|
|
|
CG0, CG1, CG2, CG3, CG4, CG5, CG6, CG7, CG8,
|
|
|
- CG0sq, CG1sq, CG2sq, CG3sq, CG4sq, CG5sq, CG6sq, CG7sq, CG8sq),
|
|
|
+ CG0sq, CG1sq, CG2sq, CG3sq, CG4sq, CG5sq, CG6sq, CG7sq, CG8sq,
|
|
|
+ CH0, CH1, CH2,
|
|
|
+ CH0sq, CH1sq, CH2sq),
|
|
|
(A, B) :
|
|
|
// Blind showing of the Lox credential
|
|
|
CBucket = (bucket*P + zbucket*A),
|
|
@@ -184,7 +204,7 @@ define_proof! {
|
|
|
EncBlockages0 = (eblockages*B),
|
|
|
EncBlockages1 = (blockages*B + eblockages*D),
|
|
|
// Prove CSince encodes a value at least LEVEL_INTERVAL
|
|
|
- // days ago (at technically at most LEVEL_INTERVAL+511 days
|
|
|
+ // days ago (and technically at most LEVEL_INTERVAL+511 days
|
|
|
// ago): first prove each of g0, ..., g8 is a bit by proving that
|
|
|
// gi = gi^2
|
|
|
CG0 = (g0*P + zg0*A), CG0sq = (g0*CG0 + wg0*A), CG0sq = (g0*P + yg0*A),
|
|
@@ -195,11 +215,20 @@ define_proof! {
|
|
|
CG5 = (g5*P + zg5*A), CG5sq = (g5*CG5 + wg5*A), CG5sq = (g5*P + yg5*A),
|
|
|
CG6 = (g6*P + zg6*A), CG6sq = (g6*CG6 + wg6*A), CG6sq = (g6*P + yg6*A),
|
|
|
CG7 = (g7*P + zg7*A), CG7sq = (g7*CG7 + wg7*A), CG7sq = (g7*P + yg7*A),
|
|
|
- CG8 = (g8*P + zg8*A), CG8sq = (g8*CG8 + wg8*A), CG8sq = (g8*P + yg8*A)
|
|
|
+ CG8 = (g8*P + zg8*A), CG8sq = (g8*CG8 + wg8*A), CG8sq = (g8*P + yg8*A),
|
|
|
// Then we'll check that CSince + LEVEL_INTERVAL*P + CG0 + 2*CG1
|
|
|
// + 4*CG2 + 8*CG3 + ... + 256*CG8 = today*P by having the verifier
|
|
|
// plug in today*P - (CSince + LEVEL_INTERVAL*P + 2*CG1 + 4*CG2
|
|
|
// + ... + 256*CG8) as its value of CG0.
|
|
|
+
|
|
|
+ // Prove CBlockage encodes a value at most MAX_BLOCKAGES (and at least
|
|
|
+ // MAX_BLOCKAGES-7)
|
|
|
+ CH0 = (h0*P + zh0*A), CH0sq = (h0*CH0 + wh0*A), CH0sq = (h0*P + yh0*A),
|
|
|
+ CH1 = (h1*P + zh1*A), CH1sq = (h1*CH1 + wh1*A), CH1sq = (h1*P + yh1*A),
|
|
|
+ CH2 = (h2*P + zh2*A), CH2sq = (h2*CH2 + wh2*A), CH2sq = (h2*P + yh2*A)
|
|
|
+ // Then we'll check that CBlockage + CH0 + 2*CH1 + 4*CH2 =
|
|
|
+ // MAX_BLOCKAGES*P by having the verifier plug in MAX_BLOCKAGES*P -
|
|
|
+ // (CBlockage - 2*CH1 - 4*CH2) as its value of CH0.
|
|
|
}
|
|
|
|
|
|
define_proof! {
|
|
@@ -271,6 +300,15 @@ pub fn request(
|
|
|
if diffdays > 511 {
|
|
|
return Err(ProofError::VerificationFailure);
|
|
|
}
|
|
|
+ // The current number of blockages
|
|
|
+ let blockages: u32 = match scalar_u32(&lox_cred.blockages) {
|
|
|
+ Some(v) => v,
|
|
|
+ None => return Err(ProofError::VerificationFailure),
|
|
|
+ };
|
|
|
+ if blockages > MAX_BLOCKAGES[trust_level as usize] {
|
|
|
+ return Err(ProofError::VerificationFailure);
|
|
|
+ }
|
|
|
+ let blockage_diff = MAX_BLOCKAGES[trust_level as usize] - blockages;
|
|
|
// The buckets in the Lox and Bucket Reachability credentials have
|
|
|
// to match
|
|
|
if lox_cred.bucket != reach_cred.bucket {
|
|
@@ -445,6 +483,37 @@ pub fn request(
|
|
|
let CG7sq = g7 * P + &yg7 * Atable;
|
|
|
let CG8sq = g8 * P + &yg8 * Atable;
|
|
|
|
|
|
+ // The range proof that 0 <= blockage_diff <= 7
|
|
|
+
|
|
|
+ // Extract the 3 bits from blockage_diff
|
|
|
+ let h0: Scalar = (blockage_diff & 1).into();
|
|
|
+ let h1: Scalar = ((blockage_diff >> 1) & 1).into();
|
|
|
+ let h2: Scalar = ((blockage_diff >> 2) & 1).into();
|
|
|
+
|
|
|
+ // Pick random factors for the Pedersen commitments
|
|
|
+ let wh0 = Scalar::random(&mut rng);
|
|
|
+ let zh1 = Scalar::random(&mut rng);
|
|
|
+ let wh1 = Scalar::random(&mut rng);
|
|
|
+ let zh2 = Scalar::random(&mut rng);
|
|
|
+ let wh2 = Scalar::random(&mut rng);
|
|
|
+
|
|
|
+ // Compute zh0 to cancel things out as
|
|
|
+ // zh0 = -(zblockages + 2*zh1 + 4*zh2)
|
|
|
+ // but use Horner's method
|
|
|
+ let zh0 = -(scalar_dbl(&(scalar_dbl(&zh2) + zh1)) + zblockages);
|
|
|
+
|
|
|
+ let yh0 = wh0 + h0 * zh0;
|
|
|
+ let yh1 = wh1 + h1 * zh1;
|
|
|
+ let yh2 = wh2 + h2 * zh2;
|
|
|
+
|
|
|
+ let CH0 = h0 * P + &zh0 * Atable;
|
|
|
+ let CH1 = h1 * P + &zh1 * Atable;
|
|
|
+ let CH2 = h2 * P + &zh2 * Atable;
|
|
|
+
|
|
|
+ let CH0sq = h0 * P + &yh0 * Atable;
|
|
|
+ let CH1sq = h1 * P + &yh1 * Atable;
|
|
|
+ let CH2sq = h2 * P + &yh2 * Atable;
|
|
|
+
|
|
|
// Construct the proof
|
|
|
let mut transcript = Transcript::new(b"level upgrade request");
|
|
|
let piUser = requestproof::prove_compact(
|
|
@@ -491,6 +560,12 @@ pub fn request(
|
|
|
CG6sq: &CG6sq,
|
|
|
CG7sq: &CG7sq,
|
|
|
CG8sq: &CG8sq,
|
|
|
+ CH0: &CH0,
|
|
|
+ CH1: &CH1,
|
|
|
+ CH2: &CH2,
|
|
|
+ CH0sq: &CH0sq,
|
|
|
+ CH1sq: &CH1sq,
|
|
|
+ CH2sq: &CH2sq,
|
|
|
bucket: &lox_cred.bucket,
|
|
|
since: &lox_cred.level_since,
|
|
|
invremain: &lox_cred.invites_remaining,
|
|
@@ -543,6 +618,18 @@ pub fn request(
|
|
|
yg6: &yg6,
|
|
|
yg7: &yg7,
|
|
|
yg8: &yg8,
|
|
|
+ h0: &h0,
|
|
|
+ h1: &h1,
|
|
|
+ h2: &h2,
|
|
|
+ zh0: &zh0,
|
|
|
+ zh1: &zh1,
|
|
|
+ zh2: &zh2,
|
|
|
+ wh0: &wh0,
|
|
|
+ wh1: &wh1,
|
|
|
+ wh2: &wh2,
|
|
|
+ yh0: &yh0,
|
|
|
+ yh1: &yh1,
|
|
|
+ yh2: &yh2,
|
|
|
},
|
|
|
)
|
|
|
.0;
|
|
@@ -581,6 +668,11 @@ pub fn request(
|
|
|
CG6sq,
|
|
|
CG7sq,
|
|
|
CG8sq,
|
|
|
+ CH1,
|
|
|
+ CH2,
|
|
|
+ CH0sq,
|
|
|
+ CH1sq,
|
|
|
+ CH2sq,
|
|
|
piUser,
|
|
|
},
|
|
|
State {
|
|
@@ -652,6 +744,10 @@ impl BridgeAuth {
|
|
|
) + req.CG1),
|
|
|
);
|
|
|
|
|
|
+ // Recompute CH0 using Horner's method
|
|
|
+ let mblk: Scalar = MAX_BLOCKAGES[level].into();
|
|
|
+ let CH0prime = mblk * req.P - req.CBlockages - pt_dbl(&(pt_dbl(&req.CH2) + req.CH1));
|
|
|
+
|
|
|
// Verify the ZKP
|
|
|
let mut transcript = Transcript::new(b"level upgrade request");
|
|
|
requestproof::verify_compact(
|
|
@@ -699,6 +795,12 @@ impl BridgeAuth {
|
|
|
CG6sq: &req.CG6sq.compress(),
|
|
|
CG7sq: &req.CG7sq.compress(),
|
|
|
CG8sq: &req.CG8sq.compress(),
|
|
|
+ CH0: &CH0prime.compress(),
|
|
|
+ CH1: &req.CH1.compress(),
|
|
|
+ CH2: &req.CH2.compress(),
|
|
|
+ CH0sq: &req.CH0sq.compress(),
|
|
|
+ CH1sq: &req.CH1sq.compress(),
|
|
|
+ CH2sq: &req.CH2sq.compress(),
|
|
|
},
|
|
|
)?;
|
|
|
|