open_invite.rs 10 KB

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