#include #include "serverEntity.hpp" /******************** * PUBLIC FUNCTIONS * ********************/ /* * CONSTRUCTORS */ PrsonaServerEntity::PrsonaServerEntity(size_t numServers) { if (numServers < 1) { std::cerr << "You have to have at least 1 server. " << "I'm making it anyways." << std::endl; } // Make the first server, which makes the BGN parameters PrsonaServer firstServer(numServers); servers.push_back(firstServer); // Make the rest of the servers, which take the BGN parameters const BGN& sharedBGN = firstServer.get_bgn_details(); for (size_t i = 1; i < numServers; i++) servers.push_back(PrsonaServer(numServers, sharedBGN)); // After all servers have made their seeds, // make sure they have the initial fresh generator std::vector pi; Curvepoint firstGenerator = get_fresh_generator(pi); for (size_t i = 0; i < numServers; i++) servers[i].initialize_fresh_generator(pi, firstGenerator); pi.clear(); // It's important that no server knows the DLOG between g and h for ElGamal, // so have each server collaborate to make h. Curvepoint blindGenerator = PrsonaServer::EL_GAMAL_GENERATOR; for (size_t i = 0; i < numServers; i++) { blindGenerator = servers[i].add_rand_seed_to_generator(pi, blindGenerator); } for (size_t i = 0; i < numServers; i++) servers[i].set_EG_blind_generator(pi, blindGenerator); } /* * BASIC PUBLIC SYSTEM INFO GETTERS */ BGNPublicKey PrsonaServerEntity::get_bgn_public_key() const { return get_bgn_public_key(0); } BGNPublicKey PrsonaServerEntity::get_bgn_public_key(size_t which) const { return servers[which].get_bgn_public_key(); } Curvepoint PrsonaServerEntity::get_blinding_generator() const { return get_blinding_generator(0); } Curvepoint PrsonaServerEntity::get_blinding_generator(size_t which) const { std::vector pi; Curvepoint retval = get_blinding_generator(pi, which); if (!servers[which].verify_generator_proof( pi, retval, servers[which].get_num_servers())) { std::cerr << "Error making the generator, aborting." << std::endl; return Curvepoint(); } return retval; } Curvepoint PrsonaServerEntity::get_blinding_generator( std::vector& pi) const { return get_blinding_generator(pi, 0); } Curvepoint PrsonaServerEntity::get_blinding_generator( std::vector& pi, size_t which) const { return servers[which].get_blinding_generator(pi); } Curvepoint PrsonaServerEntity::get_fresh_generator() const { return get_fresh_generator(0); } Curvepoint PrsonaServerEntity::get_fresh_generator(size_t which) const { std::vector pi; Curvepoint retval = get_fresh_generator(pi, which); if (!servers[which].verify_generator_proof( pi, retval, servers[which].get_num_servers())) { std::cerr << "Error making the generator, aborting." << std::endl; return Curvepoint(); } return retval; } Curvepoint PrsonaServerEntity::get_fresh_generator( std::vector& pi) const { return get_fresh_generator(pi, 0); } Curvepoint PrsonaServerEntity::get_fresh_generator( std::vector& pi, size_t which) const { Curvepoint retval = PrsonaServer::EL_GAMAL_GENERATOR; pi.clear(); for (size_t j = 0; j < servers[which].get_num_servers(); j++) { size_t index = (which + j) % servers[which].get_num_servers(); retval = servers[index].add_curr_seed_to_generator(pi, retval); } return retval; } size_t PrsonaServerEntity::get_num_clients() const { return get_num_clients(0); } size_t PrsonaServerEntity::get_num_clients(size_t which) const { return servers[which].get_num_clients(); } size_t PrsonaServerEntity::get_num_servers() const { return get_num_servers(0); } size_t PrsonaServerEntity::get_num_servers(size_t which) const { return servers[which].get_num_servers(); } /* * ENCRYPTED DATA GETTERS */ std::vector PrsonaServerEntity::get_current_votes_by( Proof& pi, const Curvepoint& shortTermPublicKey) const { return get_current_votes_by(pi, shortTermPublicKey, 0); } /* Call this in order to get the current encrypted votes cast by a given user * (who is identified by their short term public key). * In practice, this is intended for clients, * who need to know their current votes in order to rerandomize them. */ std::vector PrsonaServerEntity::get_current_votes_by( Proof& pi, const Curvepoint& shortTermPublicKey, size_t which) const { return servers[which].get_current_votes_by(pi, shortTermPublicKey); } EGCiphertext PrsonaServerEntity::get_current_tally( Proof& pi, const Curvepoint& shortTermPublicKey) const { return get_current_tally(pi, shortTermPublicKey, 0); } /* Call this in order to get the current encrypted tally of a given user * (who is identified by their short term public key). * In practice, this is intended for clients, so that the servers vouch * for their ciphertexts being valid as part of their reputation proofs. */ EGCiphertext PrsonaServerEntity::get_current_tally( Proof& pi, const Curvepoint& shortTermPublicKey, size_t which) const { return servers[which].get_current_tally(pi, shortTermPublicKey); } /* * CLIENT INTERACTIONS */ void PrsonaServerEntity::add_new_client(PrsonaClient& newUser) { add_new_client(newUser, 0); } /* Add a new client (who is identified only by their short term public key) * One server does the main work, then other servers import their (proven) * exported data. */ void PrsonaServerEntity::add_new_client(PrsonaClient& newUser, size_t which) { Proof proofOfValidSTPK, proofOfCorrectAddition, proofOfValidVotes; std::vector proofOfValidGenerator; Curvepoint freshGenerator = get_fresh_generator(proofOfValidGenerator, which); // Users can't actually announce a short term public key // if they don't know the fresh generator. newUser.receive_fresh_generator(proofOfValidGenerator, freshGenerator); Curvepoint shortTermPublicKey = newUser.get_short_term_public_key( proofOfValidSTPK); // Do the actual work of adding the client to the first server servers[which].add_new_client( proofOfValidSTPK, proofOfCorrectAddition, shortTermPublicKey); // Then, export the data to the rest of the servers std::vector previousVoteTally; std::vector currentPseudonyms; std::vector currentUserEncryptedTallies; std::vector currentTallyProofs; std::vector> voteMatrix; servers[which].export_updates( previousVoteTally, currentPseudonyms, currentUserEncryptedTallies, currentTallyProofs, voteMatrix); for (size_t j = 1; j < servers[which].get_num_servers(); j++) { size_t index = (which + j) % servers[which].get_num_servers(); servers[index].import_updates( proofOfCorrectAddition, previousVoteTally, currentPseudonyms, currentUserEncryptedTallies, currentTallyProofs, voteMatrix); } // Finally, give the user the information it needs // about its current tally and votes transmit_updates(newUser, which); } // Receive a new vote row from a user (identified by short term public key). bool PrsonaServerEntity::receive_vote( const std::vector& pi, const std::vector& newVotes, const Curvepoint& shortTermPublicKey) { return receive_vote(pi, newVotes, shortTermPublicKey, 0); } bool PrsonaServerEntity::receive_vote( const std::vector& pi, const std::vector& newVotes, const Curvepoint& shortTermPublicKey, size_t which) { bool retval = true; for (size_t i = 0; i < servers[which].get_num_servers(); i++) { size_t index = (i + which) % servers[which].get_num_servers(); retval = retval && servers[index].receive_vote(pi, newVotes, shortTermPublicKey); } return retval; } void PrsonaServerEntity::transmit_updates(PrsonaClient& currUser) const { transmit_updates(currUser, 0); } // After tallying scores and new vote matrix, // give those to a user for the new epoch void PrsonaServerEntity::transmit_updates( PrsonaClient& currUser, size_t which) const { Proof proofOfValidSTPK, proofOfScore, proofOfCorrectVotes; std::vector proofOfValidGenerator; Curvepoint freshGenerator = get_fresh_generator(proofOfValidGenerator, which); // Get users the next fresh generator so they can correctly // ask for their new scores and vote row currUser.receive_fresh_generator(proofOfValidGenerator, freshGenerator); Curvepoint shortTermPublicKey = currUser.get_short_term_public_key(proofOfValidSTPK); EGCiphertext score = get_current_tally(proofOfScore, shortTermPublicKey, which); currUser.receive_vote_tally(proofOfScore, score); } /* * EPOCH */ void PrsonaServerEntity::epoch(Proof& pi) { epoch(pi, 0); } // Do the epoch process void PrsonaServerEntity::epoch(Proof& pi, size_t which) { Curvepoint nextGenerator = PrsonaServer::EL_GAMAL_GENERATOR; std::vector previousVoteTally; std::vector currentPseudonyms; std::vector currentUserEncryptedTallies; std::vector currentTallyProofs; std::vector> voteMatrix; // go from A_0 to A_0.5 for (size_t i = 0; i < servers[which].get_num_servers(); i++) { size_t index = (which + i) % servers[which].get_num_servers(); size_t nextIndex = (which + i + 1) % servers[which].get_num_servers(); servers[index].build_up_midway_pseudonyms(pi, nextGenerator); servers[index].export_updates( previousVoteTally, currentPseudonyms, currentUserEncryptedTallies, currentTallyProofs, voteMatrix); servers[nextIndex].import_updates( pi, previousVoteTally, currentPseudonyms, currentUserEncryptedTallies, currentTallyProofs, voteMatrix); } /* Imagine that server 0 is encrypting these, then would do a ZKP that it * knows a secret mask and encrypted the correct value everyone else already * knows. Everyone else then adds a mask and proves they added a secret mask * to the committed values. */ currentUserEncryptedTallies = tally_scores(currentTallyProofs, nextGenerator, which); // go from A_0.5 to A_1 for (size_t i = 0; i < servers[which].get_num_servers(); i++) { size_t index = (which + i) % servers[which].get_num_servers(); size_t nextIndex = (which + i + 1) % servers[which].get_num_servers(); servers[index].break_down_midway_pseudonyms(pi, nextGenerator); servers[index].export_updates( previousVoteTally, currentPseudonyms, currentUserEncryptedTallies, currentTallyProofs, voteMatrix); servers[nextIndex].import_updates( pi, previousVoteTally, currentPseudonyms, currentUserEncryptedTallies, currentTallyProofs, voteMatrix); } // At the end, make sure all servers have same information for (size_t i = 1; i < servers[which].get_num_servers() - 1; i++) { size_t index = (which + i) % servers[which].get_num_servers(); servers[index].import_updates( pi, previousVoteTally, currentPseudonyms, currentUserEncryptedTallies, currentTallyProofs, voteMatrix); } } /********************* * PRIVATE FUNCTIONS * *********************/ /* * SCORE TALLYING */ /* Calculate scores, then scale the values appropriately, * so they are in the correct range to be used as vote weights. * * We're treating it as if we are one server, so that server gets the updated * weights to be sent to all other servers for the next epoch. */ std::vector PrsonaServerEntity::tally_scores( std::vector& tallyProofs, const Curvepoint& nextGenerator, size_t which) { std::vector retval; Proof maxScoreProof; std::vector decryptedTalliedScores = servers[which].tally_scores( tallyProofs); mpz_class maxScorePossibleThisRound = servers[which].get_max_possible_score(maxScoreProof).toInt() * PrsonaBase::get_max_allowed_vote(); mpz_class topOfScoreRange = decryptedTalliedScores.size() * PrsonaBase::get_max_allowed_vote(); for (size_t i = 0; i < decryptedTalliedScores.size(); i++) { decryptedTalliedScores[i] = Scalar( (decryptedTalliedScores[i].toInt() * topOfScoreRange) / maxScorePossibleThisRound ); EGCiphertext currCiphertext; retval.push_back(currCiphertext); Scalar currMask; currMask.set_random(); // Give the server the new weights, // to get passed around to the other servers servers[which].bgnSystem.encrypt( servers[which].previousVoteTallies[i], decryptedTalliedScores[i]); retval[i].mask = servers[which].currentPseudonyms[i] * currMask; retval[i].encryptedMessage = (nextGenerator * currMask) + (servers[which].get_blinding_generator() * decryptedTalliedScores[i]); } servers[which].currentUserEncryptedTallies = retval; servers[which].currentTallyProofs = tallyProofs; return retval; } /* * BINARY SEARCH */ // Completely normal binary search size_t PrsonaServerEntity::binary_search( const Curvepoint& shortTermPublicKey, size_t which) const { return servers[which].binary_search(shortTermPublicKey); }