open_invite.rs 10 KB

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