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:
- 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<Request, ProofError> {
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::<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
// 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<Response, ProofError> {
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(())
}

View File

@ -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<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();
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) {