Add proof of level 3 or 4, bucket. (Still needs to be debugged.)

This commit is contained in:
Vecna 2024-01-16 20:22:56 -05:00
parent a6c5d789e7
commit 9cd9e3fab7
2 changed files with 117 additions and 50 deletions

View File

@ -4,8 +4,8 @@ reports (indicating that a bridge has been accessed).
The user presents their current Lox credential: The user presents their current Lox credential:
- id: blinded - id: blinded
- bucket: blinded (but proven to be beta in provided beta*H) - bucket: blinded (but proven to be bucket in provided bucket*H)
- trust_level: revealed to be 3 or higher - trust_level: blinded (but proven to be 3 or higher)
- level_since: blinded - level_since: blinded
- invites_remaining: blinded - invites_remaining: blinded
- blockages: 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 This protocol does not expose the credential's ID and does not issue a
new Lox credential. The user does not receive a response. 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, Due to the way the level proof is implemented, this protocol has
so it's just "Prove that the user's level is at least 3." hardcoded assumptions that the level is 3-4.
Right now, this doesn't work.
*/ */
use curve25519_dalek::ristretto::{RistrettoBasepointTable, RistrettoPoint}; use curve25519_dalek::ristretto::{RistrettoBasepointTable, RistrettoPoint};
use curve25519_dalek::scalar::Scalar; use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::IsIdentity; use curve25519_dalek::traits::IsIdentity;
use lox_zkp::{CompactProof, ProofError, Transcript}; use lox_zkp::{CompactProof, ProofError, Transcript};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::Sha512;
use std::convert::TryInto;
use super::super::cred; use super::super::cred;
use super::super::scalar_u32; use super::super::scalar_u32;
use super::super::{BridgeAuth, IssuerPubKey}; use super::super::{BridgeAuth, IssuerPubKey};
use super::super::{CMZ_A, CMZ_A_TABLE}; use super::super::{CMZ_A, CMZ_A_TABLE};
pub const MIN_TRUST_LEVEL: u32 = 3;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Request { pub struct Request {
// Fields for blind showing the Lox credential // Fields for blind showing the Lox credential
P: RistrettoPoint, P: RistrettoPoint,
CId: RistrettoPoint, CId: RistrettoPoint,
CBucket: RistrettoPoint, CBucket: RistrettoPoint,
level: Scalar, CLevel: RistrettoPoint,
CSince: RistrettoPoint, CSince: RistrettoPoint,
CInvRemain: RistrettoPoint, CInvRemain: RistrettoPoint,
CBlockages: RistrettoPoint, CBlockages: RistrettoPoint,
CQ: 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 // The combined lox_zkp
piUser: CompactProof, piUser: CompactProof,
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct State {
level: Scalar,
}
#[derive(Serialize, Deserialize)]
pub struct Response {}
define_proof! { define_proof! {
requestproof, requestproof,
"Positive Report Request", "Positive Report Request",
(id, bucket, since, invremain, blockages, zid, zbucket, zsince, zinvremain, zblockages, negzQ), (id, bucket, level, since, invremain, blockages,
(P, CId, CBucket, CSince, CInvRemain, CBlockages, V, Xid, Xbucket, Xsince, Xinvremain, Xblockages),//, H, HBucket), 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): (A):
// Blind showing of the Lox credential // Blind showing of the Lox credential
CId = (id*P + zid*A), CId = (id*P + zid*A),
CBucket = (bucket*P + zbucket*A), CBucket = (bucket*P + zbucket*A),
CLevel = (level*P + zlevel*A),
CSince = (since*P + zsince*A), CSince = (since*P + zsince*A),
CInvRemain = (invremain*P + zinvremain*A), CInvRemain = (invremain*P + zinvremain*A),
CBlockages = (blockages*P + zblockages*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( pub fn request(
lox_cred: &cred::Lox, lox_cred: &cred::Lox,
lox_pub: &IssuerPubKey, lox_pub: &IssuerPubKey,
) -> Result<(Request, State), ProofError> { ) -> Result<Request, ProofError> {
let A: &RistrettoPoint = &CMZ_A; let A: &RistrettoPoint = &CMZ_A;
let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; 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::<Sha512>(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 // 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) { let level: u32 = match scalar_u32(&lox_cred.trust_level) {
Some(v) => v, Some(v) => v,
None => return Err(ProofError::VerificationFailure), 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); return Err(ProofError::VerificationFailure);
} }
@ -100,11 +120,13 @@ pub fn request(
// Form Pedersen commitments to the blinded attributes // Form Pedersen commitments to the blinded attributes
let zid = Scalar::random(&mut rng); let zid = Scalar::random(&mut rng);
let zbucket = Scalar::random(&mut rng); let zbucket = Scalar::random(&mut rng);
let zlevel = Scalar::random(&mut rng);
let zsince = Scalar::random(&mut rng); let zsince = Scalar::random(&mut rng);
let zinvremain = Scalar::random(&mut rng); let zinvremain = Scalar::random(&mut rng);
let zblockages = Scalar::random(&mut rng); let zblockages = Scalar::random(&mut rng);
let CId = lox_cred.id * P + &zid * Atable; let CId = lox_cred.id * P + &zid * Atable;
let CBucket = lox_cred.bucket * P + &zbucket * 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 CSince = lox_cred.level_since * P + &zsince * Atable;
let CInvRemain = lox_cred.invites_remaining * P + &zinvremain * Atable; let CInvRemain = lox_cred.invites_remaining * P + &zinvremain * Atable;
let CBlockages = lox_cred.blockages * P + &zblockages * Atable; let CBlockages = lox_cred.blockages * P + &zblockages * Atable;
@ -119,11 +141,36 @@ pub fn request(
// Compute the "error factor" // Compute the "error factor"
let V = zid * lox_pub.X[1] let V = zid * lox_pub.X[1]
+ zbucket * lox_pub.X[2] + zbucket * lox_pub.X[2]
+ zlevel * lox_pub.X[3]
+ zsince * lox_pub.X[4] + zsince * lox_pub.X[4]
+ zinvremain * lox_pub.X[5] + zinvremain * lox_pub.X[5]
+ zblockages * lox_pub.X[6] + zblockages * lox_pub.X[6]
+ &negzQ * Atable; + &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 // Construct the proof
let mut transcript = Transcript::new(b"proof of level 3 cred"); let mut transcript = Transcript::new(b"proof of level 3 cred");
let piUser = requestproof::prove_compact( let piUser = requestproof::prove_compact(
@ -133,73 +180,84 @@ pub fn request(
P: &P, P: &P,
CId: &CId, CId: &CId,
CBucket: &CBucket, CBucket: &CBucket,
CLevel: &CLevel,
CSince: &CSince, CSince: &CSince,
CInvRemain: &CInvRemain, CInvRemain: &CInvRemain,
CBlockages: &CBlockages, CBlockages: &CBlockages,
V: &V, V: &V,
Xid: &lox_pub.X[1], Xid: &lox_pub.X[1],
Xbucket: &lox_pub.X[2], Xbucket: &lox_pub.X[2],
Xlevel: &lox_pub.X[3],
Xsince: &lox_pub.X[4], Xsince: &lox_pub.X[4],
Xinvremain: &lox_pub.X[5], Xinvremain: &lox_pub.X[5],
Xblockages: &lox_pub.X[6], Xblockages: &lox_pub.X[6],
H: &H,
BP: &BP,
CG: &CG,
CGsq: &CGsq,
id: &lox_cred.id, id: &lox_cred.id,
bucket: &lox_cred.bucket, bucket: &lox_cred.bucket,
level: &lox_cred.trust_level,
since: &lox_cred.level_since, since: &lox_cred.level_since,
invremain: &lox_cred.invites_remaining, invremain: &lox_cred.invites_remaining,
blockages: &lox_cred.blockages, blockages: &lox_cred.blockages,
zid: &zid, zid: &zid,
zbucket: &zbucket, zbucket: &zbucket,
zlevel: &zlevel,
zsince: &zsince, zsince: &zsince,
zinvremain: &zinvremain, zinvremain: &zinvremain,
zblockages: &zblockages, zblockages: &zblockages,
negzQ: &negzQ, negzQ: &negzQ,
g: &g,
zg: &zg,
wg: &wg,
yg: &yg,
}, },
) )
.0; .0;
Ok(( Ok(
Request { Request {
P, P,
CId, CId,
CBucket, CBucket,
level: lox_cred.trust_level, CLevel,
CSince, CSince,
CInvRemain, CInvRemain,
CBlockages, CBlockages,
CQ, CQ,
H,
BP,
CGsq,
piUser, piUser,
}, }
State { )
level: lox_cred.trust_level,
},
))
} }
impl BridgeAuth { impl BridgeAuth {
/// Receive a positive report request /// Receive a positive report request
pub fn handle_positive_report(&mut self, req: Request) -> Result<Response, ProofError> { pub fn handle_positive_report(&mut self, req: Request) -> Result<(), ProofError> {
let A: &RistrettoPoint = &CMZ_A; let A: &RistrettoPoint = &CMZ_A;
let level: u32 = match scalar_u32(&req.level) { if req.P.is_identity() {
Some(v) => v,
None => return Err(ProofError::VerificationFailure),
};
if req.P.is_identity() || level < MIN_TRUST_LEVEL {
return Err(ProofError::VerificationFailure); return Err(ProofError::VerificationFailure);
} }
// Recompute the "error factor" using knowledge of our own // Recompute the "error factor" using knowledge of our own
// (the issuer's) private key instead of knowledge of the // (the issuer's) private key instead of knowledge of the
// hidden attributes // 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[1] * req.CId
+ self.lox_priv.x[2] * req.CBucket + 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[4] * req.CSince
+ self.lox_priv.x[5] * req.CInvRemain + self.lox_priv.x[5] * req.CInvRemain
+ self.lox_priv.x[6] * req.CBlockages + self.lox_priv.x[6] * req.CBlockages
- req.CQ; - req.CQ;
// Recompute CG
let CG = req.CLevel - Scalar::from(3 as u8) * req.P;
// TODO: Failure happens in verify_compact // TODO: Failure happens in verify_compact
// Verify the zkp // Verify the zkp
let mut transcript = Transcript::new(b"positive report request"); let mut transcript = Transcript::new(b"positive report request");
@ -211,23 +269,24 @@ impl BridgeAuth {
P: &req.P.compress(), P: &req.P.compress(),
CId: &req.CId.compress(), CId: &req.CId.compress(),
CBucket: &req.CBucket.compress(), CBucket: &req.CBucket.compress(),
CLevel: &req.CLevel.compress(),
CSince: &req.CSince.compress(), CSince: &req.CSince.compress(),
CInvRemain: &req.CInvRemain.compress(), CInvRemain: &req.CInvRemain.compress(),
CBlockages: &req.CBlockages.compress(), CBlockages: &req.CBlockages.compress(),
V: &Vprime.compress(), V: &Vprime.compress(),
Xid: &self.lox_pub.X[1].compress(), Xid: &self.lox_pub.X[1].compress(),
Xbucket: &self.lox_pub.X[2].compress(), Xbucket: &self.lox_pub.X[2].compress(),
Xlevel: &self.lox_pub.X[3].compress(),
Xsince: &self.lox_pub.X[4].compress(), Xsince: &self.lox_pub.X[4].compress(),
Xinvremain: &self.lox_pub.X[5].compress(), Xinvremain: &self.lox_pub.X[5].compress(),
Xblockages: &self.lox_pub.X[6].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(())
}

View File

@ -374,21 +374,18 @@ impl TestHarness {
fn positive_report(&mut self, cred: &cred::Lox) -> PerfStat { fn positive_report(&mut self, cred: &cred::Lox) -> PerfStat {
let req_start = Instant::now(); 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<u8> = bincode::serialize(&req).unwrap(); let encoded: Vec<u8> = bincode::serialize(&req).unwrap();
let req_t = req_start.elapsed(); let req_t = req_start.elapsed();
let req_len = encoded.len(); let req_len = encoded.len();
let resp_start = Instant::now(); let resp_start = Instant::now();
let decoded = bincode::deserialize(&encoded[..]).unwrap(); let decoded = bincode::deserialize(&encoded[..]).unwrap();
let resp = self.ba.handle_positive_report(decoded).unwrap(); self.ba.handle_positive_report(decoded).unwrap();
let encoded_resp: Vec<u8> = bincode::serialize(&resp).unwrap();
let resp_t = resp_start.elapsed(); let resp_t = resp_start.elapsed();
let resp_len = encoded_resp.len(); let resp_len = 0;
let resp_handle_start = Instant::now(); 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(); let resp_handle_t = resp_handle_start.elapsed();
PerfStat { PerfStat {
@ -1405,6 +1402,17 @@ fn test_positive_report() {
// Submit positive report // Submit positive report
let _pr_perf_stat = th.positive_report(&cred3); 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) { fn print_test_results(perf_stat: PerfStat) {