/*! 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. 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}; #[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 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, } 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 { 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 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"proof of level 3 cred"); 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, /* H, BP, CGsq, */ piUser, } ) } impl BridgeAuth { /// Receive a positive report request pub fn handle_positive_report(&mut self, req: Request) -> Result<(), ProofError> { let A: &RistrettoPoint = &CMZ_A; 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; // 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(), 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(()) } }