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

295 lines
9.3 KiB
Rust
Raw Normal View History

/*! 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 {
2024-02-28 11:49:47 -05:00
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,
2024-01-17 17:41:53 -05:00
zid, zbucket, zlevel, zsince, zinvremain, zblockages, negzQ,
g, zg, wg, yg),
(P, CId, CBucket, CLevel, CSince, CInvRemain, CBlockages,
2024-01-17 17:41:53 -05:00
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),
2024-01-17 17:41:53 -05:00
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
}
2024-01-17 17:41:53 -05:00
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()
2024-01-17 17:41:53 -05:00
.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;
2024-01-17 17:41:53 -05:00
Ok(Request {
P,
CId,
CBucket,
CLevel,
CSince,
CInvRemain,
CBlockages,
CQ,
date,
2024-01-17 17:41:53 -05:00
BP,
CGsq,
piUser,
})
}
impl BridgeAuth {
/// Receive a positive report request
2024-02-28 11:49:47 -05:00
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
2024-01-17 17:41:53 -05:00
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(())
}
}