#include #include #include "server.hpp" /******************** * PUBLIC FUNCTIONS * ********************/ /* * CONSTRUCTORS */ // Used to generate the first server; instantiates BGN for the first time PrsonaServer::PrsonaServer( size_t numServers) : numServers(numServers) { currentSeed.set_random(); decryptMtx = new std::mutex(); } // Used for all other servers, so they have the same BGN parameters PrsonaServer::PrsonaServer( size_t numServers, const BGN& otherBgn) : numServers(numServers), bgnSystem(otherBgn) { currentSeed.set_random(); decryptMtx = new std::mutex(); } PrsonaServer::PrsonaServer( const PrsonaServer& other) { copy(other); } PrsonaServer::PrsonaServer( PrsonaServer&& other) { numServers = std::move(other.numServers); decryptMtx = other.decryptMtx; other.decryptMtx = NULL; bgnSystem = std::move(other.bgnSystem); currentSeed = std::move(other.currentSeed); nextSeed = std::move(other.nextSeed); currentGeneratorProof = std::move(other.currentGeneratorProof); currentFreshGenerator = std::move(other.currentFreshGenerator); previousVoteTallies = std::move(other.previousVoteTallies); currentPseudonyms = std::move(other.currentPseudonyms); currentUserEncryptedTallies = std::move(other.currentUserEncryptedTallies); voteMatrix = std::move(other.voteMatrix); } PrsonaServer &PrsonaServer::operator=( const PrsonaServer& other) { if (&other != this) { remove(); copy(other); } return *this; } PrsonaServer &PrsonaServer::operator=( PrsonaServer&& other) { if (&other != this) { remove(); numServers = std::move(other.numServers); decryptMtx = other.decryptMtx; other.decryptMtx = new std::mutex(); bgnSystem = std::move(other.bgnSystem); currentSeed = std::move(other.currentSeed); nextSeed = std::move(other.nextSeed); currentGeneratorProof = std::move(other.currentGeneratorProof); currentFreshGenerator = std::move(other.currentFreshGenerator); previousVoteTallies = std::move(other.previousVoteTallies); currentPseudonyms = std::move(other.currentPseudonyms); currentUserEncryptedTallies = std::move(other.currentUserEncryptedTallies); voteMatrix = std::move(other.voteMatrix); } return *this; } PrsonaServer::~PrsonaServer() { remove(); } void PrsonaServer::copy( const PrsonaServer& other) { numServers = other.numServers; decryptMtx = new std::mutex(); bgnSystem = other.bgnSystem; currentSeed = other.currentSeed; nextSeed = other.nextSeed; currentGeneratorProof = other.currentGeneratorProof; currentFreshGenerator = other.currentFreshGenerator; previousVoteTallies = other.previousVoteTallies; currentPseudonyms = other.currentPseudonyms; currentUserEncryptedTallies = other.currentUserEncryptedTallies; voteMatrix = other.voteMatrix; } void PrsonaServer::remove() { delete decryptMtx; decryptMtx = NULL; } /* * BASIC PUBLIC SYSTEM INFO GETTERS */ BGNPublicKey PrsonaServer::get_bgn_public_key() const { return bgnSystem.get_public_key(); } size_t PrsonaServer::get_num_clients() const { return currentPseudonyms.size(); } size_t PrsonaServer::get_num_servers() const { return numServers; } Twistpoint PrsonaServer::get_fresh_generator( std::vector& pi) const { pi = currentGeneratorProof; return currentFreshGenerator; } /* * FRESH GENERATOR CALCULATION */ // To calculate the current epoch's generator, start from the base generator, // then have every server call this function on it iteratively (in any order). Twistpoint PrsonaServer::add_curr_seed_to_generator( std::vector& pi, const Twistpoint& currGenerator) const { pi.push_back(add_to_generator_proof(currGenerator, currentSeed)); return currGenerator * currentSeed; } // To calculate the next epoch's generator, start from the base generator, // then have every server call this function on it iteratively (in any order). Twistpoint PrsonaServer::add_next_seed_to_generator( std::vector& pi, const Twistpoint& currGenerator) const { pi.push_back(add_to_generator_proof(currGenerator, nextSeed)); return currGenerator * nextSeed; } /* * ENCRYPTED DATA GETTERS */ /* 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 PrsonaServer::get_current_votes_by( Proof& pi, const Twistpoint& shortTermPublicKey) const { std::vector retval; size_t voteSubmitter = binary_search(shortTermPublicKey); retval = voteMatrix[voteSubmitter]; pi = generate_valid_vote_row_proof(retval); return retval; } std::vector> PrsonaServer::get_all_current_votes( Proof& pi) const { pi = generate_valid_vote_matrix_proof(voteMatrix); return voteMatrix; } /* 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 PrsonaServer::get_current_user_encrypted_tally( Proof& pi, const Twistpoint& shortTermPublicKey) const { EGCiphertext retval; size_t tallyOwner = binary_search(shortTermPublicKey); retval = currentUserEncryptedTallies[tallyOwner]; pi = generate_valid_user_tally_proof(retval); return retval; } CurveBipoint PrsonaServer::get_current_server_encrypted_tally( Proof& pi, const Twistpoint& shortTermPublicKey) const { CurveBipoint retval; size_t tallyOwner = binary_search(shortTermPublicKey); retval = previousVoteTallies[tallyOwner]; pi = generate_valid_server_tally_proof(retval); return retval; } std::vector PrsonaServer::get_current_pseudonyms( Proof& pi) const { pi = generate_valid_pseudonyms_proof(currentPseudonyms); return currentPseudonyms; } std::vector PrsonaServer::get_current_pseudonyms() const { return currentPseudonyms; } /* * PROOF COMMITMENT GETTERS */ Proof PrsonaServer::get_vote_row_commitment( const Twistpoint& request) const { size_t requestID = binary_search(request); return generate_valid_vote_row_proof(voteMatrix[requestID]); } Proof PrsonaServer::get_vote_matrix_commitment() const { return generate_valid_vote_matrix_proof(voteMatrix); } Proof PrsonaServer::get_user_tally_commitment( const Twistpoint& request) const { size_t requestID = binary_search(request); return generate_valid_user_tally_proof(currentUserEncryptedTallies[requestID]); } Proof PrsonaServer::get_server_tally_commitment( const Twistpoint& request) const { size_t requestID = binary_search(request); return generate_valid_server_tally_proof(previousVoteTallies[requestID]); } Proof PrsonaServer::get_pseudonyms_commitment() const { return generate_valid_pseudonyms_proof(currentPseudonyms); } void PrsonaServer::print_current_commitments() const { std::stringstream helper; std::cout << "Commitments:\n"; std::cout << "User tallies:\n"; for (size_t i = 0; i < currentUserEncryptedTallies.size(); i++) { helper.str(""); helper << currentUserEncryptedTallies[i]; std::cout << "Tally " << i + 1 << " of " << currentUserEncryptedTallies.size() << ": " << std::hex << oracle(helper.str()) << std::dec << "\n"; } std::cout << "Server tallies:\n"; for (size_t i = 0; i < previousVoteTallies.size(); i++) { helper.str(""); helper << previousVoteTallies[i]; std::cout << "Tally " << i + 1 << " of " << previousVoteTallies.size() << ": " << std::hex << oracle(helper.str()) << std::dec << "\n"; } } /* * CLIENT INTERACTIONS */ /* Add a new client (who is identified only by their short term public key) * One server will do this, then ask all other servers to import their * (proven) exported data. */ void PrsonaServer::add_new_client( std::vector& proofOfValidAddition, const Proof& proofOfValidKey, const Twistpoint& shortTermPublicKey) { if (!verify_ownership_proof(proofOfValidKey, currentFreshGenerator, shortTermPublicKey)) { std::cerr << "Could not verify proof of valid key." << std::endl; return; } currentPseudonyms.push_back(shortTermPublicKey); // The first epoch's score for a new user will be low, // but will typically converge on an average score quickly Scalar tallySeed; CurveBipoint encryptedDefaultTally = bgnSystem.get_public_key().curveEncrypt(tallySeed, DEFAULT_TALLY); previousVoteTallies.push_back(encryptedDefaultTally); Scalar seedForUserTally; seedForUserTally.set_random(); EGCiphertext newUserEncryptedTally; newUserEncryptedTally.mask = shortTermPublicKey * seedForUserTally; newUserEncryptedTally.encryptedMessage = currentFreshGenerator * seedForUserTally + elGamalBlindGenerator * DEFAULT_TALLY; currentUserEncryptedTallies.push_back(newUserEncryptedTally); // Users are defaulted to casting a neutral vote for others. TwistBipoint encryptedDefaultVote, encryptedSelfVote; Scalar currDefaultSeed, currSelfSeed; encryptedDefaultVote = bgnSystem.get_public_key().twistEncrypt(currDefaultSeed, DEFAULT_VOTE); encryptedSelfVote = bgnSystem.get_public_key().twistEncrypt(currSelfSeed, Scalar(MAX_ALLOWED_VOTE)); std::vector newRow; std::vector userVoteSeeds; std::vector otherVoteSeeds; for (size_t i = 0; i < voteMatrix.size(); i++) { Scalar addedSeed; encryptedDefaultVote = bgnSystem.get_public_key().rerandomize(addedSeed, encryptedDefaultVote); currDefaultSeed = currDefaultSeed + addedSeed; otherVoteSeeds.push_back(Scalar()); otherVoteSeeds[i] = currDefaultSeed; voteMatrix[i].push_back(encryptedDefaultVote); encryptedDefaultVote = bgnSystem.get_public_key().rerandomize(addedSeed, encryptedDefaultVote); currDefaultSeed = currDefaultSeed + addedSeed; userVoteSeeds.push_back(Scalar()); userVoteSeeds[i] = currDefaultSeed; newRow.push_back(encryptedDefaultVote); } // Because we are adding the new user to the end (and then sorting it), // this last element (bottom right corner) is always the self vote. userVoteSeeds.push_back(Scalar()); userVoteSeeds[newRow.size()] = currSelfSeed; otherVoteSeeds.push_back(Scalar()); otherVoteSeeds[newRow.size()] = currSelfSeed; newRow.push_back(encryptedSelfVote); voteMatrix.push_back(newRow); std::vector sortOrder = order_data(); std::vector newUserVoteSeeds; std::vector newOtherVoteSeeds; for (size_t i = 0; i < sortOrder.size(); i++) { newUserVoteSeeds.push_back(userVoteSeeds[sortOrder[i]]); newOtherVoteSeeds.push_back(otherVoteSeeds[sortOrder[i]]); } proofOfValidAddition = generate_proof_of_added_user(tallySeed, seedForUserTally, newUserVoteSeeds, newOtherVoteSeeds); } // Receive a new vote row from a user (identified by short term public key). bool PrsonaServer::receive_vote( const std::vector& pi, const std::vector& newVotes, const Twistpoint& shortTermPublicKey) { size_t voteSubmitter = binary_search(shortTermPublicKey); std::vector oldVotes = voteMatrix[voteSubmitter]; if (!verify_vote_proof(pi, oldVotes, newVotes, shortTermPublicKey)) return false; voteMatrix[voteSubmitter] = newVotes; return true; } /********************* * PRIVATE FUNCTIONS * *********************/ /* * CONSTRUCTOR HELPERS */ const BGN& PrsonaServer::get_bgn_details() const { return bgnSystem; } bool PrsonaServer::initialize_fresh_generator( const std::vector& pi, const Twistpoint& firstGenerator) { if (!verify_generator_proof(pi, firstGenerator, numServers)) { std::cerr << "Could not verify generator proof, aborting." << std::endl; return false; } currentGeneratorProof = pi; currentFreshGenerator = firstGenerator; return true; } // To calculate the blind generator for ElGamal, start from the base generator, // then have every server call this function on it iteratively (in any order). Twistpoint PrsonaServer::add_rand_seed_to_generator( std::vector& pi, const Twistpoint& currGenerator) const { Scalar lambda; lambda.set_random(); pi.push_back(add_to_generator_proof(currGenerator, lambda)); return currGenerator * lambda; } bool PrsonaServer::set_EG_blind_generator( const std::vector& pi, const Twistpoint& currGenerator) { return PrsonaBase::set_EG_blind_generator(pi, currGenerator, numServers); } /* * SCORE TALLYING */ void homomorphic_multiplication_r( void *a, void *b, const void *c, const void *d) { BGN *bgnSystem = (BGN *) a; Quadripoint *dst = (Quadripoint *) b; const CurveBipoint *x = (const CurveBipoint *) c; const TwistBipoint *y = (const TwistBipoint *) d; *dst = bgnSystem->homomorphic_multiplication_no_rerandomize(*x, *y); } void homomorphic_addition_r( void *a, void *b, size_t size) { BGN *bgnSystem = (BGN *) a; Quadripoint *arr = (Quadripoint *) b; switch (size) { case 0: case 1: return; case 2: arr[0] = bgnSystem->homomorphic_addition_no_rerandomize(arr[0], arr[1]); return; default: break; } size_t halfSize = size / 2; std::thread threadA(homomorphic_addition_r, bgnSystem, arr, halfSize); std::thread threadB(homomorphic_addition_r, bgnSystem, arr + halfSize, size - halfSize); threadA.join(); threadB.join(); arr[0] = bgnSystem->homomorphic_addition_no_rerandomize(arr[0], arr[halfSize]); } void tally_r( void *a, void *b, void *c, const void *d, const void *e, size_t i) { std::mutex *decryptMtx = (std::mutex *) a; BGN *bgnSystem = (BGN *) b; Scalar *dst = (Scalar *) c; const std::vector *previousVoteTallies = (const std::vector *) d; const std::vector> *voteMatrix = (const std::vector> *) e; Quadripoint *weightedVotes = new Quadripoint[previousVoteTallies->size()]; std::vector parallelizedMults; // ZIP for (size_t j = 0; j < previousVoteTallies->size(); j++) parallelizedMults.push_back(std::thread(homomorphic_multiplication_r, bgnSystem, weightedVotes + j, &((*previousVoteTallies)[j]), &((*voteMatrix)[j][i]))); for (size_t j = 0; j < parallelizedMults.size(); j++) parallelizedMults[j].join(); // FOLDL homomorphic_addition_r(bgnSystem, weightedVotes, previousVoteTallies->size()); // DECRYPT std::unique_lock lck(*decryptMtx); *dst = bgnSystem->decrypt(weightedVotes[0]); delete [] weightedVotes; } /* Calculate scores homomorphically (as an individual server would normally) * and then decrypt them (which the servers would normally work together to do). * * Note that since these calculations are just for us, we don't need to do any * expensive rerandomizations of intermediate values. */ std::vector PrsonaServer::tally_scores() { std::vector BGNEncryptedTallies; Scalar *decryptedTallies = new Scalar[voteMatrix.size()]; std::vector parallelizedTallies; for (size_t i = 0; i < voteMatrix.size(); i++) parallelizedTallies.push_back(std::thread(tally_r, decryptMtx, &bgnSystem, decryptedTallies + i, &previousVoteTallies, &voteMatrix, i)); for (size_t i = 0; i < parallelizedTallies.size(); i++) parallelizedTallies[i].join(); std::vector retval(decryptedTallies, decryptedTallies + voteMatrix.size()); delete [] decryptedTallies; return retval; } /* Calculate what the maximum possible score this round was (that is, * given the current user weights, what was the highest possible score?). * * As with individual scores, this also does the decryption that servers * would ordinarily work together to form. */ Scalar PrsonaServer::get_max_possible_score() { // FOLDL CurveBipoint currEncryptedVal = previousVoteTallies[0]; for (size_t i = 1; i < previousVoteTallies.size(); i++) currEncryptedVal = bgnSystem.homomorphic_addition_no_rerandomize(currEncryptedVal, previousVoteTallies[i]); // DECRYPT std::unique_lock lck(*decryptMtx); Scalar retval = bgnSystem.decrypt(currEncryptedVal); return retval; } void PrsonaServer::receive_tallied_scores( const std::vector& userTallyScores, const std::vector& serverTallyScores) { currentUserEncryptedTallies = userTallyScores; previousVoteTallies = serverTallyScores; } void PrsonaServer::encrypt( CurveBipoint& element, const Scalar& value) { bgnSystem.encrypt(element, value); } /* * EPOCH ROUNDS */ // The first round, going from A_0 to A_0.5 void PrsonaServer::build_up_midway_pseudonyms( std::vector>>& pi, std::vector>>& permutationCommits, std::vector>>& freshPseudonymCommits, std::vector>>& freshPseudonymSeedCommits, std::vector>>& serverTallyCommits, std::vector>>>& partwayVoteMatrixCommits, std::vector>>>& finalVoteMatrixCommits, Twistpoint& nextGenerator) { nextSeed.set_random(); std::vector> currPermutationCommits; std::vector> currFreshPseudonymCommits; std::vector> currFreshPseudonymSeedCommits; std::vector> currServerTallyCommits; std::vector>> currPartwayVoteMatrixCommits; std::vector>> currFinalVoteMatrixCommits; std::vector> currUserTallyMaskCommits; std::vector> currUserTallyMessageCommits; std::vector> currUserTallySeedCommits; pi.push_back(epoch_calculations(currPermutationCommits, currFreshPseudonymCommits, currFreshPseudonymSeedCommits, currServerTallyCommits, currPartwayVoteMatrixCommits, currFinalVoteMatrixCommits, currUserTallyMaskCommits, currUserTallyMessageCommits, currUserTallySeedCommits, nextSeed, nextGenerator, false)); permutationCommits.push_back(currPermutationCommits); freshPseudonymCommits.push_back(currFreshPseudonymCommits); freshPseudonymSeedCommits.push_back(currFreshPseudonymSeedCommits); serverTallyCommits.push_back(currServerTallyCommits); partwayVoteMatrixCommits.push_back(currPartwayVoteMatrixCommits); finalVoteMatrixCommits.push_back(currFinalVoteMatrixCommits); pi[0][0].push_back(add_to_generator_proof(nextGenerator, nextSeed)); nextGenerator = nextGenerator * nextSeed; } void PrsonaServer::hbc_build_up_midway_pseudonyms( std::vector& generatorProof, std::vector& newFreshPseudonyms, std::vector& newServerTallies, std::vector>& newVoteMatrix, Twistpoint& nextGenerator) { nextSeed.set_random(); std::vector newUserTallies; hbc_epoch_calculations(newFreshPseudonyms, newServerTallies, newVoteMatrix, newUserTallies, nextSeed, nextGenerator, false); generatorProof.push_back(add_to_generator_proof(nextGenerator, nextSeed)); nextGenerator = nextGenerator * nextSeed; } // In between these rounds, scores are tallied, decrypted, // and encrypted to fresh user pseudonyms (possible through weird math) // The second round, going from A_0.5 to A_1 void PrsonaServer::break_down_midway_pseudonyms( const std::vector& generatorProof, std::vector>>& pi, std::vector>>& permutationCommits, std::vector>>& freshPseudonymCommits, std::vector>>& freshPseudonymSeedCommits, std::vector>>& serverTallyCommits, std::vector>>>& partwayVoteMatrixCommits, std::vector>>>& finalVoteMatrixCommits, std::vector>>& userTallyMaskCommits, std::vector>>& userTallyMessageCommits, std::vector>>& userTallySeedCommits, const Twistpoint& nextGenerator) { if (!initialize_fresh_generator(generatorProof, nextGenerator)) { std::cerr << "New fresh generator could not be verified." << std::endl; return; } Scalar inverseSeed = currentSeed.curveMultInverse(); std::vector> currPermutationCommits; std::vector> currFreshPseudonymCommits; std::vector> currFreshPseudonymSeedCommits; std::vector> currServerTallyCommits; std::vector>> currPartwayVoteMatrixCommits; std::vector>> currFinalVoteMatrixCommits; std::vector> currUserTallyMaskCommits; std::vector> currUserTallyMessageCommits; std::vector> currUserTallySeedCommits; pi.push_back(epoch_calculations(currPermutationCommits, currFreshPseudonymCommits, currFreshPseudonymSeedCommits, currServerTallyCommits, currPartwayVoteMatrixCommits, currFinalVoteMatrixCommits, currUserTallyMaskCommits, currUserTallyMessageCommits, currUserTallySeedCommits, inverseSeed, nextGenerator, true)); permutationCommits.push_back(currPermutationCommits); freshPseudonymCommits.push_back(currFreshPseudonymCommits); freshPseudonymSeedCommits.push_back(currFreshPseudonymSeedCommits); serverTallyCommits.push_back(currServerTallyCommits); partwayVoteMatrixCommits.push_back(currPartwayVoteMatrixCommits); finalVoteMatrixCommits.push_back(currFinalVoteMatrixCommits); userTallyMaskCommits.push_back(currUserTallyMaskCommits); userTallyMessageCommits.push_back(currUserTallyMessageCommits); userTallySeedCommits.push_back(currUserTallySeedCommits); currentSeed = nextSeed; } void PrsonaServer::hbc_break_down_midway_pseudonyms( const std::vector& generatorProof, std::vector& newFreshPseudonyms, std::vector& newServerTallies, std::vector>& newVoteMatrix, std::vector& newUserTallies, const Twistpoint& nextGenerator) { if (!initialize_fresh_generator(generatorProof, nextGenerator)) { std::cerr << "New fresh generator could not be verified." << std::endl; return; } Scalar inverseSeed = currentSeed.curveMultInverse(); hbc_epoch_calculations(newFreshPseudonyms, newServerTallies, newVoteMatrix, newUserTallies, inverseSeed, nextGenerator, true); currentSeed = nextSeed; } /* * EPOCH HELPERS */ void generate_permutation_commitment_r( const void *a, void *b, const void *c, void *d) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector> *permutationCommits = (std::vector> *) b; const std::vector> *permutations = (const std::vector> *) c; std::vector> *permutationSeeds = (std::vector> *) d; *permutationCommits = server->generate_commitment_matrix(*permutations, *permutationSeeds); } void generate_pseudonym_commitment_r( const void *a, void *b, const void *c, const void *d, void *e, void *f) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector> *freshPseudonymCommits = (std::vector> *) b; const std::vector> *permutations = (const std::vector> *) c; const Scalar *power = (const Scalar *) d; std::vector> *freshPseudonymSeeds = (std::vector> *) e; std::vector> *freshPseudonymSeedCommits = (std::vector> *) f; freshPseudonymSeedCommits->clear(); *freshPseudonymCommits = server->generate_pseudonym_matrix(*permutations, *power, *freshPseudonymSeeds, *freshPseudonymSeedCommits); } void generate_server_tally_commitment_r( const void *a, void *b, const void *c, void *d) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector> *serverTallyCommits = (std::vector> *) b; const std::vector> *permutations = (const std::vector> *) c; std::vector> *serverTallySeeds = (std::vector> *) d; *serverTallyCommits = server->generate_server_tally_matrix(*permutations, *serverTallySeeds); } void generate_matrix_commitment_r( const void *a, void *b, void *c, void *d, const void *e, const void *f, void *g, void *h) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector>> *partwayVoteMatrixCommits = (std::vector>> *) b; std::vector> *partialVoteMatrix = (std::vector> *) c; std::vector>> *finalVoteMatrixCommits = (std::vector>> *) d; const std::vector> *permutations = (const std::vector> *) e; const std::vector> *voteMatrix = (const std::vector> *) f; std::vector>> *partwayVoteMatrixSeeds = (std::vector>> *) g; std::vector>> *finalVoteMatrixSeeds = (std::vector>> *) h; *partwayVoteMatrixCommits = server->generate_vote_tensor(*permutations, *voteMatrix, *partwayVoteMatrixSeeds, false); *partialVoteMatrix = server->calculate_next_vote_matrix(*partwayVoteMatrixCommits); *finalVoteMatrixCommits = server->generate_vote_tensor(*permutations, *partialVoteMatrix, *finalVoteMatrixSeeds, true); } void generate_user_tally_commitment_r( const void *a, const void *b, const void *c, const void *d, const void *e, void *f, void *g, void *h, void *i, void *j, void *k) { const PrsonaServer *server = (const PrsonaServer *) a; const std::vector> *permutations = (const std::vector> *) b; const Scalar *power = (const Scalar *) c; const Twistpoint *nextGenerator = (const Twistpoint *) d; const std::vector *currentPseudonyms = (const std::vector *) e; std::vector *userTallyMasks = (std::vector *) f; std::vector> *userTallyMaskCommits = (std::vector> *) g; std::vector *userTallyMessages = (std::vector *) h; std::vector> *userTallyMessageCommits = (std::vector> *) i; std::vector> *userTallySeeds = (std::vector> *) j; std::vector> *userTallySeedCommits = (std::vector> *) k; userTallyMaskCommits->clear(); userTallyMessageCommits->clear(); userTallySeedCommits->clear(); server->generate_user_tally_matrix(*permutations, *power, *nextGenerator, *currentPseudonyms, *userTallyMasks, *userTallyMaskCommits, *userTallyMessages, *userTallyMessageCommits, *userTallySeeds, *userTallySeedCommits); } void generate_permutation_proof_r( const void *a, void *b, const void *c, const void *d, const void *e) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector *dst = (std::vector *) b; const std::vector> *permutations = (const std::vector> *) c; const std::vector> *permutationSeeds = (const std::vector> *) d; const std::vector> *permutationCommits = (const std::vector> *) e; *dst = server->generate_valid_permutation_proof(*permutations, *permutationSeeds, *permutationCommits); } void generate_pseudonym_proof_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g, const void *h, const void *i, const void *j) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector *dst = (std::vector *) b; const std::vector> *permutations = (const std::vector> *) c; const Scalar *power = (const Scalar *) d; const std::vector> *permutationSeeds = (const std::vector> *) e; const std::vector> *freshPseudonymSeeds = (const std::vector> *) f; const std::vector *currentPseudonyms = (const std::vector *) g; const std::vector> *permutationCommits = (const std::vector> *) h; const std::vector> *freshPseudonymCommits = (const std::vector> *) i; const std::vector> *freshPseudonymSeedCommits = (const std::vector> *) j; *dst = server->generate_proof_of_reordering_plus_power(*permutations, *power, *permutationSeeds, *freshPseudonymSeeds, *currentPseudonyms, *permutationCommits, *freshPseudonymCommits, *freshPseudonymSeedCommits); } void generate_server_tally_proof_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g, const void *h, const void *i, const void *j) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector *dst = (std::vector *) b; const std::vector> *permutations = (const std::vector> *) c; const std::vector> *permutationSeeds = (const std::vector> *) d; const std::vector> *serverTallySeeds = (const std::vector> *) e; const std::vector *previousVoteTallies = (const std::vector *) f; const std::vector> *permutationCommits = (const std::vector> *) g; const std::vector> *serverTallyCommits = (const std::vector> *) h; const CurveBipoint *curveG = (const CurveBipoint *) i; const CurveBipoint *curveH = (const CurveBipoint *) j; *dst = server->generate_proof_of_reordering(*permutations, *permutationSeeds, *serverTallySeeds, *previousVoteTallies, *permutationCommits, *serverTallyCommits, *curveG, *curveH); } void generate_first_half_matrix_proof_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g, const void *h) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector> *dst = (std::vector> *) b; const std::vector> *permutations = (const std::vector> *) c; const std::vector> *permutationSeeds = (const std::vector> *) d; const std::vector>> *partwayVoteMatrixSeeds = (const std::vector>> *) e; const std::vector> *voteMatrix = (const std::vector> *) f; const std::vector> *permutationCommits = (const std::vector> *) g; const std::vector>> *partwayVoteMatrixCommits = (const std::vector>> *) h; server->generate_vote_tensor_proofs(*dst, *permutations, *permutationSeeds, *partwayVoteMatrixSeeds, *voteMatrix, *permutationCommits, *partwayVoteMatrixCommits, false); } void generate_second_half_matrix_proof_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g, const void *h) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector> *dst = (std::vector> *) b; const std::vector> *permutations = (const std::vector> *) c; const std::vector> *permutationSeeds = (const std::vector> *) d; const std::vector>> *finalVoteMatrixSeeds = (const std::vector>> *) e; const std::vector> *partialVoteMatrix = (const std::vector> *) f; const std::vector> *permutationCommits = (const std::vector> *) g; const std::vector>> *finalVoteMatrixCommits = (const std::vector>> *) h; server->generate_vote_tensor_proofs(*dst, *permutations, *permutationSeeds, *finalVoteMatrixSeeds, *partialVoteMatrix, *permutationCommits, *finalVoteMatrixCommits, true); } void generate_user_tally_proof_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g, const void *h, const void *i, const void *j, const void *k, const void *l, const void *m, const void *n) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector *dst = (std::vector *) b; const std::vector> *permutations = (const std::vector> *) c; const Scalar *power = (const Scalar *) d; const Twistpoint *nextGenerator = (const Twistpoint *) e; const std::vector> *permutationSeeds = (const std::vector> *) f; const std::vector> *userTallySeeds = (const std::vector> *) g; const std::vector *currentPseudonyms = (const std::vector *) h; const std::vector *userTallyMasks = (const std::vector *) i; const std::vector *userTallyMessages = (const std::vector *) j; const std::vector> *permutationCommits = (const std::vector> *) k; const std::vector> *userTallyMaskCommits = (const std::vector> *) l; const std::vector> *userTallyMessageCommits = (const std::vector> *) m; const std::vector> *userTallySeedCommits = (const std::vector> *) n; *dst = server->generate_user_tally_proofs(*permutations, *power, *nextGenerator, *permutationSeeds, *userTallySeeds, *currentPseudonyms, *userTallyMasks, *userTallyMessages, *permutationCommits, *userTallyMaskCommits, *userTallyMessageCommits, *userTallySeedCommits); } std::vector> PrsonaServer::epoch_calculations( std::vector>& permutationCommits, std::vector>& freshPseudonymCommits, std::vector>& freshPseudonymSeedCommits, std::vector>& serverTallyCommits, std::vector>>& partwayVoteMatrixCommits, std::vector>>& finalVoteMatrixCommits, std::vector>& userTallyMaskCommits, std::vector>& userTallyMessageCommits, std::vector>& userTallySeedCommits, const Scalar& power, const Twistpoint& nextGenerator, bool doUserTallies) { std::vector> permutations = generate_permutation_matrix(power); // for (size_t i = 0; i < permutations.size(); i++) // { // std::cout << (i == 0 ? "[[" : " ["); // for (size_t j = 0; j < permutations[i].size(); j++) // std::cout << std::hex << permutations[i][j] << std::dec << (j == permutations[i].size() - 1 ? "]" : " "); // std::cout << (i == permutations.size() - 1 ? "]" : "") << std::endl; // } std::vector> retval, firstHalfTensorProof, secondHalfTensorProof; CurveBipoint curveG = bgnSystem.get_public_key().get_bipoint_curvegen(); CurveBipoint curveH = bgnSystem.get_public_key().get_bipoint_curve_subgroup_gen(); std::vector *individualProofs = new std::vector[doUserTallies ? 4 : 3]; std::vector> permutationSeeds; std::thread permutationCommitThread(generate_permutation_commitment_r, this, &permutationCommits, &permutations, &permutationSeeds); std::vector> freshPseudonymSeeds; std::thread pseudonymCommitThread(generate_pseudonym_commitment_r, this, &freshPseudonymCommits, &permutations, &power, &freshPseudonymSeeds, &freshPseudonymSeedCommits); std::vector> serverTallySeeds; std::thread serverTallyCommitThread(generate_server_tally_commitment_r, this, &serverTallyCommits, &permutations, &serverTallySeeds); std::vector>> partwayVoteMatrixSeeds; std::vector>> finalVoteMatrixSeeds; std::vector> partialVoteMatrix; std::thread voteMatrixCommitThread(generate_matrix_commitment_r, this, &partwayVoteMatrixCommits, &partialVoteMatrix, &finalVoteMatrixCommits, &permutations, &voteMatrix, &partwayVoteMatrixSeeds, &finalVoteMatrixSeeds); std::vector userTallyMasks; std::vector userTallyMessages; std::vector> userTallySeeds; std::thread *userTallyCommitThread = NULL; if (doUserTallies) userTallyCommitThread = new std::thread(generate_user_tally_commitment_r, this, &permutations, &power, &nextGenerator, ¤tPseudonyms, &userTallyMasks, &userTallyMaskCommits, &userTallyMessages, &userTallyMessageCommits, &userTallySeeds, &userTallySeedCommits); permutationCommitThread.join(); std::thread permutationProofThread(generate_permutation_proof_r, this, individualProofs, &permutations, &permutationSeeds, &permutationCommits); pseudonymCommitThread.join(); std::thread pseudonymProofThread(generate_pseudonym_proof_r, this, individualProofs + 1, &permutations, &power, &permutationSeeds, &freshPseudonymSeeds, ¤tPseudonyms, &permutationCommits, &freshPseudonymCommits, &freshPseudonymSeedCommits); serverTallyCommitThread.join(); std::thread serverTallyProofThread(generate_server_tally_proof_r, this, individualProofs + 2, &permutations, &permutationSeeds, &serverTallySeeds, &previousVoteTallies, &permutationCommits, &serverTallyCommits, &curveG, &curveH); voteMatrixCommitThread.join(); std::thread firstHalfProofThread(generate_first_half_matrix_proof_r, this, &firstHalfTensorProof, &permutations, &permutationSeeds, &partwayVoteMatrixSeeds, &voteMatrix, &permutationCommits, &partwayVoteMatrixCommits); std::thread secondHalfProofThread(generate_second_half_matrix_proof_r, this, &secondHalfTensorProof, &permutations, &permutationSeeds, &finalVoteMatrixSeeds, &partialVoteMatrix, &permutationCommits, &finalVoteMatrixCommits); if (userTallyCommitThread) { userTallyCommitThread->join(); delete userTallyCommitThread; userTallyCommitThread = NULL; } std::thread *userTallyProofThread = NULL; if (doUserTallies) userTallyProofThread = new std::thread(generate_user_tally_proof_r, this, individualProofs + 3, &permutations, &power, &nextGenerator, &permutationSeeds, &userTallySeeds, ¤tPseudonyms, &userTallyMasks, &userTallyMessages, &permutationCommits, &userTallyMaskCommits, &userTallyMessageCommits, &userTallySeedCommits); permutationProofThread.join(); pseudonymProofThread.join(); serverTallyProofThread.join(); firstHalfProofThread.join(); secondHalfProofThread.join(); if (userTallyProofThread) { userTallyProofThread->join(); delete userTallyProofThread; userTallyProofThread = NULL; } retval.push_back(individualProofs[0]); retval.push_back(individualProofs[1]); retval.push_back(individualProofs[2]); for (size_t i = 0; i < firstHalfTensorProof.size(); i++) retval.push_back(firstHalfTensorProof[i]); for (size_t i = 0; i < secondHalfTensorProof.size(); i++) retval.push_back(secondHalfTensorProof[i]); if (doUserTallies) retval.push_back(individualProofs[3]); delete [] individualProofs; // Replace internal values update_data(freshPseudonymCommits, serverTallyCommits, finalVoteMatrixCommits, userTallyMaskCommits, userTallyMessageCommits); return retval; } void PrsonaServer::hbc_epoch_calculations( std::vector& newFreshPseudonyms, std::vector& newServerTallies, std::vector>& newVoteMatrix, std::vector& newUserTallies, const Scalar& power, const Twistpoint& nextGenerator, bool doUserTallies) { std::vector shuffleOrder = generate_hbc_shuffle(power); newFreshPseudonyms.clear(); newServerTallies.clear(); newVoteMatrix.clear(); if (doUserTallies) newUserTallies.clear(); for (size_t i = 0; i < shuffleOrder.size(); i++) { newFreshPseudonyms.push_back(currentPseudonyms[shuffleOrder[i]] * power); newServerTallies.push_back(bgnSystem.rerandomize(previousVoteTallies[shuffleOrder[i]])); if (doUserTallies) { Scalar r; r.set_random(); EGCiphertext curr; curr.mask = currentUserEncryptedTallies[shuffleOrder[i]].mask * power + newFreshPseudonyms[i] * r; curr.encryptedMessage = currentUserEncryptedTallies[shuffleOrder[i]].encryptedMessage + nextGenerator * r; newUserTallies.push_back(curr); } } hbc_shuffle_vote_matrix(newVoteMatrix, voteMatrix, shuffleOrder); // Replace internal values hbc_update_data(newFreshPseudonyms, newServerTallies, newVoteMatrix, newUserTallies); } void PrsonaServer::hbc_shuffle_vote_matrix( std::vector>& newVoteMatrix, const std::vector>& oldVoteMatrix, const std::vector shuffleOrder) const { for (size_t i = 0; i < shuffleOrder.size(); i++) { std::vector currRow; for (size_t j = 0; j < shuffleOrder.size(); j++) currRow.push_back(bgnSystem.rerandomize(oldVoteMatrix[shuffleOrder[i]][shuffleOrder[j]])); newVoteMatrix.push_back(currRow); } } void verify_permutation_r( const void *a, void *b, const void *c, const void *d) { const PrsonaServer *server = (const PrsonaServer *) a; bool *dst = (bool *) b; const std::vector *pi = (const std::vector *) c; const std::vector> *permutationCommits = (const std::vector> *) d; *dst = server->verify_valid_permutation_proof(*pi, *permutationCommits); } void verify_pseudonym_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g) { const PrsonaServer *server = (const PrsonaServer *) a; bool *dst = (bool *) b; const std::vector *pi = (const std::vector *) c; const std::vector *currentPseudonyms = (const std::vector *) d; const std::vector> *permutationCommits = (const std::vector> *) e; const std::vector> *freshPseudonymCommits = (const std::vector> *) f; const std::vector> *freshPseudonymSeedCommits = (const std::vector> *) g; *dst = server->verify_proof_of_reordering_plus_power(*pi, *currentPseudonyms, *permutationCommits, *freshPseudonymCommits, *freshPseudonymSeedCommits); } void verify_server_tally_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g, const void *h) { const PrsonaServer *server = (const PrsonaServer *) a; bool *dst = (bool *) b; const std::vector *pi = (const std::vector *) c; const std::vector *previousVoteTallies = (const std::vector *) d; const std::vector> *permutationCommits = (const std::vector> *) e; const std::vector> *serverTallyCommits = (const std::vector> *) f; const CurveBipoint *curveG = (const CurveBipoint *) g; const CurveBipoint *curveH = (const CurveBipoint *) h; *dst = server->verify_proof_of_reordering(*pi, *previousVoteTallies, *permutationCommits, *serverTallyCommits, *curveG, *curveH); } void verify_first_half_matrix_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g) { const PrsonaServer *server = (const PrsonaServer *) a; bool *dst = (bool *) b; const std::vector> *pi = (const std::vector> *) c; const size_t *currOffset = (const size_t *) d; const std::vector> *voteMatrix = (const std::vector> *) e; const std::vector> *permutationCommits = (const std::vector> *) f; const std::vector>> *partwayVoteMatrixCommits = (const std::vector>> *) g; *dst = server->verify_vote_tensor_proofs(*pi, *currOffset, *voteMatrix, *permutationCommits, *partwayVoteMatrixCommits, false); } void verify_second_half_matrix_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g) { const PrsonaServer *server = (const PrsonaServer *) a; bool *dst = (bool *) b; const std::vector> *pi = (const std::vector> *) c; const size_t *currOffset = (const size_t *) d; const std::vector> *permutationCommits = (const std::vector> *) e; const std::vector>> *partwayVoteMatrixCommits = (const std::vector>> *) f; const std::vector>> *finalVoteMatrixCommits = (const std::vector>> *) g; std::vector> partialVoteMatrix = server->calculate_next_vote_matrix(*partwayVoteMatrixCommits); *dst = server->verify_vote_tensor_proofs(*pi, *currOffset, partialVoteMatrix, *permutationCommits, *finalVoteMatrixCommits, true); } void verify_user_tally_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g, const void *h, const void *i, const void *j) { const PrsonaServer *server = (const PrsonaServer *) a; bool *dst = (bool *) b; const std::vector *pi = (const std::vector *) c; const Twistpoint *nextGenerator = (const Twistpoint *) d; const std::vector *currentPseudonyms = (const std::vector *) e; const std::vector *currentUserEncryptedTallies = (const std::vector *) f; const std::vector> *permutationCommits = (const std::vector> *) g; const std::vector> *userTallyMaskCommits = (const std::vector> *) h; const std::vector> *userTallyMessageCommits = (const std::vector> *) i; const std::vector> *userTallySeedCommits = (const std::vector> *) j; std::vector userTallyMasks; std::vector userTallyMessages; for (size_t i = 0; i < currentUserEncryptedTallies->size(); i++) { userTallyMasks.push_back((*currentUserEncryptedTallies)[i].mask); userTallyMessages.push_back((*currentUserEncryptedTallies)[i].encryptedMessage); } *dst = server->verify_user_tally_proofs(*pi, *nextGenerator, *currentPseudonyms, userTallyMasks, userTallyMessages, *permutationCommits, *userTallyMaskCommits, *userTallyMessageCommits, *userTallySeedCommits); } bool PrsonaServer::accept_epoch_updates( const std::vector>& pi, const std::vector>& permutationCommits, const std::vector>& freshPseudonymCommits, const std::vector>& freshPseudonymSeedCommits, const std::vector>& serverTallyCommits, const std::vector>>& partwayVoteMatrixCommits, const std::vector>>& finalVoteMatrixCommits, const std::vector>& userTallyMaskCommits, const std::vector>& userTallyMessageCommits, const std::vector>& userTallySeedCommits, const Twistpoint& nextGenerator, bool doUserTallies) { bool permutationVerification = false; bool pseudonymVerification = false; bool serverTallyVerification = false; bool firstHalfVerification = false; bool secondHalfVerification = false; bool userTallyVerification = true; if ((userTallyMaskCommits.empty() && doUserTallies) || (!userTallyMaskCommits.empty() && !doUserTallies)) { std::cerr << "User tallies are not in expected state." << std::endl; return false; } if (pi.empty()) return false; size_t currOffset = 0; std::thread permutationThread(verify_permutation_r, this, &permutationVerification, &(pi[currOffset]), &permutationCommits); currOffset++; std::thread pseudonymThread(verify_pseudonym_r, this, &pseudonymVerification, &(pi[currOffset]), ¤tPseudonyms, &permutationCommits, &freshPseudonymCommits, &freshPseudonymSeedCommits); currOffset++; CurveBipoint curveG = bgnSystem.get_public_key().get_bipoint_curvegen(); CurveBipoint curveH = bgnSystem.get_public_key().get_bipoint_curve_subgroup_gen(); std::thread serverTallyThread(verify_server_tally_r, this, &serverTallyVerification, &(pi[currOffset]), &previousVoteTallies, &permutationCommits, &serverTallyCommits, &curveG, &curveH); currOffset++; size_t firstHalfOffset = currOffset; std::thread firstHalfThread(verify_first_half_matrix_r, this, &firstHalfVerification, &pi, &firstHalfOffset, &voteMatrix, &permutationCommits, &partwayVoteMatrixCommits); currOffset += voteMatrix.size(); size_t secondHalfOffset = currOffset; std::thread secondHalfThread(verify_second_half_matrix_r, this, &secondHalfVerification, &pi, &secondHalfOffset, &permutationCommits, &partwayVoteMatrixCommits, &finalVoteMatrixCommits); currOffset += voteMatrix.size(); std::thread *userTallyThread = NULL; if (doUserTallies) userTallyThread = new std::thread(verify_user_tally_r, this, &userTallyVerification, &(pi[currOffset]), &nextGenerator, ¤tPseudonyms, ¤tUserEncryptedTallies, &permutationCommits, &userTallyMaskCommits, &userTallyMessageCommits, &userTallySeedCommits); currOffset++; permutationThread.join(); pseudonymThread.join(); serverTallyThread.join(); firstHalfThread.join(); secondHalfThread.join(); if (userTallyThread) { userTallyThread->join(); delete userTallyThread; userTallyThread = NULL; } if (!permutationVerification) { std::cerr << "Could not verify valid permutation matrix." << std::endl; return false; } if (!pseudonymVerification) { std::cerr << "Could not verify valid pseudonym vector." << std::endl; return false; } if (!serverTallyVerification) { std::cerr << "Could not verify valid server tally vector." << std::endl; return false; } if (!userTallyVerification) { std::cerr << "Could not verify user tallies." << std::endl; return false; } if (!firstHalfVerification) { std::cerr << "Could not verify first half vote matrix." << std::endl; return false; } if (!secondHalfVerification) { std::cerr << "Could not verify second half vote matrix." << std::endl; return false; } bool finalVerification = update_data(freshPseudonymCommits, serverTallyCommits, finalVoteMatrixCommits, userTallyMaskCommits, userTallyMessageCommits); return finalVerification; } bool PrsonaServer::hbc_accept_epoch_updates( const std::vector& newFreshPseudonyms, const std::vector& newServerTallies, const std::vector>& newVoteMatrix, const std::vector& newUserTallies, bool doUserTallies) { if ((newUserTallies.empty() && doUserTallies) || (!newUserTallies.empty() && !doUserTallies)) { std::cerr << "User tallies are not in expected state." << std::endl; return false; } return hbc_update_data(newFreshPseudonyms, newServerTallies, newVoteMatrix, newUserTallies); } std::vector> PrsonaServer::generate_permutation_matrix( const Scalar& reorderSeed) const { std::vector> retval; for (size_t i = 0; i < currentPseudonyms.size(); i++) { std::vector currRow; for (size_t j = 0; j < currentPseudonyms.size(); j++) currRow.push_back(Scalar(0)); retval.push_back(currRow); } std::vector nextPseudonyms; for (size_t i = 0; i < currentPseudonyms.size(); i++) nextPseudonyms.push_back(currentPseudonyms[i] * reorderSeed); std::vector order = sort_data(nextPseudonyms); for (size_t i = 0; i < order.size(); i++) retval[order[i]][i] = Scalar(1); return retval; } std::vector PrsonaServer::generate_hbc_shuffle( const Scalar& reorderSeed) const { std::vector nextPseudonyms; for (size_t i = 0; i < currentPseudonyms.size(); i++) nextPseudonyms.push_back(currentPseudonyms[i] * reorderSeed); return sort_data(nextPseudonyms); } std::vector> PrsonaServer::generate_commitment_matrix( const std::vector>& permutations, std::vector>& seeds) const { std::vector> retval; Twistpoint g = EL_GAMAL_GENERATOR; Twistpoint h = elGamalBlindGenerator; seeds.clear(); for (size_t i = 0; i < permutations.size(); i++) { std::vector currSeeds; for (size_t j = 0; j < permutations[i].size(); j++) currSeeds.push_back(Scalar(0)); seeds.push_back(currSeeds); } for (size_t i = 0; i < permutations.size(); i++) { std::vector currRow; size_t last = permutations[i].size() - 1; for (size_t j = 0; j < permutations[i].size(); j++) { Twistpoint element; if (j != last) { seeds[i][j].set_random(); seeds[i][last] = seeds[i][last] - seeds[i][j]; } element = g * permutations[i][j] + h * seeds[i][j]; currRow.push_back(element); } retval.push_back(currRow); } return retval; } std::vector> PrsonaServer::generate_pseudonym_matrix( const std::vector>& permutations, const Scalar& power, std::vector>& seeds, std::vector>& seedCommits) const { return generate_reordered_plus_power_matrix(permutations, power, currentPseudonyms, seeds, seedCommits, elGamalBlindGenerator); } std::vector> PrsonaServer::generate_server_tally_matrix( const std::vector>& permutations, std::vector>& seeds) const { return generate_reordered_matrix(permutations, previousVoteTallies, seeds, bgnSystem.get_public_key().get_bipoint_curve_subgroup_gen(), false); } std::vector>> PrsonaServer::generate_vote_tensor( const std::vector>& permutations, const std::vector>& currVoteMatrix, std::vector>>& seeds, bool inverted) const { std::vector>> retval; for (size_t i = 0; i < currVoteMatrix.size(); i++) { std::vector> currSeeds; std::vector inputRow; if (inverted) { for (size_t j = 0; j < currVoteMatrix.size(); j++) inputRow.push_back(currVoteMatrix[j][i]); } else { inputRow = currVoteMatrix[i]; } retval.push_back(generate_reordered_matrix(permutations, inputRow, currSeeds, bgnSystem.get_public_key().get_bipoint_twist_subgroup_gen(), false)); seeds.push_back(currSeeds); } return retval; } std::vector> PrsonaServer::calculate_next_vote_matrix( const std::vector>>& voteTensor) const { std::vector> retval; for (size_t i = 0; i < voteTensor.size(); i++) { std::vector currRow; for (size_t j = 0; j < voteTensor[i].size(); j++) { TwistBipoint sum = voteTensor[i][j][0]; for (size_t k = 1; k < voteTensor[i][j].size(); k++) sum = sum + voteTensor[i][j][k]; currRow.push_back(sum); } retval.push_back(currRow); } return retval; } void generate_tensor_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g, const void *h, const void *i, const void *j) { const PrsonaServer *server = (const PrsonaServer *) a; std::vector *dst = (std::vector *) b; const std::vector> *permutations = (const std::vector> *) c; const std::vector> *permutationSeeds = (const std::vector> *) d; const std::vector> *matrixSeeds = (const std::vector> *) e; const std::vector *inputRow = (const std::vector *) f; const std::vector> *permutationCommits = (const std::vector> *) g; const std::vector> *matrixCommits = (const std::vector> *) h; const TwistBipoint *twistG = (const TwistBipoint *) i; const TwistBipoint *twistH = (const TwistBipoint *) j; *dst = server->generate_proof_of_reordering(*permutations, *permutationSeeds, *matrixSeeds, *inputRow, *permutationCommits, *matrixCommits, *twistG, *twistH); } void PrsonaServer::generate_vote_tensor_proofs( std::vector>& pi, const std::vector>& permutations, const std::vector>& permutationSeeds, const std::vector>>& matrixSeeds, const std::vector>& currMatrix, const std::vector>& permutationCommits, const std::vector>>& matrixCommits, bool inverted) const { TwistBipoint twistG = bgnSystem.get_public_key().get_bipoint_twistgen(); TwistBipoint twistH = bgnSystem.get_public_key().get_bipoint_twist_subgroup_gen(); std::vector *intermediaryProofs = new std::vector[currMatrix.size()]; std::vector *inputRows = new std::vector[currMatrix.size()]; std::vector individualTensorProofThreads; for (size_t i = 0; i < currMatrix.size(); i++) { std::vector currRow; if (inverted) { for (size_t j = 0; j < currMatrix.size(); j++) currRow.push_back(currMatrix[j][i]); } else { currRow = currMatrix[i]; } inputRows[i] = currRow; individualTensorProofThreads.push_back(std::thread(generate_tensor_r, this, intermediaryProofs + i, &permutations, &permutationSeeds, &(matrixSeeds[i]), inputRows + i, &permutationCommits, &(matrixCommits[i]), &twistG, &twistH)); } for (size_t i = 0; i < individualTensorProofThreads.size(); i++) { individualTensorProofThreads[i].join(); pi.push_back(intermediaryProofs[i]); } delete [] inputRows; delete [] intermediaryProofs; } void verify_tensor_r( const void *a, void *b, const void *c, const void *d, const void *e, const void *f, const void *g, const void *h) { const PrsonaServer *server = (const PrsonaServer *) a; bool *dst = (bool *) b; const std::vector *pi = (const std::vector *) c; const std::vector *inputRow = (const std::vector *) d; const std::vector> *permutationCommits = (const std::vector> *) e; const std::vector> *matrixCommits = (const std::vector> *) f; const TwistBipoint *twistG = (const TwistBipoint *) g; const TwistBipoint *twistH = (const TwistBipoint *) h; *dst = server->verify_proof_of_reordering(*pi, *inputRow, *permutationCommits, *matrixCommits, *twistG, *twistH); } bool PrsonaServer::verify_vote_tensor_proofs( const std::vector>& pi, size_t start_offset, const std::vector>& currMatrix, const std::vector>& permutationCommits, const std::vector>>& matrixCommits, bool inverted) const { TwistBipoint twistG = bgnSystem.get_public_key().get_bipoint_twistgen(); TwistBipoint twistH = bgnSystem.get_public_key().get_bipoint_twist_subgroup_gen(); bool *intermediaryValues = new bool[currMatrix.size()]; std::vector *inputRows = new std::vector[currMatrix.size()]; std::vector individualTensorProofThreads; for (size_t i = 0; i < currMatrix.size(); i++) { std::vector currRow; if (inverted) { for (size_t j = 0; j < currMatrix.size(); j++) currRow.push_back(currMatrix[j][i]); } else { currRow = currMatrix[i]; } inputRows[i] = currRow; size_t whichProof = i + start_offset; individualTensorProofThreads.push_back(std::thread(verify_tensor_r, this, intermediaryValues + i, &(pi[whichProof]), inputRows + i, &permutationCommits, &(matrixCommits[i]), &twistG, &twistH)); } bool retval = true; for (size_t i = 0; i < individualTensorProofThreads.size(); i++) { individualTensorProofThreads[i].join(); retval = retval && intermediaryValues[i]; } delete [] inputRows; delete [] intermediaryValues; return retval; } void PrsonaServer::generate_user_tally_matrix( const std::vector>& permutations, const Scalar& power, const Twistpoint& nextGenerator, const std::vector& currPseudonyms, std::vector& masks, std::vector>& maskCommits, std::vector& messages, std::vector>& messageCommits, std::vector>& userTallySeeds, std::vector>& userTallySeedCommits) const { masks.clear(); messages.clear(); for (size_t i = 0; i < currentUserEncryptedTallies.size(); i++) { masks.push_back(currentUserEncryptedTallies[i].mask); messages.push_back(currentUserEncryptedTallies[i].encryptedMessage); } maskCommits.clear(); messageCommits.clear(); userTallySeeds.clear(); userTallySeedCommits.clear(); for (size_t i = 0; i < permutations.size(); i++) { std::vector currSeeds; std::vector currRow; for (size_t j = 0; j < permutations[i].size(); j++) { currSeeds.push_back(Scalar(0)); currRow.push_back(Twistpoint()); } userTallySeeds.push_back(currSeeds); maskCommits.push_back(currRow); messageCommits.push_back(currRow); userTallySeedCommits.push_back(currRow); } for (size_t i = 0; i < permutations.size(); i++) { size_t last = permutations[i].size() - 1; for (size_t j = 0; j < permutations[i].size(); j++) { if (j != last) { userTallySeeds[i][j].set_random(); userTallySeeds[i][last] = userTallySeeds[i][last] - userTallySeeds[i][j]; } maskCommits[i][j] = masks[j] * permutations[j][i] * power + currPseudonyms[j] * power * permutations[j][i] * userTallySeeds[i][j] + elGamalBlindGenerator * userTallySeeds[i][j]; messageCommits[i][j] = messages[j] * permutations[j][i] + nextGenerator * permutations[j][i] * userTallySeeds[i][j] + elGamalBlindGenerator * userTallySeeds[i][j]; userTallySeedCommits[i][j] = EL_GAMAL_GENERATOR * userTallySeeds[i][j]; } } } template std::vector> PrsonaServer::generate_reordered_plus_power_matrix( const std::vector>& permutations, const Scalar& power, const std::vector& oldValues, std::vector>& seeds, std::vector>& seedCommits, const T& h) const { std::vector> permutation_plus_power; seedCommits.clear(); for (size_t i = 0; i < permutations.size(); i++) { std::vector currPermutations; std::vector currSeedCommits; for (size_t j = 0; j < permutations[i].size(); j++) { currPermutations.push_back(permutations[i][j] * power); currSeedCommits.push_back(Twistpoint()); } permutation_plus_power.push_back(currPermutations); seedCommits.push_back(currSeedCommits); } std::vector> retval = generate_reordered_matrix(permutation_plus_power, oldValues, seeds, h, true); for (size_t i = 0; i < permutations.size(); i++) for (size_t j = 0; j < permutations[i].size(); j++) seedCommits[i][j] = EL_GAMAL_GENERATOR * seeds[i][j]; return retval; } template std::vector> PrsonaServer::generate_reordered_matrix( const std::vector>& permutations, const std::vector& oldValues, std::vector>& seeds, const T& h, bool cancelOut) const { std::vector> retval; seeds.clear(); for (size_t i = 0; i < permutations.size(); i++) { std::vector currSeeds; std::vector currRow; for (size_t j = 0; j < permutations[i].size(); j++)\ { currSeeds.push_back(Scalar(0)); currRow.push_back(T()); } seeds.push_back(currSeeds); retval.push_back(currRow); } for (size_t i = 0; i < permutations.size(); i++) { size_t last = permutations[i].size() - 1; for (size_t j = 0; j < permutations[i].size(); j++) { if (!cancelOut) { seeds[i][j].set_random(); } else if (j != last) { seeds[i][j].set_random(); seeds[i][last] = seeds[i][last] - seeds[i][j]; } retval[i][j] = oldValues[j] * permutations[j][i] + h * seeds[i][j]; } } return retval; } std::vector PrsonaServer::sort_data( const std::vector& inputs) const { std::vector retval; // SortingType's index member allows us to replicate the "sort" across std::vector sortTracker; for (size_t i = 0; i < inputs.size(); i++) { SortingType curr; curr.pseudonym = inputs[i]; curr.index = i; sortTracker.push_back(curr); } std::sort(sortTracker.begin(), sortTracker.end()); for (size_t i = 0; i < inputs.size(); i++) retval.push_back(sortTracker[i].index); return retval; } bool PrsonaServer::update_data( const std::vector>& freshPseudonymCommits, const std::vector>& serverTallyCommits, const std::vector>>& voteMatrixCommits, const std::vector>& userTallyMaskCommits, const std::vector>& userTallyMessageCommits) { std::vector newPseudonyms; std::vector newVoteTallies; std::vector newUserTallies; for (size_t i = 0; i < freshPseudonymCommits.size(); i++) { Twistpoint pseudonymSum = freshPseudonymCommits[i][0]; CurveBipoint voteTallySum = serverTallyCommits[i][0]; Twistpoint userTallyMask, userTallyMessage; if (!userTallyMaskCommits.empty()) { userTallyMask = userTallyMaskCommits[i][0]; userTallyMessage = userTallyMessageCommits[i][0]; } for (size_t j = 1; j < freshPseudonymCommits[i].size(); j++) { pseudonymSum = pseudonymSum + freshPseudonymCommits[i][j]; voteTallySum = voteTallySum + serverTallyCommits[i][j]; if (!userTallyMaskCommits.empty()) { userTallyMask = userTallyMask + userTallyMaskCommits[i][j]; userTallyMessage = userTallyMessage + userTallyMessageCommits[i][j]; } } newPseudonyms.push_back(pseudonymSum); newVoteTallies.push_back(voteTallySum); if (!userTallyMaskCommits.empty()) newUserTallies.push_back(EGCiphertext(userTallyMask, userTallyMessage)); } if (!pseudonyms_sorted(newPseudonyms)) { std::cerr << "Pseudonyms not sorted correctly." << std::endl; return false; } currentPseudonyms = newPseudonyms; previousVoteTallies = newVoteTallies; voteMatrix = calculate_next_vote_matrix(voteMatrixCommits); currentUserEncryptedTallies = newUserTallies; return true; } bool PrsonaServer::hbc_update_data( const std::vector& newFreshPseudonyms, const std::vector& newServerTallies, const std::vector>& newVoteMatrix, const std::vector& newUserTallies) { if (!pseudonyms_sorted(newFreshPseudonyms)) { std::cerr << "Pseudonyms not sorted correctly." << std::endl; return false; } currentPseudonyms = newFreshPseudonyms; previousVoteTallies = newServerTallies; voteMatrix = newVoteMatrix; currentUserEncryptedTallies = newUserTallies; return true; } bool PrsonaServer::pseudonyms_sorted( const std::vector newPseudonyms) const { bool retval = true; for (size_t i = 0; i < newPseudonyms.size() - 1; i++) retval = retval && (newPseudonyms[i] < newPseudonyms[i + 1]); return retval; } /* * DATA MAINTENANCE */ void PrsonaServer::export_new_user_update( std::vector& otherPreviousVoteTallies, std::vector& otherCurrentPseudonyms, std::vector& otherCurrentUserEncryptedTallies, std::vector>& otherVoteMatrix) const { otherPreviousVoteTallies = previousVoteTallies; otherCurrentPseudonyms = currentPseudonyms; otherCurrentUserEncryptedTallies = currentUserEncryptedTallies; otherVoteMatrix = voteMatrix; } bool PrsonaServer::import_new_user_update( const std::vector& pi, const std::vector& otherPreviousVoteTallies, const std::vector& otherCurrentPseudonyms, const std::vector& otherCurrentUserEncryptedTallies, const std::vector>& otherVoteMatrix) { size_t newIndex = 0; if (!currentPseudonyms.empty()) while (otherCurrentPseudonyms[newIndex] == currentPseudonyms[newIndex]) newIndex++; Twistpoint shortTermPublicKey = otherCurrentPseudonyms[newIndex]; bool flag = verify_proof_of_added_user(pi, currentFreshGenerator, shortTermPublicKey, bgnSystem.get_public_key().get_bipoint_twistgen(), bgnSystem.get_public_key().get_bipoint_twist_subgroup_gen(), bgnSystem.get_public_key().get_bipoint_curvegen(), bgnSystem.get_public_key().get_bipoint_curve_subgroup_gen(), newIndex, otherCurrentUserEncryptedTallies[newIndex], otherPreviousVoteTallies[newIndex], otherVoteMatrix); if (!flag) { std::cerr << "Other server added new user invalidly, aborting." << std::endl; return false; } for (size_t i = 0; i < otherCurrentPseudonyms.size(); i++) { if (i == newIndex) continue; size_t otherI = (i > newIndex ? i - 1 : i); flag = flag && otherCurrentPseudonyms[i] == currentPseudonyms[otherI]; flag = flag && otherCurrentUserEncryptedTallies[i] == currentUserEncryptedTallies[otherI]; flag = flag && otherPreviousVoteTallies[i] == previousVoteTallies[otherI]; for (size_t j = 0; j < otherCurrentPseudonyms.size(); j++) { if (j == newIndex) continue; size_t otherJ = (j > newIndex ? j - 1 : j); flag = flag && otherVoteMatrix[i][j] == voteMatrix[otherI][otherJ]; } } if (!flag) { std::cerr << "Other server illicitly changed other value during new user add." << std::endl; return false; } previousVoteTallies = otherPreviousVoteTallies; currentPseudonyms = otherCurrentPseudonyms; currentUserEncryptedTallies = otherCurrentUserEncryptedTallies; voteMatrix = otherVoteMatrix; return true; } /* * DATA SAFEKEEPING */ /* This is what powers the "shuffle"; really, as pseudonyms get updated, * the pseudonyms are no longer in the order prescribed by operator<(). * So, we put them (and everything else) back into that order, * effectively shuffling them (and making lookups easier later on). */ std::vector PrsonaServer::order_data() { std::vector retval = sort_data(currentPseudonyms); // Order all other data in the same way, for consistency std::vector newPseudonyms; std::vector newVoteTallies; std::vector newUserEncryptedTallies; std::vector> newVoteMatrix; for (size_t i = 0; i < retval.size(); i++) { newPseudonyms.push_back(currentPseudonyms[retval[i]]); newVoteTallies.push_back(previousVoteTallies[retval[i]]); if (!currentUserEncryptedTallies.empty()) newUserEncryptedTallies.push_back(currentUserEncryptedTallies[retval[i]]); std::vector currNewRow; for (size_t j = 0; j < currentPseudonyms.size(); j++) currNewRow.push_back(voteMatrix[retval[i]][retval[j]]); newVoteMatrix.push_back(currNewRow); } previousVoteTallies = newVoteTallies; currentPseudonyms = newPseudonyms; currentUserEncryptedTallies = newUserEncryptedTallies; voteMatrix = newVoteMatrix; return retval; } /* * BINARY SEARCH */ // Completely normal binary search size_t PrsonaServer::binary_search( const Twistpoint& index) const { return PrsonaBase::binary_search(currentPseudonyms, index); } /* * VALID VOTE PROOFS */ bool PrsonaServer::verify_vote_proof( const std::vector& pi, const std::vector& oldVotes, const std::vector& newVotes, const Twistpoint& shortTermPublicKey) const { const BGNPublicKey& pubKey = bgnSystem.get_public_key(); return PrsonaBase::verify_vote_proof(pubKey.get_bipoint_twistgen(), pubKey.get_bipoint_twist_subgroup_gen(), pi, oldVotes, newVotes, currentFreshGenerator, shortTermPublicKey); } void PrsonaServer::print_scores( const std::vector& scores) { std::unique_lock lck(*decryptMtx); std::cout << "["; for (size_t i = 0; i < scores.size(); i++) std::cout << bgnSystem.decrypt(scores[i]) << (i == scores.size() - 1 ? "]" : " "); std::cout << std::endl; }