trust_promotion.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /*! A module for the protocol for the user to get promoted from
  2. untrusted (trust level 0) to trusted (trust level 1).
  3. They are allowed to do this as long as UNTRUSTED_INTERVAL days have
  4. passed since they obtained their level 0 Lox credential, and their
  5. bridge (level 0 users get put in a one-bridge bucket) has not been
  6. blocked. (Blocked bridges in one-bridge buckets will have their entries
  7. removed from the bridge authority's migration table.)
  8. The user presents their current Lox credential:
  9. - id: revealed
  10. - bucket: blinded
  11. - trust_level: revealed to be 0
  12. - level_since: blinded, but proved in ZK that it's at least
  13. UNTRUSTED_INTERVAL days ago
  14. - invites_remaining: revealed to be 0
  15. - blockages: revealed to be 0
  16. They will receive in return the encrypted MAC (Pk, EncQk) for their
  17. implicit Migration Key credential with attributes id and bucket,
  18. along with a HashMap of encrypted Migration credentials. For each
  19. (from_i, to_i) in the BA's migration list, there will be an entry in
  20. the HashMap with key H1(id, from_attr_i, Qk_i) and value
  21. Enc_{H2(id, from_attr_i, Qk_i)}(to_attr_i, P_i, Q_i). Here H1 and H2
  22. are the first 16 bytes and the second 16 bytes respectively of the
  23. SHA256 hash of the input, P_i and Q_i are a MAC on the Migration
  24. credential with attributes id, from_attr_i, and to_attr_i. Qk_i is the
  25. value EncQk would decrypt to if bucket were equal to from_attr_i. */
  26. use curve25519_dalek::ristretto::RistrettoBasepointTable;
  27. use curve25519_dalek::ristretto::RistrettoPoint;
  28. use curve25519_dalek::scalar::Scalar;
  29. use curve25519_dalek::traits::IsIdentity;
  30. use zkp::CompactProof;
  31. use zkp::ProofError;
  32. use zkp::Transcript;
  33. use std::collections::HashMap;
  34. use super::super::cred;
  35. use super::super::dup_filter::SeenType;
  36. use super::super::migration_table;
  37. use super::super::{pt_dbl, scalar_dbl, scalar_u32};
  38. use super::super::{BridgeAuth, IssuerPubKey};
  39. use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
  40. /// The minimum number of days a user has to be at trust level 0
  41. /// (untrusted) with their (single) bridge unblocked before they can
  42. /// move to level 1.
  43. ///
  44. /// The implementation also puts an upper bound of UNTRUSTED_INTERVAL +
  45. /// 511 days, which is not unreasonable; we want users to be engaging
  46. /// with the system in order to move up trust levels.
  47. pub const UNTRUSTED_INTERVAL: u32 = 30;
  48. pub struct Request {
  49. // Fields for blind showing the Lox credential
  50. // We don't need to include trust_level, invites_remaining, or
  51. // blockages, since they must be 0
  52. P: RistrettoPoint,
  53. id: Scalar,
  54. CBucket: RistrettoPoint,
  55. CSince: RistrettoPoint,
  56. CQ: RistrettoPoint,
  57. // Fields for user blinding of the Migration Key credential
  58. D: RistrettoPoint,
  59. EncBucket: (RistrettoPoint, RistrettoPoint),
  60. // Fields for the inequality proof (level_since +
  61. // UNTRUSTED_INTERVAL <= today)
  62. CG1: RistrettoPoint,
  63. CG2: RistrettoPoint,
  64. CG3: RistrettoPoint,
  65. CG4: RistrettoPoint,
  66. CG5: RistrettoPoint,
  67. CG6: RistrettoPoint,
  68. CG7: RistrettoPoint,
  69. CG8: RistrettoPoint,
  70. CG0sq: RistrettoPoint,
  71. CG1sq: RistrettoPoint,
  72. CG2sq: RistrettoPoint,
  73. CG3sq: RistrettoPoint,
  74. CG4sq: RistrettoPoint,
  75. CG5sq: RistrettoPoint,
  76. CG6sq: RistrettoPoint,
  77. CG7sq: RistrettoPoint,
  78. CG8sq: RistrettoPoint,
  79. // The combined ZKP
  80. piUser: CompactProof,
  81. }
  82. #[derive(Debug)]
  83. pub struct State {
  84. d: Scalar,
  85. D: RistrettoPoint,
  86. EncBucket: (RistrettoPoint, RistrettoPoint),
  87. id: Scalar,
  88. bucket: Scalar,
  89. }
  90. #[derive(Debug)]
  91. pub struct Response {
  92. // The encrypted MAC for the Migration Key credential
  93. Pk: RistrettoPoint,
  94. EncQk: (RistrettoPoint, RistrettoPoint),
  95. // A table of encrypted Migration credentials; the encryption keys
  96. // are formed from the possible values of Qk (the decrypted form of
  97. // EncQk)
  98. enc_migration_table: HashMap<[u8; 16], [u8; migration_table::ENC_MIGRATION_BYTES]>,
  99. }
  100. define_proof! {
  101. requestproof,
  102. "Trust Promotion Request",
  103. (bucket, since, zbucket, zsince, negzQ,
  104. d, ebucket,
  105. g0, g1, g2, g3, g4, g5, g6, g7, g8,
  106. zg0, zg1, zg2, zg3, zg4, zg5, zg6, zg7, zg8,
  107. wg0, wg1, wg2, wg3, wg4, wg5, wg6, wg7, wg8,
  108. yg0, yg1, yg2, yg3, yg4, yg5, yg6, yg7, yg8),
  109. (P, CBucket, CSince, V, Xbucket, Xsince,
  110. EncBucket0, EncBucket1, D,
  111. CG0, CG1, CG2, CG3, CG4, CG5, CG6, CG7, CG8,
  112. CG0sq, CG1sq, CG2sq, CG3sq, CG4sq, CG5sq, CG6sq, CG7sq, CG8sq),
  113. (A, B):
  114. // Blind showing of the Lox credential
  115. CBucket = (bucket*P + zbucket*A),
  116. CSince = (since*P + zsince*A),
  117. V = (zbucket*Xbucket + zsince*Xsince + negzQ*A),
  118. // User blinding of the Migration Key credential
  119. EncBucket0 = (ebucket*B),
  120. EncBucket1 = (bucket*B + ebucket*D),
  121. D = (d*B),
  122. // Prove CSince encodes a value at least UNTRUSTED_INTERVAL
  123. // days ago (and technically at most UNTRUSTED_INTERVAL+511 days
  124. // ago): first prove each of g0, ..., g8 is a bit by proving that
  125. // gi = gi^2
  126. CG0 = (g0*P + zg0*A), CG0sq = (g0*CG0 + wg0*A), CG0sq = (g0*P + yg0*A),
  127. CG1 = (g1*P + zg1*A), CG1sq = (g1*CG1 + wg1*A), CG1sq = (g1*P + yg1*A),
  128. CG2 = (g2*P + zg2*A), CG2sq = (g2*CG2 + wg2*A), CG2sq = (g2*P + yg2*A),
  129. CG3 = (g3*P + zg3*A), CG3sq = (g3*CG3 + wg3*A), CG3sq = (g3*P + yg3*A),
  130. CG4 = (g4*P + zg4*A), CG4sq = (g4*CG4 + wg4*A), CG4sq = (g4*P + yg4*A),
  131. CG5 = (g5*P + zg5*A), CG5sq = (g5*CG5 + wg5*A), CG5sq = (g5*P + yg5*A),
  132. CG6 = (g6*P + zg6*A), CG6sq = (g6*CG6 + wg6*A), CG6sq = (g6*P + yg6*A),
  133. CG7 = (g7*P + zg7*A), CG7sq = (g7*CG7 + wg7*A), CG7sq = (g7*P + yg7*A),
  134. CG8 = (g8*P + zg8*A), CG8sq = (g8*CG8 + wg8*A), CG8sq = (g8*P + yg8*A)
  135. // Then we'll check that CSince + UNTRUSTED_INTERVAL*P + CG0 + 2*CG1
  136. // + 4*CG2 + 8*CG3 + ... + 256*CG8 = today*P by having the verifier
  137. // plug in today*P - (CSince + UNTRUSTED_INTERVAL*P + 2*CG1 + 4*CG2
  138. // + ... + 256*CG8) as its value of CG0.
  139. }
  140. pub fn request(
  141. lox_cred: &cred::Lox,
  142. lox_pub: &IssuerPubKey,
  143. today: u32,
  144. ) -> Result<(Request, State), ProofError> {
  145. let A: &RistrettoPoint = &CMZ_A;
  146. let B: &RistrettoPoint = &CMZ_B;
  147. let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
  148. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  149. // Ensure the credential can be correctly shown: it must be the case
  150. // that level_since + UNTRUSTED_INTERVAL <= today.
  151. let level_since: u32 = match scalar_u32(&lox_cred.level_since) {
  152. Some(v) => v,
  153. None => return Err(ProofError::VerificationFailure),
  154. };
  155. if level_since + UNTRUSTED_INTERVAL > today {
  156. return Err(ProofError::VerificationFailure);
  157. }
  158. let diffdays = today - (level_since + UNTRUSTED_INTERVAL);
  159. if diffdays > 511 {
  160. return Err(ProofError::VerificationFailure);
  161. }
  162. // Blind showing the Lox credential
  163. // Reblind P and Q
  164. let mut rng = rand::thread_rng();
  165. let t = Scalar::random(&mut rng);
  166. let P = t * lox_cred.P;
  167. let Q = t * lox_cred.Q;
  168. // Form Pedersen commitments to the blinded attributes
  169. let zbucket = Scalar::random(&mut rng);
  170. let zsince = Scalar::random(&mut rng);
  171. let CBucket = lox_cred.bucket * P + &zbucket * Atable;
  172. let CSince = lox_cred.level_since * P + &zsince * Atable;
  173. // Form a Pedersen commitment to the MAC Q
  174. // We flip the sign of zQ from that of the Hyphae paper so that
  175. // the ZKP has a "+" instead of a "-", as that's what the zkp
  176. // macro supports.
  177. let negzQ = Scalar::random(&mut rng);
  178. let CQ = Q - &negzQ * Atable;
  179. // Compute the "error factor"
  180. let V = zbucket * lox_pub.X[2] + zsince * lox_pub.X[4] + &negzQ * Atable;
  181. // User blinding the Migration Key credential
  182. // Pick an ElGamal keypair
  183. let d = Scalar::random(&mut rng);
  184. let D = &d * Btable;
  185. // Encrypt the attributes to be blinded (each times the
  186. // basepoint B) to the public key we just created
  187. let ebucket = Scalar::random(&mut rng);
  188. let EncBucket = (&ebucket * Btable, &lox_cred.bucket * Btable + ebucket * D);
  189. // The range proof that 0 <= diffdays <= 511
  190. // Extract the 9 bits from diffdays
  191. let g0: Scalar = (diffdays & 1).into();
  192. let g1: Scalar = ((diffdays >> 1) & 1).into();
  193. let g2: Scalar = ((diffdays >> 2) & 1).into();
  194. let g3: Scalar = ((diffdays >> 3) & 1).into();
  195. let g4: Scalar = ((diffdays >> 4) & 1).into();
  196. let g5: Scalar = ((diffdays >> 5) & 1).into();
  197. let g6: Scalar = ((diffdays >> 6) & 1).into();
  198. let g7: Scalar = ((diffdays >> 7) & 1).into();
  199. let g8: Scalar = ((diffdays >> 8) & 1).into();
  200. // Pick random factors for the Pedersen commitments
  201. let wg0 = Scalar::random(&mut rng);
  202. let zg1 = Scalar::random(&mut rng);
  203. let wg1 = Scalar::random(&mut rng);
  204. let zg2 = Scalar::random(&mut rng);
  205. let wg2 = Scalar::random(&mut rng);
  206. let zg3 = Scalar::random(&mut rng);
  207. let wg3 = Scalar::random(&mut rng);
  208. let zg4 = Scalar::random(&mut rng);
  209. let wg4 = Scalar::random(&mut rng);
  210. let zg5 = Scalar::random(&mut rng);
  211. let wg5 = Scalar::random(&mut rng);
  212. let zg6 = Scalar::random(&mut rng);
  213. let wg6 = Scalar::random(&mut rng);
  214. let zg7 = Scalar::random(&mut rng);
  215. let wg7 = Scalar::random(&mut rng);
  216. let zg8 = Scalar::random(&mut rng);
  217. let wg8 = Scalar::random(&mut rng);
  218. // Compute zg0 to cancel things out as
  219. // zg0 = -(zsince + 2*zg1 + 4*zg2 + 8*zg3 + 16*zg4 + 32*zg5 + 64*zg6 + 128*zg7 + 256*zg8)
  220. // but use Horner's method
  221. let zg0 = -(scalar_dbl(
  222. &(scalar_dbl(
  223. &(scalar_dbl(
  224. &(scalar_dbl(
  225. &(scalar_dbl(
  226. &(scalar_dbl(&(scalar_dbl(&(scalar_dbl(&zg8) + zg7)) + zg6)) + zg5),
  227. ) + zg4),
  228. ) + zg3),
  229. ) + zg2),
  230. ) + zg1),
  231. ) + zsince);
  232. let yg0 = wg0 + g0 * zg0;
  233. let yg1 = wg1 + g1 * zg1;
  234. let yg2 = wg2 + g2 * zg2;
  235. let yg3 = wg3 + g3 * zg3;
  236. let yg4 = wg4 + g4 * zg4;
  237. let yg5 = wg5 + g5 * zg5;
  238. let yg6 = wg6 + g6 * zg6;
  239. let yg7 = wg7 + g7 * zg7;
  240. let yg8 = wg8 + g8 * zg8;
  241. let CG0 = g0 * P + &zg0 * Atable;
  242. let CG1 = g1 * P + &zg1 * Atable;
  243. let CG2 = g2 * P + &zg2 * Atable;
  244. let CG3 = g3 * P + &zg3 * Atable;
  245. let CG4 = g4 * P + &zg4 * Atable;
  246. let CG5 = g5 * P + &zg5 * Atable;
  247. let CG6 = g6 * P + &zg6 * Atable;
  248. let CG7 = g7 * P + &zg7 * Atable;
  249. let CG8 = g8 * P + &zg8 * Atable;
  250. let CG0sq = g0 * P + &yg0 * Atable;
  251. let CG1sq = g1 * P + &yg1 * Atable;
  252. let CG2sq = g2 * P + &yg2 * Atable;
  253. let CG3sq = g3 * P + &yg3 * Atable;
  254. let CG4sq = g4 * P + &yg4 * Atable;
  255. let CG5sq = g5 * P + &yg5 * Atable;
  256. let CG6sq = g6 * P + &yg6 * Atable;
  257. let CG7sq = g7 * P + &yg7 * Atable;
  258. let CG8sq = g8 * P + &yg8 * Atable;
  259. // Construct the proof
  260. let mut transcript = Transcript::new(b"trust promotion request");
  261. let piUser = requestproof::prove_compact(
  262. &mut transcript,
  263. requestproof::ProveAssignments {
  264. A: &A,
  265. B: &B,
  266. P: &P,
  267. CBucket: &CBucket,
  268. CSince: &CSince,
  269. V: &V,
  270. Xbucket: &lox_pub.X[2],
  271. Xsince: &lox_pub.X[4],
  272. EncBucket0: &EncBucket.0,
  273. EncBucket1: &EncBucket.1,
  274. D: &D,
  275. CG0: &CG0,
  276. CG1: &CG1,
  277. CG2: &CG2,
  278. CG3: &CG3,
  279. CG4: &CG4,
  280. CG5: &CG5,
  281. CG6: &CG6,
  282. CG7: &CG7,
  283. CG8: &CG8,
  284. CG0sq: &CG0sq,
  285. CG1sq: &CG1sq,
  286. CG2sq: &CG2sq,
  287. CG3sq: &CG3sq,
  288. CG4sq: &CG4sq,
  289. CG5sq: &CG5sq,
  290. CG6sq: &CG6sq,
  291. CG7sq: &CG7sq,
  292. CG8sq: &CG8sq,
  293. bucket: &lox_cred.bucket,
  294. since: &lox_cred.level_since,
  295. zbucket: &zbucket,
  296. zsince: &zsince,
  297. negzQ: &negzQ,
  298. d: &d,
  299. ebucket: &ebucket,
  300. g0: &g0,
  301. g1: &g1,
  302. g2: &g2,
  303. g3: &g3,
  304. g4: &g4,
  305. g5: &g5,
  306. g6: &g6,
  307. g7: &g7,
  308. g8: &g8,
  309. zg0: &zg0,
  310. zg1: &zg1,
  311. zg2: &zg2,
  312. zg3: &zg3,
  313. zg4: &zg4,
  314. zg5: &zg5,
  315. zg6: &zg6,
  316. zg7: &zg7,
  317. zg8: &zg8,
  318. wg0: &wg0,
  319. wg1: &wg1,
  320. wg2: &wg2,
  321. wg3: &wg3,
  322. wg4: &wg4,
  323. wg5: &wg5,
  324. wg6: &wg6,
  325. wg7: &wg7,
  326. wg8: &wg8,
  327. yg0: &yg0,
  328. yg1: &yg1,
  329. yg2: &yg2,
  330. yg3: &yg3,
  331. yg4: &yg4,
  332. yg5: &yg5,
  333. yg6: &yg6,
  334. yg7: &yg7,
  335. yg8: &yg8,
  336. },
  337. )
  338. .0;
  339. Ok((
  340. Request {
  341. P,
  342. id: lox_cred.id,
  343. CBucket,
  344. CSince,
  345. CQ,
  346. D,
  347. EncBucket,
  348. CG1,
  349. CG2,
  350. CG3,
  351. CG4,
  352. CG5,
  353. CG6,
  354. CG7,
  355. CG8,
  356. CG0sq,
  357. CG1sq,
  358. CG2sq,
  359. CG3sq,
  360. CG4sq,
  361. CG5sq,
  362. CG6sq,
  363. CG7sq,
  364. CG8sq,
  365. piUser,
  366. },
  367. State {
  368. d,
  369. D,
  370. EncBucket,
  371. id: lox_cred.id,
  372. bucket: lox_cred.bucket,
  373. },
  374. ))
  375. }
  376. impl BridgeAuth {
  377. /// Receive a trust promotion request
  378. pub fn handle_trust_promotion(&mut self, req: Request) -> Result<Response, ProofError> {
  379. let A: &RistrettoPoint = &CMZ_A;
  380. let B: &RistrettoPoint = &CMZ_B;
  381. let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
  382. if req.P.is_identity() {
  383. return Err(ProofError::VerificationFailure);
  384. }
  385. // Recompute the "error factor" using knowledge of our own
  386. // (the issuer's) private key instead of knowledge of the
  387. // hidden attributes
  388. let Vprime = (self.lox_priv.x[0] + self.lox_priv.x[1] * req.id) * req.P
  389. + self.lox_priv.x[2] * req.CBucket
  390. + self.lox_priv.x[4] * req.CSince
  391. - req.CQ;
  392. // Recompute CG0 using Horner's method
  393. let today: Scalar = self.today().into();
  394. let unt: Scalar = UNTRUSTED_INTERVAL.into();
  395. let CG0prime = (today - unt) * req.P
  396. - req.CSince
  397. - pt_dbl(
  398. &(pt_dbl(
  399. &(pt_dbl(
  400. &(pt_dbl(
  401. &(pt_dbl(
  402. &(pt_dbl(&(pt_dbl(&(pt_dbl(&req.CG8) + req.CG7)) + req.CG6))
  403. + req.CG5),
  404. ) + req.CG4),
  405. ) + req.CG3),
  406. ) + req.CG2),
  407. ) + req.CG1),
  408. );
  409. // Verify the ZKP
  410. let mut transcript = Transcript::new(b"trust promotion request");
  411. requestproof::verify_compact(
  412. &req.piUser,
  413. &mut transcript,
  414. requestproof::VerifyAssignments {
  415. A: &A.compress(),
  416. B: &B.compress(),
  417. P: &req.P.compress(),
  418. CBucket: &req.CBucket.compress(),
  419. CSince: &req.CSince.compress(),
  420. V: &Vprime.compress(),
  421. Xbucket: &self.lox_pub.X[2].compress(),
  422. Xsince: &self.lox_pub.X[4].compress(),
  423. EncBucket0: &req.EncBucket.0.compress(),
  424. EncBucket1: &req.EncBucket.1.compress(),
  425. D: &req.D.compress(),
  426. CG0: &CG0prime.compress(),
  427. CG1: &req.CG1.compress(),
  428. CG2: &req.CG2.compress(),
  429. CG3: &req.CG3.compress(),
  430. CG4: &req.CG4.compress(),
  431. CG5: &req.CG5.compress(),
  432. CG6: &req.CG6.compress(),
  433. CG7: &req.CG7.compress(),
  434. CG8: &req.CG8.compress(),
  435. CG0sq: &req.CG0sq.compress(),
  436. CG1sq: &req.CG1sq.compress(),
  437. CG2sq: &req.CG2sq.compress(),
  438. CG3sq: &req.CG3sq.compress(),
  439. CG4sq: &req.CG4sq.compress(),
  440. CG5sq: &req.CG5sq.compress(),
  441. CG6sq: &req.CG6sq.compress(),
  442. CG7sq: &req.CG7sq.compress(),
  443. CG8sq: &req.CG8sq.compress(),
  444. },
  445. )?;
  446. // Ensure the id has not been seen before, either in the general
  447. // id filter, or the filter specifically for trust promotion.
  448. // Add the id to the latter, but not the former.
  449. if self.id_filter.check(&req.id) == SeenType::Seen
  450. || self.trust_promotion_filter.filter(&req.id) == SeenType::Seen
  451. {
  452. return Err(ProofError::VerificationFailure);
  453. }
  454. // Compute the encrypted MAC (Pk, EncQk) for the Migration Key
  455. // credential.
  456. // Compute the MAC on the visible attributes
  457. let mut rng = rand::thread_rng();
  458. let b = Scalar::random(&mut rng);
  459. let Pk = &b * Btable;
  460. let Pktable = RistrettoBasepointTable::create(&Pk);
  461. let Qid = &(self.migrationkey_priv.x[0] + self.migrationkey_priv.x[1] * req.id) * &Pktable;
  462. // El Gamal encrypt it to the public key req.D
  463. let s = Scalar::random(&mut rng);
  464. let EncQkid = (&s * Btable, Qid + s * req.D);
  465. // Homomorphically compute the part of the MAC corresponding to
  466. // the blinded attributes
  467. let tbucket = self.migrationkey_priv.x[2] * b;
  468. let EncQkBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
  469. let EncQk = (EncQkid.0 + EncQkBucket.0, EncQkid.1 + EncQkBucket.1);
  470. Ok(Response {
  471. Pk,
  472. EncQk,
  473. enc_migration_table: self.trustup_migration_table.encrypt_table(
  474. &req.id,
  475. &self.bridge_table,
  476. &Pktable,
  477. &self.migration_priv,
  478. &self.migrationkey_priv,
  479. ),
  480. })
  481. }
  482. }
  483. /// Handle the response to the request, producing a Migration credential
  484. /// if successful.
  485. ///
  486. /// The Migration credential can then be used in the migration protocol
  487. /// to actually upgrade to trust level 1.
  488. pub fn handle_response(state: State, resp: Response) -> Result<cred::Migration, ProofError> {
  489. if resp.Pk.is_identity() {
  490. return Err(ProofError::VerificationFailure);
  491. }
  492. // Decrypt the MAC on the Migration Key credential
  493. let Qk = resp.EncQk.1 - (state.d * resp.EncQk.0);
  494. // Use Qk to locate and decrypt the Migration credential
  495. match migration_table::decrypt_cred(&Qk, &state.id, &state.bucket, &resp.enc_migration_table) {
  496. Some(m) => Ok(m),
  497. None => Err(ProofError::VerificationFailure),
  498. }
  499. }