redeem_invite.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. /*! A module for the protocol for a new user to redeem an Invitation
  2. credential. The user will start at trust level 1 (instead of 0 for
  3. untrusted uninvited users).
  4. The user presents the Invitation credential:
  5. - id: revealed
  6. - date: blinded, but proved in ZK to be at most INVITATION_EXPIRY days ago
  7. - bucket: blinded
  8. - blockages: blinded
  9. and a new Lox credential to be issued:
  10. - id: jointly chosen by the user and BA
  11. - bucket: blinded, but proved in ZK that it's the same as in the
  12. Invitation credential above
  13. - trust_level: revealed to be 1
  14. - level_since: today
  15. - invites_remaining: revealed to be 0
  16. - blockages: blinded, but proved in ZK that it's the same as in the
  17. Invitations credential above
  18. */
  19. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  20. use curve25519_dalek::ristretto::RistrettoPoint;
  21. use curve25519_dalek::scalar::Scalar;
  22. use curve25519_dalek::traits::IsIdentity;
  23. use zkp::CompactProof;
  24. use zkp::ProofError;
  25. use zkp::Transcript;
  26. use super::super::cred;
  27. use super::super::dup_filter::SeenType;
  28. use super::super::{pt_dbl, scalar_dbl, scalar_u32};
  29. use super::super::{BridgeAuth, IssuerPubKey};
  30. use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
  31. /// Invitations must be used within this many days of being issued.
  32. /// Note that if you change this number to be larger than 15, you must
  33. /// also add bits to the zero knowledge proof.
  34. pub const INVITATION_EXPIRY: u32 = 15;
  35. pub struct Request {
  36. // Fields for showing the Invitation credential
  37. P: RistrettoPoint,
  38. inv_id: Scalar,
  39. CDate: RistrettoPoint,
  40. CBucket: RistrettoPoint,
  41. CBlockages: RistrettoPoint,
  42. CQ: RistrettoPoint,
  43. // Fields for the inequality proof
  44. // date + INVITATION_EXPIRY >= today
  45. CG1: RistrettoPoint,
  46. CG2: RistrettoPoint,
  47. CG3: RistrettoPoint,
  48. CG0sq: RistrettoPoint,
  49. CG1sq: RistrettoPoint,
  50. CG2sq: RistrettoPoint,
  51. CG3sq: RistrettoPoint,
  52. // Fields for user blinding of the Lox credential to be issued
  53. D: RistrettoPoint,
  54. EncIdClient: (RistrettoPoint, RistrettoPoint),
  55. EncBucket: (RistrettoPoint, RistrettoPoint),
  56. EncBlockages: (RistrettoPoint, RistrettoPoint),
  57. // The combined ZKP
  58. piUser: CompactProof,
  59. }
  60. #[derive(Debug)]
  61. pub struct State {
  62. d: Scalar,
  63. D: RistrettoPoint,
  64. EncIdClient: (RistrettoPoint, RistrettoPoint),
  65. EncBucket: (RistrettoPoint, RistrettoPoint),
  66. EncBlockages: (RistrettoPoint, RistrettoPoint),
  67. id_client: Scalar,
  68. bucket: Scalar,
  69. blockages: Scalar,
  70. }
  71. pub struct Response {
  72. // The fields for the new Lox credential; the new trust level is 1
  73. // and the new invites_remaining is 0, so we don't have to include
  74. // them here explicitly
  75. P: RistrettoPoint,
  76. EncQ: (RistrettoPoint, RistrettoPoint),
  77. id_server: Scalar,
  78. level_since: Scalar,
  79. TId: RistrettoPoint,
  80. TBucket: RistrettoPoint,
  81. TBlockages: RistrettoPoint,
  82. // The ZKP
  83. piBlindIssue: CompactProof,
  84. }
  85. define_proof! {
  86. requestproof,
  87. "Redeem Invite Request",
  88. (date, bucket, blockages, zdate, zbucket, zblockages, negzQ,
  89. d, eid_client, ebucket, eblockages, id_client,
  90. g0, g1, g2, g3,
  91. zg0, zg1, zg2, zg3,
  92. wg0, wg1, wg2, wg3,
  93. yg0, yg1, yg2, yg3),
  94. (P, CDate, CBucket, CBlockages, V, Xdate, Xbucket, Xblockages,
  95. D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1,
  96. EncBlockages0, EncBlockages1,
  97. CG0, CG1, CG2, CG3,
  98. CG0sq, CG1sq, CG2sq, CG3sq),
  99. (A, B):
  100. // Blind showing of the Invitation credential
  101. CDate = (date*P + zdate*A),
  102. CBucket = (bucket*P + zbucket*A),
  103. CBlockages = (blockages*P + zblockages*A),
  104. // User blinding of the Lox credential to be issued
  105. D = (d*B),
  106. EncIdClient0 = (eid_client*B),
  107. EncIdClient1 = (id_client*B + eid_client*D),
  108. EncBucket0 = (ebucket*B),
  109. EncBucket1 = (bucket*B + ebucket*D),
  110. EncBlockages0 = (eblockages*B),
  111. EncBlockages1 = (blockages*B + eblockages*D),
  112. // Prove CDate encodes a value at most INVITATION_EXPIRY
  113. // days ago: first prove each of g0, ..., g3 is a bit by
  114. // proving that gi = gi^2
  115. CG0 = (g0*P + zg0*A), CG0sq = (g0*CG0 + wg0*A), CG0sq = (g0*P + yg0*A),
  116. CG1 = (g1*P + zg1*A), CG1sq = (g1*CG1 + wg1*A), CG1sq = (g1*P + yg1*A),
  117. CG2 = (g2*P + zg2*A), CG2sq = (g2*CG2 + wg2*A), CG2sq = (g2*P + yg2*A),
  118. CG3 = (g3*P + zg3*A), CG3sq = (g3*CG3 + wg3*A), CG3sq = (g3*P + yg3*A)
  119. // Then we'll check that today*P + CG0 + 2*CG1 + 4*CG2 + 8*CG3 =
  120. // CDate + INVITATION_EXPIRY*P by having the verifier
  121. // plug in CDate + INVITATION_EXPIRY*P - (today*P + 2*CG1 + 4*CG2
  122. // + 8*CG3) as its value of CG0.
  123. }
  124. define_proof! {
  125. blindissue,
  126. "Redeem Invite Issuing",
  127. (x0, x0tilde, xid, xbucket, xlevel, xsince, xblockages,
  128. s, b, tid, tbucket, tblockages),
  129. (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xlevel, Xsince, Xblockages,
  130. Psince, TId, TBucket, TBlockages,
  131. D, EncId0, EncId1, EncBucket0, EncBucket1, EncBlockages0, EncBlockages1),
  132. (A, B):
  133. Xid = (xid*A),
  134. Xbucket = (xbucket*A),
  135. Xlevel = (xlevel*A),
  136. Xsince = (xsince*A),
  137. Xblockages = (xblockages*A),
  138. X0 = (x0*B + x0tilde*A),
  139. P = (b*B),
  140. TId = (b*Xid),
  141. TId = (tid*A),
  142. TBucket = (b*Xbucket),
  143. TBucket = (tbucket*A),
  144. TBlockages = (b*Xblockages),
  145. TBlockages = (tblockages*A),
  146. EncQ0 = (s*B + tid*EncId0 + tbucket*EncBucket0 + tblockages*EncBlockages0),
  147. // level=1 (so Plevel = P) and invremain=0 (so the term is omitted)
  148. EncQ1 = (s*D + tid*EncId1 + tbucket*EncBucket1
  149. + tblockages*EncBlockages1 + x0*P + xlevel*P + xsince*Psince)
  150. }
  151. pub fn request(
  152. inv_cred: &cred::Invitation,
  153. invitation_pub: &IssuerPubKey,
  154. today: u32,
  155. ) -> Result<(Request, State), ProofError> {
  156. let A: &RistrettoPoint = &CMZ_A;
  157. let B: &RistrettoPoint = &CMZ_B;
  158. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  159. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  160. // Ensure the credential can be correctly shown: it must be the case
  161. // that date + INVITATION_EXPIRY >= today.
  162. let date: u32 = match scalar_u32(&inv_cred.date) {
  163. Some(v) => v,
  164. None => return Err(ProofError::VerificationFailure),
  165. };
  166. if date + INVITATION_EXPIRY < today {
  167. return Err(ProofError::VerificationFailure);
  168. }
  169. let diffdays = date + INVITATION_EXPIRY - today;
  170. // If diffdays > 15, then since INVITATION_EXPIRY <= 15, then date
  171. // must be in the future. Reject.
  172. if diffdays > 15 {
  173. return Err(ProofError::VerificationFailure);
  174. }
  175. // Blind showing the Invitation credential
  176. // Reblind P and Q
  177. let mut rng = rand::thread_rng();
  178. let t = Scalar::random(&mut rng);
  179. let P = t * inv_cred.P;
  180. let Q = t * inv_cred.Q;
  181. // Form Pedersen commitments to the blinded attributes
  182. let zdate = Scalar::random(&mut rng);
  183. let zbucket = Scalar::random(&mut rng);
  184. let zblockages = Scalar::random(&mut rng);
  185. let CDate = inv_cred.date * P + &zdate * Atable;
  186. let CBucket = inv_cred.bucket * P + &zbucket * Atable;
  187. let CBlockages = inv_cred.blockages * P + &zblockages * Atable;
  188. // Form a Pedersen commitment to the MAC Q
  189. // We flip the sign of zQ from that of the Hyphae paper so that
  190. // the ZKP has a "+" instead of a "-", as that's what the zkp
  191. // macro supports.
  192. let negzQ = Scalar::random(&mut rng);
  193. let CQ = Q - &negzQ * Atable;
  194. // Compute the "error factor"
  195. let V = zdate * invitation_pub.X[2]
  196. + zbucket * invitation_pub.X[3]
  197. + zblockages * invitation_pub.X[4]
  198. + &negzQ * Atable;
  199. // User blinding for the Lox certificate to be issued
  200. // Pick an ElGamal keypair
  201. let d = Scalar::random(&mut rng);
  202. let D = &d * Btable;
  203. // Pick a random client component of the id
  204. let id_client = Scalar::random(&mut rng);
  205. // Encrypt it (times the basepoint B) to the ElGamal public key D we
  206. // just created
  207. let eid_client = Scalar::random(&mut rng);
  208. let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
  209. // Encrypt the other blinded fields (times B) to D as well
  210. let ebucket = Scalar::random(&mut rng);
  211. let EncBucket = (&ebucket * Btable, &inv_cred.bucket * Btable + ebucket * D);
  212. let eblockages = Scalar::random(&mut rng);
  213. let EncBlockages = (
  214. &eblockages * Btable,
  215. &inv_cred.blockages * Btable + eblockages * D,
  216. );
  217. // The range proof that 0 <= diffdays <= 15
  218. // Extract the 4 bits from diffdays
  219. let g0: Scalar = (diffdays & 1).into();
  220. let g1: Scalar = ((diffdays >> 1) & 1).into();
  221. let g2: Scalar = ((diffdays >> 2) & 1).into();
  222. let g3: Scalar = ((diffdays >> 3) & 1).into();
  223. // Pick random factors for the Pedersen commitments
  224. let wg0 = Scalar::random(&mut rng);
  225. let zg1 = Scalar::random(&mut rng);
  226. let wg1 = Scalar::random(&mut rng);
  227. let zg2 = Scalar::random(&mut rng);
  228. let wg2 = Scalar::random(&mut rng);
  229. let zg3 = Scalar::random(&mut rng);
  230. let wg3 = Scalar::random(&mut rng);
  231. // Compute zg0 to cancel things out as
  232. // zg0 = zdate - (2*zg1 + 4*zg2 + 8*zg3)
  233. // but use Horner's method
  234. let zg0 = zdate - scalar_dbl(&(scalar_dbl(&(scalar_dbl(&zg3) + zg2)) + zg1));
  235. let yg0 = wg0 + g0 * zg0;
  236. let yg1 = wg1 + g1 * zg1;
  237. let yg2 = wg2 + g2 * zg2;
  238. let yg3 = wg3 + g3 * zg3;
  239. let CG0 = g0 * P + &zg0 * Atable;
  240. let CG1 = g1 * P + &zg1 * Atable;
  241. let CG2 = g2 * P + &zg2 * Atable;
  242. let CG3 = g3 * P + &zg3 * Atable;
  243. let CG0sq = g0 * P + &yg0 * Atable;
  244. let CG1sq = g1 * P + &yg1 * Atable;
  245. let CG2sq = g2 * P + &yg2 * Atable;
  246. let CG3sq = g3 * P + &yg3 * Atable;
  247. // Construct the proof
  248. let mut transcript = Transcript::new(b"redeem invite request");
  249. let piUser = requestproof::prove_compact(
  250. &mut transcript,
  251. requestproof::ProveAssignments {
  252. A: &A,
  253. B: &B,
  254. P: &P,
  255. CDate: &CDate,
  256. CBucket: &CBucket,
  257. CBlockages: &CBlockages,
  258. V: &V,
  259. Xdate: &invitation_pub.X[2],
  260. Xbucket: &invitation_pub.X[3],
  261. Xblockages: &invitation_pub.X[4],
  262. D: &D,
  263. EncIdClient0: &EncIdClient.0,
  264. EncIdClient1: &EncIdClient.1,
  265. EncBucket0: &EncBucket.0,
  266. EncBucket1: &EncBucket.1,
  267. EncBlockages0: &EncBlockages.0,
  268. EncBlockages1: &EncBlockages.1,
  269. CG0: &CG0,
  270. CG1: &CG1,
  271. CG2: &CG2,
  272. CG3: &CG3,
  273. CG0sq: &CG0sq,
  274. CG1sq: &CG1sq,
  275. CG2sq: &CG2sq,
  276. CG3sq: &CG3sq,
  277. date: &inv_cred.date,
  278. bucket: &inv_cred.bucket,
  279. blockages: &inv_cred.blockages,
  280. zdate: &zdate,
  281. zbucket: &zbucket,
  282. zblockages: &zblockages,
  283. negzQ: &negzQ,
  284. d: &d,
  285. eid_client: &eid_client,
  286. ebucket: &ebucket,
  287. eblockages: &eblockages,
  288. id_client: &id_client,
  289. g0: &g0,
  290. g1: &g1,
  291. g2: &g2,
  292. g3: &g3,
  293. zg0: &zg0,
  294. zg1: &zg1,
  295. zg2: &zg2,
  296. zg3: &zg3,
  297. wg0: &wg0,
  298. wg1: &wg1,
  299. wg2: &wg2,
  300. wg3: &wg3,
  301. yg0: &yg0,
  302. yg1: &yg1,
  303. yg2: &yg2,
  304. yg3: &yg3,
  305. },
  306. )
  307. .0;
  308. Ok((
  309. Request {
  310. P,
  311. inv_id: inv_cred.inv_id,
  312. CDate,
  313. CBucket,
  314. CBlockages,
  315. CQ,
  316. D,
  317. EncIdClient,
  318. EncBucket,
  319. EncBlockages,
  320. CG1,
  321. CG2,
  322. CG3,
  323. CG0sq,
  324. CG1sq,
  325. CG2sq,
  326. CG3sq,
  327. piUser,
  328. },
  329. State {
  330. d,
  331. D,
  332. EncIdClient,
  333. EncBucket,
  334. EncBlockages,
  335. id_client,
  336. bucket: inv_cred.bucket,
  337. blockages: inv_cred.blockages,
  338. },
  339. ))
  340. }
  341. impl BridgeAuth {
  342. /// Receive a redeem invite request
  343. pub fn handle_redeem_invite(&mut self, req: Request) -> Result<Response, ProofError> {
  344. let A: &RistrettoPoint = &CMZ_A;
  345. let B: &RistrettoPoint = &CMZ_B;
  346. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  347. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  348. if req.P.is_identity() {
  349. return Err(ProofError::VerificationFailure);
  350. }
  351. let today: Scalar = self.today().into();
  352. // Recompute the "error factor" using knowledge of our own
  353. // (the issuer's) private key instead of knowledge of the
  354. // hidden attributes
  355. let Vprime = (self.invitation_priv.x[0] + self.invitation_priv.x[1] * req.inv_id) * req.P
  356. + self.invitation_priv.x[2] * req.CDate
  357. + self.invitation_priv.x[3] * req.CBucket
  358. + self.invitation_priv.x[4] * req.CBlockages
  359. - req.CQ;
  360. // Recompute CG0 using Horner's method
  361. let expiry: Scalar = INVITATION_EXPIRY.into();
  362. let CG0prime = (expiry - today) * req.P + req.CDate
  363. - pt_dbl(&(pt_dbl(&(pt_dbl(&req.CG3) + req.CG2)) + req.CG1));
  364. // Verify the ZKP
  365. let mut transcript = Transcript::new(b"redeem invite request");
  366. requestproof::verify_compact(
  367. &req.piUser,
  368. &mut transcript,
  369. requestproof::VerifyAssignments {
  370. A: &A.compress(),
  371. B: &B.compress(),
  372. P: &req.P.compress(),
  373. CDate: &req.CDate.compress(),
  374. CBucket: &req.CBucket.compress(),
  375. CBlockages: &req.CBlockages.compress(),
  376. V: &Vprime.compress(),
  377. Xdate: &self.invitation_pub.X[2].compress(),
  378. Xbucket: &self.invitation_pub.X[3].compress(),
  379. Xblockages: &self.invitation_pub.X[4].compress(),
  380. D: &req.D.compress(),
  381. EncIdClient0: &req.EncIdClient.0.compress(),
  382. EncIdClient1: &req.EncIdClient.1.compress(),
  383. EncBucket0: &req.EncBucket.0.compress(),
  384. EncBucket1: &req.EncBucket.1.compress(),
  385. EncBlockages0: &req.EncBlockages.0.compress(),
  386. EncBlockages1: &req.EncBlockages.1.compress(),
  387. CG0: &CG0prime.compress(),
  388. CG1: &req.CG1.compress(),
  389. CG2: &req.CG2.compress(),
  390. CG3: &req.CG3.compress(),
  391. CG0sq: &req.CG0sq.compress(),
  392. CG1sq: &req.CG1sq.compress(),
  393. CG2sq: &req.CG2sq.compress(),
  394. CG3sq: &req.CG3sq.compress(),
  395. },
  396. )?;
  397. // Ensure the id has not been seen before, and add it to the
  398. // invite id seen list.
  399. if self.inv_id_filter.filter(&req.inv_id) == SeenType::Seen {
  400. return Err(ProofError::VerificationFailure);
  401. }
  402. // Blind issuing of the new Lox credential
  403. // Choose a random server id component to add to the client's
  404. // (blinded) id component
  405. let mut rng = rand::thread_rng();
  406. let id_server = Scalar::random(&mut rng);
  407. let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable);
  408. // The trust level for invitees is always 1
  409. let level = Scalar::one();
  410. // The invites remaining for invitees is always 0 (as
  411. // appropriate for trust level 1), so we don't need to actually
  412. // construct it
  413. // Compute the MAC on the visible attributes
  414. let b = Scalar::random(&mut rng);
  415. let P = &b * Btable;
  416. let QHc =
  417. (self.lox_priv.x[0] + self.lox_priv.x[3] * level + self.lox_priv.x[4] * today) * P;
  418. // El Gamal encrypt it to the public key req.D
  419. let s = Scalar::random(&mut rng);
  420. let EncQHc = (&s * Btable, QHc + s * req.D);
  421. // Homomorphically compute the part of the MAC corresponding to
  422. // the blinded attributes
  423. let tid = self.lox_priv.x[1] * b;
  424. let TId = &tid * Atable;
  425. let EncQId = (tid * EncId.0, tid * EncId.1);
  426. let tbucket = self.lox_priv.x[2] * b;
  427. let TBucket = &tbucket * Atable;
  428. let EncQBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
  429. let tblockages = self.lox_priv.x[6] * b;
  430. let TBlockages = &tblockages * Atable;
  431. let EncQBlockages = (
  432. tblockages * req.EncBlockages.0,
  433. tblockages * req.EncBlockages.1,
  434. );
  435. let EncQ = (
  436. EncQHc.0 + EncQId.0 + EncQBucket.0 + EncQBlockages.0,
  437. EncQHc.1 + EncQId.1 + EncQBucket.1 + EncQBlockages.1,
  438. );
  439. let mut transcript = Transcript::new(b"redeem invite issuing");
  440. let piBlindIssue = blindissue::prove_compact(
  441. &mut transcript,
  442. blindissue::ProveAssignments {
  443. A: &A,
  444. B: &B,
  445. P: &P,
  446. EncQ0: &EncQ.0,
  447. EncQ1: &EncQ.1,
  448. X0: &self.lox_pub.X[0],
  449. Xid: &self.lox_pub.X[1],
  450. Xbucket: &self.lox_pub.X[2],
  451. Xlevel: &self.lox_pub.X[3],
  452. Xsince: &self.lox_pub.X[4],
  453. Xblockages: &self.lox_pub.X[6],
  454. Psince: &(today * P),
  455. TId: &TId,
  456. TBucket: &TBucket,
  457. TBlockages: &TBlockages,
  458. D: &req.D,
  459. EncId0: &EncId.0,
  460. EncId1: &EncId.1,
  461. EncBucket0: &req.EncBucket.0,
  462. EncBucket1: &req.EncBucket.1,
  463. EncBlockages0: &req.EncBlockages.0,
  464. EncBlockages1: &req.EncBlockages.1,
  465. x0: &self.lox_priv.x[0],
  466. x0tilde: &self.lox_priv.x0tilde,
  467. xid: &self.lox_priv.x[1],
  468. xbucket: &self.lox_priv.x[2],
  469. xlevel: &self.lox_priv.x[3],
  470. xsince: &self.lox_priv.x[4],
  471. xblockages: &self.lox_priv.x[6],
  472. s: &s,
  473. b: &b,
  474. tid: &tid,
  475. tbucket: &tbucket,
  476. tblockages: &tblockages,
  477. },
  478. )
  479. .0;
  480. Ok(Response {
  481. P,
  482. EncQ,
  483. id_server,
  484. level_since: today,
  485. TId,
  486. TBucket,
  487. TBlockages,
  488. piBlindIssue,
  489. })
  490. }
  491. }
  492. /// Handle the response to the request, producing the new Lox credential
  493. /// if successful.
  494. pub fn handle_response(
  495. state: State,
  496. resp: Response,
  497. lox_pub: &IssuerPubKey,
  498. ) -> Result<cred::Lox, ProofError> {
  499. let A: &RistrettoPoint = &CMZ_A;
  500. let B: &RistrettoPoint = &CMZ_B;
  501. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  502. if resp.P.is_identity() {
  503. return Err(ProofError::VerificationFailure);
  504. }
  505. // Add the server's contribution to the id to our own, both in plain
  506. // and encrypted form
  507. let id = state.id_client + resp.id_server;
  508. let EncId = (
  509. state.EncIdClient.0,
  510. state.EncIdClient.1 + &resp.id_server * Btable,
  511. );
  512. // Verify the proof
  513. let mut transcript = Transcript::new(b"redeem invite issuing");
  514. blindissue::verify_compact(
  515. &resp.piBlindIssue,
  516. &mut transcript,
  517. blindissue::VerifyAssignments {
  518. A: &A.compress(),
  519. B: &B.compress(),
  520. P: &resp.P.compress(),
  521. EncQ0: &resp.EncQ.0.compress(),
  522. EncQ1: &resp.EncQ.1.compress(),
  523. X0: &lox_pub.X[0].compress(),
  524. Xid: &lox_pub.X[1].compress(),
  525. Xbucket: &lox_pub.X[2].compress(),
  526. Xlevel: &lox_pub.X[3].compress(),
  527. Xsince: &lox_pub.X[4].compress(),
  528. Xblockages: &lox_pub.X[6].compress(),
  529. Psince: &(resp.level_since * resp.P).compress(),
  530. TId: &resp.TId.compress(),
  531. TBucket: &resp.TBucket.compress(),
  532. TBlockages: &resp.TBlockages.compress(),
  533. D: &state.D.compress(),
  534. EncId0: &EncId.0.compress(),
  535. EncId1: &EncId.1.compress(),
  536. EncBucket0: &state.EncBucket.0.compress(),
  537. EncBucket1: &state.EncBucket.1.compress(),
  538. EncBlockages0: &state.EncBlockages.0.compress(),
  539. EncBlockages1: &state.EncBlockages.1.compress(),
  540. },
  541. )?;
  542. // Decrypt EncQ
  543. let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
  544. Ok(cred::Lox {
  545. P: resp.P,
  546. Q,
  547. id,
  548. bucket: state.bucket,
  549. trust_level: Scalar::one(),
  550. level_since: resp.level_since,
  551. invites_remaining: Scalar::zero(),
  552. blockages: state.blockages,
  553. })
  554. }