redeem_invite.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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. 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. pub fn request(
  125. inv_cred: &cred::Invitation,
  126. invitation_pub: &IssuerPubKey,
  127. today: u32,
  128. ) -> Result<(Request, State), ProofError> {
  129. let A: &RistrettoPoint = &CMZ_A;
  130. let B: &RistrettoPoint = &CMZ_B;
  131. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  132. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  133. // Ensure the credential can be correctly shown: it must be the case
  134. // that date + INVITATION_EXPIRY >= today.
  135. let date: u32 = match scalar_u32(&inv_cred.date) {
  136. Some(v) => v,
  137. None => return Err(ProofError::VerificationFailure),
  138. };
  139. if date + INVITATION_EXPIRY < today {
  140. return Err(ProofError::VerificationFailure);
  141. }
  142. let diffdays = date + INVITATION_EXPIRY - today;
  143. // If diffdays > 15, then since INVITATION_EXPIRY <= 15, then date
  144. // must be in the future. Reject.
  145. if diffdays > 15 {
  146. return Err(ProofError::VerificationFailure);
  147. }
  148. // Blind showing the Invitation credential
  149. // Reblind P and Q
  150. let mut rng = rand::thread_rng();
  151. let t = Scalar::random(&mut rng);
  152. let P = t * inv_cred.P;
  153. let Q = t * inv_cred.Q;
  154. // Form Pedersen commitments to the blinded attributes
  155. let zdate = Scalar::random(&mut rng);
  156. let zbucket = Scalar::random(&mut rng);
  157. let zblockages = Scalar::random(&mut rng);
  158. let CDate = inv_cred.date * P + &zdate * Atable;
  159. let CBucket = inv_cred.bucket * P + &zbucket * Atable;
  160. let CBlockages = inv_cred.blockages * P + &zblockages * Atable;
  161. // Form a Pedersen commitment to the MAC Q
  162. // We flip the sign of zQ from that of the Hyphae paper so that
  163. // the ZKP has a "+" instead of a "-", as that's what the zkp
  164. // macro supports.
  165. let negzQ = Scalar::random(&mut rng);
  166. let CQ = Q - &negzQ * Atable;
  167. // Compute the "error factor"
  168. let V = zdate * invitation_pub.X[2]
  169. + zbucket * invitation_pub.X[3]
  170. + zblockages * invitation_pub.X[4]
  171. + &negzQ * Atable;
  172. // User blinding for the Lox certificate to be issued
  173. // Pick an ElGamal keypair
  174. let d = Scalar::random(&mut rng);
  175. let D = &d * Btable;
  176. // Pick a random client component of the id
  177. let id_client = Scalar::random(&mut rng);
  178. // Encrypt it (times the basepoint B) to the ElGamal public key D we
  179. // just created
  180. let eid_client = Scalar::random(&mut rng);
  181. let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
  182. // Encrypt the other blinded fields (times B) to D as well
  183. let ebucket = Scalar::random(&mut rng);
  184. let EncBucket = (&ebucket * Btable, &inv_cred.bucket * Btable + ebucket * D);
  185. let eblockages = Scalar::random(&mut rng);
  186. let EncBlockages = (
  187. &eblockages * Btable,
  188. &inv_cred.blockages * Btable + eblockages * D,
  189. );
  190. // The range proof that 0 <= diffdays <= 15
  191. // Extract the 4 bits from diffdays
  192. let g0: Scalar = (diffdays & 1).into();
  193. let g1: Scalar = ((diffdays >> 1) & 1).into();
  194. let g2: Scalar = ((diffdays >> 2) & 1).into();
  195. let g3: Scalar = ((diffdays >> 3) & 1).into();
  196. // Pick random factors for the Pedersen commitments
  197. let wg0 = Scalar::random(&mut rng);
  198. let zg1 = Scalar::random(&mut rng);
  199. let wg1 = Scalar::random(&mut rng);
  200. let zg2 = Scalar::random(&mut rng);
  201. let wg2 = Scalar::random(&mut rng);
  202. let zg3 = Scalar::random(&mut rng);
  203. let wg3 = Scalar::random(&mut rng);
  204. // Compute zg0 to cancel things out as
  205. // zg0 = -(zdate + 2*zg1 + 4*zg2 + 8*zg3)
  206. // but use Horner's method
  207. let zg0 = -(scalar_dbl(&(scalar_dbl(&(scalar_dbl(&zg3) + zg2)) + zg1)) + zdate);
  208. let yg0 = wg0 + g0 * zg0;
  209. let yg1 = wg1 + g1 * zg1;
  210. let yg2 = wg2 + g2 * zg2;
  211. let yg3 = wg3 + g3 * zg3;
  212. let CG0 = g0 * P + &zg0 * Atable;
  213. let CG1 = g1 * P + &zg1 * Atable;
  214. let CG2 = g2 * P + &zg2 * Atable;
  215. let CG3 = g3 * P + &zg3 * Atable;
  216. let CG0sq = g0 * P + &yg0 * Atable;
  217. let CG1sq = g1 * P + &yg1 * Atable;
  218. let CG2sq = g2 * P + &yg2 * Atable;
  219. let CG3sq = g3 * P + &yg3 * Atable;
  220. // Construct the proof
  221. let mut transcript = Transcript::new(b"redeem invite request");
  222. let piUser = requestproof::prove_compact(
  223. &mut transcript,
  224. requestproof::ProveAssignments {
  225. A: &A,
  226. B: &B,
  227. P: &P,
  228. CDate: &CDate,
  229. CBucket: &CBucket,
  230. CBlockages: &CBlockages,
  231. V: &V,
  232. Xdate: &invitation_pub.X[2],
  233. Xbucket: &invitation_pub.X[3],
  234. Xblockages: &invitation_pub.X[4],
  235. D: &D,
  236. EncIdClient0: &EncIdClient.0,
  237. EncIdClient1: &EncIdClient.1,
  238. EncBucket0: &EncBucket.0,
  239. EncBucket1: &EncBucket.1,
  240. EncBlockages0: &EncBlockages.0,
  241. EncBlockages1: &EncBlockages.1,
  242. CG0: &CG0,
  243. CG1: &CG1,
  244. CG2: &CG2,
  245. CG3: &CG3,
  246. CG0sq: &CG0sq,
  247. CG1sq: &CG1sq,
  248. CG2sq: &CG2sq,
  249. CG3sq: &CG3sq,
  250. date: &inv_cred.date,
  251. bucket: &inv_cred.bucket,
  252. blockages: &inv_cred.blockages,
  253. zdate: &zdate,
  254. zbucket: &zbucket,
  255. zblockages: &zblockages,
  256. negzQ: &negzQ,
  257. d: &d,
  258. eid_client: &eid_client,
  259. ebucket: &ebucket,
  260. eblockages: &eblockages,
  261. id_client: &id_client,
  262. g0: &g0,
  263. g1: &g1,
  264. g2: &g2,
  265. g3: &g3,
  266. zg0: &zg0,
  267. zg1: &zg1,
  268. zg2: &zg2,
  269. zg3: &zg3,
  270. wg0: &wg0,
  271. wg1: &wg1,
  272. wg2: &wg2,
  273. wg3: &wg3,
  274. yg0: &yg0,
  275. yg1: &yg1,
  276. yg2: &yg2,
  277. yg3: &yg3,
  278. },
  279. )
  280. .0;
  281. Ok((
  282. Request {
  283. P,
  284. id: inv_cred.inv_id,
  285. CDate,
  286. CBucket,
  287. CBlockages,
  288. CQ,
  289. D,
  290. EncIdClient,
  291. EncBucket,
  292. EncBlockages,
  293. CG1,
  294. CG2,
  295. CG3,
  296. CG0sq,
  297. CG1sq,
  298. CG2sq,
  299. CG3sq,
  300. piUser,
  301. },
  302. State {
  303. d,
  304. D,
  305. EncIdClient,
  306. EncBucket,
  307. EncBlockages,
  308. id_client,
  309. bucket: inv_cred.bucket,
  310. blockages: inv_cred.blockages,
  311. },
  312. ))
  313. }