open_invite.rs 9.8 KB

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