295 lines
9.3 KiB
Rust
295 lines
9.3 KiB
Rust
/*! A module to allow a user to prove they have trust level 3 or greater
|
|
and a particular bucket. This is used by Troll Patrol for positive
|
|
reports (indicating that a bridge has been accessed).
|
|
|
|
The user presents their current Lox credential:
|
|
- id: blinded
|
|
- bucket: blinded (but proven to be bucket in provided bucket*H)
|
|
- trust_level: blinded (but proven to be 3 or higher)
|
|
- level_since: blinded
|
|
- invites_remaining: blinded
|
|
- blockages: blinded
|
|
|
|
This protocol does not expose the credential's ID and does not issue a
|
|
new Lox credential. The user does not receive a response.
|
|
|
|
Due to the way the level proof is implemented, this protocol has
|
|
hardcoded assumptions that the level is 3-4.
|
|
|
|
*/
|
|
|
|
use curve25519_dalek::ristretto::{RistrettoBasepointTable, RistrettoPoint};
|
|
use curve25519_dalek::scalar::Scalar;
|
|
use curve25519_dalek::traits::IsIdentity;
|
|
use lox_zkp::{CompactProof, ProofError, Transcript};
|
|
use serde::{Deserialize, Serialize};
|
|
use sha2::Sha512;
|
|
use std::convert::TryInto;
|
|
|
|
use super::super::cred;
|
|
use super::super::scalar_u32;
|
|
use super::super::{BridgeAuth, IssuerPubKey};
|
|
use super::super::{CMZ_A, CMZ_A_TABLE};
|
|
|
|
pub fn compute_H(date: u32) -> RistrettoPoint {
|
|
RistrettoPoint::hash_from_bytes::<Sha512>(format!("PR Generator H for {}", date).as_bytes())
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct Request {
|
|
// Fields for blind showing the Lox credential
|
|
P: RistrettoPoint,
|
|
CId: RistrettoPoint,
|
|
CBucket: RistrettoPoint,
|
|
CLevel: RistrettoPoint,
|
|
CSince: RistrettoPoint,
|
|
CInvRemain: RistrettoPoint,
|
|
CBlockages: RistrettoPoint,
|
|
CQ: RistrettoPoint,
|
|
|
|
// Fields for proving which bucket we have
|
|
pub date: u32, // date is used to compute H
|
|
pub BP: RistrettoPoint,
|
|
|
|
// Fields for proving 3 <= trust_level <= 4
|
|
// CG can be computed by verifier
|
|
CGsq: RistrettoPoint,
|
|
|
|
// The combined lox_zkp
|
|
piUser: CompactProof,
|
|
}
|
|
|
|
define_proof! {
|
|
requestproof,
|
|
"Positive Report Request",
|
|
(id, bucket, level, since, invremain, blockages,
|
|
zid, zbucket, zlevel, zsince, zinvremain, zblockages, negzQ,
|
|
g, zg, wg, yg),
|
|
(P, CId, CBucket, CLevel, CSince, CInvRemain, CBlockages,
|
|
V, Xid, Xbucket, Xlevel, Xsince, Xinvremain, Xblockages,
|
|
H, BP, CG, CGsq),
|
|
(A):
|
|
// Blind showing of the Lox credential
|
|
CId = (id*P + zid*A),
|
|
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),
|
|
V = (zid*Xid + zbucket*Xbucket + zlevel*Xlevel + zsince*Xsince + zinvremain*Xinvremain + zblockages*Xblockages + negzQ*A),
|
|
// Prove bucket is same bucket used in BP
|
|
BP = (bucket*H),
|
|
// Prove CLevel encodes a value of 3 or 4
|
|
// First prove g is a bit by proving that g = g^2
|
|
CG = (g*P + zg*A), CGsq = (g*CG + wg*A), CGsq = (g*P + yg*A)
|
|
// The verifier will compute CG = CLevel - 3P
|
|
}
|
|
|
|
pub fn request(lox_cred: &cred::Lox, lox_pub: &IssuerPubKey) -> Result<Request, ProofError> {
|
|
let A: &RistrettoPoint = &CMZ_A;
|
|
let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
|
|
|
|
// TODO: Where should this go? For efficiency, this should probably be global
|
|
let date: u32 = time::OffsetDateTime::now_utc()
|
|
.date()
|
|
.to_julian_day()
|
|
.try_into()
|
|
.unwrap();
|
|
let H = compute_H(date);
|
|
let Htable: RistrettoBasepointTable = RistrettoBasepointTable::create(&H);
|
|
|
|
// Ensure that the credential can be correctly shown: it must be the case
|
|
// that trust_level is 3 or 4
|
|
let level: u32 = match scalar_u32(&lox_cred.trust_level) {
|
|
Some(v) => v,
|
|
None => return Err(ProofError::VerificationFailure),
|
|
};
|
|
// level must be 3 + one bit or we need to add more bits
|
|
if level < 3 || level > 4 {
|
|
return Err(ProofError::VerificationFailure);
|
|
}
|
|
|
|
// 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 zid = Scalar::random(&mut rng);
|
|
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 CId = lox_cred.id * P + &zid * Atable;
|
|
let CBucket = lox_cred.bucket * P + &zbucket * Atable;
|
|
let CLevel = lox_cred.trust_level * 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 lox_zkp has a "+" instead of a "-", as that's what the lox_zkp
|
|
// macro supports.
|
|
let negzQ = Scalar::random(&mut rng);
|
|
let CQ = Q - &negzQ * Atable;
|
|
|
|
// Compute the "error factor"
|
|
let V = zid * lox_pub.X[1]
|
|
+ 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;
|
|
|
|
// Compute BP for proving knowledge of bucket
|
|
let BP = &lox_cred.bucket * &Htable;
|
|
|
|
// Proof that 3 <= trust_level
|
|
|
|
let g: Scalar = (level - 3).into();
|
|
|
|
// Pick random factor for the Pedersen commitment
|
|
let wg = Scalar::random(&mut rng);
|
|
|
|
// zg equals zlevel so that
|
|
// 3*P + CG
|
|
// = 3*P + (g*P + zg*A)
|
|
// = (3+g)*P + zlevel*A
|
|
// = level*P + zlevel*A
|
|
// = CLevel
|
|
let zg = zlevel;
|
|
|
|
let yg = wg + g * zg;
|
|
|
|
let CG = g * P + &zg * Atable;
|
|
|
|
let CGsq = g * P + &yg * Atable;
|
|
|
|
// Construct the proof
|
|
let mut transcript = Transcript::new(b"positive report request");
|
|
let piUser = requestproof::prove_compact(
|
|
&mut transcript,
|
|
requestproof::ProveAssignments {
|
|
A,
|
|
P: &P,
|
|
CId: &CId,
|
|
CBucket: &CBucket,
|
|
CLevel: &CLevel,
|
|
CSince: &CSince,
|
|
CInvRemain: &CInvRemain,
|
|
CBlockages: &CBlockages,
|
|
V: &V,
|
|
Xid: &lox_pub.X[1],
|
|
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],
|
|
H: &H,
|
|
BP: &BP,
|
|
CG: &CG,
|
|
CGsq: &CGsq,
|
|
id: &lox_cred.id,
|
|
bucket: &lox_cred.bucket,
|
|
level: &lox_cred.trust_level,
|
|
since: &lox_cred.level_since,
|
|
invremain: &lox_cred.invites_remaining,
|
|
blockages: &lox_cred.blockages,
|
|
zid: &zid,
|
|
zbucket: &zbucket,
|
|
zlevel: &zlevel,
|
|
zsince: &zsince,
|
|
zinvremain: &zinvremain,
|
|
zblockages: &zblockages,
|
|
negzQ: &negzQ,
|
|
g: &g,
|
|
zg: &zg,
|
|
wg: &wg,
|
|
yg: &yg,
|
|
},
|
|
)
|
|
.0;
|
|
|
|
Ok(Request {
|
|
P,
|
|
CId,
|
|
CBucket,
|
|
CLevel,
|
|
CSince,
|
|
CInvRemain,
|
|
CBlockages,
|
|
CQ,
|
|
date,
|
|
BP,
|
|
CGsq,
|
|
piUser,
|
|
})
|
|
}
|
|
|
|
impl BridgeAuth {
|
|
/// Receive a positive report request
|
|
pub fn handle_positive_report(
|
|
&mut self,
|
|
req: Request,
|
|
Htable: &RistrettoBasepointTable,
|
|
) -> Result<(), ProofError> {
|
|
let A: &RistrettoPoint = &CMZ_A;
|
|
let H = Htable.basepoint();
|
|
|
|
if req.P.is_identity() {
|
|
return Err(ProofError::VerificationFailure);
|
|
}
|
|
|
|
// Recompute the "error factor" using knowledge of our own
|
|
// (the issuer's) private key instead of knowledge of the
|
|
// hidden attributes
|
|
let Vprime = self.lox_priv.x[0] * req.P
|
|
+ self.lox_priv.x[1] * req.CId
|
|
+ self.lox_priv.x[2] * req.CBucket
|
|
+ self.lox_priv.x[3] * req.CLevel
|
|
+ self.lox_priv.x[4] * req.CSince
|
|
+ self.lox_priv.x[5] * req.CInvRemain
|
|
+ self.lox_priv.x[6] * req.CBlockages
|
|
- req.CQ;
|
|
|
|
// Recompute CG
|
|
let CG = req.CLevel - Scalar::from(3 as u8) * req.P;
|
|
|
|
// Verify the zkp
|
|
let mut transcript = Transcript::new(b"positive report request");
|
|
requestproof::verify_compact(
|
|
&req.piUser,
|
|
&mut transcript,
|
|
requestproof::VerifyAssignments {
|
|
A: &A.compress(),
|
|
P: &req.P.compress(),
|
|
CId: &req.CId.compress(),
|
|
CBucket: &req.CBucket.compress(),
|
|
CLevel: &req.CLevel.compress(),
|
|
CSince: &req.CSince.compress(),
|
|
CInvRemain: &req.CInvRemain.compress(),
|
|
CBlockages: &req.CBlockages.compress(),
|
|
V: &Vprime.compress(),
|
|
Xid: &self.lox_pub.X[1].compress(),
|
|
Xbucket: &self.lox_pub.X[2].compress(),
|
|
Xlevel: &self.lox_pub.X[3].compress(),
|
|
Xsince: &self.lox_pub.X[4].compress(),
|
|
Xinvremain: &self.lox_pub.X[5].compress(),
|
|
Xblockages: &self.lox_pub.X[6].compress(),
|
|
H: &H.compress(),
|
|
BP: &req.BP.compress(),
|
|
CG: &CG.compress(),
|
|
CGsq: &req.CGsq.compress(),
|
|
},
|
|
)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|