open_invite.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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. - blockages: 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, BridgeLine}};
  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. piBlindIssue: CompactProof,
  49. bridge_line: BridgeLine,
  50. }
  51. // The userblinding ZKP
  52. define_proof! {
  53. userblinding,
  54. "Open Invitation User Blinding",
  55. (d, eid_client, id_client),
  56. (D, EncIdClient0, EncIdClient1),
  57. (B) :
  58. D = (d*B),
  59. EncIdClient0 = (eid_client*B),
  60. EncIdClient1 = (id_client*B + eid_client*D)
  61. }
  62. // The issuing ZKP
  63. define_proof! {
  64. blindissue,
  65. "Open Invitation Blind Issuing",
  66. (x0, x0tilde, xid, xbucket, xsince, s, b, tid),
  67. (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xsince, Pbucket, Psince, TId,
  68. D, EncId0, EncId1),
  69. (A, B) :
  70. Xid = (xid*A),
  71. Xbucket = (xbucket*A),
  72. Xsince = (xsince*A),
  73. X0 = (x0*B + x0tilde*A),
  74. P = (b*B),
  75. TId = (b*Xid),
  76. TId = (tid*A),
  77. EncQ0 = (s*B + tid*EncId0),
  78. EncQ1 = (s*D + tid*EncId1 + x0*P + xbucket*Pbucket + xsince*Psince)
  79. }
  80. /// Submit an open invitation issued by the BridgeDb to receive your
  81. /// first Lox credential
  82. pub fn request(invite: &[u8; OPENINV_LENGTH]) -> (Request, State) {
  83. let B: &RistrettoPoint = &CMZ_B;
  84. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  85. // Pick an ElGamal keypair
  86. let mut rng = rand::thread_rng();
  87. let d = Scalar::random(&mut rng);
  88. let D = &d * Btable;
  89. // Pick a random client component of the id
  90. let id_client = Scalar::random(&mut rng);
  91. // Encrypt it (times the basepoint B) to the ElGamal public key D we
  92. // just created
  93. let eid_client = Scalar::random(&mut rng);
  94. let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
  95. // Construct the proof of correct user blinding
  96. let mut transcript = Transcript::new(b"open invite user blinding");
  97. let piUserBlinding = userblinding::prove_compact(
  98. &mut transcript,
  99. userblinding::ProveAssignments {
  100. B: &B,
  101. D: &D,
  102. EncIdClient0: &EncIdClient.0,
  103. EncIdClient1: &EncIdClient.1,
  104. d: &d,
  105. eid_client: &eid_client,
  106. id_client: &id_client,
  107. },
  108. )
  109. .0;
  110. (
  111. Request {
  112. invite: *invite,
  113. D,
  114. EncIdClient,
  115. piUserBlinding,
  116. },
  117. State {
  118. d,
  119. D,
  120. EncIdClient,
  121. id_client,
  122. },
  123. )
  124. }
  125. impl BridgeAuth {
  126. /// Receive an open invitation issued by the BridgeDb and if it is
  127. /// valid and fresh, issue a Lox credential at trust level 0.
  128. pub fn handle_open_invite(&mut self, req: Request) -> Result<Response, ProofError> {
  129. // Check the signature on the open_invite. We manually match
  130. // here because we're changing the Err type from SignatureError
  131. // to ProofError
  132. let (invite_id, bucket_id_u32) = match BridgeDb::verify(req.invite, self.bridgedb_pub) {
  133. Ok(res) => res,
  134. Err(_) => return Err(ProofError::VerificationFailure),
  135. };
  136. let bucket_id: usize = bucket_id_u32 as usize;
  137. // Only proceed if the invite_id is fresh
  138. if self.openinv_filter.filter(&invite_id) == SeenType::Seen {
  139. return Err(ProofError::VerificationFailure);
  140. }
  141. // And also check that the bucket id is valid
  142. if bucket_id >= self.bridge_table.num_buckets() {
  143. return Err(ProofError::VerificationFailure);
  144. }
  145. let A: &RistrettoPoint = &CMZ_A;
  146. let B: &RistrettoPoint = &CMZ_B;
  147. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  148. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  149. // Next check the proof in the request
  150. let mut transcript = Transcript::new(b"open invite user blinding");
  151. userblinding::verify_compact(
  152. &req.piUserBlinding,
  153. &mut transcript,
  154. userblinding::VerifyAssignments {
  155. B: &B.compress(),
  156. EncIdClient0: &req.EncIdClient.0.compress(),
  157. EncIdClient1: &req.EncIdClient.1.compress(),
  158. D: &req.D.compress(),
  159. },
  160. )?;
  161. // Choose a random server id component to add to the client's
  162. // (blinded) id component
  163. let mut rng = rand::thread_rng();
  164. let id_server = Scalar::random(&mut rng);
  165. let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable);
  166. // Create the bucket attribute (Scalar), which is a combination
  167. // of the bucket id (u32) and the bucket's decryption key ([u8; 16])
  168. let bucket_key = self.bridge_table.keys[bucket_id];
  169. let bucket: Scalar = bridge_table::to_scalar(bucket_id_u32, &bucket_key);
  170. let pre_line = self.bridge_table.decrypt_bucket_id(bucket_id_u32, &bucket_key).unwrap().0;
  171. let bridge_line: BridgeLine = pre_line[0];
  172. // Create the level_since attribute (Scalar), which is today's
  173. // Julian date
  174. let level_since: Scalar = self.today().into();
  175. // Compute the MAC on the visible attributes
  176. let b = Scalar::random(&mut rng);
  177. let P = &b * Btable;
  178. // trust_level = invites_remaining = blockages = 0
  179. let QHc =
  180. (self.lox_priv.x[0] + self.lox_priv.x[2] * bucket + self.lox_priv.x[4] * level_since)
  181. * P;
  182. // El Gamal encrypt it to the public key req.D
  183. let s = Scalar::random(&mut rng);
  184. let EncQHc = (&s * Btable, QHc + s * req.D);
  185. // Homomorphically compute the part of the MAC corresponding to
  186. // the blinded id attribute
  187. let tid = self.lox_priv.x[1] * b;
  188. let TId = &tid * Atable;
  189. let EncQId = (tid * EncId.0, tid * EncId.1);
  190. let EncQ = (EncQHc.0 + EncQId.0, EncQHc.1 + EncQId.1);
  191. let mut transcript = Transcript::new(b"open invite issuing");
  192. let piBlindIssue = blindissue::prove_compact(
  193. &mut transcript,
  194. blindissue::ProveAssignments {
  195. A: &A,
  196. B: &B,
  197. P: &P,
  198. EncQ0: &EncQ.0,
  199. EncQ1: &EncQ.1,
  200. X0: &self.lox_pub.X[0],
  201. Xid: &self.lox_pub.X[1],
  202. Xbucket: &self.lox_pub.X[2],
  203. Xsince: &self.lox_pub.X[4],
  204. Pbucket: &(bucket * P),
  205. Psince: &(level_since * P),
  206. TId: &TId,
  207. D: &req.D,
  208. EncId0: &EncId.0,
  209. EncId1: &EncId.1,
  210. x0: &self.lox_priv.x[0],
  211. x0tilde: &self.lox_priv.x0tilde,
  212. xid: &self.lox_priv.x[1],
  213. xbucket: &self.lox_priv.x[2],
  214. xsince: &self.lox_priv.x[4],
  215. s: &s,
  216. b: &b,
  217. tid: &tid,
  218. },
  219. )
  220. .0;
  221. Ok(Response {
  222. P,
  223. EncQ,
  224. id_server,
  225. TId,
  226. bucket,
  227. level_since,
  228. piBlindIssue,
  229. bridge_line,
  230. })
  231. }
  232. }
  233. /// Handle the reponse to the request, producing the desired Lox
  234. /// credential if successful.
  235. pub fn handle_response(
  236. state: State,
  237. resp: Response,
  238. lox_pub: &IssuerPubKey,
  239. ) -> Result<(cred::Lox, bridge_table::BridgeLine), ProofError> {
  240. let A: &RistrettoPoint = &CMZ_A;
  241. let B: &RistrettoPoint = &CMZ_B;
  242. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  243. if resp.P.is_identity() {
  244. return Err(ProofError::VerificationFailure);
  245. }
  246. // Add the server's contribution to the id to our own, both in plain
  247. // and encrypted form
  248. let id = state.id_client + resp.id_server;
  249. let EncId = (
  250. state.EncIdClient.0,
  251. state.EncIdClient.1 + &resp.id_server * Btable,
  252. );
  253. // Verify the proof
  254. let mut transcript = Transcript::new(b"open invite issuing");
  255. blindissue::verify_compact(
  256. &resp.piBlindIssue,
  257. &mut transcript,
  258. blindissue::VerifyAssignments {
  259. A: &A.compress(),
  260. B: &B.compress(),
  261. P: &resp.P.compress(),
  262. EncQ0: &resp.EncQ.0.compress(),
  263. EncQ1: &resp.EncQ.1.compress(),
  264. X0: &lox_pub.X[0].compress(),
  265. Xid: &lox_pub.X[1].compress(),
  266. Xbucket: &lox_pub.X[2].compress(),
  267. Xsince: &lox_pub.X[4].compress(),
  268. Pbucket: &(resp.bucket * resp.P).compress(),
  269. Psince: &(resp.level_since * resp.P).compress(),
  270. TId: &resp.TId.compress(),
  271. D: &state.D.compress(),
  272. EncId0: &EncId.0.compress(),
  273. EncId1: &EncId.1.compress(),
  274. },
  275. )?;
  276. // Decrypt EncQ
  277. let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
  278. Ok((cred::Lox {
  279. P: resp.P,
  280. Q,
  281. id,
  282. bucket: resp.bucket,
  283. trust_level: Scalar::zero(),
  284. level_since: resp.level_since,
  285. invites_remaining: Scalar::zero(),
  286. blockages: Scalar::zero(),
  287. },
  288. resp.bridge_line,
  289. ))
  290. }