1use curve25519_dalek::ristretto::RistrettoBasepointTable;
23use curve25519_dalek::ristretto::RistrettoPoint;
24use curve25519_dalek::scalar::Scalar;
25use curve25519_dalek::traits::IsIdentity;
26
27use lox_zkp::CompactProof;
28use lox_zkp::ProofError;
29use lox_zkp::Transcript;
30
31use serde::{Deserialize, Serialize};
32
33use super::super::cred;
34#[cfg(feature = "bridgeauth")]
35use super::super::dup_filter::SeenType;
36#[cfg(feature = "bridgeauth")]
37use super::super::BridgeAuth;
38use super::super::IssuerPubKey;
39use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
40
41use super::errors::CredentialError;
42
43#[derive(Serialize, Deserialize)]
44pub struct Request {
45 OldPubKey: IssuerPubKey,
47 P: RistrettoPoint,
48 inv_id: Scalar,
49 CDate: RistrettoPoint,
50 CBucket: RistrettoPoint,
51 CBlockages: RistrettoPoint,
52 CQ: RistrettoPoint,
53
54 D: RistrettoPoint,
56 EncInvIdClient: (RistrettoPoint, RistrettoPoint),
57 EncDate: (RistrettoPoint, RistrettoPoint),
58 EncBucket: (RistrettoPoint, RistrettoPoint),
59 EncBlockages: (RistrettoPoint, RistrettoPoint),
60
61 piUser: CompactProof,
63}
64
65#[derive(Debug, Serialize, Deserialize)]
66pub struct State {
67 d: Scalar,
68 D: RistrettoPoint,
69 EncInvIdClient: (RistrettoPoint, RistrettoPoint),
70 EncDate: (RistrettoPoint, RistrettoPoint),
71 EncBucket: (RistrettoPoint, RistrettoPoint),
72 EncBlockages: (RistrettoPoint, RistrettoPoint),
73 inv_id_client: Scalar,
74 date: Scalar,
75 bucket: Scalar,
76 blockages: Scalar,
77}
78
79#[derive(Serialize, Deserialize)]
80pub struct Response {
81 P: RistrettoPoint,
83 EncQ: (RistrettoPoint, RistrettoPoint),
84 inv_id_server: Scalar,
85 TInvId: RistrettoPoint,
86 TDate: RistrettoPoint,
87 TBucket: RistrettoPoint,
88 TBlockages: RistrettoPoint,
89
90 piBlindIssue: CompactProof,
92}
93
94define_proof! {
95 requestproof,
96 "Update Invite Request",
97 (date, bucket, blockages, zdate, zbucket, zblockages, negzQ,
98 d, einv_id_client, edate, ebucket, eblockages, inv_id_client),
99 (P, CDate, CBucket, CBlockages, V, Xdate, Xbucket, Xblockages,
100 D, EncInvIdClient0, EncInvIdClient1, EncDate0, EncDate1, EncBucket0, EncBucket1,
101 EncBlockages0, EncBlockages1),
102 (A, B):
103 CDate = (date*P + zdate*A),
105 CBucket = (bucket*P + zbucket*A),
106 CBlockages = (blockages*P + zblockages*A),
107 D = (d*B),
109 EncInvIdClient0 = (einv_id_client*B),
110 EncInvIdClient1 = (inv_id_client*B + einv_id_client*D),
111 EncDate0 = (edate*B),
112 EncDate1 = (date*B + edate*D),
113 EncBucket0 = (ebucket*B),
114 EncBucket1 = (bucket*B + ebucket*D),
115 EncBlockages0 = (eblockages*B),
116 EncBlockages1 = (blockages*B + eblockages*D)
117}
118
119define_proof! {
120 blindissue,
121 "Issue Updated Invitation",
122 (x0, x0tilde, xinv_id, xdate, xbucket, xblockages,
123 s, b, tinv_id, tdate, tbucket, tblockages),
124 (P, EncQ0, EncQ1, X0, Xinv_id, Xdate, Xbucket, Xblockages,
125 TInvId, TDate, TBucket, TBlockages,
126 D, EncInvId0, EncInvId1, EncDate0, EncDate1, EncBucket0, EncBucket1, EncBlockages0, EncBlockages1),
127 (A, B):
128 Xinv_id = (xinv_id*A),
129 Xdate = (xdate*A),
130 Xbucket = (xbucket*A),
131 Xblockages = (xblockages*A),
132 X0 = (x0*B + x0tilde*A),
133 P = (b*B),
134 TInvId = (b*Xinv_id),
135 TInvId = (tinv_id*A),
136 TDate = (b*Xdate),
137 TDate = (tdate*A),
138 TBucket = (b*Xbucket),
139 TBucket = (tbucket*A),
140 TBlockages = (b*Xblockages),
141 TBlockages = (tblockages*A),
142 EncQ0 = (s*B + tinv_id*EncInvId0 + tdate*EncDate0 + tbucket*EncBucket0 + tblockages*EncBlockages0),
143 EncQ1 = (s*D + tinv_id*EncInvId1 + tdate*EncDate1 + tbucket*EncBucket1
144 + tblockages*EncBlockages1 + x0*P)
145}
146
147pub fn request(
148 inv_cred: &cred::Invitation,
149 invitation_pub: &IssuerPubKey,
150) -> Result<(Request, State), CredentialError> {
151 let A: &RistrettoPoint = &CMZ_A;
152 let B: &RistrettoPoint = &CMZ_B;
153 let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
154 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
155
156 let mut rng = rand::thread_rng();
160 let t = Scalar::random(&mut rng);
161 let P = t * inv_cred.P;
162 let Q = t * inv_cred.Q;
163
164 let zdate = Scalar::random(&mut rng);
166 let zbucket = Scalar::random(&mut rng);
167 let zblockages = Scalar::random(&mut rng);
168 let CDate = inv_cred.date * P + &zdate * Atable;
169 let CBucket = inv_cred.bucket * P + &zbucket * Atable;
170 let CBlockages = inv_cred.blockages * P + &zblockages * Atable;
171
172 let negzQ = Scalar::random(&mut rng);
177 let CQ = Q - &negzQ * Atable;
178
179 let V = zdate * invitation_pub.X[2]
181 + zbucket * invitation_pub.X[3]
182 + zblockages * invitation_pub.X[4]
183 + &negzQ * Atable;
184
185 let d = Scalar::random(&mut rng);
189 let D = &d * Btable;
190
191 let inv_id_client = Scalar::random(&mut rng);
193
194 let einv_id_client = Scalar::random(&mut rng);
197 let EncInvIdClient = (
198 &einv_id_client * Btable,
199 &inv_id_client * Btable + einv_id_client * D,
200 );
201
202 let edate = Scalar::random(&mut rng);
204 let EncDate = (&edate * Btable, &inv_cred.date * Btable + edate * D);
205 let ebucket = Scalar::random(&mut rng);
206 let EncBucket = (&ebucket * Btable, &inv_cred.bucket * Btable + ebucket * D);
207 let eblockages = Scalar::random(&mut rng);
208 let EncBlockages = (
209 &eblockages * Btable,
210 &inv_cred.blockages * Btable + eblockages * D,
211 );
212
213 let mut transcript = Transcript::new(b"update invite request");
215 let piUser = requestproof::prove_compact(
216 &mut transcript,
217 requestproof::ProveAssignments {
218 A,
219 B,
220 P: &P,
221 CDate: &CDate,
222 CBucket: &CBucket,
223 CBlockages: &CBlockages,
224 V: &V,
225 Xdate: &invitation_pub.X[2],
226 Xbucket: &invitation_pub.X[3],
227 Xblockages: &invitation_pub.X[4],
228 D: &D,
229 EncInvIdClient0: &EncInvIdClient.0,
230 EncInvIdClient1: &EncInvIdClient.1,
231 EncDate0: &EncDate.0,
232 EncDate1: &EncDate.1,
233 EncBucket0: &EncBucket.0,
234 EncBucket1: &EncBucket.1,
235 EncBlockages0: &EncBlockages.0,
236 EncBlockages1: &EncBlockages.1,
237 date: &inv_cred.date,
238 bucket: &inv_cred.bucket,
239 blockages: &inv_cred.blockages,
240 zdate: &zdate,
241 zbucket: &zbucket,
242 zblockages: &zblockages,
243 negzQ: &negzQ,
244 d: &d,
245 einv_id_client: &einv_id_client,
246 edate: &edate,
247 ebucket: &ebucket,
248 eblockages: &eblockages,
249 inv_id_client: &inv_id_client,
250 },
251 )
252 .0;
253
254 Ok((
255 Request {
256 OldPubKey: invitation_pub.clone(),
257 P,
258 inv_id: inv_cred.inv_id,
259 CDate,
260 CBucket,
261 CBlockages,
262 CQ,
263 D,
264 EncInvIdClient,
265 EncDate,
266 EncBucket,
267 EncBlockages,
268 piUser,
269 },
270 State {
271 d,
272 D,
273 EncInvIdClient,
274 EncDate,
275 EncBucket,
276 EncBlockages,
277 inv_id_client,
278 date: inv_cred.date,
279 bucket: inv_cred.bucket,
280 blockages: inv_cred.blockages,
281 },
282 ))
283}
284
285#[cfg(feature = "bridgeauth")]
286impl BridgeAuth {
287 pub fn handle_update_invite(&mut self, req: Request) -> Result<Response, ProofError> {
289 let A: &RistrettoPoint = &CMZ_A;
290 let B: &RistrettoPoint = &CMZ_B;
291 let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
292 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
293
294 if req.P.is_identity() {
295 return Err(ProofError::VerificationFailure);
296 }
297
298 if self.old_keys.invitation_keys.is_empty() || self.old_filters.invitation_filter.is_empty()
300 {
301 return Err(ProofError::VerificationFailure);
302 }
303
304 let old_keys = match self
311 .old_keys
312 .invitation_keys
313 .iter()
314 .find(|x| x.pub_key == req.OldPubKey)
315 {
316 Some(old_keys) => old_keys,
317 None => return Err(ProofError::VerificationFailure),
318 };
319 let index = self
320 .old_keys
321 .invitation_keys
322 .iter()
323 .position(|x| x.pub_key == old_keys.pub_key)
324 .unwrap();
325 let old_priv_key = old_keys.priv_key.clone();
326 let old_pub_key = old_keys.pub_key.clone();
327
328 let Vprime = (old_priv_key.x[0] + old_priv_key.x[1] * req.inv_id) * req.P
332 + old_priv_key.x[2] * req.CDate
333 + old_priv_key.x[3] * req.CBucket
334 + old_priv_key.x[4] * req.CBlockages
335 - req.CQ;
336
337 let mut transcript = Transcript::new(b"update invite request");
339 requestproof::verify_compact(
340 &req.piUser,
341 &mut transcript,
342 requestproof::VerifyAssignments {
343 A: &A.compress(),
344 B: &B.compress(),
345 P: &req.P.compress(),
346 CDate: &req.CDate.compress(),
347 CBucket: &req.CBucket.compress(),
348 CBlockages: &req.CBlockages.compress(),
349 V: &Vprime.compress(),
350 Xdate: &old_pub_key.X[2].compress(),
351 Xbucket: &old_pub_key.X[3].compress(),
352 Xblockages: &old_pub_key.X[4].compress(),
353 D: &req.D.compress(),
354 EncInvIdClient0: &req.EncInvIdClient.0.compress(),
355 EncInvIdClient1: &req.EncInvIdClient.1.compress(),
356 EncDate0: &req.EncDate.0.compress(),
357 EncDate1: &req.EncDate.1.compress(),
358 EncBucket0: &req.EncBucket.0.compress(),
359 EncBucket1: &req.EncBucket.1.compress(),
360 EncBlockages0: &req.EncBlockages.0.compress(),
361 EncBlockages1: &req.EncBlockages.1.compress(),
362 },
363 )?;
364
365 if self
368 .old_filters
369 .invitation_filter
370 .get_mut(index)
371 .unwrap()
372 .filter(&req.inv_id)
373 == SeenType::Seen
374 {
375 return Err(ProofError::VerificationFailure);
376 }
377
378 let mut rng = rand::thread_rng();
383 let inv_id_server = Scalar::random(&mut rng);
384 let EncInvId = (
385 req.EncInvIdClient.0,
386 req.EncInvIdClient.1 + &inv_id_server * Btable,
387 );
388
389 let b = Scalar::random(&mut rng);
391 let P = &b * Btable;
392 let QHc = self.invitation_priv.x[0] * P;
393
394 let s = Scalar::random(&mut rng);
396 let EncQHc = (&s * Btable, QHc + s * req.D);
397
398 let tinv_id = self.invitation_priv.x[1] * b;
401 let TInvId = &tinv_id * Atable;
402 let EncQId = (tinv_id * EncInvId.0, tinv_id * EncInvId.1);
403 let tdate = self.invitation_priv.x[2] * b;
404 let TDate = &tdate * Atable;
405 let EncQDate = (tdate * req.EncDate.0, tdate * req.EncDate.1);
406 let tbucket = self.invitation_priv.x[3] * b;
407 let TBucket = &tbucket * Atable;
408 let EncQBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
409 let tblockages = self.invitation_priv.x[4] * b;
410 let TBlockages = &tblockages * Atable;
411 let EncQBlockages = (
412 tblockages * req.EncBlockages.0,
413 tblockages * req.EncBlockages.1,
414 );
415
416 let EncQ = (
417 EncQHc.0 + EncQId.0 + EncQDate.0 + EncQBucket.0 + EncQBlockages.0,
418 EncQHc.1 + EncQId.1 + EncQDate.1 + EncQBucket.1 + EncQBlockages.1,
419 );
420
421 let mut transcript = Transcript::new(b"issue updated invitation");
422 let piBlindIssue = blindissue::prove_compact(
423 &mut transcript,
424 blindissue::ProveAssignments {
425 A,
426 B,
427 P: &P,
428 EncQ0: &EncQ.0,
429 EncQ1: &EncQ.1,
430 X0: &self.invitation_pub.X[0],
431 Xinv_id: &self.invitation_pub.X[1],
432 Xdate: &self.invitation_pub.X[2],
433 Xbucket: &self.invitation_pub.X[3],
434 Xblockages: &self.invitation_pub.X[4],
435 TInvId: &TInvId,
436 TDate: &TDate,
437 TBucket: &TBucket,
438 TBlockages: &TBlockages,
439 D: &req.D,
440 EncInvId0: &EncInvId.0,
441 EncInvId1: &EncInvId.1,
442 EncDate0: &req.EncDate.0,
443 EncDate1: &req.EncDate.1,
444 EncBucket0: &req.EncBucket.0,
445 EncBucket1: &req.EncBucket.1,
446 EncBlockages0: &req.EncBlockages.0,
447 EncBlockages1: &req.EncBlockages.1,
448 x0: &self.invitation_priv.x[0],
449 x0tilde: &self.invitation_priv.x0tilde,
450 xinv_id: &self.invitation_priv.x[1],
451 xdate: &self.invitation_priv.x[2],
452 xbucket: &self.invitation_priv.x[3],
453 xblockages: &self.invitation_priv.x[4],
454 s: &s,
455 b: &b,
456 tinv_id: &tinv_id,
457 tdate: &tdate,
458 tbucket: &tbucket,
459 tblockages: &tblockages,
460 },
461 )
462 .0;
463
464 Ok(Response {
465 P,
466 EncQ,
467 inv_id_server,
468 TInvId,
469 TDate,
470 TBucket,
471 TBlockages,
472 piBlindIssue,
473 })
474 }
475}
476
477pub fn handle_response(
480 state: State,
481 resp: Response,
482 invitation_pub: &IssuerPubKey,
483) -> Result<cred::Invitation, ProofError> {
484 let A: &RistrettoPoint = &CMZ_A;
485 let B: &RistrettoPoint = &CMZ_B;
486 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
487
488 if resp.P.is_identity() {
489 return Err(ProofError::VerificationFailure);
490 }
491 let inv_id = state.inv_id_client + resp.inv_id_server;
494 let EncInvId = (
495 state.EncInvIdClient.0,
496 state.EncInvIdClient.1 + &resp.inv_id_server * Btable,
497 );
498
499 let mut transcript = Transcript::new(b"issue updated invitation");
501 blindissue::verify_compact(
502 &resp.piBlindIssue,
503 &mut transcript,
504 blindissue::VerifyAssignments {
505 A: &A.compress(),
506 B: &B.compress(),
507 P: &resp.P.compress(),
508 EncQ0: &resp.EncQ.0.compress(),
509 EncQ1: &resp.EncQ.1.compress(),
510 X0: &invitation_pub.X[0].compress(),
511 Xinv_id: &invitation_pub.X[1].compress(),
512 Xdate: &invitation_pub.X[2].compress(),
513 Xbucket: &invitation_pub.X[3].compress(),
514 Xblockages: &invitation_pub.X[4].compress(),
515 TInvId: &resp.TInvId.compress(),
516 TDate: &resp.TDate.compress(),
517 TBucket: &resp.TBucket.compress(),
518 TBlockages: &resp.TBlockages.compress(),
519 D: &state.D.compress(),
520 EncInvId0: &EncInvId.0.compress(),
521 EncInvId1: &EncInvId.1.compress(),
522 EncDate0: &state.EncDate.0.compress(),
523 EncDate1: &state.EncDate.1.compress(),
524 EncBucket0: &state.EncBucket.0.compress(),
525 EncBucket1: &state.EncBucket.1.compress(),
526 EncBlockages0: &state.EncBlockages.0.compress(),
527 EncBlockages1: &state.EncBlockages.1.compress(),
528 },
529 )?;
530
531 let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
533
534 Ok(cred::Invitation {
535 P: resp.P,
536 Q,
537 inv_id,
538 date: state.date,
539 bucket: state.bucket,
540 blockages: state.blockages,
541 })
542}