check_blockage.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /*! A module for the protocol for the user to check for the availability
  2. of a migration credential they can use in order to move to a new bucket
  3. if theirs has been blocked.
  4. The user presents their current Lox credential:
  5. - id: revealed
  6. - bucket: blinded
  7. - trust_level: revealed to be 3 or above
  8. - level_since: blinded
  9. - invites_remaining: blinded
  10. - blockages: blinded
  11. They are allowed to to this as long as they are level 3 or above. If
  12. they have too many blockages (but are level 3 or above), they will be
  13. allowed to perform this migration, but will not be able to advance to
  14. level 3 in their new bucket, so this will be their last allowed
  15. migration without rejoining the system either with a new invitation or
  16. an open invitation.
  17. They will receive in return the encrypted MAC (Pk, EncQk) for their
  18. implicit Migration Key credential with attributes id and bucket,
  19. along with a HashMap of encrypted Migration credentials. For each
  20. (from_i, to_i) in the BA's migration list, there will be an entry in
  21. the HashMap with key H1(id, from_attr_i, Qk_i) and value
  22. Enc_{H2(id, from_attr_i, Qk_i)}(to_attr_i, P_i, Q_i). Here H1 and H2
  23. are the first 16 bytes and the second 16 bytes respectively of the
  24. SHA256 hash of the input, P_i and Q_i are a MAC on the Migration
  25. credential with attributes id, from_attr_i, and to_attr_i. Qk_i is the
  26. value EncQk would decrypt to if bucket were equal to from_attr_i. */
  27. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  28. use curve25519_dalek::ristretto::RistrettoPoint;
  29. use curve25519_dalek::scalar::Scalar;
  30. use curve25519_dalek::traits::IsIdentity;
  31. use zkp::CompactProof;
  32. use zkp::ProofError;
  33. use zkp::Transcript;
  34. use serde::{Deserialize, Serialize};
  35. use serde_with::serde_as;
  36. use std::collections::HashMap;
  37. use super::super::cred;
  38. use super::super::dup_filter::SeenType;
  39. use super::super::migration_table;
  40. use super::super::scalar_u32;
  41. use super::super::{BridgeAuth, IssuerPubKey};
  42. use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
  43. /// The minimum trust level a Lox credential must have to be allowed to
  44. /// perform this protocol.
  45. pub const MIN_TRUST_LEVEL: u32 = 3;
  46. #[derive(Serialize, Deserialize)]
  47. pub struct Request {
  48. // Fields for blind showing the Lox credential
  49. P: RistrettoPoint,
  50. id: Scalar,
  51. CBucket: RistrettoPoint,
  52. level: Scalar,
  53. CSince: RistrettoPoint,
  54. CInvRemain: RistrettoPoint,
  55. CBlockages: RistrettoPoint,
  56. CQ: RistrettoPoint,
  57. // Fields for user blinding of the Migration Key credential
  58. D: RistrettoPoint,
  59. EncBucket: (RistrettoPoint, RistrettoPoint),
  60. // The combined ZKP
  61. piUser: CompactProof,
  62. }
  63. #[derive(Debug)]
  64. pub struct State {
  65. d: Scalar,
  66. D: RistrettoPoint,
  67. EncBucket: (RistrettoPoint, RistrettoPoint),
  68. id: Scalar,
  69. bucket: Scalar,
  70. }
  71. #[serde_as]
  72. #[derive(Serialize, Deserialize, Debug)]
  73. pub struct Response {
  74. // The encrypted MAC for the Migration Key credential
  75. Pk: RistrettoPoint,
  76. EncQk: (RistrettoPoint, RistrettoPoint),
  77. // A table of encrypted Migration credentials; the encryption keys
  78. // are formed from the possible values of Qk (the decrypted form of
  79. // EncQk)
  80. #[serde_as(as = "Vec<(_,[_; migration_table::ENC_MIGRATION_BYTES])>")]
  81. enc_migration_table: HashMap<[u8; 16], [u8; migration_table::ENC_MIGRATION_BYTES]>,
  82. }
  83. define_proof! {
  84. requestproof,
  85. "Check Blockage Request",
  86. (bucket, since, invremain, blockages, zbucket, zsince, zinvremain,
  87. zblockages, negzQ,
  88. d, ebucket),
  89. (P, CBucket, CSince, CInvRemain, CBlockages, V, Xbucket, Xsince,
  90. Xinvremain, Xblockages,
  91. D, EncBucket0, EncBucket1),
  92. (A, B):
  93. // Blind showing of the Lox credential
  94. CBucket = (bucket*P + zbucket*A),
  95. CSince = (since*P + zsince*A),
  96. CInvRemain = (invremain*P + zinvremain*A),
  97. CBlockages = (blockages*P + zblockages*A),
  98. V = (zbucket*Xbucket + zsince*Xsince + zinvremain*Xinvremain
  99. + zblockages*Xblockages + negzQ*A),
  100. // User blinding of the Migration Key credential
  101. D = (d*B),
  102. EncBucket0 = (ebucket*B),
  103. EncBucket1 = (bucket*B + ebucket*D)
  104. }
  105. pub fn request(
  106. lox_cred: &cred::Lox,
  107. lox_pub: &IssuerPubKey,
  108. ) -> Result<(Request, State), ProofError> {
  109. let A: &RistrettoPoint = &CMZ_A;
  110. let B: &RistrettoPoint = &CMZ_B;
  111. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  112. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  113. // Ensure the credential can be correctly shown: it must be the case
  114. // that trust_level >= MIN_TRUST_LEVEL
  115. let level: u32 = match scalar_u32(&lox_cred.trust_level) {
  116. Some(v) => v,
  117. None => return Err(ProofError::VerificationFailure),
  118. };
  119. if level < MIN_TRUST_LEVEL {
  120. return Err(ProofError::VerificationFailure);
  121. }
  122. // Blind showing the Lox credential
  123. // Reblind P and Q
  124. let mut rng = rand::thread_rng();
  125. let t = Scalar::random(&mut rng);
  126. let P = t * lox_cred.P;
  127. let Q = t * lox_cred.Q;
  128. // Form Pedersen commitments to the blinded attributes
  129. let zbucket = Scalar::random(&mut rng);
  130. let zsince = Scalar::random(&mut rng);
  131. let zinvremain = Scalar::random(&mut rng);
  132. let zblockages = Scalar::random(&mut rng);
  133. let CBucket = lox_cred.bucket * P + &zbucket * Atable;
  134. let CSince = lox_cred.level_since * P + &zsince * Atable;
  135. let CInvRemain = lox_cred.invites_remaining * P + &zinvremain * Atable;
  136. let CBlockages = lox_cred.blockages * P + &zblockages * Atable;
  137. // Form a Pedersen commitment to the MAC Q
  138. // We flip the sign of zQ from that of the Hyphae paper so that
  139. // the ZKP has a "+" instead of a "-", as that's what the zkp
  140. // macro supports.
  141. let negzQ = Scalar::random(&mut rng);
  142. let CQ = Q - &negzQ * Atable;
  143. // Compute the "error factor"
  144. let V = zbucket * lox_pub.X[2]
  145. + zsince * lox_pub.X[4]
  146. + zinvremain * lox_pub.X[5]
  147. + zblockages * lox_pub.X[6]
  148. + &negzQ * Atable;
  149. // User blinding the Migration Key credential
  150. // Pick an ElGamal keypair
  151. let d = Scalar::random(&mut rng);
  152. let D = &d * Btable;
  153. // Encrypt the attributes to be blinded (each times the
  154. // basepoint B) to the public key we just created
  155. let ebucket = Scalar::random(&mut rng);
  156. let EncBucket = (&ebucket * Btable, &lox_cred.bucket * Btable + ebucket * D);
  157. // Construct the proof
  158. let mut transcript = Transcript::new(b"check blockage request");
  159. let piUser = requestproof::prove_compact(
  160. &mut transcript,
  161. requestproof::ProveAssignments {
  162. A: &A,
  163. B: &B,
  164. P: &P,
  165. CBucket: &CBucket,
  166. CSince: &CSince,
  167. CInvRemain: &CInvRemain,
  168. CBlockages: &CBlockages,
  169. V: &V,
  170. Xbucket: &lox_pub.X[2],
  171. Xsince: &lox_pub.X[4],
  172. Xinvremain: &lox_pub.X[5],
  173. Xblockages: &lox_pub.X[6],
  174. D: &D,
  175. EncBucket0: &EncBucket.0,
  176. EncBucket1: &EncBucket.1,
  177. bucket: &lox_cred.bucket,
  178. since: &lox_cred.level_since,
  179. invremain: &lox_cred.invites_remaining,
  180. blockages: &lox_cred.blockages,
  181. zbucket: &zbucket,
  182. zsince: &zsince,
  183. zinvremain: &zinvremain,
  184. zblockages: &zblockages,
  185. negzQ: &negzQ,
  186. d: &d,
  187. ebucket: &ebucket,
  188. },
  189. )
  190. .0;
  191. Ok((
  192. Request {
  193. P,
  194. id: lox_cred.id,
  195. CBucket,
  196. level: lox_cred.trust_level,
  197. CSince,
  198. CInvRemain,
  199. CBlockages,
  200. CQ,
  201. D,
  202. EncBucket,
  203. piUser,
  204. },
  205. State {
  206. d,
  207. D,
  208. EncBucket,
  209. id: lox_cred.id,
  210. bucket: lox_cred.bucket,
  211. },
  212. ))
  213. }
  214. impl BridgeAuth {
  215. /// Receive a check blockage request
  216. pub fn handle_check_blockage(&mut self, req: Request) -> Result<Response, ProofError> {
  217. let A: &RistrettoPoint = &CMZ_A;
  218. let B: &RistrettoPoint = &CMZ_B;
  219. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  220. let level: u32 = match scalar_u32(&req.level) {
  221. Some(v) => v,
  222. None => return Err(ProofError::VerificationFailure),
  223. };
  224. if req.P.is_identity() || level < MIN_TRUST_LEVEL {
  225. return Err(ProofError::VerificationFailure);
  226. }
  227. // Recompute the "error factor" using knowledge of our own
  228. // (the issuer's) private key instead of knowledge of the
  229. // hidden attributes
  230. let Vprime =
  231. (self.lox_priv.x[0] + self.lox_priv.x[1] * req.id + self.lox_priv.x[3] * req.level)
  232. * req.P
  233. + self.lox_priv.x[2] * req.CBucket
  234. + self.lox_priv.x[4] * req.CSince
  235. + self.lox_priv.x[5] * req.CInvRemain
  236. + self.lox_priv.x[6] * req.CBlockages
  237. - req.CQ;
  238. // Verify the ZKP
  239. let mut transcript = Transcript::new(b"check blockage request");
  240. requestproof::verify_compact(
  241. &req.piUser,
  242. &mut transcript,
  243. requestproof::VerifyAssignments {
  244. A: &A.compress(),
  245. B: &B.compress(),
  246. P: &req.P.compress(),
  247. CBucket: &req.CBucket.compress(),
  248. CSince: &req.CSince.compress(),
  249. CInvRemain: &req.CInvRemain.compress(),
  250. CBlockages: &req.CBlockages.compress(),
  251. V: &Vprime.compress(),
  252. Xbucket: &self.lox_pub.X[2].compress(),
  253. Xsince: &self.lox_pub.X[4].compress(),
  254. Xinvremain: &self.lox_pub.X[5].compress(),
  255. Xblockages: &self.lox_pub.X[6].compress(),
  256. D: &req.D.compress(),
  257. EncBucket0: &req.EncBucket.0.compress(),
  258. EncBucket1: &req.EncBucket.1.compress(),
  259. },
  260. )?;
  261. // Ensure the id has not been seen before in the general id
  262. // filter, but do not add it, so that the user can potentially
  263. // run this protocol multiple times.
  264. if self.id_filter.check(&req.id) == SeenType::Seen {
  265. return Err(ProofError::VerificationFailure);
  266. }
  267. // Compute the encrypted MAC (Pk, EncQk) for the Migration Key
  268. // credential.
  269. // Compute the MAC on the visible attributes
  270. let mut rng = rand::thread_rng();
  271. let b = Scalar::random(&mut rng);
  272. let Pk = &b * Btable;
  273. let Pktable = RistrettoBasepointTable::create(&Pk);
  274. let Qid = &(self.migrationkey_priv.x[0] + self.migrationkey_priv.x[1] * req.id) * &Pktable;
  275. // El Gamal encrypt it to the public key req.D
  276. let s = Scalar::random(&mut rng);
  277. let EncQkid = (&s * Btable, Qid + s * req.D);
  278. // Homomorphically compute the part of the MAC corresponding to
  279. // the blinded attributes
  280. let tbucket = self.migrationkey_priv.x[2] * b;
  281. let EncQkBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
  282. let EncQk = (EncQkid.0 + EncQkBucket.0, EncQkid.1 + EncQkBucket.1);
  283. Ok(Response {
  284. Pk,
  285. EncQk,
  286. enc_migration_table: self.blockage_migration_table.encrypt_table(
  287. &req.id,
  288. &self.bridge_table,
  289. &Pktable,
  290. &self.migration_priv,
  291. &self.migrationkey_priv,
  292. ),
  293. })
  294. }
  295. }
  296. /// Handle the response to the request, producing a Migration credential
  297. /// if successful.
  298. ///
  299. /// The Migration credential can then be used in the migration protocol
  300. /// to actually change buckets
  301. pub fn handle_response(state: State, resp: Response) -> Result<cred::Migration, ProofError> {
  302. if resp.Pk.is_identity() {
  303. return Err(ProofError::VerificationFailure);
  304. }
  305. // Decrypt the MAC on the Migration Key credential
  306. let Qk = resp.EncQk.1 - (state.d * resp.EncQk.0);
  307. // Use Qk to locate and decrypt the Migration credential
  308. match migration_table::decrypt_cred(
  309. &Qk,
  310. &state.id,
  311. &state.bucket,
  312. migration_table::MigrationType::Blockage,
  313. &resp.enc_migration_table,
  314. ) {
  315. Some(m) => Ok(m),
  316. None => Err(ProofError::VerificationFailure),
  317. }
  318. }