server.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. #include <iostream>
  2. #include "server.hpp"
  3. /********************
  4. * PUBLIC FUNCTIONS *
  5. ********************/
  6. /*
  7. * CONSTRUCTORS
  8. */
  9. // Used to generate the first server; instantiates BGN for the first time
  10. PrsonaServer::PrsonaServer(size_t numServers)
  11. : numServers(numServers)
  12. {
  13. currentSeed.set_random();
  14. }
  15. // Used for all other servers, so they have the same BGN parameters
  16. PrsonaServer::PrsonaServer(size_t numServers, const BGN& otherBgn)
  17. : numServers(numServers), bgnSystem(otherBgn)
  18. {
  19. currentSeed.set_random();
  20. }
  21. /*
  22. * BASIC PUBLIC SYSTEM INFO GETTERS
  23. */
  24. BGNPublicKey PrsonaServer::get_bgn_public_key() const
  25. {
  26. return bgnSystem.get_public_key();
  27. }
  28. size_t PrsonaServer::get_num_clients() const
  29. {
  30. return currentPseudonyms.size();
  31. }
  32. size_t PrsonaServer::get_num_servers() const
  33. {
  34. return numServers;
  35. }
  36. /*
  37. * FRESH GENERATOR CALCULATION
  38. */
  39. // To calculate the current epoch's generator, start from the base generator,
  40. // then have every server call this function on it iteratively (in any order).
  41. Curvepoint PrsonaServer::add_curr_seed_to_generator(
  42. std::vector<Proof>& pi,
  43. const Curvepoint& currGenerator) const
  44. {
  45. pi.push_back(add_to_generator_proof(currGenerator, currentSeed));
  46. return currGenerator * currentSeed;
  47. }
  48. // To calculate the next epoch's generator, start from the base generator,
  49. // then have every server call this function on it iteratively (in any order).
  50. Curvepoint PrsonaServer::add_next_seed_to_generator(
  51. std::vector<Proof>& pi,
  52. const Curvepoint& currGenerator) const
  53. {
  54. pi.push_back(add_to_generator_proof(currGenerator, nextSeed));
  55. return currGenerator * nextSeed;
  56. }
  57. /*
  58. * ENCRYPTED DATA GETTERS
  59. */
  60. /* Call this in order to get the current encrypted votes cast by a given user
  61. * (who is identified by their short term public key).
  62. * In practice, this is intended for clients,
  63. * who need to know their current votes in order to rerandomize them. */
  64. std::vector<CurveBipoint> PrsonaServer::get_current_votes_by(
  65. Proof& pi, const Curvepoint& shortTermPublicKey) const
  66. {
  67. std::vector<CurveBipoint> retval;
  68. size_t voteSubmitter = binary_search(shortTermPublicKey);
  69. retval = voteMatrix[voteSubmitter];
  70. pi = generate_votes_valid_proof();
  71. return retval;
  72. }
  73. /* Call this in order to get the current encrypted tally of a given user
  74. * (who is identified by their short term public key).
  75. * In practice, this is intended for clients, so that the servers vouch
  76. * for their ciphertexts being valid as part of their reputation proofs. */
  77. EGCiphertext PrsonaServer::get_current_tally(
  78. Proof& pi, const Curvepoint& shortTermPublicKey) const
  79. {
  80. EGCiphertext retval;
  81. size_t tallyOwner = binary_search(shortTermPublicKey);
  82. retval = currentUserEncryptedTallies[tallyOwner];
  83. pi = currentTallyProofs[tallyOwner];
  84. return retval;
  85. }
  86. std::vector<Curvepoint> PrsonaServer::get_current_pseudonyms(Proof& pi) const
  87. {
  88. pi = generate_valid_pseudonyms_proof();
  89. return currentPseudonyms;
  90. }
  91. /*
  92. * CLIENT INTERACTIONS
  93. */
  94. /* Add a new client (who is identified only by their short term public key)
  95. * One server will do this, then ask all other servers to import their
  96. * (proven) exported data. */
  97. void PrsonaServer::add_new_client(
  98. const Proof& proofOfValidKey,
  99. Proof& proofOfValidAddition,
  100. const Curvepoint& shortTermPublicKey)
  101. {
  102. if (!verify_ownership_proof(
  103. proofOfValidKey, currentFreshGenerator, shortTermPublicKey))
  104. {
  105. std::cerr << "Could not verify proof of valid key." << std::endl;
  106. return;
  107. }
  108. currentPseudonyms.push_back(shortTermPublicKey);
  109. // The first epoch's score for a new user will be low,
  110. // but will typically converge on an average score quickly
  111. TwistBipoint encryptedDefaultTally;
  112. bgnSystem.encrypt(encryptedDefaultTally, DEFAULT_TALLY);
  113. previousVoteTallies.push_back(encryptedDefaultTally);
  114. Scalar mask;
  115. mask.set_random();
  116. EGCiphertext newUserEncryptedTally;
  117. newUserEncryptedTally.mask = shortTermPublicKey * mask;
  118. newUserEncryptedTally.encryptedMessage =
  119. currentFreshGenerator * mask +
  120. get_blinding_generator() * DEFAULT_TALLY;
  121. currentUserEncryptedTallies.push_back(newUserEncryptedTally);
  122. currentTallyProofs.push_back(generate_valid_default_tally_proof());
  123. // Users are defaulted to casting a neutral vote for others.
  124. CurveBipoint encryptedDefaultVote, encryptedSelfVote;
  125. bgnSystem.encrypt(encryptedDefaultVote, DEFAULT_VOTE);
  126. bgnSystem.encrypt(encryptedSelfVote, Scalar(MAX_ALLOWED_VOTE));
  127. std::vector<CurveBipoint> newRow;
  128. for (size_t i = 0; i < voteMatrix.size(); i++)
  129. {
  130. encryptedDefaultVote = bgnSystem.rerandomize(encryptedDefaultVote);
  131. voteMatrix[i].push_back(encryptedDefaultVote);
  132. encryptedDefaultVote = bgnSystem.rerandomize(encryptedDefaultVote);
  133. newRow.push_back(encryptedDefaultVote);
  134. }
  135. // Because we are adding the new user to the end (and then sorting it),
  136. // this last element (bottom right corner) is always the self vote.
  137. newRow.push_back(encryptedSelfVote);
  138. voteMatrix.push_back(newRow);
  139. order_data(proofOfValidAddition);
  140. proofOfValidAddition = generate_proof_of_added_user();
  141. }
  142. // Receive a new vote row from a user (identified by short term public key).
  143. bool PrsonaServer::receive_vote(
  144. const std::vector<Proof>& pi,
  145. const std::vector<CurveBipoint>& newVotes,
  146. const Curvepoint& shortTermPublicKey)
  147. {
  148. size_t voteSubmitter = binary_search(shortTermPublicKey);
  149. std::vector<CurveBipoint> oldVotes = voteMatrix[voteSubmitter];
  150. if (!verify_vote_proof(pi, oldVotes, newVotes, shortTermPublicKey))
  151. return false;
  152. voteMatrix[voteSubmitter] = newVotes;
  153. return true;
  154. }
  155. /*********************
  156. * PRIVATE FUNCTIONS *
  157. *********************/
  158. /*
  159. * CONSTRUCTOR HELPERS
  160. */
  161. const BGN& PrsonaServer::get_bgn_details() const
  162. {
  163. return bgnSystem;
  164. }
  165. bool PrsonaServer::initialize_fresh_generator(
  166. const std::vector<Proof>& pi,
  167. const Curvepoint& firstGenerator)
  168. {
  169. if (!verify_generator_proof(pi, firstGenerator, numServers))
  170. {
  171. std::cerr << "Could not verify generator proof, aborting." << std::endl;
  172. return false;
  173. }
  174. currentFreshGenerator = firstGenerator;
  175. return true;
  176. }
  177. // To calculate the blind generator for ElGamal, start from the base generator,
  178. // then have every server call this function on it iteratively (in any order).
  179. Curvepoint PrsonaServer::add_rand_seed_to_generator(
  180. std::vector<Proof>& pi,
  181. const Curvepoint& currGenerator) const
  182. {
  183. Scalar lambda;
  184. lambda.set_random();
  185. pi.push_back(add_to_generator_proof(currGenerator, lambda));
  186. return currGenerator * lambda;
  187. }
  188. bool PrsonaServer::set_EG_blind_generator(
  189. const std::vector<Proof>& pi,
  190. const Curvepoint& currGenerator)
  191. {
  192. return PrsonaBase::set_EG_blind_generator(pi, currGenerator, numServers);
  193. }
  194. /*
  195. * SCORE TALLYING
  196. */
  197. /* Calculate scores homomorphically (as an individual server would normally)
  198. * and then decrypt them (which the servers would normally work together to do).
  199. *
  200. * Note that since these calculations are just for us, we don't need to do any
  201. * expensive rerandomizations of intermediate values. */
  202. std::vector<Scalar> PrsonaServer::tally_scores(std::vector<Proof>& tallyProofs)
  203. {
  204. std::vector<Quadripoint> BGNEncryptedTallies;
  205. std::vector<Scalar> decryptedTallies;
  206. for (size_t i = 0; i < voteMatrix.size(); i++)
  207. {
  208. std::vector<Quadripoint> weightedVotes;
  209. // ZIP
  210. for (size_t j = 0; j < previousVoteTallies.size(); j++)
  211. {
  212. Quadripoint curr =
  213. bgnSystem.homomorphic_multiplication_no_rerandomize(
  214. voteMatrix[j][i], previousVoteTallies[j]);
  215. weightedVotes.push_back(curr);
  216. }
  217. // FOLDL
  218. Quadripoint currEncryptedTally = weightedVotes[0];
  219. for (size_t j = 1; j < weightedVotes.size(); j++)
  220. {
  221. currEncryptedTally =
  222. bgnSystem.homomorphic_addition_no_rerandomize(
  223. currEncryptedTally, weightedVotes[j]);
  224. }
  225. // DECRYPT
  226. decryptedTallies.push_back(bgnSystem.decrypt(currEncryptedTally));
  227. tallyProofs.push_back(generate_proof_of_correct_tally());
  228. }
  229. return decryptedTallies;
  230. }
  231. /* Calculate what the maximum possible score this round was (that is,
  232. * given the current user weights, what was the highest possible score?).
  233. *
  234. * As with individual scores, this also does the decryption that servers
  235. * would ordinarily work together to form. */
  236. Scalar PrsonaServer::get_max_possible_score(Proof& pi)
  237. {
  238. // FOLDL
  239. TwistBipoint currEncryptedVal = previousVoteTallies[0];
  240. for (size_t i = 1; i < previousVoteTallies.size(); i++)
  241. {
  242. currEncryptedVal =
  243. bgnSystem.homomorphic_addition_no_rerandomize(
  244. currEncryptedVal, previousVoteTallies[i]);
  245. }
  246. // DECRYPT
  247. Scalar retval = bgnSystem.decrypt(currEncryptedVal);
  248. pi = generate_proof_of_correct_sum();
  249. return retval;
  250. }
  251. /*
  252. * EPOCH ROUNDS
  253. */
  254. // The first round, going from A_0 to A_0.5
  255. void PrsonaServer::build_up_midway_pseudonyms(
  256. Proof& pi, Curvepoint& nextGenerator)
  257. {
  258. nextSeed.set_random();
  259. nextGenerator = nextGenerator * nextSeed;
  260. currentUserEncryptedTallies.clear();
  261. currentTallyProofs.clear();
  262. for (size_t i = 0; i < currentPseudonyms.size(); i++)
  263. currentPseudonyms[i] = currentPseudonyms[i] * nextSeed;
  264. rerandomize_data();
  265. order_data(pi);
  266. }
  267. // In between these rounds, scores are tallied, decrypted,
  268. // and encrypted to fresh user pseudonyms (possible through weird math)
  269. // The second round, going from A_0.5 to A_1
  270. void PrsonaServer::break_down_midway_pseudonyms(
  271. Proof& pi, const Curvepoint& nextGenerator)
  272. {
  273. Scalar inverseSeed = currentSeed.curveInverse();
  274. for (size_t i = 0; i < currentPseudonyms.size(); i++)
  275. {
  276. currentPseudonyms[i] = currentPseudonyms[i] * inverseSeed;
  277. currentUserEncryptedTallies[i].mask =
  278. currentUserEncryptedTallies[i].mask * inverseSeed;
  279. }
  280. currentSeed = nextSeed;
  281. currentFreshGenerator = nextGenerator;
  282. rerandomize_data();
  283. order_data(pi);
  284. }
  285. /*
  286. * DATA MAINTENANCE
  287. */
  288. void PrsonaServer::import_updates(
  289. const Proof& pi,
  290. const std::vector<TwistBipoint>& otherPreviousVoteTallies,
  291. const std::vector<Curvepoint>& otherCurrentPseudonyms,
  292. const std::vector<EGCiphertext>& otherCurrentUserEncryptedTallies,
  293. const std::vector<Proof>& otherCurrentTallyProofs,
  294. const std::vector<std::vector<CurveBipoint>>& otherVoteMatrix)
  295. {
  296. if (!verify_update_proof(pi))
  297. {
  298. std::cerr << "Could not verify valid update." << std::endl;
  299. return;
  300. }
  301. previousVoteTallies = otherPreviousVoteTallies;
  302. currentPseudonyms = otherCurrentPseudonyms;
  303. currentUserEncryptedTallies = otherCurrentUserEncryptedTallies;
  304. currentTallyProofs = otherCurrentTallyProofs;
  305. voteMatrix = otherVoteMatrix;
  306. }
  307. void PrsonaServer::export_updates(
  308. std::vector<TwistBipoint>& otherPreviousVoteTallies,
  309. std::vector<Curvepoint>& otherCurrentPseudonyms,
  310. std::vector<EGCiphertext>& otherCurrentUserEncryptedTallies,
  311. std::vector<Proof>& otherCurrentTallyProofs,
  312. std::vector<std::vector<CurveBipoint>>& otherVoteMatrix) const
  313. {
  314. otherPreviousVoteTallies = previousVoteTallies;
  315. otherCurrentPseudonyms = currentPseudonyms;
  316. otherCurrentUserEncryptedTallies = currentUserEncryptedTallies;
  317. otherCurrentTallyProofs = currentTallyProofs;
  318. otherVoteMatrix = voteMatrix;
  319. }
  320. /*
  321. * DATA SAFEKEEPING
  322. */
  323. // Everything needs to be rerandomized during epoch rounds
  324. // NOTE: this may need to add something about proofs later?
  325. void PrsonaServer::rerandomize_data()
  326. {
  327. for (size_t i = 0; i < voteMatrix.size(); i++)
  328. {
  329. for (size_t j = 0; j < voteMatrix[0].size(); j++)
  330. voteMatrix[i][j] = bgnSystem.rerandomize(voteMatrix[i][j]);
  331. bgnSystem.rerandomize(previousVoteTallies[i]);
  332. if (!currentUserEncryptedTallies.empty())
  333. {
  334. Scalar rerandomizer;
  335. rerandomizer.set_random();
  336. currentUserEncryptedTallies[i].mask =
  337. currentUserEncryptedTallies[i].mask +
  338. currentPseudonyms[i] * rerandomizer;
  339. currentUserEncryptedTallies[i].encryptedMessage =
  340. currentUserEncryptedTallies[i].encryptedMessage +
  341. currentFreshGenerator * rerandomizer;
  342. }
  343. }
  344. }
  345. /* This is what powers the "shuffle"; really, as pseudonyms get updated,
  346. * the pseudonyms are no longer in the order prescribed by operator<().
  347. * So, we put them (and everything else) back into that order,
  348. * effectively shuffling them (and making lookups easier later on). */
  349. std::vector<size_t> PrsonaServer::order_data(Proof& pi)
  350. {
  351. std::vector<size_t> retval;
  352. // SortingType's index member allows us to replicate the "sort" across
  353. std::vector<SortingType> sortTracker;
  354. for (size_t i = 0; i < currentPseudonyms.size(); i++)
  355. {
  356. SortingType curr;
  357. curr.pseudonym = currentPseudonyms[i];
  358. curr.index = i;
  359. sortTracker.push_back(curr);
  360. }
  361. std::sort(sortTracker.begin(), sortTracker.end());
  362. // Order all other data in the same way, for consistency
  363. std::vector<Curvepoint> newPseudonyms;
  364. std::vector<TwistBipoint> newVoteTallies;
  365. std::vector<EGCiphertext> newUserEncryptedTallies;
  366. std::vector<Proof> newTallyProofs;
  367. std::vector<std::vector<CurveBipoint>> newVoteMatrix;
  368. for (size_t i = 0; i < sortTracker.size(); i++)
  369. {
  370. newPseudonyms.push_back(sortTracker[i].pseudonym);
  371. newVoteTallies.push_back(previousVoteTallies[sortTracker[i].index]);
  372. if (!currentUserEncryptedTallies.empty())
  373. {
  374. newUserEncryptedTallies.push_back(
  375. currentUserEncryptedTallies[sortTracker[i].index]);
  376. }
  377. if (!currentTallyProofs.empty())
  378. {
  379. newTallyProofs.push_back(
  380. currentTallyProofs[sortTracker[i].index]);
  381. }
  382. std::vector<CurveBipoint> currNewRow;
  383. for (size_t j = 0; j < currentPseudonyms.size(); j++)
  384. {
  385. currNewRow.push_back(
  386. voteMatrix[sortTracker[i].index][sortTracker[j].index]);
  387. }
  388. newVoteMatrix.push_back(currNewRow);
  389. retval.push_back(sortTracker[i].index);
  390. }
  391. previousVoteTallies = newVoteTallies;
  392. currentPseudonyms = newPseudonyms;
  393. currentUserEncryptedTallies = newUserEncryptedTallies;
  394. currentTallyProofs = newTallyProofs;
  395. voteMatrix = newVoteMatrix;
  396. pi = generate_proof_of_shuffle();
  397. return retval;
  398. }
  399. /*
  400. * BINARY SEARCH
  401. */
  402. /* Completely normal binary search
  403. * There might be a standard function for this in <algorithms>?
  404. * But it returns an iterator, not a size_t, so less useful. */
  405. size_t PrsonaServer::binary_search(const Curvepoint& index) const
  406. {
  407. size_t lo, hi;
  408. lo = 0;
  409. hi = currentPseudonyms.size() - 1;
  410. while (lo < hi)
  411. {
  412. size_t mid = (lo + hi) / 2;
  413. if (currentPseudonyms[mid] < index)
  414. lo = mid + 1;
  415. else if (index == currentPseudonyms[mid])
  416. return mid;
  417. else hi = mid - 1;
  418. }
  419. return lo;
  420. }
  421. /*
  422. * VALID VOTE PROOFS
  423. */
  424. bool PrsonaServer::verify_vote_proof(
  425. const std::vector<Proof>& pi,
  426. const std::vector<CurveBipoint>& oldVotes,
  427. const std::vector<CurveBipoint>& newVotes,
  428. const Curvepoint& shortTermPublicKey) const
  429. {
  430. const BGNPublicKey& pubKey = bgnSystem.get_public_key();
  431. return PrsonaBase::verify_vote_proof(
  432. pubKey.get_bipoint_curvegen(),
  433. pubKey.get_bipoint_curve_subgroup_gen(),
  434. pi,
  435. oldVotes,
  436. newVotes,
  437. currentFreshGenerator,
  438. shortTermPublicKey);
  439. }