1use curve25519_dalek::ristretto::RistrettoBasepointTable;
31use curve25519_dalek::ristretto::RistrettoPoint;
32use curve25519_dalek::scalar::Scalar;
33use curve25519_dalek::traits::IsIdentity;
34
35use lox_zkp::CompactProof;
36use lox_zkp::ProofError;
37use lox_zkp::Transcript;
38
39use serde::{Deserialize, Serialize};
40use serde_with::serde_as;
41
42use std::collections::HashMap;
43
44use super::super::cred;
45#[cfg(feature = "bridgeauth")]
46use super::super::dup_filter::SeenType;
47use super::super::migration_table;
48#[cfg(feature = "bridgeauth")]
49use super::super::pt_dbl;
50#[cfg(feature = "bridgeauth")]
51use super::super::BridgeAuth;
52use super::super::IssuerPubKey;
53use super::super::{scalar_dbl, scalar_u32};
54use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
55
56use super::errors::CredentialError;
57
58pub const UNTRUSTED_INTERVAL: u32 = 30;
66
67#[derive(Serialize, Deserialize)]
68pub struct Request {
69 P: RistrettoPoint,
73 id: Scalar,
74 CBucket: RistrettoPoint,
75 CSince: RistrettoPoint,
76 CQ: RistrettoPoint,
77
78 D: RistrettoPoint,
80 EncBucket: (RistrettoPoint, RistrettoPoint),
81
82 CG1: RistrettoPoint,
85 CG2: RistrettoPoint,
86 CG3: RistrettoPoint,
87 CG4: RistrettoPoint,
88 CG5: RistrettoPoint,
89 CG6: RistrettoPoint,
90 CG7: RistrettoPoint,
91 CG8: RistrettoPoint,
92 CG0sq: RistrettoPoint,
93 CG1sq: RistrettoPoint,
94 CG2sq: RistrettoPoint,
95 CG3sq: RistrettoPoint,
96 CG4sq: RistrettoPoint,
97 CG5sq: RistrettoPoint,
98 CG6sq: RistrettoPoint,
99 CG7sq: RistrettoPoint,
100 CG8sq: RistrettoPoint,
101
102 piUser: CompactProof,
104}
105
106#[derive(Debug, Serialize, Deserialize)]
107pub struct State {
108 d: Scalar,
109 D: RistrettoPoint,
110 EncBucket: (RistrettoPoint, RistrettoPoint),
111 id: Scalar,
112 bucket: Scalar,
113}
114
115#[serde_as]
116#[derive(Serialize, Deserialize, Debug)]
117pub struct Response {
118 Pk: RistrettoPoint,
120 EncQk: (RistrettoPoint, RistrettoPoint),
121
122 #[serde_as(as = "Vec<(_,[_; migration_table::ENC_MIGRATION_BYTES])>")]
126 enc_migration_table: HashMap<[u8; 16], [u8; migration_table::ENC_MIGRATION_BYTES]>,
127}
128
129define_proof! {
130 requestproof,
131 "Trust Promotion Request",
132 (bucket, since, zbucket, zsince, negzQ,
133 d, ebucket,
134 g0, g1, g2, g3, g4, g5, g6, g7, g8,
135 zg0, zg1, zg2, zg3, zg4, zg5, zg6, zg7, zg8,
136 wg0, wg1, wg2, wg3, wg4, wg5, wg6, wg7, wg8,
137 yg0, yg1, yg2, yg3, yg4, yg5, yg6, yg7, yg8),
138 (P, CBucket, CSince, V, Xbucket, Xsince,
139 D, EncBucket0, EncBucket1,
140 CG0, CG1, CG2, CG3, CG4, CG5, CG6, CG7, CG8,
141 CG0sq, CG1sq, CG2sq, CG3sq, CG4sq, CG5sq, CG6sq, CG7sq, CG8sq),
142 (A, B):
143 CBucket = (bucket*P + zbucket*A),
145 CSince = (since*P + zsince*A),
146 V = (zbucket*Xbucket + zsince*Xsince + negzQ*A),
147 D = (d*B),
149 EncBucket0 = (ebucket*B),
150 EncBucket1 = (bucket*B + ebucket*D),
151 CG0 = (g0*P + zg0*A), CG0sq = (g0*CG0 + wg0*A), CG0sq = (g0*P + yg0*A),
156 CG1 = (g1*P + zg1*A), CG1sq = (g1*CG1 + wg1*A), CG1sq = (g1*P + yg1*A),
157 CG2 = (g2*P + zg2*A), CG2sq = (g2*CG2 + wg2*A), CG2sq = (g2*P + yg2*A),
158 CG3 = (g3*P + zg3*A), CG3sq = (g3*CG3 + wg3*A), CG3sq = (g3*P + yg3*A),
159 CG4 = (g4*P + zg4*A), CG4sq = (g4*CG4 + wg4*A), CG4sq = (g4*P + yg4*A),
160 CG5 = (g5*P + zg5*A), CG5sq = (g5*CG5 + wg5*A), CG5sq = (g5*P + yg5*A),
161 CG6 = (g6*P + zg6*A), CG6sq = (g6*CG6 + wg6*A), CG6sq = (g6*P + yg6*A),
162 CG7 = (g7*P + zg7*A), CG7sq = (g7*CG7 + wg7*A), CG7sq = (g7*P + yg7*A),
163 CG8 = (g8*P + zg8*A), CG8sq = (g8*CG8 + wg8*A), CG8sq = (g8*P + yg8*A)
164 }
169
170pub fn request(
171 lox_cred: &cred::Lox,
172 lox_pub: &IssuerPubKey,
173 today: u32,
174) -> Result<(Request, State), CredentialError> {
175 let A: &RistrettoPoint = &CMZ_A;
176 let B: &RistrettoPoint = &CMZ_B;
177 let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
178 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
179
180 let level_since: u32 = match scalar_u32(&lox_cred.level_since) {
183 Some(v) => v,
184 None => {
185 return Err(CredentialError::InvalidField(
186 String::from("level_since"),
187 String::from("could not be converted to u32"),
188 ))
189 }
190 };
191 if level_since + UNTRUSTED_INTERVAL > today {
192 return Err(CredentialError::TimeThresholdNotMet(
193 level_since + UNTRUSTED_INTERVAL - today,
194 ));
195 }
196 let diffdays = today - (level_since + UNTRUSTED_INTERVAL);
197 if diffdays > 511 {
198 return Err(CredentialError::CredentialExpired);
199 }
200
201 let mut rng = rand::thread_rng();
205 let t = Scalar::random(&mut rng);
206 let P = t * lox_cred.P;
207 let Q = t * lox_cred.Q;
208
209 let zbucket = Scalar::random(&mut rng);
211 let zsince = Scalar::random(&mut rng);
212 let CBucket = lox_cred.bucket * P + &zbucket * Atable;
213 let CSince = lox_cred.level_since * P + &zsince * Atable;
214
215 let negzQ = Scalar::random(&mut rng);
220 let CQ = Q - &negzQ * Atable;
221
222 let V = zbucket * lox_pub.X[2] + zsince * lox_pub.X[4] + &negzQ * Atable;
224
225 let d = Scalar::random(&mut rng);
229 let D = &d * Btable;
230
231 let ebucket = Scalar::random(&mut rng);
234 let EncBucket = (&ebucket * Btable, &lox_cred.bucket * Btable + ebucket * D);
235
236 let g0: Scalar = (diffdays & 1).into();
240 let g1: Scalar = ((diffdays >> 1) & 1).into();
241 let g2: Scalar = ((diffdays >> 2) & 1).into();
242 let g3: Scalar = ((diffdays >> 3) & 1).into();
243 let g4: Scalar = ((diffdays >> 4) & 1).into();
244 let g5: Scalar = ((diffdays >> 5) & 1).into();
245 let g6: Scalar = ((diffdays >> 6) & 1).into();
246 let g7: Scalar = ((diffdays >> 7) & 1).into();
247 let g8: Scalar = ((diffdays >> 8) & 1).into();
248
249 let wg0 = Scalar::random(&mut rng);
251 let zg1 = Scalar::random(&mut rng);
252 let wg1 = Scalar::random(&mut rng);
253 let zg2 = Scalar::random(&mut rng);
254 let wg2 = Scalar::random(&mut rng);
255 let zg3 = Scalar::random(&mut rng);
256 let wg3 = Scalar::random(&mut rng);
257 let zg4 = Scalar::random(&mut rng);
258 let wg4 = Scalar::random(&mut rng);
259 let zg5 = Scalar::random(&mut rng);
260 let wg5 = Scalar::random(&mut rng);
261 let zg6 = Scalar::random(&mut rng);
262 let wg6 = Scalar::random(&mut rng);
263 let zg7 = Scalar::random(&mut rng);
264 let wg7 = Scalar::random(&mut rng);
265 let zg8 = Scalar::random(&mut rng);
266 let wg8 = Scalar::random(&mut rng);
267
268 let zg0 = -(scalar_dbl(
272 &(scalar_dbl(
273 &(scalar_dbl(
274 &(scalar_dbl(
275 &(scalar_dbl(
276 &(scalar_dbl(&(scalar_dbl(&(scalar_dbl(&zg8) + zg7)) + zg6)) + zg5),
277 ) + zg4),
278 ) + zg3),
279 ) + zg2),
280 ) + zg1),
281 ) + zsince);
282
283 let yg0 = wg0 + g0 * zg0;
284 let yg1 = wg1 + g1 * zg1;
285 let yg2 = wg2 + g2 * zg2;
286 let yg3 = wg3 + g3 * zg3;
287 let yg4 = wg4 + g4 * zg4;
288 let yg5 = wg5 + g5 * zg5;
289 let yg6 = wg6 + g6 * zg6;
290 let yg7 = wg7 + g7 * zg7;
291 let yg8 = wg8 + g8 * zg8;
292
293 let CG0 = g0 * P + &zg0 * Atable;
294 let CG1 = g1 * P + &zg1 * Atable;
295 let CG2 = g2 * P + &zg2 * Atable;
296 let CG3 = g3 * P + &zg3 * Atable;
297 let CG4 = g4 * P + &zg4 * Atable;
298 let CG5 = g5 * P + &zg5 * Atable;
299 let CG6 = g6 * P + &zg6 * Atable;
300 let CG7 = g7 * P + &zg7 * Atable;
301 let CG8 = g8 * P + &zg8 * Atable;
302
303 let CG0sq = g0 * P + &yg0 * Atable;
304 let CG1sq = g1 * P + &yg1 * Atable;
305 let CG2sq = g2 * P + &yg2 * Atable;
306 let CG3sq = g3 * P + &yg3 * Atable;
307 let CG4sq = g4 * P + &yg4 * Atable;
308 let CG5sq = g5 * P + &yg5 * Atable;
309 let CG6sq = g6 * P + &yg6 * Atable;
310 let CG7sq = g7 * P + &yg7 * Atable;
311 let CG8sq = g8 * P + &yg8 * Atable;
312
313 let mut transcript = Transcript::new(b"trust promotion request");
315 let piUser = requestproof::prove_compact(
316 &mut transcript,
317 requestproof::ProveAssignments {
318 A,
319 B,
320 P: &P,
321 CBucket: &CBucket,
322 CSince: &CSince,
323 V: &V,
324 Xbucket: &lox_pub.X[2],
325 Xsince: &lox_pub.X[4],
326 D: &D,
327 EncBucket0: &EncBucket.0,
328 EncBucket1: &EncBucket.1,
329 CG0: &CG0,
330 CG1: &CG1,
331 CG2: &CG2,
332 CG3: &CG3,
333 CG4: &CG4,
334 CG5: &CG5,
335 CG6: &CG6,
336 CG7: &CG7,
337 CG8: &CG8,
338 CG0sq: &CG0sq,
339 CG1sq: &CG1sq,
340 CG2sq: &CG2sq,
341 CG3sq: &CG3sq,
342 CG4sq: &CG4sq,
343 CG5sq: &CG5sq,
344 CG6sq: &CG6sq,
345 CG7sq: &CG7sq,
346 CG8sq: &CG8sq,
347 bucket: &lox_cred.bucket,
348 since: &lox_cred.level_since,
349 zbucket: &zbucket,
350 zsince: &zsince,
351 negzQ: &negzQ,
352 d: &d,
353 ebucket: &ebucket,
354 g0: &g0,
355 g1: &g1,
356 g2: &g2,
357 g3: &g3,
358 g4: &g4,
359 g5: &g5,
360 g6: &g6,
361 g7: &g7,
362 g8: &g8,
363 zg0: &zg0,
364 zg1: &zg1,
365 zg2: &zg2,
366 zg3: &zg3,
367 zg4: &zg4,
368 zg5: &zg5,
369 zg6: &zg6,
370 zg7: &zg7,
371 zg8: &zg8,
372 wg0: &wg0,
373 wg1: &wg1,
374 wg2: &wg2,
375 wg3: &wg3,
376 wg4: &wg4,
377 wg5: &wg5,
378 wg6: &wg6,
379 wg7: &wg7,
380 wg8: &wg8,
381 yg0: &yg0,
382 yg1: &yg1,
383 yg2: &yg2,
384 yg3: &yg3,
385 yg4: &yg4,
386 yg5: &yg5,
387 yg6: &yg6,
388 yg7: &yg7,
389 yg8: &yg8,
390 },
391 )
392 .0;
393
394 Ok((
395 Request {
396 P,
397 id: lox_cred.id,
398 CBucket,
399 CSince,
400 CQ,
401 D,
402 EncBucket,
403 CG1,
404 CG2,
405 CG3,
406 CG4,
407 CG5,
408 CG6,
409 CG7,
410 CG8,
411 CG0sq,
412 CG1sq,
413 CG2sq,
414 CG3sq,
415 CG4sq,
416 CG5sq,
417 CG6sq,
418 CG7sq,
419 CG8sq,
420 piUser,
421 },
422 State {
423 d,
424 D,
425 EncBucket,
426 id: lox_cred.id,
427 bucket: lox_cred.bucket,
428 },
429 ))
430}
431
432#[cfg(feature = "bridgeauth")]
433impl BridgeAuth {
434 pub fn handle_trust_promotion(&mut self, req: Request) -> Result<Response, ProofError> {
436 let A: &RistrettoPoint = &CMZ_A;
437 let B: &RistrettoPoint = &CMZ_B;
438 let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
439
440 if req.P.is_identity() {
441 return Err(ProofError::VerificationFailure);
442 }
443
444 let Vprime = (self.lox_priv.x[0] + self.lox_priv.x[1] * req.id) * req.P
448 + self.lox_priv.x[2] * req.CBucket
449 + self.lox_priv.x[4] * req.CSince
450 - req.CQ;
451
452 let today: Scalar = self.today().into();
454 let unt: Scalar = UNTRUSTED_INTERVAL.into();
455 let CG0prime = (today - unt) * req.P
456 - req.CSince
457 - pt_dbl(
458 &(pt_dbl(
459 &(pt_dbl(
460 &(pt_dbl(
461 &(pt_dbl(
462 &(pt_dbl(&(pt_dbl(&(pt_dbl(&req.CG8) + req.CG7)) + req.CG6))
463 + req.CG5),
464 ) + req.CG4),
465 ) + req.CG3),
466 ) + req.CG2),
467 ) + req.CG1),
468 );
469
470 let mut transcript = Transcript::new(b"trust promotion request");
472 requestproof::verify_compact(
473 &req.piUser,
474 &mut transcript,
475 requestproof::VerifyAssignments {
476 A: &A.compress(),
477 B: &B.compress(),
478 P: &req.P.compress(),
479 CBucket: &req.CBucket.compress(),
480 CSince: &req.CSince.compress(),
481 V: &Vprime.compress(),
482 Xbucket: &self.lox_pub.X[2].compress(),
483 Xsince: &self.lox_pub.X[4].compress(),
484 D: &req.D.compress(),
485 EncBucket0: &req.EncBucket.0.compress(),
486 EncBucket1: &req.EncBucket.1.compress(),
487 CG0: &CG0prime.compress(),
488 CG1: &req.CG1.compress(),
489 CG2: &req.CG2.compress(),
490 CG3: &req.CG3.compress(),
491 CG4: &req.CG4.compress(),
492 CG5: &req.CG5.compress(),
493 CG6: &req.CG6.compress(),
494 CG7: &req.CG7.compress(),
495 CG8: &req.CG8.compress(),
496 CG0sq: &req.CG0sq.compress(),
497 CG1sq: &req.CG1sq.compress(),
498 CG2sq: &req.CG2sq.compress(),
499 CG3sq: &req.CG3sq.compress(),
500 CG4sq: &req.CG4sq.compress(),
501 CG5sq: &req.CG5sq.compress(),
502 CG6sq: &req.CG6sq.compress(),
503 CG7sq: &req.CG7sq.compress(),
504 CG8sq: &req.CG8sq.compress(),
505 },
506 )?;
507
508 if self.id_filter.check(&req.id) == SeenType::Seen
512 || self.trust_promotion_filter.filter(&req.id) == SeenType::Seen
513 {
514 return Err(ProofError::VerificationFailure);
515 }
516
517 let mut rng = rand::thread_rng();
522 let b = Scalar::random(&mut rng);
523 let Pk = &b * Btable;
524 let Pktable = RistrettoBasepointTable::create(&Pk);
525 let Qid = &(self.migrationkey_priv.x[0] + self.migrationkey_priv.x[1] * req.id) * &Pktable;
526
527 let s = Scalar::random(&mut rng);
529 let EncQkid = (&s * Btable, Qid + s * req.D);
530
531 let tbucket = self.migrationkey_priv.x[2] * b;
534 let EncQkBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
535
536 let EncQk = (EncQkid.0 + EncQkBucket.0, EncQkid.1 + EncQkBucket.1);
537
538 Ok(Response {
539 Pk,
540 EncQk,
541 enc_migration_table: self.trustup_migration_table.encrypt_table(
542 &req.id,
543 &self.bridge_table,
544 &Pktable,
545 &self.migration_priv,
546 &self.migrationkey_priv,
547 ),
548 })
549 }
550}
551
552pub fn handle_response(state: State, resp: Response) -> Result<cred::Migration, ProofError> {
558 if resp.Pk.is_identity() {
559 return Err(ProofError::VerificationFailure);
560 }
561
562 let Qk = resp.EncQk.1 - (state.d * resp.EncQk.0);
564
565 match migration_table::decrypt_cred(
567 &Qk,
568 &state.id,
569 &state.bucket,
570 migration_table::MigrationType::TrustUpgrade,
571 &resp.enc_migration_table,
572 ) {
573 Some(m) => Ok(m),
574 None => Err(ProofError::VerificationFailure),
575 }
576}