1use curve25519_dalek::ristretto::RistrettoBasepointTable;
34use curve25519_dalek::ristretto::RistrettoPoint;
35use curve25519_dalek::scalar::Scalar;
36use curve25519_dalek::traits::IsIdentity;
37
38use lox_zkp::CompactProof;
39use lox_zkp::ProofError;
40use lox_zkp::Transcript;
41
42use serde::{Deserialize, Serialize};
43
44use super::super::cred;
45#[cfg(feature = "bridgeauth")]
46use super::super::dup_filter::SeenType;
47#[cfg(feature = "bridgeauth")]
48use super::super::BridgeAuth;
49use super::super::IssuerPubKey;
50use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
51
52use super::errors::CredentialError;
53
54#[derive(Serialize, Deserialize)]
55pub struct Request {
56 P_lox: RistrettoPoint,
60 id: Scalar,
61 CBucket: RistrettoPoint,
62 trust_level: Scalar,
63 CSince: RistrettoPoint,
64 CQ_lox: RistrettoPoint,
65
66 P_mig: RistrettoPoint,
68 CFromBucket: RistrettoPoint,
69 CToBucket: RistrettoPoint,
70 CQ_mig: RistrettoPoint,
71
72 D: RistrettoPoint,
74 EncIdClient: (RistrettoPoint, RistrettoPoint),
75 EncBucket: (RistrettoPoint, RistrettoPoint),
76
77 piUser: CompactProof,
79}
80
81#[derive(Debug, Serialize, Deserialize)]
82pub struct State {
83 d: Scalar,
84 D: RistrettoPoint,
85 EncIdClient: (RistrettoPoint, RistrettoPoint),
86 EncBucket: (RistrettoPoint, RistrettoPoint),
87 id_client: Scalar,
88 to_bucket: Scalar,
89}
90
91#[derive(Serialize, Deserialize)]
92pub struct Response {
93 level_since: Scalar,
95
96 P: RistrettoPoint,
98 EncQ: (RistrettoPoint, RistrettoPoint),
99 id_server: Scalar,
100 TId: RistrettoPoint,
101 TBucket: RistrettoPoint,
102
103 piBlindIssue: CompactProof,
105}
106
107define_proof! {
108 requestproof,
109 "Migration Request",
110 (bucket, since, zbucket, zsince, negzQ_lox,
111 tobucket, zfrombucket, ztobucket, negzQ_mig,
112 d, eid_client, ebucket, id_client),
113 (P_lox, CBucket, CSince, V_lox, Xbucket, Xsince,
114 P_mig, CFromBucket, CToBucket, V_mig, Xfrombucket, Xtobucket,
115 D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1),
116 (A, B):
117 CBucket = (bucket*P_lox + zbucket*A),
119 CSince = (since*P_lox + zsince*A),
120 V_lox = (zbucket*Xbucket + zsince*Xsince + negzQ_lox*A),
121 CFromBucket = (bucket*P_mig + zfrombucket*A),
124 CToBucket = (tobucket*P_mig + ztobucket*A),
125 V_mig = (zfrombucket*Xfrombucket + ztobucket*Xtobucket + negzQ_mig*A),
126 D = (d*B),
129 EncIdClient0 = (eid_client*B),
130 EncIdClient1 = (id_client*B + eid_client*D),
131 EncBucket0 = (ebucket*B),
132 EncBucket1 = (tobucket*B + ebucket*D)
133}
134
135define_proof! {
136 blindissue,
137 "Migration Blind Issuing",
138 (x0, x0tilde, xid, xbucket, xlevel, xsince, s, b, tid, tbucket),
139 (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xlevel, Xsince, Plevel, Psince, TId, TBucket,
140 D, EncId0, EncId1, EncBucket0, EncBucket1),
141 (A, B):
142 Xid = (xid*A),
143 Xlevel = (xlevel*A),
144 Xbucket = (xbucket*A),
145 Xsince = (xsince*A),
146 X0 = (x0*B + x0tilde*A),
147 P = (b*B),
148 TId = (b*Xid),
149 TId = (tid*A),
150 TBucket = (b*Xbucket),
151 TBucket = (tbucket*A),
152 EncQ0 = (s*B + tid*EncId0 + tbucket*EncBucket0),
153 EncQ1 = (s*D + tid*EncId1 + tbucket*EncBucket1 + x0*P + xlevel*Plevel + xsince*Psince)
154}
155
156pub fn request(
157 lox_cred: &cred::Lox,
158 migration_cred: &cred::Migration,
159 lox_pub: &IssuerPubKey,
160 migration_pub: &IssuerPubKey,
161) -> Result<(Request, State), CredentialError> {
162 let A: &RistrettoPoint = &CMZ_A;
163 let B: &RistrettoPoint = &CMZ_B;
164 let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
165 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
166
167 if lox_cred.id != migration_cred.lox_id || lox_cred.bucket != migration_cred.from_bucket {
171 return Err(CredentialError::CredentialMismatch);
172 }
173
174 if lox_cred.trust_level != Scalar::ZERO {
177 return Err(CredentialError::InvalidField(
178 String::from("trust_level"),
179 String::from("must be zero"),
180 ));
181 }
182
183 let mut rng = rand::thread_rng();
187 let t_lox = Scalar::random(&mut rng);
188 let P_lox = t_lox * lox_cred.P;
189 let Q_lox = t_lox * lox_cred.Q;
190
191 let zbucket = Scalar::random(&mut rng);
193 let zsince = Scalar::random(&mut rng);
194 let CBucket = lox_cred.bucket * P_lox + &zbucket * Atable;
195 let CSince = lox_cred.level_since * P_lox + &zsince * Atable;
196
197 let negzQ_lox = Scalar::random(&mut rng);
202 let CQ_lox = Q_lox - &negzQ_lox * Atable;
203
204 let V_lox = zbucket * lox_pub.X[2] + zsince * lox_pub.X[4] + &negzQ_lox * Atable;
206
207 let t_mig = Scalar::random(&mut rng);
211 let P_mig = t_mig * migration_cred.P;
212 let Q_mig = t_mig * migration_cred.Q;
213
214 let zfrombucket = Scalar::random(&mut rng);
216 let ztobucket = Scalar::random(&mut rng);
217 let CFromBucket = migration_cred.from_bucket * P_mig + &zfrombucket * Atable;
218 let CToBucket = migration_cred.to_bucket * P_mig + &ztobucket * Atable;
219
220 let negzQ_mig = Scalar::random(&mut rng);
225 let CQ_mig = Q_mig - &negzQ_mig * Atable;
226
227 let V_mig =
229 zfrombucket * migration_pub.X[2] + ztobucket * migration_pub.X[3] + &negzQ_mig * Atable;
230
231 let d = Scalar::random(&mut rng);
235 let D = &d * Btable;
236
237 let id_client = Scalar::random(&mut rng);
239
240 let eid_client = Scalar::random(&mut rng);
243 let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
244
245 let ebucket = Scalar::random(&mut rng);
247 let EncBucket = (
248 &ebucket * Btable,
249 &migration_cred.to_bucket * Btable + ebucket * D,
250 );
251
252 let mut transcript = Transcript::new(b"migration request");
254 let piUser = requestproof::prove_compact(
255 &mut transcript,
256 requestproof::ProveAssignments {
257 A,
258 B,
259 P_lox: &P_lox,
260 CBucket: &CBucket,
261 CSince: &CSince,
262 V_lox: &V_lox,
263 Xbucket: &lox_pub.X[2],
264 Xsince: &lox_pub.X[4],
265 P_mig: &P_mig,
266 CFromBucket: &CFromBucket,
267 CToBucket: &CToBucket,
268 V_mig: &V_mig,
269 Xfrombucket: &migration_pub.X[2],
270 Xtobucket: &migration_pub.X[3],
271 D: &D,
272 EncIdClient0: &EncIdClient.0,
273 EncIdClient1: &EncIdClient.1,
274 EncBucket0: &EncBucket.0,
275 EncBucket1: &EncBucket.1,
276 bucket: &lox_cred.bucket,
277 since: &lox_cred.level_since,
278 zbucket: &zbucket,
279 zsince: &zsince,
280 negzQ_lox: &negzQ_lox,
281 tobucket: &migration_cred.to_bucket,
282 zfrombucket: &zfrombucket,
283 ztobucket: &ztobucket,
284 negzQ_mig: &negzQ_mig,
285 d: &d,
286 eid_client: &eid_client,
287 ebucket: &ebucket,
288 id_client: &id_client,
289 },
290 )
291 .0;
292
293 Ok((
294 Request {
295 P_lox,
296 id: lox_cred.id,
297 CBucket,
298 trust_level: lox_cred.trust_level,
299 CSince,
300 CQ_lox,
301 P_mig,
302 CFromBucket,
303 CToBucket,
304 CQ_mig,
305 D,
306 EncIdClient,
307 EncBucket,
308 piUser,
309 },
310 State {
311 d,
312 D,
313 EncIdClient,
314 EncBucket,
315 id_client,
316 to_bucket: migration_cred.to_bucket,
317 },
318 ))
319}
320
321#[cfg(feature = "bridgeauth")]
322impl BridgeAuth {
323 pub fn handle_migration(&mut self, req: Request) -> Result<Response, ProofError> {
325 let A: &RistrettoPoint = &CMZ_A;
326 let B: &RistrettoPoint = &CMZ_B;
327 let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
328 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
329
330 if req.P_lox.is_identity() || req.P_mig.is_identity() {
331 return Err(ProofError::VerificationFailure);
332 }
333
334 if req.trust_level != Scalar::ZERO {
336 return Err(ProofError::VerificationFailure);
337 }
338
339 let Vprime_lox = (self.lox_priv.x[0]
343 + self.lox_priv.x[1] * req.id
344 + self.lox_priv.x[3] * req.trust_level)
345 * req.P_lox
346 + self.lox_priv.x[2] * req.CBucket
347 + self.lox_priv.x[4] * req.CSince
348 - req.CQ_lox;
349
350 let Vprime_mig = (self.migration_priv.x[0] + self.migration_priv.x[1] * req.id) * req.P_mig
351 + self.migration_priv.x[2] * req.CFromBucket
352 + self.migration_priv.x[3] * req.CToBucket
353 - req.CQ_mig;
354
355 let mut transcript = Transcript::new(b"migration request");
357 requestproof::verify_compact(
358 &req.piUser,
359 &mut transcript,
360 requestproof::VerifyAssignments {
361 A: &A.compress(),
362 B: &B.compress(),
363 P_lox: &req.P_lox.compress(),
364 CBucket: &req.CBucket.compress(),
365 CSince: &req.CSince.compress(),
366 V_lox: &Vprime_lox.compress(),
367 Xbucket: &self.lox_pub.X[2].compress(),
368 Xsince: &self.lox_pub.X[4].compress(),
369 P_mig: &req.P_mig.compress(),
370 CFromBucket: &req.CFromBucket.compress(),
371 CToBucket: &req.CToBucket.compress(),
372 V_mig: &Vprime_mig.compress(),
373 Xfrombucket: &self.migration_pub.X[2].compress(),
374 Xtobucket: &self.migration_pub.X[3].compress(),
375 D: &req.D.compress(),
376 EncIdClient0: &req.EncIdClient.0.compress(),
377 EncIdClient1: &req.EncIdClient.1.compress(),
378 EncBucket0: &req.EncBucket.0.compress(),
379 EncBucket1: &req.EncBucket.1.compress(),
380 },
381 )?;
382
383 if self.id_filter.filter(&req.id) == SeenType::Seen {
386 return Err(ProofError::VerificationFailure);
387 }
388
389 let mut rng = rand::thread_rng();
394 let id_server = Scalar::random(&mut rng);
395 let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable);
396
397 let trust_level: Scalar = Scalar::ONE;
400
401 let level_since: Scalar = self.today().into();
404
405 let b = Scalar::random(&mut rng);
411 let P = &b * Btable;
412 let QHc = (self.lox_priv.x[0]
414 + self.lox_priv.x[3] * trust_level
415 + self.lox_priv.x[4] * level_since)
416 * P;
417
418 let s = Scalar::random(&mut rng);
420 let EncQHc = (&s * Btable, QHc + s * req.D);
421
422 let tid = self.lox_priv.x[1] * b;
425 let TId = &tid * Atable;
426 let EncQId = (tid * EncId.0, tid * EncId.1);
427 let tbucket = self.lox_priv.x[2] * b;
428 let TBucket = &tbucket * Atable;
429 let EncQBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
430
431 let EncQ = (
432 EncQHc.0 + EncQId.0 + EncQBucket.0,
433 EncQHc.1 + EncQId.1 + EncQBucket.1,
434 );
435
436 let mut transcript = Transcript::new(b"migration issuing");
437 let piBlindIssue = blindissue::prove_compact(
438 &mut transcript,
439 blindissue::ProveAssignments {
440 A,
441 B,
442 P: &P,
443 EncQ0: &EncQ.0,
444 EncQ1: &EncQ.1,
445 X0: &self.lox_pub.X[0],
446 Xid: &self.lox_pub.X[1],
447 Xbucket: &self.lox_pub.X[2],
448 Xlevel: &self.lox_pub.X[3],
449 Xsince: &self.lox_pub.X[4],
450 Plevel: &(trust_level * P),
451 Psince: &(level_since * P),
452 TId: &TId,
453 TBucket: &TBucket,
454 D: &req.D,
455 EncId0: &EncId.0,
456 EncId1: &EncId.1,
457 EncBucket0: &req.EncBucket.0,
458 EncBucket1: &req.EncBucket.1,
459 x0: &self.lox_priv.x[0],
460 x0tilde: &self.lox_priv.x0tilde,
461 xid: &self.lox_priv.x[1],
462 xbucket: &self.lox_priv.x[2],
463 xlevel: &self.lox_priv.x[3],
464 xsince: &self.lox_priv.x[4],
465 s: &s,
466 b: &b,
467 tid: &tid,
468 tbucket: &tbucket,
469 },
470 )
471 .0;
472
473 Ok(Response {
474 level_since,
475 P,
476 EncQ,
477 id_server,
478 TId,
479 TBucket,
480 piBlindIssue,
481 })
482 }
483}
484
485pub fn handle_response(
488 state: State,
489 resp: Response,
490 lox_pub: &IssuerPubKey,
491) -> Result<cred::Lox, ProofError> {
492 let A: &RistrettoPoint = &CMZ_A;
493 let B: &RistrettoPoint = &CMZ_B;
494 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
495
496 if resp.P.is_identity() {
497 return Err(ProofError::VerificationFailure);
498 }
499
500 let id = state.id_client + resp.id_server;
503 let EncId = (
504 state.EncIdClient.0,
505 state.EncIdClient.1 + &resp.id_server * Btable,
506 );
507
508 let mut transcript = Transcript::new(b"migration issuing");
510 blindissue::verify_compact(
511 &resp.piBlindIssue,
512 &mut transcript,
513 blindissue::VerifyAssignments {
514 A: &A.compress(),
515 B: &B.compress(),
516 P: &resp.P.compress(),
517 EncQ0: &resp.EncQ.0.compress(),
518 EncQ1: &resp.EncQ.1.compress(),
519 X0: &lox_pub.X[0].compress(),
520 Xid: &lox_pub.X[1].compress(),
521 Xbucket: &lox_pub.X[2].compress(),
522 Xlevel: &lox_pub.X[3].compress(),
523 Xsince: &lox_pub.X[4].compress(),
524 Plevel: &(Scalar::ONE * resp.P).compress(),
526 Psince: &(resp.level_since * resp.P).compress(),
527 TId: &resp.TId.compress(),
528 TBucket: &resp.TBucket.compress(),
529 D: &state.D.compress(),
530 EncId0: &EncId.0.compress(),
531 EncId1: &EncId.1.compress(),
532 EncBucket0: &state.EncBucket.0.compress(),
533 EncBucket1: &state.EncBucket.1.compress(),
534 },
535 )?;
536
537 let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
539
540 Ok(cred::Lox {
541 P: resp.P,
542 Q,
543 id,
544 bucket: state.to_bucket,
545 trust_level: Scalar::ONE,
546 level_since: resp.level_since,
547 invites_remaining: Scalar::ZERO,
548 blockages: Scalar::ZERO,
549 })
550}