migration.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. /*! A module for the protocol for the user to migrate from one bucket to
  2. another and change trust level from untrusted (trust level 0) to trusted
  3. (trust level 1).
  4. The user presents their current Lox credential:
  5. - id: revealed
  6. - bucket: blinded
  7. - trust_level: revealed to be 0
  8. - level_since: blinded
  9. - invites_remaining: revealed to be 0
  10. - blockages: revealed to be 0
  11. and a Migration credential:
  12. - id: revealed as the same as the Lox credential id above
  13. - from_bucket: blinded, but proved in ZK that it's the same as the
  14. bucket in the Lox credential above
  15. - to_bucket: blinded
  16. and a new Lox credential to be issued:
  17. - id: jointly chosen by the user and BA
  18. - bucket: blinded, but proved in ZK that it's the same as the to_bucket
  19. in the Migration credential above
  20. - trust_level: 1
  21. - level_since: today
  22. - invites_remaining: 0
  23. - blockages: 0
  24. */
  25. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  26. use curve25519_dalek::ristretto::RistrettoPoint;
  27. use curve25519_dalek::scalar::Scalar;
  28. use curve25519_dalek::traits::IsIdentity;
  29. use zkp::CompactProof;
  30. use zkp::ProofError;
  31. use zkp::Transcript;
  32. use serde::{Deserialize, Serialize};
  33. use super::super::cred;
  34. use super::super::dup_filter::SeenType;
  35. use super::super::{BridgeAuth, IssuerPubKey};
  36. use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
  37. #[derive(Serialize, Deserialize)]
  38. pub struct Request {
  39. // Fields for blind showing the Lox credential
  40. // We don't need to include invites_remaining or blockages,
  41. // since they must be 0
  42. P_lox: RistrettoPoint,
  43. id: Scalar,
  44. CBucket: RistrettoPoint,
  45. trust_level: Scalar,
  46. CSince: RistrettoPoint,
  47. CQ_lox: RistrettoPoint,
  48. // Fields for blind showing the Migration credential
  49. P_mig: RistrettoPoint,
  50. CFromBucket: RistrettoPoint,
  51. CToBucket: RistrettoPoint,
  52. CQ_mig: RistrettoPoint,
  53. // Fields for user blinding of the Lox credential to be issued
  54. D: RistrettoPoint,
  55. EncIdClient: (RistrettoPoint, RistrettoPoint),
  56. EncBucket: (RistrettoPoint, RistrettoPoint),
  57. // The combined ZKP
  58. piUser: CompactProof,
  59. }
  60. #[derive(Debug)]
  61. pub struct State {
  62. d: Scalar,
  63. D: RistrettoPoint,
  64. EncIdClient: (RistrettoPoint, RistrettoPoint),
  65. EncBucket: (RistrettoPoint, RistrettoPoint),
  66. id_client: Scalar,
  67. to_bucket: Scalar,
  68. }
  69. #[derive(Serialize, Deserialize)]
  70. pub struct Response {
  71. // The new attributes; trust_level = 1 is implicit
  72. level_since: Scalar,
  73. // The fields for the new Lox credential
  74. P: RistrettoPoint,
  75. EncQ: (RistrettoPoint, RistrettoPoint),
  76. id_server: Scalar,
  77. TId: RistrettoPoint,
  78. TBucket: RistrettoPoint,
  79. // The ZKP
  80. piBlindIssue: CompactProof,
  81. }
  82. define_proof! {
  83. requestproof,
  84. "Migration Request",
  85. (bucket, since, zbucket, zsince, negzQ_lox,
  86. tobucket, zfrombucket, ztobucket, negzQ_mig,
  87. d, eid_client, ebucket, id_client),
  88. (P_lox, CBucket, CSince, V_lox, Xbucket, Xsince,
  89. P_mig, CFromBucket, CToBucket, V_mig, Xfrombucket, Xtobucket,
  90. D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1),
  91. (A, B):
  92. // Blind showing of the Lox credential
  93. CBucket = (bucket*P_lox + zbucket*A),
  94. CSince = (since*P_lox + zsince*A),
  95. V_lox = (zbucket*Xbucket + zsince*Xsince + negzQ_lox*A),
  96. // Blind showing of the Migration credential; note the use of the
  97. // same "bucket" secret variable
  98. CFromBucket = (bucket*P_mig + zfrombucket*A),
  99. CToBucket = (tobucket*P_mig + ztobucket*A),
  100. V_mig = (zfrombucket*Xfrombucket + ztobucket*Xtobucket + negzQ_mig*A),
  101. // User blinding of the Lox credential to be issued; note the use of
  102. // the same "tobucket" secret variable
  103. D = (d*B),
  104. EncIdClient0 = (eid_client*B),
  105. EncIdClient1 = (id_client*B + eid_client*D),
  106. EncBucket0 = (ebucket*B),
  107. EncBucket1 = (tobucket*B + ebucket*D)
  108. }
  109. define_proof! {
  110. blindissue,
  111. "Migration Blind Issuing",
  112. (x0, x0tilde, xid, xbucket, xlevel, xsince, s, b, tid, tbucket),
  113. (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xlevel, Xsince, Plevel, Psince, TId, TBucket,
  114. D, EncId0, EncId1, EncBucket0, EncBucket1),
  115. (A, B):
  116. Xid = (xid*A),
  117. Xlevel = (xlevel*A),
  118. Xbucket = (xbucket*A),
  119. Xsince = (xsince*A),
  120. X0 = (x0*B + x0tilde*A),
  121. P = (b*B),
  122. TId = (b*Xid),
  123. TId = (tid*A),
  124. TBucket = (b*Xbucket),
  125. TBucket = (tbucket*A),
  126. EncQ0 = (s*B + tid*EncId0 + tbucket*EncBucket0),
  127. EncQ1 = (s*D + tid*EncId1 + tbucket*EncBucket1 + x0*P + xlevel*Plevel + xsince*Psince)
  128. }
  129. pub fn request(
  130. lox_cred: &cred::Lox,
  131. migration_cred: &cred::Migration,
  132. lox_pub: &IssuerPubKey,
  133. migration_pub: &IssuerPubKey,
  134. ) -> Result<(Request, State), ProofError> {
  135. let A: &RistrettoPoint = &CMZ_A;
  136. let B: &RistrettoPoint = &CMZ_B;
  137. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  138. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  139. // Ensure that the credenials can be correctly shown; that is, the
  140. // ids match and the Lox credential bucket matches the Migration
  141. // credential from_bucket
  142. if lox_cred.id != migration_cred.lox_id || lox_cred.bucket != migration_cred.from_bucket {
  143. return Err(ProofError::VerificationFailure);
  144. }
  145. // This protocol only allows migrating from trust level 0 to trust
  146. // level 1
  147. if lox_cred.trust_level != Scalar::zero() {
  148. return Err(ProofError::VerificationFailure);
  149. }
  150. // Blind showing the Lox credential
  151. // Reblind P and Q
  152. let mut rng = rand::thread_rng();
  153. let t_lox = Scalar::random(&mut rng);
  154. let P_lox = t_lox * lox_cred.P;
  155. let Q_lox = t_lox * lox_cred.Q;
  156. // Form Pedersen commitments to the blinded attributes
  157. let zbucket = Scalar::random(&mut rng);
  158. let zsince = Scalar::random(&mut rng);
  159. let CBucket = lox_cred.bucket * P_lox + &zbucket * Atable;
  160. let CSince = lox_cred.level_since * P_lox + &zsince * Atable;
  161. // Form a Pedersen commitment to the MAC Q
  162. // We flip the sign of zQ from that of the Hyphae paper so that
  163. // the ZKP has a "+" instead of a "-", as that's what the zkp
  164. // macro supports.
  165. let negzQ_lox = Scalar::random(&mut rng);
  166. let CQ_lox = Q_lox - &negzQ_lox * Atable;
  167. // Compute the "error factor"
  168. let V_lox = zbucket * lox_pub.X[2] + zsince * lox_pub.X[4] + &negzQ_lox * Atable;
  169. // Blind showing the Migration credential
  170. // Reblind P and Q
  171. let t_mig = Scalar::random(&mut rng);
  172. let P_mig = t_mig * migration_cred.P;
  173. let Q_mig = t_mig * migration_cred.Q;
  174. // Form Pedersen commitments to the blinded attributes
  175. let zfrombucket = Scalar::random(&mut rng);
  176. let ztobucket = Scalar::random(&mut rng);
  177. let CFromBucket = migration_cred.from_bucket * P_mig + &zfrombucket * Atable;
  178. let CToBucket = migration_cred.to_bucket * P_mig + &ztobucket * Atable;
  179. // Form a Pedersen commitment to the MAC Q
  180. // We flip the sign of zQ from that of the Hyphae paper so that
  181. // the ZKP has a "+" instead of a "-", as that's what the zkp
  182. // macro supports.
  183. let negzQ_mig = Scalar::random(&mut rng);
  184. let CQ_mig = Q_mig - &negzQ_mig * Atable;
  185. // Compute the "error factor"
  186. let V_mig =
  187. zfrombucket * migration_pub.X[2] + ztobucket * migration_pub.X[3] + &negzQ_mig * Atable;
  188. // User blinding for the Lox certificate to be issued
  189. // Pick an ElGamal keypair
  190. let d = Scalar::random(&mut rng);
  191. let D = &d * Btable;
  192. // Pick a random client component of the id
  193. let id_client = Scalar::random(&mut rng);
  194. // Encrypt it (times the basepoint B) to the ElGamal public key D we
  195. // just created
  196. let eid_client = Scalar::random(&mut rng);
  197. let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
  198. // Encrypt the bucket field (times B) to D as well
  199. let ebucket = Scalar::random(&mut rng);
  200. let EncBucket = (
  201. &ebucket * Btable,
  202. &migration_cred.to_bucket * Btable + ebucket * D,
  203. );
  204. // Construct the proof
  205. let mut transcript = Transcript::new(b"migration request");
  206. let piUser = requestproof::prove_compact(
  207. &mut transcript,
  208. requestproof::ProveAssignments {
  209. A,
  210. B,
  211. P_lox: &P_lox,
  212. CBucket: &CBucket,
  213. CSince: &CSince,
  214. V_lox: &V_lox,
  215. Xbucket: &lox_pub.X[2],
  216. Xsince: &lox_pub.X[4],
  217. P_mig: &P_mig,
  218. CFromBucket: &CFromBucket,
  219. CToBucket: &CToBucket,
  220. V_mig: &V_mig,
  221. Xfrombucket: &migration_pub.X[2],
  222. Xtobucket: &migration_pub.X[3],
  223. D: &D,
  224. EncIdClient0: &EncIdClient.0,
  225. EncIdClient1: &EncIdClient.1,
  226. EncBucket0: &EncBucket.0,
  227. EncBucket1: &EncBucket.1,
  228. bucket: &lox_cred.bucket,
  229. since: &lox_cred.level_since,
  230. zbucket: &zbucket,
  231. zsince: &zsince,
  232. negzQ_lox: &negzQ_lox,
  233. tobucket: &migration_cred.to_bucket,
  234. zfrombucket: &zfrombucket,
  235. ztobucket: &ztobucket,
  236. negzQ_mig: &negzQ_mig,
  237. d: &d,
  238. eid_client: &eid_client,
  239. ebucket: &ebucket,
  240. id_client: &id_client,
  241. },
  242. )
  243. .0;
  244. Ok((
  245. Request {
  246. P_lox,
  247. id: lox_cred.id,
  248. CBucket,
  249. trust_level: lox_cred.trust_level,
  250. CSince,
  251. CQ_lox,
  252. P_mig,
  253. CFromBucket,
  254. CToBucket,
  255. CQ_mig,
  256. D,
  257. EncIdClient,
  258. EncBucket,
  259. piUser,
  260. },
  261. State {
  262. d,
  263. D,
  264. EncIdClient,
  265. EncBucket,
  266. id_client,
  267. to_bucket: migration_cred.to_bucket,
  268. },
  269. ))
  270. }
  271. impl BridgeAuth {
  272. /// Receive a migration request
  273. pub fn handle_migration(&mut self, req: Request) -> Result<Response, ProofError> {
  274. let A: &RistrettoPoint = &CMZ_A;
  275. let B: &RistrettoPoint = &CMZ_B;
  276. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  277. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  278. if req.P_lox.is_identity() || req.P_mig.is_identity() {
  279. return Err(ProofError::VerificationFailure);
  280. }
  281. // We only currently support migrating from trust level 0
  282. if req.trust_level != Scalar::zero() {
  283. return Err(ProofError::VerificationFailure);
  284. }
  285. // Recompute the "error factors" using knowledge of our own
  286. // (the issuer's) private key instead of knowledge of the
  287. // hidden attributes
  288. let Vprime_lox = (self.lox_priv.x[0]
  289. + self.lox_priv.x[1] * req.id
  290. + self.lox_priv.x[3] * req.trust_level)
  291. * req.P_lox
  292. + self.lox_priv.x[2] * req.CBucket
  293. + self.lox_priv.x[4] * req.CSince
  294. - req.CQ_lox;
  295. let Vprime_mig = (self.migration_priv.x[0] + self.migration_priv.x[1] * req.id) * req.P_mig
  296. + self.migration_priv.x[2] * req.CFromBucket
  297. + self.migration_priv.x[3] * req.CToBucket
  298. - req.CQ_mig;
  299. // Verify the ZKP
  300. let mut transcript = Transcript::new(b"migration request");
  301. requestproof::verify_compact(
  302. &req.piUser,
  303. &mut transcript,
  304. requestproof::VerifyAssignments {
  305. A: &A.compress(),
  306. B: &B.compress(),
  307. P_lox: &req.P_lox.compress(),
  308. CBucket: &req.CBucket.compress(),
  309. CSince: &req.CSince.compress(),
  310. V_lox: &Vprime_lox.compress(),
  311. Xbucket: &self.lox_pub.X[2].compress(),
  312. Xsince: &self.lox_pub.X[4].compress(),
  313. P_mig: &req.P_mig.compress(),
  314. CFromBucket: &req.CFromBucket.compress(),
  315. CToBucket: &req.CToBucket.compress(),
  316. V_mig: &Vprime_mig.compress(),
  317. Xfrombucket: &self.migration_pub.X[2].compress(),
  318. Xtobucket: &self.migration_pub.X[3].compress(),
  319. D: &req.D.compress(),
  320. EncIdClient0: &req.EncIdClient.0.compress(),
  321. EncIdClient1: &req.EncIdClient.1.compress(),
  322. EncBucket0: &req.EncBucket.0.compress(),
  323. EncBucket1: &req.EncBucket.1.compress(),
  324. },
  325. )?;
  326. // Ensure the id has not been seen before, and add it to the
  327. // seen list.
  328. if self.id_filter.filter(&req.id) == SeenType::Seen {
  329. return Err(ProofError::VerificationFailure);
  330. }
  331. // Blind issuing of the new Lox credential
  332. // Choose a random server id component to add to the client's
  333. // (blinded) id component
  334. let mut rng = rand::thread_rng();
  335. let id_server = Scalar::random(&mut rng);
  336. let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable);
  337. // Create the trust_level attrubute (Scalar), which will be
  338. // level 1
  339. let trust_level: Scalar = Scalar::one();
  340. // Create the level_since attribute (Scalar), which is today's
  341. // Julian date
  342. let level_since: Scalar = self.today().into();
  343. // The invitations_remaining and blockages attributes are 0 for
  344. // level 0 and level 1 Lox credentials, so we don't need to
  345. // explicitly create them.
  346. // Compute the MAC on the visible attributes
  347. let b = Scalar::random(&mut rng);
  348. let P = &b * Btable;
  349. // invites_remaining = blockages = 0
  350. let QHc = (self.lox_priv.x[0]
  351. + self.lox_priv.x[3] * trust_level
  352. + self.lox_priv.x[4] * level_since)
  353. * P;
  354. // El Gamal encrypt it to the public key req.D
  355. let s = Scalar::random(&mut rng);
  356. let EncQHc = (&s * Btable, QHc + s * req.D);
  357. // Homomorphically compute the part of the MAC corresponding to
  358. // the blinded attributes
  359. let tid = self.lox_priv.x[1] * b;
  360. let TId = &tid * Atable;
  361. let EncQId = (tid * EncId.0, tid * EncId.1);
  362. let tbucket = self.lox_priv.x[2] * b;
  363. let TBucket = &tbucket * Atable;
  364. let EncQBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
  365. let EncQ = (
  366. EncQHc.0 + EncQId.0 + EncQBucket.0,
  367. EncQHc.1 + EncQId.1 + EncQBucket.1,
  368. );
  369. let mut transcript = Transcript::new(b"migration issuing");
  370. let piBlindIssue = blindissue::prove_compact(
  371. &mut transcript,
  372. blindissue::ProveAssignments {
  373. A,
  374. B,
  375. P: &P,
  376. EncQ0: &EncQ.0,
  377. EncQ1: &EncQ.1,
  378. X0: &self.lox_pub.X[0],
  379. Xid: &self.lox_pub.X[1],
  380. Xbucket: &self.lox_pub.X[2],
  381. Xlevel: &self.lox_pub.X[3],
  382. Xsince: &self.lox_pub.X[4],
  383. Plevel: &(trust_level * P),
  384. Psince: &(level_since * P),
  385. TId: &TId,
  386. TBucket: &TBucket,
  387. D: &req.D,
  388. EncId0: &EncId.0,
  389. EncId1: &EncId.1,
  390. EncBucket0: &req.EncBucket.0,
  391. EncBucket1: &req.EncBucket.1,
  392. x0: &self.lox_priv.x[0],
  393. x0tilde: &self.lox_priv.x0tilde,
  394. xid: &self.lox_priv.x[1],
  395. xbucket: &self.lox_priv.x[2],
  396. xlevel: &self.lox_priv.x[3],
  397. xsince: &self.lox_priv.x[4],
  398. s: &s,
  399. b: &b,
  400. tid: &tid,
  401. tbucket: &tbucket,
  402. },
  403. )
  404. .0;
  405. Ok(Response {
  406. level_since,
  407. P,
  408. EncQ,
  409. id_server,
  410. TId,
  411. TBucket,
  412. piBlindIssue,
  413. })
  414. }
  415. }
  416. /// Handle the response to the request, producing the new Lox credential
  417. /// if successful.
  418. pub fn handle_response(
  419. state: State,
  420. resp: Response,
  421. lox_pub: &IssuerPubKey,
  422. ) -> Result<cred::Lox, ProofError> {
  423. let A: &RistrettoPoint = &CMZ_A;
  424. let B: &RistrettoPoint = &CMZ_B;
  425. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  426. if resp.P.is_identity() {
  427. return Err(ProofError::VerificationFailure);
  428. }
  429. // Add the server's contribution to the id to our own, both in plain
  430. // and encrypted form
  431. let id = state.id_client + resp.id_server;
  432. let EncId = (
  433. state.EncIdClient.0,
  434. state.EncIdClient.1 + &resp.id_server * Btable,
  435. );
  436. // Verify the proof
  437. let mut transcript = Transcript::new(b"migration issuing");
  438. blindissue::verify_compact(
  439. &resp.piBlindIssue,
  440. &mut transcript,
  441. blindissue::VerifyAssignments {
  442. A: &A.compress(),
  443. B: &B.compress(),
  444. P: &resp.P.compress(),
  445. EncQ0: &resp.EncQ.0.compress(),
  446. EncQ1: &resp.EncQ.1.compress(),
  447. X0: &lox_pub.X[0].compress(),
  448. Xid: &lox_pub.X[1].compress(),
  449. Xbucket: &lox_pub.X[2].compress(),
  450. Xlevel: &lox_pub.X[3].compress(),
  451. Xsince: &lox_pub.X[4].compress(),
  452. // The new trust level is 1
  453. Plevel: &(Scalar::one() * resp.P).compress(),
  454. Psince: &(resp.level_since * resp.P).compress(),
  455. TId: &resp.TId.compress(),
  456. TBucket: &resp.TBucket.compress(),
  457. D: &state.D.compress(),
  458. EncId0: &EncId.0.compress(),
  459. EncId1: &EncId.1.compress(),
  460. EncBucket0: &state.EncBucket.0.compress(),
  461. EncBucket1: &state.EncBucket.1.compress(),
  462. },
  463. )?;
  464. // Decrypt EncQ
  465. let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
  466. Ok(cred::Lox {
  467. P: resp.P,
  468. Q,
  469. id,
  470. bucket: state.to_bucket,
  471. trust_level: Scalar::one(),
  472. level_since: resp.level_since,
  473. invites_remaining: Scalar::zero(),
  474. blockages: Scalar::zero(),
  475. })
  476. }