issue_invite.rs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908
  1. /*! A module for the protocol for a user to request the issuing of an
  2. Invitation credential they can pass to someone they know.
  3. They are allowed to do this as long as their current Lox credentials has
  4. a non-zero "invites_remaining" attribute (which will be decreased by
  5. one), and they have a Bucket Reachability credential for their current
  6. bucket and today's date. (Such credentials are placed daily in the
  7. encrypted bridge table.)
  8. The user presents their current Lox credential:
  9. - id: revealed
  10. - bucket: blinded
  11. - trust_level: blinded
  12. - level_since: blinded
  13. - invites_remaining: blinded, but proved in ZK that it's not zero
  14. - blockages: blinded
  15. and a Bucket Reachability credential:
  16. - date: revealed to be today
  17. - bucket: blinded, but proved in ZK that it's the same as in the Lox
  18. credential above
  19. and a new Lox credential to be issued:
  20. - id: jointly chosen by the user and BA
  21. - bucket: blinded, but proved in ZK that it's the same as in the Lox
  22. credential above
  23. - trust_level: blinded, but proved in ZK that it's the same as in the
  24. Lox credential above
  25. - level_since: blinded, but proved in ZK that it's the same as in the
  26. Lox credential above
  27. - invites_remaining: blinded, but proved in ZK that it's one less than
  28. the number in the Lox credential above
  29. - blockages: blinded, but proved in ZK that it's the same as in the
  30. Lox credential above
  31. and a new Invitation credential to be issued:
  32. - inv_id: jointly chosen by the user and BA
  33. - date: revealed to be today
  34. - bucket: blinded, but proved in ZK that it's the same as in the Lox
  35. credential above
  36. - blockages: blinded, but proved in ZK that it's the same as in the Lox
  37. credential above
  38. */
  39. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  40. use curve25519_dalek::ristretto::RistrettoPoint;
  41. use curve25519_dalek::scalar::Scalar;
  42. use curve25519_dalek::traits::IsIdentity;
  43. use zkp::CompactProof;
  44. use zkp::ProofError;
  45. use zkp::Transcript;
  46. use serde::{Deserialize, Serialize};
  47. use super::super::cred;
  48. use super::super::dup_filter::SeenType;
  49. use super::super::scalar_u32;
  50. use super::super::{BridgeAuth, IssuerPubKey};
  51. use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
  52. #[derive(Serialize, Deserialize)]
  53. pub struct Request {
  54. // Fields for blind showing the Lox credential
  55. P: RistrettoPoint,
  56. id: Scalar,
  57. CBucket: RistrettoPoint,
  58. CLevel: RistrettoPoint,
  59. CSince: RistrettoPoint,
  60. CInvRemain: RistrettoPoint,
  61. CBlockages: RistrettoPoint,
  62. CQ: RistrettoPoint,
  63. // Fields for blind showing the Bucket Reachability credential
  64. P_reach: RistrettoPoint,
  65. CBucket_reach: RistrettoPoint,
  66. CQ_reach: RistrettoPoint,
  67. // Fields for user blinding of the Lox credential to be issued
  68. D: RistrettoPoint,
  69. EncIdClient: (RistrettoPoint, RistrettoPoint),
  70. EncBucket: (RistrettoPoint, RistrettoPoint),
  71. EncLevel: (RistrettoPoint, RistrettoPoint),
  72. EncSince: (RistrettoPoint, RistrettoPoint),
  73. EncInvRemain: (RistrettoPoint, RistrettoPoint),
  74. EncBlockages: (RistrettoPoint, RistrettoPoint),
  75. // Fields for user blinding of the Inivtation credential to be
  76. // issued
  77. EncInvIdClient: (RistrettoPoint, RistrettoPoint),
  78. // The bucket and blockages attributes in the Invitation credential
  79. // issuing protocol can just reuse the exact encryptions as for the
  80. // Lox credential issuing protocol above.
  81. // The combined ZKP
  82. piUser: CompactProof,
  83. }
  84. #[derive(Debug)]
  85. pub struct State {
  86. d: Scalar,
  87. D: RistrettoPoint,
  88. EncIdClient: (RistrettoPoint, RistrettoPoint),
  89. EncBucket: (RistrettoPoint, RistrettoPoint),
  90. EncLevel: (RistrettoPoint, RistrettoPoint),
  91. EncSince: (RistrettoPoint, RistrettoPoint),
  92. EncInvRemain: (RistrettoPoint, RistrettoPoint),
  93. EncBlockages: (RistrettoPoint, RistrettoPoint),
  94. EncInvIdClient: (RistrettoPoint, RistrettoPoint),
  95. id_client: Scalar,
  96. bucket: Scalar,
  97. level: Scalar,
  98. since: Scalar,
  99. invremain: Scalar,
  100. blockages: Scalar,
  101. inv_id_client: Scalar,
  102. }
  103. #[derive(Serialize, Deserialize)]
  104. pub struct Response {
  105. // The fields for the new Lox credential; the new invites_remaining
  106. // is one less than the old value, so we don't have to include it
  107. // here explicitly
  108. P: RistrettoPoint,
  109. EncQ: (RistrettoPoint, RistrettoPoint),
  110. id_server: Scalar,
  111. TId: RistrettoPoint,
  112. TBucket: RistrettoPoint,
  113. TLevel: RistrettoPoint,
  114. TSince: RistrettoPoint,
  115. TInvRemain: RistrettoPoint,
  116. TBlockages: RistrettoPoint,
  117. // The fields for the new Invitation credential
  118. P_inv: RistrettoPoint,
  119. EncQ_inv: (RistrettoPoint, RistrettoPoint),
  120. inv_id_server: Scalar,
  121. TId_inv: RistrettoPoint,
  122. date_inv: Scalar,
  123. TBucket_inv: RistrettoPoint,
  124. TBlockages_inv: RistrettoPoint,
  125. // The ZKP
  126. piBlindIssue: CompactProof,
  127. }
  128. define_proof! {
  129. requestproof,
  130. "Issue Invite Request",
  131. (bucket, level, since, invremain, blockages, zbucket, zlevel,
  132. zsince, zinvremain, zblockages, negzQ,
  133. zbucket_reach, negzQ_reach,
  134. d, eid_client, ebucket, elevel, esince, einvremain, eblockages, id_client,
  135. inv_id_client, einv_id_client,
  136. invremain_inverse, zinvremain_inverse),
  137. (P, CBucket, CLevel, CSince, CInvRemain, CBlockages, V, Xbucket,
  138. Xlevel, Xsince, Xinvremain, Xblockages,
  139. P_reach, CBucket_reach, V_reach, Xbucket_reach,
  140. D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1,
  141. EncLevel0, EncLevel1, EncSince0, EncSince1,
  142. EncInvRemain0, EncInvRemain1_plus_B, EncBlockages0, EncBlockages1,
  143. EncInvIdClient0, EncInvIdClient1),
  144. (A, B):
  145. // Blind showing of the Lox credential
  146. CBucket = (bucket*P + zbucket*A),
  147. CLevel = (level*P + zlevel*A),
  148. CSince = (since*P + zsince*A),
  149. CInvRemain = (invremain*P + zinvremain*A),
  150. CBlockages = (blockages*P + zblockages*A),
  151. // Proof that invremain is not 0
  152. P = (invremain_inverse*CInvRemain + zinvremain_inverse*A),
  153. // Blind showing of the Bucket Reachability credential; note the
  154. // same bucket is used in the proof
  155. CBucket_reach = (bucket*P_reach + zbucket_reach*A),
  156. // User blinding of the Lox credential to be issued
  157. D = (d*B),
  158. EncIdClient0 = (eid_client*B),
  159. EncIdClient1 = (id_client*B + eid_client*D),
  160. EncBucket0 = (ebucket*B),
  161. EncBucket1 = (bucket*B + ebucket*D),
  162. EncLevel0 = (elevel*B),
  163. EncLevel1 = (level*B + elevel*D),
  164. EncSince0 = (esince*B),
  165. EncSince1 = (since*B + esince*D),
  166. EncInvRemain0 = (einvremain*B),
  167. EncInvRemain1_plus_B = (invremain*B + einvremain*D),
  168. EncBlockages0 = (eblockages*B),
  169. EncBlockages1 = (blockages*B + eblockages*D),
  170. // User blinding of the Invitation to be issued
  171. EncInvIdClient0 = (einv_id_client*B),
  172. EncInvIdClient1 = (inv_id_client*B + einv_id_client*D)
  173. }
  174. define_proof! {
  175. blindissue,
  176. "Issue Invite Issuing",
  177. (x0, x0tilde, xid, xbucket, xlevel, xsince, xinvremain, xblockages,
  178. s, b, tid, tbucket, tlevel, tsince, tinvremain, tblockages,
  179. x0_inv, x0tilde_inv, xid_inv, xdate_inv, xbucket_inv,
  180. xblockages_inv,
  181. s_inv, b_inv, tid_inv, tbucket_inv, tblockages_inv),
  182. (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xlevel, Xsince, Xinvremain,
  183. Xblockages, TId, TBucket, TLevel, TSince, TInvRemain, TBlockages,
  184. P_inv, EncQ_inv0, EncQ_inv1, X0_inv, Xid_inv, Xdate_inv,
  185. Xbucket_inv, Xblockages_inv, Pdate_inv, TId_inv, TBucket_inv,
  186. TBlockages_inv,
  187. D, EncId0, EncId1, EncBucket0, EncBucket1, EncLevel0, EncLevel1,
  188. EncSince0, EncSince1, EncInvRemain0, EncInvRemain1,
  189. EncBlockages0, EncBlockages1,
  190. EncInvId0, EncInvId1),
  191. (A, B):
  192. Xid = (xid*A),
  193. Xbucket = (xbucket*A),
  194. Xlevel = (xlevel*A),
  195. Xsince = (xsince*A),
  196. Xinvremain = (xinvremain*A),
  197. Xblockages = (xblockages*A),
  198. X0 = (x0*B + x0tilde*A),
  199. P = (b*B),
  200. TId = (b*Xid),
  201. TId = (tid*A),
  202. TBucket = (b*Xbucket),
  203. TBucket = (tbucket*A),
  204. TLevel = (b*Xlevel),
  205. TLevel = (tlevel*A),
  206. TSince = (b*Xsince),
  207. TSince = (tsince*A),
  208. TInvRemain = (b*Xinvremain),
  209. TInvRemain = (tinvremain*A),
  210. TBlockages = (b*Xblockages),
  211. TBlockages = (tblockages*A),
  212. EncQ0 = (s*B + tid*EncId0 + tbucket*EncBucket0 + tlevel*EncLevel0
  213. + tsince*EncSince0 + tinvremain*EncInvRemain0 + tblockages*EncBlockages0),
  214. EncQ1 = (s*D + tid*EncId1 + tbucket*EncBucket1 + tlevel*EncLevel1
  215. + tsince*EncSince1 + tinvremain*EncInvRemain1 + tblockages*EncBlockages1
  216. + x0*P),
  217. Xid_inv = (xid_inv*A),
  218. Xdate_inv = (xdate_inv*A),
  219. Xbucket_inv = (xbucket_inv*A),
  220. Xblockages_inv = (xblockages_inv*A),
  221. X0_inv = (x0_inv*B + x0tilde_inv*A),
  222. P_inv = (b_inv*B),
  223. TId_inv = (b_inv*Xid_inv),
  224. TId_inv = (tid_inv*A),
  225. TBucket_inv = (b_inv*Xbucket_inv),
  226. TBucket_inv = (tbucket_inv*A),
  227. TBlockages_inv = (b_inv*Xblockages_inv),
  228. TBlockages_inv = (tblockages_inv*A),
  229. EncQ_inv0 = (s_inv*B + tid_inv*EncInvId0 + tbucket_inv*EncBucket0
  230. + tblockages_inv*EncBlockages0),
  231. EncQ_inv1 = (s_inv*D + tid_inv*EncInvId1 + tbucket_inv*EncBucket1
  232. + tblockages_inv*EncBlockages1 + x0_inv*P_inv + xdate_inv*Pdate_inv)
  233. }
  234. pub fn request(
  235. lox_cred: &cred::Lox,
  236. reach_cred: &cred::BucketReachability,
  237. lox_pub: &IssuerPubKey,
  238. reach_pub: &IssuerPubKey,
  239. today: u32,
  240. ) -> Result<(Request, State), ProofError> {
  241. let A: &RistrettoPoint = &CMZ_A;
  242. let B: &RistrettoPoint = &CMZ_B;
  243. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  244. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  245. // Ensure the credential can be correctly shown: it must be the case
  246. // that invites_remaining not be 0
  247. if lox_cred.invites_remaining == Scalar::zero() {
  248. return Err(ProofError::VerificationFailure);
  249. }
  250. // The buckets in the Lox and Bucket Reachability credentials have
  251. // to match
  252. if lox_cred.bucket != reach_cred.bucket {
  253. return Err(ProofError::VerificationFailure);
  254. }
  255. // The Bucket Reachability credential has to be dated today
  256. let reach_date: u32 = match scalar_u32(&reach_cred.date) {
  257. Some(v) => v,
  258. None => return Err(ProofError::VerificationFailure),
  259. };
  260. if reach_date != today {
  261. return Err(ProofError::VerificationFailure);
  262. }
  263. // The new invites_remaining
  264. let new_invites_remaining = lox_cred.invites_remaining - Scalar::one();
  265. // Blind showing the Lox credential
  266. // Reblind P and Q
  267. let mut rng = rand::thread_rng();
  268. let t = Scalar::random(&mut rng);
  269. let P = t * lox_cred.P;
  270. let Q = t * lox_cred.Q;
  271. // Form Pedersen commitments to the blinded attributes
  272. let zbucket = Scalar::random(&mut rng);
  273. let zlevel = Scalar::random(&mut rng);
  274. let zsince = Scalar::random(&mut rng);
  275. let zinvremain = Scalar::random(&mut rng);
  276. let zblockages = Scalar::random(&mut rng);
  277. let CBucket = lox_cred.bucket * P + &zbucket * Atable;
  278. let CLevel = lox_cred.trust_level * P + &zlevel * Atable;
  279. let CSince = lox_cred.level_since * P + &zsince * Atable;
  280. let CInvRemain = lox_cred.invites_remaining * P + &zinvremain * Atable;
  281. let CBlockages = lox_cred.blockages * P + &zblockages * Atable;
  282. // Form a Pedersen commitment to the MAC Q
  283. // We flip the sign of zQ from that of the Hyphae paper so that
  284. // the ZKP has a "+" instead of a "-", as that's what the zkp
  285. // macro supports.
  286. let negzQ = Scalar::random(&mut rng);
  287. let CQ = Q - &negzQ * Atable;
  288. // Compute the "error factor"
  289. let V = zbucket * lox_pub.X[2]
  290. + zlevel * lox_pub.X[3]
  291. + zsince * lox_pub.X[4]
  292. + zinvremain * lox_pub.X[5]
  293. + zblockages * lox_pub.X[6]
  294. + &negzQ * Atable;
  295. // Blind showing the Bucket Reachability credential
  296. // Reblind P and Q
  297. let t_reach = Scalar::random(&mut rng);
  298. let P_reach = t_reach * reach_cred.P;
  299. let Q_reach = t_reach * reach_cred.Q;
  300. // Form Pedersen commitments to the blinded attributes
  301. let zbucket_reach = Scalar::random(&mut rng);
  302. let CBucket_reach = reach_cred.bucket * P_reach + &zbucket_reach * Atable;
  303. // Form a Pedersen commitment to the MAC Q
  304. // We flip the sign of zQ from that of the Hyphae paper so that
  305. // the ZKP has a "+" instead of a "-", as that's what the zkp
  306. // macro supports.
  307. let negzQ_reach = Scalar::random(&mut rng);
  308. let CQ_reach = Q_reach - &negzQ_reach * Atable;
  309. // Compute the "error factor"
  310. let V_reach = zbucket_reach * reach_pub.X[2] + &negzQ_reach * Atable;
  311. // User blinding for the Lox certificate to be issued
  312. // Pick an ElGamal keypair
  313. let d = Scalar::random(&mut rng);
  314. let D = &d * Btable;
  315. // Pick a random client component of the id
  316. let id_client = Scalar::random(&mut rng);
  317. // Encrypt it (times the basepoint B) to the ElGamal public key D we
  318. // just created
  319. let eid_client = Scalar::random(&mut rng);
  320. let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
  321. // Encrypt the other blinded fields (times B) to D as well
  322. let ebucket = Scalar::random(&mut rng);
  323. let EncBucket = (&ebucket * Btable, &lox_cred.bucket * Btable + ebucket * D);
  324. let elevel = Scalar::random(&mut rng);
  325. let EncLevel = (
  326. &elevel * Btable,
  327. &lox_cred.trust_level * Btable + elevel * D,
  328. );
  329. let esince = Scalar::random(&mut rng);
  330. let EncSince = (
  331. &esince * Btable,
  332. &lox_cred.level_since * Btable + esince * D,
  333. );
  334. let einvremain = Scalar::random(&mut rng);
  335. let EncInvRemain = (
  336. &einvremain * Btable,
  337. &new_invites_remaining * Btable + einvremain * D,
  338. );
  339. let eblockages = Scalar::random(&mut rng);
  340. let EncBlockages = (
  341. &eblockages * Btable,
  342. &lox_cred.blockages * Btable + eblockages * D,
  343. );
  344. // User blinding for the Invitation certificate to be issued
  345. // Pick a random client component of the id
  346. let inv_id_client = Scalar::random(&mut rng);
  347. // Encrypt it (times the basepoint B) to the ElGamal public key D we
  348. // just created
  349. let einv_id_client = Scalar::random(&mut rng);
  350. let EncInvIdClient = (
  351. &einv_id_client * Btable,
  352. &inv_id_client * Btable + einv_id_client * D,
  353. );
  354. // The proof that invites_remaining is not zero. We prove this by
  355. // demonstrating that we know its inverse.
  356. let invremain_inverse = &lox_cred.invites_remaining.invert();
  357. let zinvremain_inverse = -zinvremain * invremain_inverse;
  358. // So now invremain_inverse * CInvRemain + zinvremain_inverse * A = P
  359. // Construct the proof
  360. let mut transcript = Transcript::new(b"issue invite request");
  361. let piUser = requestproof::prove_compact(
  362. &mut transcript,
  363. requestproof::ProveAssignments {
  364. A,
  365. B,
  366. P: &P,
  367. CBucket: &CBucket,
  368. CLevel: &CLevel,
  369. CSince: &CSince,
  370. CInvRemain: &CInvRemain,
  371. CBlockages: &CBlockages,
  372. V: &V,
  373. Xbucket: &lox_pub.X[2],
  374. Xlevel: &lox_pub.X[3],
  375. Xsince: &lox_pub.X[4],
  376. Xinvremain: &lox_pub.X[5],
  377. Xblockages: &lox_pub.X[6],
  378. P_reach: &P_reach,
  379. CBucket_reach: &CBucket_reach,
  380. V_reach: &V_reach,
  381. Xbucket_reach: &reach_pub.X[2],
  382. D: &D,
  383. EncIdClient0: &EncIdClient.0,
  384. EncIdClient1: &EncIdClient.1,
  385. EncBucket0: &EncBucket.0,
  386. EncBucket1: &EncBucket.1,
  387. EncLevel0: &EncLevel.0,
  388. EncLevel1: &EncLevel.1,
  389. EncSince0: &EncSince.0,
  390. EncSince1: &EncSince.1,
  391. EncInvRemain0: &EncInvRemain.0,
  392. EncInvRemain1_plus_B: &(EncInvRemain.1 + B),
  393. EncBlockages0: &EncBlockages.0,
  394. EncBlockages1: &EncBlockages.1,
  395. EncInvIdClient0: &EncInvIdClient.0,
  396. EncInvIdClient1: &EncInvIdClient.1,
  397. bucket: &lox_cred.bucket,
  398. level: &lox_cred.trust_level,
  399. since: &lox_cred.level_since,
  400. invremain: &lox_cred.invites_remaining,
  401. blockages: &lox_cred.blockages,
  402. zbucket: &zbucket,
  403. zlevel: &zlevel,
  404. zsince: &zsince,
  405. zinvremain: &zinvremain,
  406. zblockages: &zblockages,
  407. negzQ: &negzQ,
  408. zbucket_reach: &zbucket_reach,
  409. negzQ_reach: &negzQ_reach,
  410. d: &d,
  411. eid_client: &eid_client,
  412. ebucket: &ebucket,
  413. elevel: &elevel,
  414. esince: &esince,
  415. einvremain: &einvremain,
  416. eblockages: &eblockages,
  417. id_client: &id_client,
  418. inv_id_client: &inv_id_client,
  419. einv_id_client: &einv_id_client,
  420. invremain_inverse,
  421. zinvremain_inverse: &zinvremain_inverse,
  422. },
  423. )
  424. .0;
  425. Ok((
  426. Request {
  427. P,
  428. id: lox_cred.id,
  429. CBucket,
  430. CLevel,
  431. CSince,
  432. CInvRemain,
  433. CBlockages,
  434. CQ,
  435. P_reach,
  436. CBucket_reach,
  437. CQ_reach,
  438. D,
  439. EncIdClient,
  440. EncBucket,
  441. EncLevel,
  442. EncSince,
  443. EncInvRemain,
  444. EncBlockages,
  445. EncInvIdClient,
  446. piUser,
  447. },
  448. State {
  449. d,
  450. D,
  451. EncIdClient,
  452. EncBucket,
  453. EncLevel,
  454. EncSince,
  455. EncInvRemain,
  456. EncBlockages,
  457. EncInvIdClient,
  458. id_client,
  459. bucket: lox_cred.bucket,
  460. level: lox_cred.trust_level,
  461. since: lox_cred.level_since,
  462. invremain: new_invites_remaining,
  463. blockages: lox_cred.blockages,
  464. inv_id_client,
  465. },
  466. ))
  467. }
  468. impl BridgeAuth {
  469. /// Receive an issue invite request
  470. pub fn handle_issue_invite(&mut self, req: Request) -> Result<Response, ProofError> {
  471. let A: &RistrettoPoint = &CMZ_A;
  472. let B: &RistrettoPoint = &CMZ_B;
  473. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  474. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  475. if req.P.is_identity() || req.P_reach.is_identity() {
  476. return Err(ProofError::VerificationFailure);
  477. }
  478. let today: Scalar = self.today().into();
  479. // Recompute the "error factors" using knowledge of our own
  480. // (the issuer's) private key instead of knowledge of the
  481. // hidden attributes
  482. let Vprime = (self.lox_priv.x[0] + self.lox_priv.x[1] * req.id) * req.P
  483. + self.lox_priv.x[2] * req.CBucket
  484. + self.lox_priv.x[3] * req.CLevel
  485. + self.lox_priv.x[4] * req.CSince
  486. + self.lox_priv.x[5] * req.CInvRemain
  487. + self.lox_priv.x[6] * req.CBlockages
  488. - req.CQ;
  489. let Vprime_reach = (self.reachability_priv.x[0] + self.reachability_priv.x[1] * today)
  490. * req.P_reach
  491. + self.reachability_priv.x[2] * req.CBucket_reach
  492. - req.CQ_reach;
  493. // Verify the ZKP
  494. let mut transcript = Transcript::new(b"issue invite request");
  495. requestproof::verify_compact(
  496. &req.piUser,
  497. &mut transcript,
  498. requestproof::VerifyAssignments {
  499. A: &A.compress(),
  500. B: &B.compress(),
  501. P: &req.P.compress(),
  502. CBucket: &req.CBucket.compress(),
  503. CLevel: &req.CLevel.compress(),
  504. CSince: &req.CSince.compress(),
  505. CInvRemain: &req.CInvRemain.compress(),
  506. CBlockages: &req.CBlockages.compress(),
  507. V: &Vprime.compress(),
  508. Xbucket: &self.lox_pub.X[2].compress(),
  509. Xlevel: &self.lox_pub.X[3].compress(),
  510. Xsince: &self.lox_pub.X[4].compress(),
  511. Xinvremain: &self.lox_pub.X[5].compress(),
  512. Xblockages: &self.lox_pub.X[6].compress(),
  513. P_reach: &req.P_reach.compress(),
  514. CBucket_reach: &req.CBucket_reach.compress(),
  515. V_reach: &Vprime_reach.compress(),
  516. Xbucket_reach: &self.reachability_pub.X[2].compress(),
  517. D: &req.D.compress(),
  518. EncIdClient0: &req.EncIdClient.0.compress(),
  519. EncIdClient1: &req.EncIdClient.1.compress(),
  520. EncBucket0: &req.EncBucket.0.compress(),
  521. EncBucket1: &req.EncBucket.1.compress(),
  522. EncLevel0: &req.EncLevel.0.compress(),
  523. EncLevel1: &req.EncLevel.1.compress(),
  524. EncSince0: &req.EncSince.0.compress(),
  525. EncSince1: &req.EncSince.1.compress(),
  526. EncInvRemain0: &req.EncInvRemain.0.compress(),
  527. EncInvRemain1_plus_B: &(req.EncInvRemain.1 + B).compress(),
  528. EncBlockages0: &req.EncBlockages.0.compress(),
  529. EncBlockages1: &req.EncBlockages.1.compress(),
  530. EncInvIdClient0: &req.EncInvIdClient.0.compress(),
  531. EncInvIdClient1: &req.EncInvIdClient.1.compress(),
  532. },
  533. )?;
  534. // Ensure the id has not been seen before, and add it to the
  535. // seen list.
  536. if self.id_filter.filter(&req.id) == SeenType::Seen {
  537. return Err(ProofError::VerificationFailure);
  538. }
  539. // Blind issuing of the new Lox credential
  540. // Choose a random server id component to add to the client's
  541. // (blinded) id component
  542. let mut rng = rand::thread_rng();
  543. let id_server = Scalar::random(&mut rng);
  544. let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable);
  545. // Compute the MAC on the visible attributes (none here)
  546. let b = Scalar::random(&mut rng);
  547. let P = &b * Btable;
  548. let QHc = self.lox_priv.x[0] * P;
  549. // El Gamal encrypt it to the public key req.D
  550. let s = Scalar::random(&mut rng);
  551. let EncQHc = (&s * Btable, QHc + s * req.D);
  552. // Homomorphically compute the part of the MAC corresponding to
  553. // the blinded attributes
  554. let tid = self.lox_priv.x[1] * b;
  555. let TId = &tid * Atable;
  556. let EncQId = (tid * EncId.0, tid * EncId.1);
  557. let tbucket = self.lox_priv.x[2] * b;
  558. let TBucket = &tbucket * Atable;
  559. let EncQBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
  560. let tlevel = self.lox_priv.x[3] * b;
  561. let TLevel = &tlevel * Atable;
  562. let EncQLevel = (tlevel * req.EncLevel.0, tlevel * req.EncLevel.1);
  563. let tsince = self.lox_priv.x[4] * b;
  564. let TSince = &tsince * Atable;
  565. let EncQSince = (tsince * req.EncSince.0, tsince * req.EncSince.1);
  566. let tinvremain = self.lox_priv.x[5] * b;
  567. let TInvRemain = &tinvremain * Atable;
  568. let EncQInvRemain = (
  569. tinvremain * req.EncInvRemain.0,
  570. tinvremain * req.EncInvRemain.1,
  571. );
  572. let tblockages = self.lox_priv.x[6] * b;
  573. let TBlockages = &tblockages * Atable;
  574. let EncQBlockages = (
  575. tblockages * req.EncBlockages.0,
  576. tblockages * req.EncBlockages.1,
  577. );
  578. let EncQ = (
  579. EncQHc.0
  580. + EncQId.0
  581. + EncQBucket.0
  582. + EncQLevel.0
  583. + EncQSince.0
  584. + EncQInvRemain.0
  585. + EncQBlockages.0,
  586. EncQHc.1
  587. + EncQId.1
  588. + EncQBucket.1
  589. + EncQLevel.1
  590. + EncQSince.1
  591. + EncQInvRemain.1
  592. + EncQBlockages.1,
  593. );
  594. // Blind issuing of the new Invitation credential
  595. // Choose a random server id component to add to the client's
  596. // (blinded) id component
  597. let inv_id_server = Scalar::random(&mut rng);
  598. let EncInvId = (
  599. req.EncInvIdClient.0,
  600. req.EncInvIdClient.1 + &inv_id_server * Btable,
  601. );
  602. // Compute the MAC on the visible attributes
  603. let b_inv = Scalar::random(&mut rng);
  604. let P_inv = &b_inv * Btable;
  605. let QHc_inv = (self.invitation_priv.x[0] + self.invitation_priv.x[2] * today) * P_inv;
  606. // El Gamal encrypt it to the public key req.D
  607. let s_inv = Scalar::random(&mut rng);
  608. let EncQHc_inv = (&s_inv * Btable, QHc_inv + s_inv * req.D);
  609. // Homomorphically compute the part of the MAC corresponding to
  610. // the blinded attributes
  611. let tinvid = self.invitation_priv.x[1] * b_inv;
  612. let TId_inv = &tinvid * Atable;
  613. let EncQInvId = (tinvid * EncInvId.0, tinvid * EncInvId.1);
  614. let tinvbucket = self.invitation_priv.x[3] * b_inv;
  615. let TBucket_inv = &tinvbucket * Atable;
  616. // The bucket and blockages encrypted attributes are reused from
  617. // the Lox credential
  618. let EncQInvBucket = (tinvbucket * req.EncBucket.0, tinvbucket * req.EncBucket.1);
  619. let tinvblockages = self.invitation_priv.x[4] * b_inv;
  620. let TBlockages_inv = &tinvblockages * Atable;
  621. let EncQInvBlockages = (
  622. tinvblockages * req.EncBlockages.0,
  623. tinvblockages * req.EncBlockages.1,
  624. );
  625. let EncQ_inv = (
  626. EncQHc_inv.0 + EncQInvId.0 + EncQInvBucket.0 + EncQInvBlockages.0,
  627. EncQHc_inv.1 + EncQInvId.1 + EncQInvBucket.1 + EncQInvBlockages.1,
  628. );
  629. let mut transcript = Transcript::new(b"issue invite issuing");
  630. let piBlindIssue = blindissue::prove_compact(
  631. &mut transcript,
  632. blindissue::ProveAssignments {
  633. A,
  634. B,
  635. P: &P,
  636. EncQ0: &EncQ.0,
  637. EncQ1: &EncQ.1,
  638. X0: &self.lox_pub.X[0],
  639. Xid: &self.lox_pub.X[1],
  640. Xbucket: &self.lox_pub.X[2],
  641. Xlevel: &self.lox_pub.X[3],
  642. Xsince: &self.lox_pub.X[4],
  643. Xinvremain: &self.lox_pub.X[5],
  644. Xblockages: &self.lox_pub.X[6],
  645. TId: &TId,
  646. TBucket: &TBucket,
  647. TLevel: &TLevel,
  648. TSince: &TSince,
  649. TInvRemain: &TInvRemain,
  650. TBlockages: &TBlockages,
  651. P_inv: &P_inv,
  652. EncQ_inv0: &EncQ_inv.0,
  653. EncQ_inv1: &EncQ_inv.1,
  654. X0_inv: &self.invitation_pub.X[0],
  655. Xid_inv: &self.invitation_pub.X[1],
  656. Xdate_inv: &self.invitation_pub.X[2],
  657. Xbucket_inv: &self.invitation_pub.X[3],
  658. Xblockages_inv: &self.invitation_pub.X[4],
  659. Pdate_inv: &(today * P_inv),
  660. TId_inv: &TId_inv,
  661. TBucket_inv: &TBucket_inv,
  662. TBlockages_inv: &TBlockages_inv,
  663. D: &req.D,
  664. EncId0: &EncId.0,
  665. EncId1: &EncId.1,
  666. EncBucket0: &req.EncBucket.0,
  667. EncBucket1: &req.EncBucket.1,
  668. EncLevel0: &req.EncLevel.0,
  669. EncLevel1: &req.EncLevel.1,
  670. EncSince0: &req.EncSince.0,
  671. EncSince1: &req.EncSince.1,
  672. EncInvRemain0: &req.EncInvRemain.0,
  673. EncInvRemain1: &req.EncInvRemain.1,
  674. EncBlockages0: &req.EncBlockages.0,
  675. EncBlockages1: &req.EncBlockages.1,
  676. EncInvId0: &EncInvId.0,
  677. EncInvId1: &EncInvId.1,
  678. x0: &self.lox_priv.x[0],
  679. x0tilde: &self.lox_priv.x0tilde,
  680. xid: &self.lox_priv.x[1],
  681. xbucket: &self.lox_priv.x[2],
  682. xlevel: &self.lox_priv.x[3],
  683. xsince: &self.lox_priv.x[4],
  684. xinvremain: &self.lox_priv.x[5],
  685. xblockages: &self.lox_priv.x[6],
  686. s: &s,
  687. b: &b,
  688. tid: &tid,
  689. tbucket: &tbucket,
  690. tlevel: &tlevel,
  691. tsince: &tsince,
  692. tinvremain: &tinvremain,
  693. tblockages: &tblockages,
  694. x0_inv: &self.invitation_priv.x[0],
  695. x0tilde_inv: &self.invitation_priv.x0tilde,
  696. xid_inv: &self.invitation_priv.x[1],
  697. xdate_inv: &self.invitation_priv.x[2],
  698. xbucket_inv: &self.invitation_priv.x[3],
  699. xblockages_inv: &self.invitation_priv.x[4],
  700. s_inv: &s_inv,
  701. b_inv: &b_inv,
  702. tid_inv: &tinvid,
  703. tbucket_inv: &tinvbucket,
  704. tblockages_inv: &tinvblockages,
  705. },
  706. )
  707. .0;
  708. Ok(Response {
  709. P,
  710. EncQ,
  711. id_server,
  712. TId,
  713. TBucket,
  714. TLevel,
  715. TSince,
  716. TInvRemain,
  717. TBlockages,
  718. P_inv,
  719. EncQ_inv,
  720. inv_id_server,
  721. TId_inv,
  722. date_inv: today,
  723. TBucket_inv,
  724. TBlockages_inv,
  725. piBlindIssue,
  726. })
  727. }
  728. }
  729. /// Handle the response to the request, producing the new Lox credential
  730. /// and Invitation credential if successful.
  731. pub fn handle_response(
  732. state: State,
  733. resp: Response,
  734. lox_pub: &IssuerPubKey,
  735. invitation_pub: &IssuerPubKey,
  736. ) -> Result<(cred::Lox, cred::Invitation), ProofError> {
  737. let A: &RistrettoPoint = &CMZ_A;
  738. let B: &RistrettoPoint = &CMZ_B;
  739. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  740. if resp.P.is_identity() || resp.P_inv.is_identity() {
  741. return Err(ProofError::VerificationFailure);
  742. }
  743. // Add the server's contribution to the id to our own, both in plain
  744. // and encrypted form and for both the Lox credential id and the
  745. // Invitation credential id
  746. let id = state.id_client + resp.id_server;
  747. let EncId = (
  748. state.EncIdClient.0,
  749. state.EncIdClient.1 + &resp.id_server * Btable,
  750. );
  751. let inv_id = state.inv_id_client + resp.inv_id_server;
  752. let EncInvId = (
  753. state.EncInvIdClient.0,
  754. state.EncInvIdClient.1 + &resp.inv_id_server * Btable,
  755. );
  756. // Verify the proof
  757. let mut transcript = Transcript::new(b"issue invite issuing");
  758. blindissue::verify_compact(
  759. &resp.piBlindIssue,
  760. &mut transcript,
  761. blindissue::VerifyAssignments {
  762. A: &A.compress(),
  763. B: &B.compress(),
  764. P: &resp.P.compress(),
  765. EncQ0: &resp.EncQ.0.compress(),
  766. EncQ1: &resp.EncQ.1.compress(),
  767. X0: &lox_pub.X[0].compress(),
  768. Xid: &lox_pub.X[1].compress(),
  769. Xbucket: &lox_pub.X[2].compress(),
  770. Xlevel: &lox_pub.X[3].compress(),
  771. Xsince: &lox_pub.X[4].compress(),
  772. Xinvremain: &lox_pub.X[5].compress(),
  773. Xblockages: &lox_pub.X[6].compress(),
  774. TId: &resp.TId.compress(),
  775. TBucket: &resp.TBucket.compress(),
  776. TLevel: &resp.TLevel.compress(),
  777. TSince: &resp.TSince.compress(),
  778. TInvRemain: &resp.TInvRemain.compress(),
  779. TBlockages: &resp.TBlockages.compress(),
  780. P_inv: &resp.P_inv.compress(),
  781. EncQ_inv0: &resp.EncQ_inv.0.compress(),
  782. EncQ_inv1: &resp.EncQ_inv.1.compress(),
  783. X0_inv: &invitation_pub.X[0].compress(),
  784. Xid_inv: &invitation_pub.X[1].compress(),
  785. Xdate_inv: &invitation_pub.X[2].compress(),
  786. Xbucket_inv: &invitation_pub.X[3].compress(),
  787. Xblockages_inv: &invitation_pub.X[4].compress(),
  788. Pdate_inv: &(resp.date_inv * resp.P_inv).compress(),
  789. TId_inv: &resp.TId_inv.compress(),
  790. TBucket_inv: &resp.TBucket_inv.compress(),
  791. TBlockages_inv: &resp.TBlockages_inv.compress(),
  792. D: &state.D.compress(),
  793. EncId0: &EncId.0.compress(),
  794. EncId1: &EncId.1.compress(),
  795. EncBucket0: &state.EncBucket.0.compress(),
  796. EncBucket1: &state.EncBucket.1.compress(),
  797. EncLevel0: &state.EncLevel.0.compress(),
  798. EncLevel1: &state.EncLevel.1.compress(),
  799. EncSince0: &state.EncSince.0.compress(),
  800. EncSince1: &state.EncSince.1.compress(),
  801. EncInvRemain0: &state.EncInvRemain.0.compress(),
  802. EncInvRemain1: &state.EncInvRemain.1.compress(),
  803. EncBlockages0: &state.EncBlockages.0.compress(),
  804. EncBlockages1: &state.EncBlockages.1.compress(),
  805. EncInvId0: &EncInvId.0.compress(),
  806. EncInvId1: &EncInvId.1.compress(),
  807. },
  808. )?;
  809. // Decrypt EncQ and EncQ_inv
  810. let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
  811. let Q_inv = resp.EncQ_inv.1 - (state.d * resp.EncQ_inv.0);
  812. Ok((
  813. cred::Lox {
  814. P: resp.P,
  815. Q,
  816. id,
  817. bucket: state.bucket,
  818. trust_level: state.level,
  819. level_since: state.since,
  820. invites_remaining: state.invremain,
  821. blockages: state.blockages,
  822. },
  823. cred::Invitation {
  824. P: resp.P_inv,
  825. Q: Q_inv,
  826. inv_id,
  827. date: resp.date_inv,
  828. bucket: state.bucket,
  829. blockages: state.blockages,
  830. },
  831. ))
  832. }