From a6c5d789e7e458e2f6b6740e7d1fb89f69580274 Mon Sep 17 00:00:00 2001 From: Vecna Date: Mon, 15 Jan 2024 19:22:59 -0500 Subject: [PATCH] Begin work on LV3+ proof for Troll patrol positive reports --- crates/lox-library/src/lib.rs | 1 + .../lox-library/src/proto/positive_report.rs | 233 ++++++++++++++++++ crates/lox-library/src/tests.rs | 65 +++++ 3 files changed, 299 insertions(+) create mode 100644 crates/lox-library/src/proto/positive_report.rs diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index d04c297..4160bd9 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -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; } diff --git a/crates/lox-library/src/proto/positive_report.rs b/crates/lox-library/src/proto/positive_report.rs new file mode 100644 index 0000000..f6ff49c --- /dev/null +++ b/crates/lox-library/src/proto/positive_report.rs @@ -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 { + 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(()) +} diff --git a/crates/lox-library/src/tests.rs b/crates/lox-library/src/tests.rs index bb2a986..623e8cc 100644 --- a/crates/lox-library/src/tests.rs +++ b/crates/lox-library/src/tests.rs @@ -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 = 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 = 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);