Begin work on LV3+ proof for Troll patrol positive reports

This commit is contained in:
Vecna 2024-01-15 19:22:59 -05:00
parent 0041116e65
commit a6c5d789e7
3 changed files with 299 additions and 0 deletions

View File

@ -999,6 +999,7 @@ pub mod proto {
pub mod level_up;
pub mod migration;
pub mod open_invite;
pub mod positive_report;
pub mod redeem_invite;
pub mod trust_promotion;
}

View File

@ -0,0 +1,233 @@
/*! 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 beta in provided beta*H)
- trust_level: revealed 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.
Right now, this doesn't work. Also, it doesn't yet check the user's bucket,
so it's just "Prove that the user's level is at least 3."
*/
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 super::super::cred;
use super::super::scalar_u32;
use super::super::{BridgeAuth, IssuerPubKey};
use super::super::{CMZ_A, CMZ_A_TABLE};
pub const MIN_TRUST_LEVEL: u32 = 3;
#[derive(Serialize, Deserialize)]
pub struct Request {
// Fields for blind showing the Lox credential
P: RistrettoPoint,
CId: RistrettoPoint,
CBucket: RistrettoPoint,
level: Scalar,
CSince: RistrettoPoint,
CInvRemain: RistrettoPoint,
CBlockages: RistrettoPoint,
CQ: RistrettoPoint,
// The combined lox_zkp
piUser: CompactProof,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct State {
level: Scalar,
}
#[derive(Serialize, Deserialize)]
pub struct Response {}
define_proof! {
requestproof,
"Positive Report Request",
(id, bucket, since, invremain, blockages, zid, zbucket, zsince, zinvremain, zblockages, negzQ),
(P, CId, CBucket, CSince, CInvRemain, CBlockages, V, Xid, Xbucket, Xsince, Xinvremain, Xblockages),//, H, HBucket),
(A):
// Blind showing of the Lox credential
CId = (id*P + zid*A),
CBucket = (bucket*P + zbucket*A),
CSince = (since*P + zsince*A),
CInvRemain = (invremain*P + zinvremain*A),
CBlockages = (blockages*P + zblockages*A),
V = (zid*Xid + zbucket*Xbucket + zsince*Xsince + zinvremain*Xinvremain + zblockages*Xblockages + negzQ*A)
}
pub fn request(
lox_cred: &cred::Lox,
lox_pub: &IssuerPubKey,
) -> Result<(Request, State), ProofError> {
let A: &RistrettoPoint = &CMZ_A;
let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
// Ensure that the credential can be correctly shown: it must be the case
// that trust_level >= MIN_TRUST_LEVEL
let level: u32 = match scalar_u32(&lox_cred.trust_level) {
Some(v) => v,
None => return Err(ProofError::VerificationFailure),
};
if level < MIN_TRUST_LEVEL {
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 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 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]
+ zsince * lox_pub.X[4]
+ zinvremain * lox_pub.X[5]
+ zblockages * lox_pub.X[6]
+ &negzQ * Atable;
// Construct the proof
let mut transcript = Transcript::new(b"proof of level 3 cred");
let piUser = requestproof::prove_compact(
&mut transcript,
requestproof::ProveAssignments {
A,
P: &P,
CId: &CId,
CBucket: &CBucket,
CSince: &CSince,
CInvRemain: &CInvRemain,
CBlockages: &CBlockages,
V: &V,
Xid: &lox_pub.X[1],
Xbucket: &lox_pub.X[2],
Xsince: &lox_pub.X[4],
Xinvremain: &lox_pub.X[5],
Xblockages: &lox_pub.X[6],
id: &lox_cred.id,
bucket: &lox_cred.bucket,
since: &lox_cred.level_since,
invremain: &lox_cred.invites_remaining,
blockages: &lox_cred.blockages,
zid: &zid,
zbucket: &zbucket,
zsince: &zsince,
zinvremain: &zinvremain,
zblockages: &zblockages,
negzQ: &negzQ,
},
)
.0;
Ok((
Request {
P,
CId,
CBucket,
level: lox_cred.trust_level,
CSince,
CInvRemain,
CBlockages,
CQ,
piUser,
},
State {
level: lox_cred.trust_level,
},
))
}
impl BridgeAuth {
/// Receive a positive report request
pub fn handle_positive_report(&mut self, req: Request) -> Result<Response, ProofError> {
let A: &RistrettoPoint = &CMZ_A;
let level: u32 = match scalar_u32(&req.level) {
Some(v) => v,
None => return Err(ProofError::VerificationFailure),
};
if req.P.is_identity() || level < MIN_TRUST_LEVEL {
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] + self.lox_priv.x[3] * req.level) * req.P
+ self.lox_priv.x[1] * req.CId
+ self.lox_priv.x[2] * req.CBucket
+ self.lox_priv.x[4] * req.CSince
+ self.lox_priv.x[5] * req.CInvRemain
+ self.lox_priv.x[6] * req.CBlockages
- req.CQ;
// TODO: Failure happens in verify_compact
// 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(),
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(),
Xsince: &self.lox_pub.X[4].compress(),
Xinvremain: &self.lox_pub.X[5].compress(),
Xblockages: &self.lox_pub.X[6].compress(),
},
)?;
Ok(Response {})
}
}
/// Handle the response to the request, returning true if the proof is valid.
pub fn handle_response(_state: State, _resp: Response) -> Result<(), ProofError> {
Ok(())
}

View File

@ -371,6 +371,34 @@ impl TestHarness {
cred,
)
}
fn positive_report(&mut self, cred: &cred::Lox) -> PerfStat {
let req_start = Instant::now();
let (req, state) = positive_report::request(cred, &self.ba.lox_pub).unwrap();
let encoded: Vec<u8> = bincode::serialize(&req).unwrap();
let req_t = req_start.elapsed();
let req_len = encoded.len();
let resp_start = Instant::now();
let decoded = bincode::deserialize(&encoded[..]).unwrap();
let resp = self.ba.handle_positive_report(decoded).unwrap();
let encoded_resp: Vec<u8> = bincode::serialize(&resp).unwrap();
let resp_t = resp_start.elapsed();
let resp_len = encoded_resp.len();
let resp_handle_start = Instant::now();
let decode_resp = bincode::deserialize(&encoded_resp[..]).unwrap();
positive_report::handle_response(state, decode_resp).unwrap();
let resp_handle_t = resp_handle_start.elapsed();
PerfStat {
req_len,
resp_len,
req_t,
resp_t,
resp_handle_t,
}
}
}
#[test]
@ -1342,6 +1370,43 @@ fn test_blockage_migration() {
assert!(th.ba.verify_lox(&cred4));
}
#[test]
fn test_positive_report() {
let mut th = TestHarness::new();
// Join an untrusted user
let cred = th.open_invite().1 .0;
// Time passes
th.advance_days(47);
// Go up to level 1
let (_mperf_stat, migcred) = th.trust_promotion(&cred);
let (_perf_stat, cred1) = th.level0_migration(&cred, &migcred);
assert!(scalar_u32(&cred1.trust_level).unwrap() == 1);
// Time passes
th.advance_days(20);
// Go up to level 2
let (_two_perf_stat, cred2) = th.level_up(&cred1);
assert!(scalar_u32(&cred2.trust_level).unwrap() == 2);
// println!("cred2 = {:?}", cred2);
assert!(th.ba.verify_lox(&cred2));
// Time passes
th.advance_days(29);
// Go up to level 3
let (_three_perf_stat, cred3) = th.level_up(&cred2);
assert!(scalar_u32(&cred3.trust_level).unwrap() == 3);
// println!("cred3 = {:?}", cred3);
assert!(th.ba.verify_lox(&cred3));
// Submit positive report
let _pr_perf_stat = th.positive_report(&cred3);
}
fn print_test_results(perf_stat: PerfStat) {
println!("Request size = {:?} bytes", perf_stat.req_len);
println!("Request time = {:?}", perf_stat.req_t);