open_invite.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*! A module for the protocol for the user to redeem an open invitation
  2. with the BA (bridge authority) to receive their initial Lox
  3. credential.
  4. The credential will have attributes:
  5. - id: jointly chosen by the user and BA
  6. - bucket: set by the BA
  7. - trust_level: 0
  8. - level_since: today
  9. - invites_remaining: 0
  10. - invites_issued: 0
  11. */
  12. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  13. use curve25519_dalek::ristretto::RistrettoPoint;
  14. use curve25519_dalek::scalar::Scalar;
  15. use curve25519_dalek::traits::IsIdentity;
  16. use zkp::CompactProof;
  17. use zkp::ProofError;
  18. use zkp::Transcript;
  19. use super::super::bridge_table;
  20. use super::super::cred;
  21. use super::super::dup_filter::SeenType;
  22. use super::super::OPENINV_LENGTH;
  23. use super::super::{BridgeAuth, BridgeDb, IssuerPubKey};
  24. use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
  25. /// The request message for this protocol
  26. pub struct Request {
  27. invite: [u8; OPENINV_LENGTH],
  28. D: RistrettoPoint,
  29. EncIdClient: (RistrettoPoint, RistrettoPoint),
  30. piUserBlinding: CompactProof,
  31. }
  32. #[derive(Debug)]
  33. /// The client state for this protocol
  34. pub struct State {
  35. d: Scalar,
  36. D: RistrettoPoint,
  37. EncIdClient: (RistrettoPoint, RistrettoPoint),
  38. id_client: Scalar,
  39. }
  40. /// The response message for this protocol
  41. pub struct Response {
  42. P: RistrettoPoint,
  43. EncQ: (RistrettoPoint, RistrettoPoint),
  44. id_server: Scalar,
  45. TId: RistrettoPoint,
  46. bucket: Scalar,
  47. level_since: Scalar,
  48. P_noopmigration: RistrettoPoint,
  49. EncQ_noopmigration: (RistrettoPoint, RistrettoPoint),
  50. TId_noopmigration: RistrettoPoint,
  51. piBlindIssue: CompactProof,
  52. }
  53. // The userblinding ZKP
  54. define_proof! {
  55. userblinding,
  56. "Open Invitation User Blinding",
  57. (d, eid_client, id_client),
  58. (EncIdClient0, EncIdClient1, D),
  59. (B) :
  60. EncIdClient0 = (eid_client*B),
  61. EncIdClient1 = (id_client*B + eid_client*D),
  62. D = (d*B)
  63. }
  64. // The issuing ZKP
  65. define_proof! {
  66. blindissue,
  67. "Open Invitation Blind Issuing",
  68. (x0, x0tilde, xid, xbucket, xsince, s, b, tid,
  69. x0_nm, x0tilde_nm, xid_nm, xfrom_nm, xto_nm, s_nm, b_nm, tid_nm),
  70. (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xsince, Pbucket, Psince, TId,
  71. P_nm, EncQ0_nm, EncQ1_nm, X0_nm, Xid_nm, Xfrom_nm, Xto_nm,
  72. Pbucket_nm, TId_nm,
  73. D, EncId0, EncId1),
  74. (A, B) :
  75. Xid = (xid*A),
  76. Xbucket = (xbucket*A),
  77. Xsince = (xsince*A),
  78. X0 = (x0*B + x0tilde*A),
  79. P = (b*B),
  80. TId = (b*Xid),
  81. TId = (tid*A),
  82. EncQ0 = (s*B + tid*EncId0),
  83. EncQ1 = (s*D + tid*EncId1 + x0*P + xbucket*Pbucket + xsince*Psince),
  84. Xid_nm = (xid_nm*A),
  85. Xfrom_nm = (xfrom_nm*A),
  86. Xto_nm = (xto_nm*A),
  87. X0_nm = (x0_nm*B + x0tilde_nm*A),
  88. P_nm = (b_nm*B),
  89. TId_nm = (b_nm*Xid_nm),
  90. TId_nm = (tid_nm*A),
  91. EncQ0_nm = (s_nm*B + tid_nm*EncId0),
  92. EncQ1_nm = (s_nm*D + tid_nm*EncId1 + x0_nm*P_nm + xfrom_nm*Pbucket_nm + xto_nm*Pbucket_nm)
  93. }
  94. /// Submit an open invitation issued by the BridgeDb to receive your
  95. /// first Lox credential
  96. pub fn request(invite: &[u8; OPENINV_LENGTH]) -> (Request, State) {
  97. let B: &RistrettoPoint = &CMZ_B;
  98. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  99. // Pick an ElGamal keypair
  100. let mut rng = rand::thread_rng();
  101. let d = Scalar::random(&mut rng);
  102. let D = &d * Btable;
  103. // Pick a random client component of the id
  104. let id_client = Scalar::random(&mut rng);
  105. // Encrypt it (times the basepoint B) to the ElGamal public key D we
  106. // just created
  107. let eid_client = Scalar::random(&mut rng);
  108. let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
  109. // Construct the proof of correct user blinding
  110. let mut transcript = Transcript::new(b"open invite user blinding");
  111. let piUserBlinding = userblinding::prove_compact(
  112. &mut transcript,
  113. userblinding::ProveAssignments {
  114. B: &B,
  115. EncIdClient0: &EncIdClient.0,
  116. EncIdClient1: &EncIdClient.1,
  117. D: &D,
  118. d: &d,
  119. eid_client: &eid_client,
  120. id_client: &id_client,
  121. },
  122. )
  123. .0;
  124. (
  125. Request {
  126. invite: *invite,
  127. D,
  128. EncIdClient,
  129. piUserBlinding,
  130. },
  131. State {
  132. d,
  133. D,
  134. EncIdClient,
  135. id_client,
  136. },
  137. )
  138. }
  139. impl BridgeAuth {
  140. /// Receive an open invitation issued by the BridgeDb and if it is
  141. /// valid and fresh, issue a Lox credential at trust level 0.
  142. pub fn handle_open_invite(&mut self, req: Request) -> Result<Response, ProofError> {
  143. // Check the signature on the open_invite. We manually match
  144. // here because we're changing the Err type from SignatureError
  145. // to ProofError
  146. let (invite_id, bucket_id_u32) = match BridgeDb::verify(req.invite, self.bridgedb_pub) {
  147. Ok(res) => res,
  148. Err(_) => return Err(ProofError::VerificationFailure),
  149. };
  150. let bucket_id: usize = bucket_id_u32 as usize;
  151. // Only proceed if the invite_id is fresh
  152. if self.openinv_filter.filter(&invite_id) == SeenType::Seen {
  153. return Err(ProofError::VerificationFailure);
  154. }
  155. // And also check that the bucket id is valid
  156. if bucket_id >= self.bridge_table.num_buckets() {
  157. return Err(ProofError::VerificationFailure);
  158. }
  159. let A: &RistrettoPoint = &CMZ_A;
  160. let B: &RistrettoPoint = &CMZ_B;
  161. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  162. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  163. // Next check the proof in the request
  164. let mut transcript = Transcript::new(b"open invite user blinding");
  165. userblinding::verify_compact(
  166. &req.piUserBlinding,
  167. &mut transcript,
  168. userblinding::VerifyAssignments {
  169. B: &B.compress(),
  170. EncIdClient0: &req.EncIdClient.0.compress(),
  171. EncIdClient1: &req.EncIdClient.1.compress(),
  172. D: &req.D.compress(),
  173. },
  174. )?;
  175. // Choose a random server id component to add to the client's
  176. // (blinded) id component
  177. let mut rng = rand::thread_rng();
  178. let id_server = Scalar::random(&mut rng);
  179. let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable);
  180. // Create the bucket attribute (Scalar), which is a combination
  181. // of the bucket id (u32) and the bucket's decryption key ([u8; 16])
  182. let bucket_key = self.bridge_table.keys[bucket_id];
  183. let bucket: Scalar = bridge_table::to_scalar(bucket_id_u32, &bucket_key);
  184. // Create the level_since attribute (Scalar), which is today's
  185. // Julian date
  186. let level_since: Scalar = self.today().into();
  187. // Compute the MAC on the visible attributes
  188. let b = Scalar::random(&mut rng);
  189. let P = &b * Btable;
  190. // trust_level = invites_remaining = invites_issued = 0
  191. let QHc =
  192. (self.lox_priv.x[0] + self.lox_priv.x[2] * bucket + self.lox_priv.x[4] * level_since)
  193. * P;
  194. // El Gamal encrypt it to the public key req.D
  195. let s = Scalar::random(&mut rng);
  196. let EncQHc = (&s * Btable, QHc + s * req.D);
  197. // Homomorphically compute the part of the MAC corresponding to
  198. // the blinded id attribute
  199. let tid = self.lox_priv.x[1] * b;
  200. let TId = &tid * Atable;
  201. let EncQId = (tid * EncId.0, tid * EncId.1);
  202. let EncQ = (EncQHc.0 + EncQId.0, EncQHc.1 + EncQId.1);
  203. // Now the no-op migration credential
  204. // Compute the MAC on the visible attributes
  205. let b_noopmigration = Scalar::random(&mut rng);
  206. let P_noopmigration = &b_noopmigration * Btable;
  207. let QHc_noopmigration = (self.migration_priv.x[0]
  208. + self.migration_priv.x[2] * bucket
  209. + self.migration_priv.x[3] * bucket)
  210. * P_noopmigration;
  211. // El Gamal encrypt it to the public key req.D
  212. let s_noopmigration = Scalar::random(&mut rng);
  213. let EncQHc_noopmigration = (
  214. &s_noopmigration * Btable,
  215. QHc_noopmigration + s_noopmigration * req.D,
  216. );
  217. // Homomorphically compute the part of the MAC corresponding to
  218. // the blinded id attribute
  219. let tid_noopmigration = self.migration_priv.x[1] * b_noopmigration;
  220. let TId_noopmigration = &tid_noopmigration * Atable;
  221. let EncQId_noopmigration = (tid_noopmigration * EncId.0, tid_noopmigration * EncId.1);
  222. let EncQ_noopmigration = (
  223. EncQHc_noopmigration.0 + EncQId_noopmigration.0,
  224. EncQHc_noopmigration.1 + EncQId_noopmigration.1,
  225. );
  226. let mut transcript = Transcript::new(b"open invite issuing");
  227. let piBlindIssue = blindissue::prove_compact(
  228. &mut transcript,
  229. blindissue::ProveAssignments {
  230. A: &A,
  231. B: &B,
  232. P: &P,
  233. EncQ0: &EncQ.0,
  234. EncQ1: &EncQ.1,
  235. X0: &self.lox_pub.X[0],
  236. Xid: &self.lox_pub.X[1],
  237. Xbucket: &self.lox_pub.X[2],
  238. Xsince: &self.lox_pub.X[4],
  239. Pbucket: &(bucket * P),
  240. Psince: &(level_since * P),
  241. TId: &TId,
  242. P_nm: &P_noopmigration,
  243. EncQ0_nm: &EncQ_noopmigration.0,
  244. EncQ1_nm: &EncQ_noopmigration.1,
  245. X0_nm: &self.migration_pub.X[0],
  246. Xid_nm: &self.migration_pub.X[1],
  247. Xfrom_nm: &self.migration_pub.X[2],
  248. Xto_nm: &self.migration_pub.X[3],
  249. Pbucket_nm: &(bucket * P_noopmigration),
  250. TId_nm: &TId_noopmigration,
  251. D: &req.D,
  252. EncId0: &EncId.0,
  253. EncId1: &EncId.1,
  254. x0: &self.lox_priv.x[0],
  255. x0tilde: &self.lox_priv.x0tilde,
  256. xid: &self.lox_priv.x[1],
  257. xbucket: &self.lox_priv.x[2],
  258. xsince: &self.lox_priv.x[4],
  259. s: &s,
  260. b: &b,
  261. tid: &tid,
  262. x0_nm: &self.migration_priv.x[0],
  263. x0tilde_nm: &self.migration_priv.x0tilde,
  264. xid_nm: &self.migration_priv.x[1],
  265. xfrom_nm: &self.migration_priv.x[2],
  266. xto_nm: &self.migration_priv.x[3],
  267. s_nm: &s_noopmigration,
  268. b_nm: &b_noopmigration,
  269. tid_nm: &tid_noopmigration,
  270. },
  271. )
  272. .0;
  273. Ok(Response {
  274. P,
  275. EncQ,
  276. id_server,
  277. TId,
  278. bucket,
  279. level_since,
  280. P_noopmigration,
  281. EncQ_noopmigration,
  282. TId_noopmigration,
  283. piBlindIssue,
  284. })
  285. }
  286. }
  287. /// Handle the reponse to the request, producing the desired Lox
  288. /// credential if successful.
  289. pub fn handle_response(
  290. state: State,
  291. resp: Response,
  292. lox_pub: &IssuerPubKey,
  293. migration_pub: &IssuerPubKey,
  294. ) -> Result<cred::Lox, ProofError> {
  295. let A: &RistrettoPoint = &CMZ_A;
  296. let B: &RistrettoPoint = &CMZ_B;
  297. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  298. if resp.P.is_identity() || resp.P_noopmigration.is_identity() {
  299. return Err(ProofError::VerificationFailure);
  300. }
  301. // Add the server's contribution to the id to our own, both in plain
  302. // and encrypted form
  303. let id = state.id_client + resp.id_server;
  304. let EncId = (
  305. state.EncIdClient.0,
  306. state.EncIdClient.1 + &resp.id_server * Btable,
  307. );
  308. // Verify the proof
  309. let mut transcript = Transcript::new(b"open invite issuing");
  310. blindissue::verify_compact(
  311. &resp.piBlindIssue,
  312. &mut transcript,
  313. blindissue::VerifyAssignments {
  314. A: &A.compress(),
  315. B: &B.compress(),
  316. P: &resp.P.compress(),
  317. EncQ0: &resp.EncQ.0.compress(),
  318. EncQ1: &resp.EncQ.1.compress(),
  319. X0: &lox_pub.X[0].compress(),
  320. Xid: &lox_pub.X[1].compress(),
  321. Xbucket: &lox_pub.X[2].compress(),
  322. Xsince: &lox_pub.X[4].compress(),
  323. Pbucket: &(resp.bucket * resp.P).compress(),
  324. Psince: &(resp.level_since * resp.P).compress(),
  325. TId: &resp.TId.compress(),
  326. P_nm: &resp.P_noopmigration.compress(),
  327. EncQ0_nm: &resp.EncQ_noopmigration.0.compress(),
  328. EncQ1_nm: &resp.EncQ_noopmigration.1.compress(),
  329. X0_nm: &migration_pub.X[0].compress(),
  330. Xid_nm: &migration_pub.X[1].compress(),
  331. Xfrom_nm: &migration_pub.X[2].compress(),
  332. Xto_nm: &migration_pub.X[3].compress(),
  333. Pbucket_nm: &(resp.bucket * resp.P_noopmigration).compress(),
  334. TId_nm: &resp.TId_noopmigration.compress(),
  335. D: &state.D.compress(),
  336. EncId0: &EncId.0.compress(),
  337. EncId1: &EncId.1.compress(),
  338. },
  339. )?;
  340. // Decrypt EncQ
  341. let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
  342. // Decrypt EncQ_noopmigration
  343. let Q_noopmigration = resp.EncQ_noopmigration.1 - (state.d * resp.EncQ_noopmigration.0);
  344. Ok(cred::Lox {
  345. P: resp.P,
  346. Q,
  347. id,
  348. bucket: resp.bucket,
  349. trust_level: Scalar::zero(),
  350. level_since: resp.level_since,
  351. invites_remaining: Scalar::zero(),
  352. invites_issued: Scalar::zero(),
  353. P_noopmigration: resp.P_noopmigration,
  354. Q_noopmigration,
  355. })
  356. }