1use curve25519_dalek::ristretto::RistrettoBasepointTable;
36use curve25519_dalek::ristretto::RistrettoPoint;
37use curve25519_dalek::scalar::Scalar;
38use curve25519_dalek::traits::IsIdentity;
39
40use lox_zkp::CompactProof;
41use lox_zkp::ProofError;
42use lox_zkp::Transcript;
43
44use serde::{Deserialize, Serialize};
45
46use super::super::cred;
47#[cfg(feature = "bridgeauth")]
48use super::super::dup_filter::SeenType;
49#[cfg(feature = "bridgeauth")]
50use super::super::migration_table::MigrationType;
51use super::super::scalar_u32;
52#[cfg(feature = "bridgeauth")]
53use super::super::BridgeAuth;
54use super::super::IssuerPubKey;
55
56use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
57use super::check_blockage::MIN_TRUST_LEVEL;
58use super::level_up::LEVEL_INVITATIONS;
59
60use super::errors::CredentialError;
61
62#[derive(Serialize, Deserialize)]
63pub struct Request {
64 P_lox: RistrettoPoint,
66 id: Scalar,
67 CBucket: RistrettoPoint,
68 trust_level: Scalar,
69 CSince: RistrettoPoint,
70 CInvRemain: RistrettoPoint,
71 CBlockages: RistrettoPoint,
72 CQ_lox: RistrettoPoint,
73
74 P_mig: RistrettoPoint,
76 CFromBucket: RistrettoPoint,
77 CToBucket: RistrettoPoint,
78 CQ_mig: RistrettoPoint,
79
80 D: RistrettoPoint,
82 EncIdClient: (RistrettoPoint, RistrettoPoint),
83 EncBucket: (RistrettoPoint, RistrettoPoint),
84 EncBlockages: (RistrettoPoint, RistrettoPoint),
85
86 piUser: CompactProof,
88}
89
90#[derive(Debug, Serialize, Deserialize)]
91pub struct State {
92 d: Scalar,
93 D: RistrettoPoint,
94 EncIdClient: (RistrettoPoint, RistrettoPoint),
95 EncBucket: (RistrettoPoint, RistrettoPoint),
96 EncBlockages: (RistrettoPoint, RistrettoPoint),
97 id_client: Scalar,
98 to_bucket: Scalar,
99 trust_level: Scalar,
100 blockages: Scalar,
101}
102
103#[derive(Serialize, Deserialize)]
104pub struct Response {
105 level_since: Scalar,
108
109 P: RistrettoPoint,
111 EncQ: (RistrettoPoint, RistrettoPoint),
112 id_server: Scalar,
113 TId: RistrettoPoint,
114 TBucket: RistrettoPoint,
115 TBlockages: RistrettoPoint,
116
117 piBlindIssue: CompactProof,
119}
120
121define_proof! {
122 requestproof,
123 "Blockage Migration Request",
124 (bucket, since, invremain, blockages, zbucket, zsince, zinvremain,
125 zblockages, negzQ_lox,
126 tobucket, zfrombucket, ztobucket, negzQ_mig,
127 d, eid_client, ebucket, eblockages, id_client),
128 (P_lox, CBucket, CSince, CInvRemain, CBlockages, V_lox, Xbucket,
129 Xsince, Xinvremain, Xblockages,
130 P_mig, CFromBucket, CToBucket, V_mig, Xfrombucket, Xtobucket,
131 D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1,
132 EncBlockages0, EncBlockages1_minus_B),
133 (A, B):
134 CBucket = (bucket*P_lox + zbucket*A),
136 CSince = (since*P_lox + zsince*A),
137 CInvRemain = (invremain*P_lox + zinvremain*A),
138 CBlockages = (blockages*P_lox + zblockages*A),
139 V_lox = (zbucket*Xbucket + zsince*Xsince + zinvremain*Xinvremain
140 + zblockages*Xblockages + negzQ_lox*A),
141 CFromBucket = (bucket*P_mig + zfrombucket*A),
144 CToBucket = (tobucket*P_mig + ztobucket*A),
145 V_mig = (zfrombucket*Xfrombucket + ztobucket*Xtobucket + negzQ_mig*A),
146 D = (d*B),
149 EncIdClient0 = (eid_client*B),
150 EncIdClient1 = (id_client*B + eid_client*D),
151 EncBucket0 = (ebucket*B),
152 EncBucket1 = (tobucket*B + ebucket*D),
153 EncBlockages0 = (eblockages*B),
154 EncBlockages1_minus_B = (blockages*B + eblockages*D)
155}
156
157define_proof! {
158 blindissue,
159 "Blockage Migration Blind Issuing",
160 (x0, x0tilde, xid, xbucket, xlevel, xsince, xinvremain, xblockages,
161 s, b, tid, tbucket, tblockages),
162 (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xlevel, Xsince, Xinvremain,
163 Xblockages, Plevel, Psince, Pinvremain, TId, TBucket, TBlockages,
164 D, EncId0, EncId1, EncBucket0, EncBucket1, EncBlockages0, EncBlockages1),
165 (A, B):
166 Xid = (xid*A),
167 Xlevel = (xlevel*A),
168 Xbucket = (xbucket*A),
169 Xsince = (xsince*A),
170 Xinvremain = (xinvremain*A),
171 Xblockages = (xblockages*A),
172 X0 = (x0*B + x0tilde*A),
173 P = (b*B),
174 TId = (b*Xid),
175 TId = (tid*A),
176 TBucket = (b*Xbucket),
177 TBucket = (tbucket*A),
178 TBlockages = (b*Xblockages),
179 TBlockages = (tblockages*A),
180 EncQ0 = (s*B + tid*EncId0 + tbucket*EncBucket0
181 + tblockages*EncBlockages0),
182 EncQ1 = (s*D + tid*EncId1 + tbucket*EncBucket1
183 + tblockages*EncBlockages1
184 + x0*P + xlevel*Plevel + xsince*Psince + xinvremain*Pinvremain)
185}
186
187pub fn request(
188 lox_cred: &cred::Lox,
189 migration_cred: &cred::Migration,
190 lox_pub: &IssuerPubKey,
191 migration_pub: &IssuerPubKey,
192) -> Result<(Request, State), CredentialError> {
193 let A: &RistrettoPoint = &CMZ_A;
194 let B: &RistrettoPoint = &CMZ_B;
195 let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
196 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
197
198 if lox_cred.id != migration_cred.lox_id || lox_cred.bucket != migration_cred.from_bucket {
202 return Err(CredentialError::CredentialMismatch);
203 }
204
205 let level: u32 = match scalar_u32(&lox_cred.trust_level) {
207 Some(v) => v,
208 None => {
209 return Err(CredentialError::InvalidField(
210 String::from("trust_level"),
211 String::from("could not be converted to u32"),
212 ))
213 }
214 };
215 if level < MIN_TRUST_LEVEL {
216 return Err(CredentialError::InvalidField(
217 String::from("trust_level"),
218 format!("level {:?} not in range", level),
219 ));
220 }
221
222 let mut rng = rand::thread_rng();
226 let t_lox = Scalar::random(&mut rng);
227 let P_lox = t_lox * lox_cred.P;
228 let Q_lox = t_lox * lox_cred.Q;
229
230 let zbucket = Scalar::random(&mut rng);
232 let zsince = Scalar::random(&mut rng);
233 let zinvremain = Scalar::random(&mut rng);
234 let zblockages = Scalar::random(&mut rng);
235 let CBucket = lox_cred.bucket * P_lox + &zbucket * Atable;
236 let CSince = lox_cred.level_since * P_lox + &zsince * Atable;
237 let CInvRemain = lox_cred.invites_remaining * P_lox + &zinvremain * Atable;
238 let CBlockages = lox_cred.blockages * P_lox + &zblockages * Atable;
239
240 let negzQ_lox = Scalar::random(&mut rng);
245 let CQ_lox = Q_lox - &negzQ_lox * Atable;
246
247 let V_lox = zbucket * lox_pub.X[2]
249 + zsince * lox_pub.X[4]
250 + zinvremain * lox_pub.X[5]
251 + zblockages * lox_pub.X[6]
252 + &negzQ_lox * Atable;
253
254 let t_mig = Scalar::random(&mut rng);
258 let P_mig = t_mig * migration_cred.P;
259 let Q_mig = t_mig * migration_cred.Q;
260
261 let zfrombucket = Scalar::random(&mut rng);
263 let ztobucket = Scalar::random(&mut rng);
264 let CFromBucket = migration_cred.from_bucket * P_mig + &zfrombucket * Atable;
265 let CToBucket = migration_cred.to_bucket * P_mig + &ztobucket * Atable;
266
267 let negzQ_mig = Scalar::random(&mut rng);
272 let CQ_mig = Q_mig - &negzQ_mig * Atable;
273
274 let V_mig =
276 zfrombucket * migration_pub.X[2] + ztobucket * migration_pub.X[3] + &negzQ_mig * Atable;
277
278 let d = Scalar::random(&mut rng);
282 let D = &d * Btable;
283
284 let id_client = Scalar::random(&mut rng);
286
287 let eid_client = Scalar::random(&mut rng);
290 let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
291
292 let ebucket = Scalar::random(&mut rng);
294 let EncBucket = (
295 &ebucket * Btable,
296 &migration_cred.to_bucket * Btable + ebucket * D,
297 );
298 let eblockages = Scalar::random(&mut rng);
299 let new_blockages = lox_cred.blockages + Scalar::ONE;
300 let EncBlockages = (
301 &eblockages * Btable,
302 &new_blockages * Btable + eblockages * D,
303 );
304
305 let mut transcript = Transcript::new(b"blockage migration request");
307 let piUser = requestproof::prove_compact(
308 &mut transcript,
309 requestproof::ProveAssignments {
310 A,
311 B,
312 P_lox: &P_lox,
313 CBucket: &CBucket,
314 CSince: &CSince,
315 CInvRemain: &CInvRemain,
316 CBlockages: &CBlockages,
317 V_lox: &V_lox,
318 Xbucket: &lox_pub.X[2],
319 Xsince: &lox_pub.X[4],
320 Xinvremain: &lox_pub.X[5],
321 Xblockages: &lox_pub.X[6],
322 P_mig: &P_mig,
323 CFromBucket: &CFromBucket,
324 CToBucket: &CToBucket,
325 V_mig: &V_mig,
326 Xfrombucket: &migration_pub.X[2],
327 Xtobucket: &migration_pub.X[3],
328 D: &D,
329 EncIdClient0: &EncIdClient.0,
330 EncIdClient1: &EncIdClient.1,
331 EncBucket0: &EncBucket.0,
332 EncBucket1: &EncBucket.1,
333 EncBlockages0: &EncBlockages.0,
334 EncBlockages1_minus_B: &(EncBlockages.1 - B),
335 bucket: &lox_cred.bucket,
336 since: &lox_cred.level_since,
337 invremain: &lox_cred.invites_remaining,
338 blockages: &lox_cred.blockages,
339 zbucket: &zbucket,
340 zsince: &zsince,
341 zinvremain: &zinvremain,
342 zblockages: &zblockages,
343 negzQ_lox: &negzQ_lox,
344 tobucket: &migration_cred.to_bucket,
345 zfrombucket: &zfrombucket,
346 ztobucket: &ztobucket,
347 negzQ_mig: &negzQ_mig,
348 d: &d,
349 eid_client: &eid_client,
350 ebucket: &ebucket,
351 eblockages: &eblockages,
352 id_client: &id_client,
353 },
354 )
355 .0;
356
357 Ok((
358 Request {
359 P_lox,
360 id: lox_cred.id,
361 CBucket,
362 trust_level: lox_cred.trust_level,
363 CSince,
364 CInvRemain,
365 CBlockages,
366 CQ_lox,
367 P_mig,
368 CFromBucket,
369 CToBucket,
370 CQ_mig,
371 D,
372 EncIdClient,
373 EncBucket,
374 EncBlockages,
375 piUser,
376 },
377 State {
378 d,
379 D,
380 EncIdClient,
381 EncBucket,
382 EncBlockages,
383 id_client,
384 to_bucket: migration_cred.to_bucket,
385 trust_level: (level - 2).into(),
386 blockages: new_blockages,
387 },
388 ))
389}
390
391#[cfg(feature = "bridgeauth")]
392impl BridgeAuth {
393 pub fn handle_blockage_migration(&mut self, req: Request) -> Result<Response, ProofError> {
395 let A: &RistrettoPoint = &CMZ_A;
396 let B: &RistrettoPoint = &CMZ_B;
397 let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
398 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
399
400 if req.P_lox.is_identity() || req.P_mig.is_identity() {
401 return Err(ProofError::VerificationFailure);
402 }
403
404 let level: u32 = match scalar_u32(&req.trust_level) {
406 Some(v) => v,
407 None => return Err(ProofError::VerificationFailure),
408 };
409 if level < MIN_TRUST_LEVEL {
410 return Err(ProofError::VerificationFailure);
411 }
412
413 let Vprime_lox = (self.lox_priv.x[0]
417 + self.lox_priv.x[1] * req.id
418 + self.lox_priv.x[3] * req.trust_level)
419 * req.P_lox
420 + self.lox_priv.x[2] * req.CBucket
421 + self.lox_priv.x[4] * req.CSince
422 + self.lox_priv.x[5] * req.CInvRemain
423 + self.lox_priv.x[6] * req.CBlockages
424 - req.CQ_lox;
425
426 let migration_type: Scalar = MigrationType::Blockage.into();
427 let Vprime_mig = (self.migration_priv.x[0]
428 + self.migration_priv.x[1] * req.id
429 + self.migration_priv.x[4] * migration_type)
430 * req.P_mig
431 + self.migration_priv.x[2] * req.CFromBucket
432 + self.migration_priv.x[3] * req.CToBucket
433 - req.CQ_mig;
434
435 let mut transcript = Transcript::new(b"blockage migration request");
437 requestproof::verify_compact(
438 &req.piUser,
439 &mut transcript,
440 requestproof::VerifyAssignments {
441 A: &A.compress(),
442 B: &B.compress(),
443 P_lox: &req.P_lox.compress(),
444 CBucket: &req.CBucket.compress(),
445 CSince: &req.CSince.compress(),
446 CInvRemain: &req.CInvRemain.compress(),
447 CBlockages: &req.CBlockages.compress(),
448 V_lox: &Vprime_lox.compress(),
449 Xbucket: &self.lox_pub.X[2].compress(),
450 Xsince: &self.lox_pub.X[4].compress(),
451 Xinvremain: &self.lox_pub.X[5].compress(),
452 Xblockages: &self.lox_pub.X[6].compress(),
453 P_mig: &req.P_mig.compress(),
454 CFromBucket: &req.CFromBucket.compress(),
455 CToBucket: &req.CToBucket.compress(),
456 V_mig: &Vprime_mig.compress(),
457 Xfrombucket: &self.migration_pub.X[2].compress(),
458 Xtobucket: &self.migration_pub.X[3].compress(),
459 D: &req.D.compress(),
460 EncIdClient0: &req.EncIdClient.0.compress(),
461 EncIdClient1: &req.EncIdClient.1.compress(),
462 EncBucket0: &req.EncBucket.0.compress(),
463 EncBucket1: &req.EncBucket.1.compress(),
464 EncBlockages0: &req.EncBlockages.0.compress(),
465 EncBlockages1_minus_B: &(req.EncBlockages.1 - B).compress(),
466 },
467 )?;
468
469 if self.id_filter.filter(&req.id) == SeenType::Seen {
472 return Err(ProofError::VerificationFailure);
473 }
474
475 let mut rng = rand::thread_rng();
480 let id_server = Scalar::random(&mut rng);
481 let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable);
482
483 let trust_level: Scalar = (level - 2).into();
486
487 let level_since: Scalar = self.today().into();
490
491 let invremain: Scalar = LEVEL_INVITATIONS[(level - 3) as usize].into();
495
496 let b = Scalar::random(&mut rng);
498 let P = &b * Btable;
499 let QHc = (self.lox_priv.x[0]
500 + self.lox_priv.x[3] * trust_level
501 + self.lox_priv.x[4] * level_since
502 + self.lox_priv.x[5] * invremain)
503 * P;
504
505 let s = Scalar::random(&mut rng);
507 let EncQHc = (&s * Btable, QHc + s * req.D);
508
509 let tid = self.lox_priv.x[1] * b;
512 let TId = &tid * Atable;
513 let EncQId = (tid * EncId.0, tid * EncId.1);
514 let tbucket = self.lox_priv.x[2] * b;
515 let TBucket = &tbucket * Atable;
516 let EncQBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
517 let tblockages = self.lox_priv.x[6] * b;
518 let TBlockages = &tblockages * Atable;
519 let EncQBlockages = (
520 tblockages * req.EncBlockages.0,
521 tblockages * req.EncBlockages.1,
522 );
523
524 let EncQ = (
525 EncQHc.0 + EncQId.0 + EncQBucket.0 + EncQBlockages.0,
526 EncQHc.1 + EncQId.1 + EncQBucket.1 + EncQBlockages.1,
527 );
528
529 let mut transcript = Transcript::new(b"blockage migration issuing");
530 let piBlindIssue = blindissue::prove_compact(
531 &mut transcript,
532 blindissue::ProveAssignments {
533 A,
534 B,
535 P: &P,
536 EncQ0: &EncQ.0,
537 EncQ1: &EncQ.1,
538 X0: &self.lox_pub.X[0],
539 Xid: &self.lox_pub.X[1],
540 Xbucket: &self.lox_pub.X[2],
541 Xlevel: &self.lox_pub.X[3],
542 Xsince: &self.lox_pub.X[4],
543 Xinvremain: &self.lox_pub.X[5],
544 Xblockages: &self.lox_pub.X[6],
545 Plevel: &(trust_level * P),
546 Psince: &(level_since * P),
547 Pinvremain: &(invremain * P),
548 TId: &TId,
549 TBucket: &TBucket,
550 TBlockages: &TBlockages,
551 D: &req.D,
552 EncId0: &EncId.0,
553 EncId1: &EncId.1,
554 EncBucket0: &req.EncBucket.0,
555 EncBucket1: &req.EncBucket.1,
556 EncBlockages0: &req.EncBlockages.0,
557 EncBlockages1: &req.EncBlockages.1,
558 x0: &self.lox_priv.x[0],
559 x0tilde: &self.lox_priv.x0tilde,
560 xid: &self.lox_priv.x[1],
561 xbucket: &self.lox_priv.x[2],
562 xlevel: &self.lox_priv.x[3],
563 xsince: &self.lox_priv.x[4],
564 xinvremain: &self.lox_priv.x[5],
565 xblockages: &self.lox_priv.x[6],
566 s: &s,
567 b: &b,
568 tid: &tid,
569 tbucket: &tbucket,
570 tblockages: &tblockages,
571 },
572 )
573 .0;
574
575 Ok(Response {
576 level_since,
577 P,
578 EncQ,
579 id_server,
580 TId,
581 TBucket,
582 TBlockages,
583 piBlindIssue,
584 })
585 }
586}
587
588pub fn handle_response(
591 state: State,
592 resp: Response,
593 lox_pub: &IssuerPubKey,
594) -> Result<cred::Lox, ProofError> {
595 let A: &RistrettoPoint = &CMZ_A;
596 let B: &RistrettoPoint = &CMZ_B;
597 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
598 if resp.P.is_identity() {
599 return Err(ProofError::VerificationFailure);
600 }
601
602 let id = state.id_client + resp.id_server;
605 let EncId = (
606 state.EncIdClient.0,
607 state.EncIdClient.1 + &resp.id_server * Btable,
608 );
609
610 let new_level: u32 = match scalar_u32(&state.trust_level) {
611 Some(v) => v,
612 None => return Err(ProofError::VerificationFailure),
613 };
614 if new_level < 1 {
615 return Err(ProofError::VerificationFailure);
616 }
617
618 let invremain: Scalar = LEVEL_INVITATIONS[(new_level - 1) as usize].into();
622
623 let mut transcript = Transcript::new(b"blockage migration issuing");
625 blindissue::verify_compact(
626 &resp.piBlindIssue,
627 &mut transcript,
628 blindissue::VerifyAssignments {
629 A: &A.compress(),
630 B: &B.compress(),
631 P: &resp.P.compress(),
632 EncQ0: &resp.EncQ.0.compress(),
633 EncQ1: &resp.EncQ.1.compress(),
634 X0: &lox_pub.X[0].compress(),
635 Xid: &lox_pub.X[1].compress(),
636 Xbucket: &lox_pub.X[2].compress(),
637 Xlevel: &lox_pub.X[3].compress(),
638 Xsince: &lox_pub.X[4].compress(),
639 Xinvremain: &lox_pub.X[5].compress(),
640 Xblockages: &lox_pub.X[6].compress(),
641 Plevel: &(state.trust_level * resp.P).compress(),
642 Psince: &(resp.level_since * resp.P).compress(),
643 Pinvremain: &(invremain * resp.P).compress(),
644 TId: &resp.TId.compress(),
645 TBucket: &resp.TBucket.compress(),
646 TBlockages: &resp.TBlockages.compress(),
647 D: &state.D.compress(),
648 EncId0: &EncId.0.compress(),
649 EncId1: &EncId.1.compress(),
650 EncBucket0: &state.EncBucket.0.compress(),
651 EncBucket1: &state.EncBucket.1.compress(),
652 EncBlockages0: &state.EncBlockages.0.compress(),
653 EncBlockages1: &state.EncBlockages.1.compress(),
654 },
655 )?;
656
657 let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
659
660 Ok(cred::Lox {
661 P: resp.P,
662 Q,
663 id,
664 bucket: state.to_bucket,
665 trust_level: new_level.into(),
666 level_since: resp.level_since,
667 invites_remaining: invremain,
668 blockages: state.blockages,
669 })
670}