From 9cd9e3fab757da4fd367174cbd0deda075af9a33 Mon Sep 17 00:00:00 2001 From: Vecna Date: Tue, 16 Jan 2024 20:22:56 -0500 Subject: [PATCH] Add proof of level 3 or 4, bucket. (Still needs to be debugged.) --- .../lox-library/src/proto/positive_report.rs | 147 ++++++++++++------ crates/lox-library/src/tests.rs | 20 ++- 2 files changed, 117 insertions(+), 50 deletions(-) diff --git a/crates/lox-library/src/proto/positive_report.rs b/crates/lox-library/src/proto/positive_report.rs index f6ff49c..8ee7dc2 100644 --- a/crates/lox-library/src/proto/positive_report.rs +++ b/crates/lox-library/src/proto/positive_report.rs @@ -4,8 +4,8 @@ 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 +- 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 @@ -13,79 +13,99 @@ The user presents their current Lox credential: 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." +Due to the way the level proof is implemented, this protocol has +hardcoded assumptions that the level is 3-4. + +Right now, this doesn't work. */ 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 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, + CLevel: RistrettoPoint, CSince: RistrettoPoint, CInvRemain: RistrettoPoint, CBlockages: RistrettoPoint, CQ: RistrettoPoint, + // Fields for proving which bucket we have + H: RistrettoPoint, + BP: RistrettoPoint, + + // Fields for proving 3 <= trust_level <= 4 + // CG can be computed by verifier + CGsq: 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), + (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 + zsince*Xsince + zinvremain*Xinvremain + zblockages*Xblockages + negzQ*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, State), ProofError> { +) -> Result { let A: &RistrettoPoint = &CMZ_A; let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; + // TODO: Where should this go? For efficiency, this should probably be global + let today: u32 = time::OffsetDateTime::now_utc().date() + .to_julian_day() + .try_into() + .unwrap(); + let H: RistrettoPoint = RistrettoPoint::hash_from_bytes::(format!("PR Generator H for {}",today).as_bytes()); + let Htable: RistrettoBasepointTable = RistrettoBasepointTable::create(&H); + // Ensure that the credential can be correctly shown: it must be the case - // that trust_level >= MIN_TRUST_LEVEL + // 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), }; - if level < MIN_TRUST_LEVEL { + // level must be 3 + one bit or we need to add more bits + if level < 3 || level > 4 { return Err(ProofError::VerificationFailure); } @@ -100,11 +120,13 @@ pub fn request( // 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; @@ -119,11 +141,36 @@ pub fn request( // 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"proof of level 3 cred"); let piUser = requestproof::prove_compact( @@ -133,73 +180,84 @@ pub fn request( 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(( + Ok( Request { P, CId, CBucket, - level: lox_cred.trust_level, + CLevel, CSince, CInvRemain, CBlockages, CQ, + H, + BP, + CGsq, piUser, - }, - State { - level: lox_cred.trust_level, - }, - )) + } + ) } impl BridgeAuth { /// Receive a positive report request - pub fn handle_positive_report(&mut self, req: Request) -> Result { + pub fn handle_positive_report(&mut self, req: Request) -> Result<(), 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 { + 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] + self.lox_priv.x[3] * req.level) * req.P + 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; + // TODO: Failure happens in verify_compact // Verify the zkp let mut transcript = Transcript::new(b"positive report request"); @@ -211,23 +269,24 @@ impl BridgeAuth { 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: &req.H.compress(), + BP: &req.BP.compress(), + CG: &CG.compress(), + CGsq: &req.CGsq.compress(), }, )?; - Ok(Response {}) + Ok(()) } } - -/// 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 623e8cc..f9e20b5 100644 --- a/crates/lox-library/src/tests.rs +++ b/crates/lox-library/src/tests.rs @@ -374,21 +374,18 @@ impl TestHarness { 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 req = 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(); + self.ba.handle_positive_report(decoded).unwrap(); let resp_t = resp_start.elapsed(); - let resp_len = encoded_resp.len(); + let resp_len = 0; 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 { @@ -1405,6 +1402,17 @@ fn test_positive_report() { // Submit positive report let _pr_perf_stat = th.positive_report(&cred3); + + // Time passes + th.advance_days(60); + + // Go up to level 4 + let (_four_perf_stat, cred4) = th.level_up(&cred3); + assert!(scalar_u32(&cred4.trust_level).unwrap() == 4); + assert!(th.ba.verify_lox(&cred4)); + + // Submit positive report + let _pr_perf_stat2 = th.positive_report(&cred4); } fn print_test_results(perf_stat: PerfStat) {