#include #include #include "base.hpp" extern const scalar_t bn_n; extern const twistpoint_fp2_t bn_twistgen; /* These lines need to be here so these static variables are defined, * but in C++ putting code here doesn't actually execute * (or at least, with g++, whenever it would execute is not at a useful time) * so we have an init() function to actually put the correct values in them. */ Twistpoint PrsonaBase::EL_GAMAL_GENERATOR = Twistpoint(); Scalar PrsonaBase::SCALAR_N = Scalar(); Scalar PrsonaBase::DEFAULT_TALLY = Scalar(); Scalar PrsonaBase::DEFAULT_VOTE = Scalar(); bool PrsonaBase::SERVER_IS_MALICIOUS = false; bool PrsonaBase::CLIENT_IS_MALICIOUS = false; size_t PrsonaBase::MAX_ALLOWED_VOTE = 2; // Quick and dirty function to calculate ceil(log base 2) with mpz_class mpz_class log2(mpz_class x) { mpz_class retval = 0; while (x > 0) { retval++; x = x >> 1; } return retval; } mpz_class bit(mpz_class x) { return x > 0 ? 1 : 0; } /******************** * PUBLIC FUNCTIONS * ********************/ /* * SETUP FUNCTIONS */ // Must be called once before any usage of this class void PrsonaBase::init() { EL_GAMAL_GENERATOR = Twistpoint(bn_twistgen); SCALAR_N = Scalar(bn_n); DEFAULT_TALLY = Scalar(1); DEFAULT_VOTE = Scalar(1); } // Call this (once) if using malicious-security servers void PrsonaBase::set_server_malicious() { SERVER_IS_MALICIOUS = true; } // Call this (once) if using malicious-security clients void PrsonaBase::set_client_malicious() { CLIENT_IS_MALICIOUS = true; } /* * CONST GETTERS */ size_t PrsonaBase::get_max_allowed_vote() { return MAX_ALLOWED_VOTE; } Twistpoint PrsonaBase::get_blinding_generator() const { return elGamalBlindGenerator; } Twistpoint PrsonaBase::get_blinding_generator(std::vector& pi) const { pi = elGamalBlindGeneratorProof; return elGamalBlindGenerator; } /*********************** * PROTECTED FUNCTIONS * ***********************/ /* * PRIVATE ELEMENT SETTER */ bool PrsonaBase::set_EG_blind_generator( const std::vector& pi, const Twistpoint& currGenerator, size_t numServers) { if (!verify_generator_proof(pi, currGenerator, numServers)) return false; elGamalBlindGeneratorProof = pi; elGamalBlindGenerator = currGenerator; return true; } /* * BINARY SEARCH */ /* Completely normal binary search * There might be a standard function for this in ? * But it returns an iterator, not a size_t, so less useful. */ size_t PrsonaBase::binary_search( const std::vector list, const Twistpoint& index) const { size_t lo, hi; lo = 0; hi = list.size() - 1; while (lo < hi) { size_t mid = (lo + hi) / 2; if (list[mid] < index) lo = mid + 1; else if (index == list[mid]) return mid; else if (mid == lo) return lo; else hi = mid - 1; } return lo; } /* * SCHNORR PROOFS */ Proof PrsonaBase::schnorr_generation( const Twistpoint& generator, const Twistpoint& commitment, const Scalar& log) const { Proof retval; std::stringstream oracleInput; Scalar u; u.set_random(); Twistpoint U = generator * u; oracleInput << generator << commitment << U; Scalar x = oracle(oracleInput.str()); Scalar z = log * x + u; retval.challengeParts.push_back(x); retval.responseParts.push_back(z); return retval; } bool PrsonaBase::schnorr_verification( const Twistpoint& generator, const Twistpoint& commitment, const Scalar& x, const Scalar& z) const { Twistpoint U = generator * z - commitment * x; std::stringstream oracleInput; oracleInput << generator << commitment << U; return x == oracle(oracleInput.str()); } /* * OWNERSHIP PROOFS */ // Prove ownership of the short term public key Proof PrsonaBase::generate_ownership_proof( const Twistpoint& generator, const Twistpoint& commitment, const Scalar& log) const { if (!CLIENT_IS_MALICIOUS) { Proof retval; retval.hbc = "PROOF"; return retval; } return schnorr_generation(generator, commitment, log); } bool PrsonaBase::verify_ownership_proof( const Proof& pi, const Twistpoint& generator, const Twistpoint& commitment) const { if (!CLIENT_IS_MALICIOUS) return pi.hbc == "PROOF"; Scalar c = pi.challengeParts[0]; Scalar z = pi.responseParts[0]; return schnorr_verification(generator, commitment, c, z); } /* * ITERATED SCHNORR PROOFS */ Proof PrsonaBase::add_to_generator_proof( const Twistpoint& currGenerator, const Scalar& seed) const { Proof retval; if (!SERVER_IS_MALICIOUS) { retval.hbc = "PROOF"; return retval; } Twistpoint nextGenerator = currGenerator * seed; retval = schnorr_generation(currGenerator, nextGenerator, seed); retval.curvepointUniversals.push_back(currGenerator); return retval; } bool PrsonaBase::verify_generator_proof( const std::vector& pi, const Twistpoint& currGenerator, size_t numServers) const { if (pi.size() != numServers || numServers == 0) return false; bool retval = true; if (!SERVER_IS_MALICIOUS) { for (size_t i = 0; i < pi.size(); i++) retval = retval && pi[i].hbc == "PROOF"; return retval; } if (pi[0].curvepointUniversals[0] != EL_GAMAL_GENERATOR) return false; for (size_t i = 0; i < pi.size(); i++) { Twistpoint generator = pi[i].curvepointUniversals[0]; Twistpoint commitment = (i == pi.size() - 1 ? currGenerator : pi[i + 1].curvepointUniversals[0]); Scalar c = pi[i].challengeParts[0]; Scalar z = pi[i].responseParts[0]; retval = retval && schnorr_verification(generator, commitment, c, z); if (!retval) std::cerr << "Error in index " << i+1 << " of " << pi.size() << std::endl; } return retval; } /* * REPUTATION PROOFS */ // A pretty straightforward range proof (generation) std::vector PrsonaBase::generate_reputation_proof( const Proof& ownershipProof, const EGCiphertext& commitment, const Scalar& currentScore, const Scalar& threshold, const Scalar& inverseKey, size_t numClients) const { std::vector retval; // Base case if (!CLIENT_IS_MALICIOUS) { retval.push_back(Proof("PROOF")); return retval; } // Don't even try if the user asks to make an illegitimate proof if (threshold.toInt() > (numClients * MAX_ALLOWED_VOTE)) return retval; // We really have two consecutive proofs in a junction. // The first is to prove that we are the stpk we claim we are retval.push_back(ownershipProof); // The value we're actually using in our proof mpz_class proofVal = (currentScore - threshold).toInt(); // Top of the range in our proof determined by what scores are even possible mpz_class proofBits = log2(numClients * MAX_ALLOWED_VOTE - threshold.toInt()); // Don't risk a situation that would divulge our private key if (proofBits <= 1) proofBits = 2; // This seems weird, but remember our base is A_t^r, not g^t std::vector masksPerBit; masksPerBit.push_back(inverseKey); for (size_t i = 1; i < proofBits; i++) { Scalar currMask; currMask.set_random(); masksPerBit.push_back(currMask); masksPerBit[0] = masksPerBit[0] - (currMask * Scalar(1 << i)); } // Taken from Fig. 1 in https://eprint.iacr.org/2014/764.pdf for (size_t i = 0; i < proofBits; i++) { Proof currProof; Twistpoint g, h, C, C_a, C_b; g = commitment.mask; h = elGamalBlindGenerator; mpz_class currBit = bit(proofVal & (1 << i)); Scalar a, s, t, m, r; a.set_random(); s.set_random(); t.set_random(); m = Scalar(currBit); r = masksPerBit[i]; C = g * r + h * m; currProof.curvepointUniversals.push_back(C); C_a = g * s + h * a; C_b = g * t + h * a * m; std::stringstream oracleInput; oracleInput << g << h << C << C_a << C_b; Scalar x = oracle(oracleInput.str()); currProof.challengeParts.push_back(x); Scalar f, z_a, z_b; f = m * x + a; z_a = r * x + s; z_b = r * (x - f) + t; currProof.responseParts.push_back(f); currProof.responseParts.push_back(z_a); currProof.responseParts.push_back(z_b); retval.push_back(currProof); } return retval; } // A pretty straightforward range proof (verification) bool PrsonaBase::verify_reputation_proof( const std::vector& pi, const Twistpoint& generator, const Twistpoint& owner, const EGCiphertext& commitment, const Scalar& threshold) const { // Reject outright if there's no proof to check if (pi.empty()) { std::cerr << "Proof was empty, aborting." << std::endl; return false; } // If the range is so big that it wraps around mod n, // there's a chance the user actually made a proof for a very low reputation if (pi.size() > 256) { std::cerr << "Proof was too big, prover could have cheated." << std::endl; return false; } if (!CLIENT_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Scalar ownerChallenge, ownerResponse; ownerChallenge = pi[0].challengeParts[0]; ownerResponse = pi[0].responseParts[0]; // User should be able to prove they are who they say they are if (!schnorr_verification(generator, owner, ownerChallenge, ownerResponse)) { std::cerr << "Schnorr proof failed, aborting." << std::endl; return false; } // X is the thing we're going to be checking in on throughout // to try to get our score commitment back in the end. Twistpoint X; for (size_t i = 1; i < pi.size(); i++) { Twistpoint C, g, h; C = pi[i].curvepointUniversals[0]; g = commitment.mask; h = elGamalBlindGenerator; X = X + C * Scalar(1 << (i - 1)); Scalar x, f, z_a, z_b; x = pi[i].challengeParts[0]; f = pi[i].responseParts[0]; z_a = pi[i].responseParts[1]; z_b = pi[i].responseParts[2]; // Taken from Fig. 1 in https://eprint.iacr.org/2014/764.pdf Twistpoint C_a, C_b; C_a = g * z_a + h * f - C * x; C_b = g * z_b - C * (x - f); std::stringstream oracleInput; oracleInput << g << h << C << C_a << C_b; if (oracle(oracleInput.str()) != x) { std::cerr << "0 or 1 proof failed at index " << i << " of " << pi.size() - 1 << ", aborting." << std::endl; return false; } } Twistpoint scoreCommitment = commitment.encryptedMessage + elGamalBlindGenerator * -threshold; return X == scoreCommitment; } /* * VALID VOTE PROOFS */ std::vector PrsonaBase::generate_vote_proof( const Proof& ownershipProof, const TwistBipoint& g, const TwistBipoint& h, const std::vector& replaces, const std::vector& oldEncryptedVotes, const std::vector& newEncryptedVotes, const std::vector& seeds, const std::vector& votes) const { std::vector retval; // Base case if (!CLIENT_IS_MALICIOUS) { retval.push_back(Proof("PROOF")); return retval; } // The first need is to prove that we are the stpk we claim we are retval.push_back(ownershipProof); // Then, we iterate over all votes for the proofs that they are correct for (size_t i = 0; i < replaces.size(); i++) { std::stringstream oracleInput; oracleInput << g << h << oldEncryptedVotes[i] << newEncryptedVotes[i]; Scalar m = votes[i]; Scalar r = seeds[i]; /* This proof structure is documented in my notes. * It's inspired by the proof in Fig. 1 at * https://eprint.iacr.org/2014/764.pdf, but adapted so that you prove * m(m-1)(m-2) = 0 instead of m(m-1) = 0. * * The rerandomization part is just a slight variation on an * ordinary Schnorr proof, so that part's less scary. */ if (replaces[i]) // CASE: Make new vote { Proof currProof; Scalar x_r, z_r, a, s, t_1, t_2; x_r.set_random(); z_r.set_random(); a.set_random(); s.set_random(); t_1.set_random(); t_2.set_random(); TwistBipoint U = h * z_r + oldEncryptedVotes[i] * x_r - newEncryptedVotes[i] * x_r; TwistBipoint C_a = g * a + h * s; Scalar power = ((a + a) * m * m - (a + a + a) * m); TwistBipoint C_b = g * power + h * t_1; currProof.curveBipointUniversals.push_back(C_b); TwistBipoint C_c = g * (a * a * m) + h * t_2; oracleInput << U << C_a << C_b << C_c; Scalar x = oracle(oracleInput.str()); Scalar x_n = x - x_r; currProof.challengeParts.push_back(x_r); currProof.challengeParts.push_back(x_n); Scalar f = m * x_n + a; Scalar z_na = r * x_n + s; Scalar z_nb = r * (f - x_n) * (x_n + x_n - f) + t_1 * x_n + t_2; currProof.responseParts.push_back(z_r); currProof.responseParts.push_back(f); currProof.responseParts.push_back(z_na); currProof.responseParts.push_back(z_nb); retval.push_back(currProof); } else // CASE: Rerandomize existing vote { Proof currProof; Scalar u, commitmentLambda_1, commitmentLambda_2, x_n, z_na, z_nb, f; u.set_random(); commitmentLambda_1.set_random(); commitmentLambda_2.set_random(); x_n.set_random(); z_na.set_random(); z_nb.set_random(); f.set_random(); TwistBipoint U = h * u; TwistBipoint C_a = g * f + h * z_na - newEncryptedVotes[i] * x_n; TwistBipoint C_b = g * commitmentLambda_1 + h * commitmentLambda_2; currProof.curveBipointUniversals.push_back(C_b); TwistBipoint C_c = h * z_nb - newEncryptedVotes[i] * ((f - x_n) * (x_n + x_n - f)) - C_b * x_n; oracleInput << U << C_a << C_b << C_c; Scalar x = oracle(oracleInput.str()); Scalar x_r = x - x_n; currProof.challengeParts.push_back(x_r); currProof.challengeParts.push_back(x_n); Scalar z_r = r * x_r + u; currProof.responseParts.push_back(z_r); currProof.responseParts.push_back(f); currProof.responseParts.push_back(z_na); currProof.responseParts.push_back(z_nb); retval.push_back(currProof); } } return retval; } bool PrsonaBase::verify_vote_proof( const TwistBipoint& g, const TwistBipoint& h, const std::vector& pi, const std::vector& oldEncryptedVotes, const std::vector& newEncryptedVotes, const Twistpoint& freshGenerator, const Twistpoint& owner) const { // Reject outright if there's no proof to check if (pi.empty()) { std::cerr << "Proof was empty, aborting." << std::endl; return false; } // Base case if (!CLIENT_IS_MALICIOUS) return pi[0].hbc == "PROOF"; // User should be able to prove they are who they say they are if (!verify_ownership_proof(pi[0], freshGenerator, owner)) { std::cerr << "Schnorr proof failed, aborting." << std::endl; return false; } /* This proof structure is documented in my notes. * It's inspired by the proof in Fig. 1 at * https://eprint.iacr.org/2014/764.pdf, but adapted so that you prove * m(m-1)(m-2) = 0 instead of m(m-1) = 0. * * The rerandomization part is just a slight variation on an * ordinary Schnorr proof, so that part's less scary. */ for (size_t i = 1; i < pi.size(); i++) { size_t voteIndex = i - 1; TwistBipoint C_b; C_b = pi[i].curveBipointUniversals[0]; Scalar x_r, x_n, z_r, f, z_na, z_nb; x_r = pi[i].challengeParts[0]; x_n = pi[i].challengeParts[1]; z_r = pi[i].responseParts[0]; f = pi[i].responseParts[1]; z_na = pi[i].responseParts[2]; z_nb = pi[i].responseParts[3]; TwistBipoint U, C_a, C_c; U = h * z_r + oldEncryptedVotes[voteIndex] * x_r - newEncryptedVotes[voteIndex] * x_r; C_a = g * f + h * z_na - newEncryptedVotes[voteIndex] * x_n; C_c = h * z_nb - newEncryptedVotes[voteIndex] * ((f - x_n) * (x_n + x_n - f)) - C_b * x_n; std::stringstream oracleInput; oracleInput << g << h << oldEncryptedVotes[voteIndex] << newEncryptedVotes[voteIndex] << U << C_a << C_b << C_c; if (oracle(oracleInput.str()) != x_r + x_n) return false; } return true; } /* * NEW USER PROOFS */ std::vector PrsonaBase::generate_proof_of_added_user( const Scalar& twistBipointSeed, const Scalar& EGCiphertextSeed, const std::vector& curveBipointSelfSeeds, const std::vector& curveBipointOtherSeeds) const { std::vector retval; if (!SERVER_IS_MALICIOUS) { retval.push_back(Proof("PROOF")); return retval; } Proof currProof; currProof.responseParts.push_back(twistBipointSeed); retval.push_back(currProof); currProof.responseParts.clear(); currProof.responseParts.push_back(EGCiphertextSeed); retval.push_back(currProof); currProof.responseParts.clear(); for (size_t i = 0; i < curveBipointSelfSeeds.size(); i++) currProof.responseParts.push_back(curveBipointSelfSeeds[i]); retval.push_back(currProof); currProof.responseParts.clear(); for (size_t i = 0; i < curveBipointOtherSeeds.size(); i++) currProof.responseParts.push_back(curveBipointOtherSeeds[i]); retval.push_back(currProof); return retval; } bool PrsonaBase::verify_proof_of_added_user( const std::vector& pi, const Twistpoint& currentFreshGenerator, const Twistpoint& shortTermPublicKey, const TwistBipoint& curveG, const TwistBipoint& curveH, const CurveBipoint& twistG, const CurveBipoint& twistH, size_t selfIndex, const EGCiphertext& userEncryptedScore, const CurveBipoint& serverEncryptedScore, const std::vector> encryptedVoteMatrix) const { if (pi.empty()) { std::cerr << "Proof empty." << std::endl; return false; } if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Scalar currSeed = pi[0].responseParts[0]; if (serverEncryptedScore != twistG * DEFAULT_TALLY + twistH * currSeed) { std::cerr << "Issue in server encrypted score." << std::endl; return false; } currSeed = pi[1].responseParts[0]; if (userEncryptedScore.mask != shortTermPublicKey * currSeed) { std::cerr << "Issue in user encrypted score: mask." << std::endl; return false; } if (userEncryptedScore.encryptedMessage != currentFreshGenerator * currSeed + elGamalBlindGenerator * DEFAULT_TALLY) { std::cerr << "Issue in user encrypted score: value." << std::endl; return false; } for (size_t i = 0; i < pi[2].responseParts.size(); i++) { TwistBipoint currVote = encryptedVoteMatrix[selfIndex][i]; currSeed = pi[2].responseParts[i]; if (i == selfIndex) { if (currVote != curveG * Scalar(MAX_ALLOWED_VOTE) + curveH * currSeed) { std::cerr << "Issue in self vote." << std::endl; return false; } } else { if (currVote != curveG * DEFAULT_VOTE + curveH * currSeed) { std::cerr << "Issue in vote by verifier for user " << i + 1 << " of " << pi[2].responseParts.size() << "." << std::endl; return false; } } } for (size_t i = 0; i < pi[3].responseParts.size(); i++) { TwistBipoint currVote = encryptedVoteMatrix[i][selfIndex]; currSeed = pi[3].responseParts[i]; if (i != selfIndex) { if (currVote != curveG * DEFAULT_VOTE + curveH * currSeed) { std::cerr << "Issue in vote for verifier by user " << i + 1 << " of " << pi[3].responseParts.size() << "." << std::endl; return false; } } } return true; } /* * EPOCH PROOFS */ std::vector PrsonaBase::generate_valid_permutation_proof( const std::vector>& permutations, const std::vector>& seeds, const std::vector>& commits) const { std::vector retval; if (!SERVER_IS_MALICIOUS) { retval.push_back(Proof("PROOF")); return retval; } Twistpoint g, h; g = EL_GAMAL_GENERATOR; h = elGamalBlindGenerator; std::stringstream oracleInput; oracleInput << g << h; retval.push_back(Proof()); std::vector> a, s, t; for (size_t i = 0; i < commits.size(); i++) { std::vector currA, currS, currT; for (size_t j = 0; j < commits[i].size(); j++) { oracleInput << commits[i][j]; currA.push_back(Scalar()); currS.push_back(Scalar()); currT.push_back(Scalar()); retval.push_back(Proof()); } a.push_back(currA); s.push_back(currS); t.push_back(currT); } // Taken from Fig. 1 in https://eprint.iacr.org/2014/764.pdf for (size_t i = 0; i < permutations.size(); i++) { for (size_t j = 0; j < permutations[i].size(); j++) { Twistpoint C, C_a, C_b; Scalar p, r; a[i][j].set_random(); s[i][j].set_random(); t[i][j].set_random(); p = permutations[i][j]; r = seeds[i][j]; C = commits[i][j]; C_a = g * a[i][j] + h * s[i][j]; C_b = g * a[i][j] * p + h * t[i][j]; oracleInput << C << C_a << C_b; } } Scalar x = oracle(oracleInput.str()); retval[0].challengeParts.push_back(x); for (size_t i = 0; i < permutations.size(); i++) { for (size_t j = 0; j < permutations[i].size(); j++) { size_t piIndex = i * permutations.size() + j + 1; Scalar f, z_a, z_b, p, r; p = permutations[i][j]; r = seeds[i][j]; f = p * x + a[i][j]; z_a = r * x + s[i][j]; z_b = r * (x - f) + t[i][j]; retval[piIndex].responseParts.push_back(f); retval[piIndex].responseParts.push_back(z_a); retval[piIndex].responseParts.push_back(z_b); } } return retval; } bool PrsonaBase::verify_valid_permutation_proof( const std::vector& pi, const std::vector>& commits) const { // Reject outright if there's no proof to check if (pi.empty()) { std::cerr << "Proof was empty, aborting." << std::endl; return false; } if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Twistpoint g, h; g = EL_GAMAL_GENERATOR; h = elGamalBlindGenerator; std::stringstream oracleInput; oracleInput << g << h; for (size_t i = 0; i < commits.size(); i++) for (size_t j = 0; j < commits[i].size(); j++) oracleInput << commits[i][j]; Scalar x = pi[0].challengeParts[0]; for (size_t i = 0; i < commits.size(); i++) { for (size_t j = 0; j < commits[i].size(); j++) { size_t piIndex = i * commits.size() + j + 1; Twistpoint C, C_a, C_b; C = commits[i][j]; Scalar f, z_a, z_b; f = pi[piIndex].responseParts[0]; z_a = pi[piIndex].responseParts[1]; z_b = pi[piIndex].responseParts[2]; // Taken from Fig. 1 in https://eprint.iacr.org/2014/764.pdf C_a = g * f + h * z_a - C * x; C_b = h * z_b - C * (x - f); oracleInput << C << C_a << C_b; } } if (oracle(oracleInput.str()) != x) { std::cerr << "0 or 1 proof failed, aborting." << std::endl; return false; } for (size_t i = 0; i < commits.size(); i++) { Twistpoint sum = commits[i][0]; for (size_t j = 1; j < commits[i].size(); j++) sum = sum + commits[i][j]; if (sum != g) { std::cerr << "Commits did not sum to g, aborting." << std::endl; return false; } } return true; } std::vector PrsonaBase::generate_proof_of_reordering_plus_power( const std::vector>& permutations, const Scalar& power, const std::vector>& permutationSeeds, const std::vector>& productSeeds, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const std::vector>& seedCommits) const { std::vector retval; if (!SERVER_IS_MALICIOUS) { retval.push_back(Proof("PROOF")); return retval; } Proof first; retval.push_back(first); Twistpoint g = EL_GAMAL_GENERATOR; Twistpoint h = elGamalBlindGenerator; std::stringstream oracleInput; oracleInput << g << h; for (size_t i = 0; i < oldValues.size(); i++) oracleInput << oldValues[i]; for (size_t i = 0; i < permutationCommits.size(); i++) for (size_t j = 0; j < permutationCommits[i].size(); j++) oracleInput << permutationCommits[i][j]; for (size_t i = 0; i < productCommits.size(); i++) for (size_t j = 0; j < productCommits[i].size(); j++) oracleInput << productCommits[i][j]; for (size_t i = 0; i < seedCommits.size(); i++) for (size_t j = 0; j < seedCommits[i].size(); j++) oracleInput << seedCommits[i][j]; Scalar b1; b1.set_random(); std::vector> b2; std::vector> t1; std::vector> t2; for (size_t i = 0; i < permutationCommits.size(); i++) { std::vector currb2Row; std::vector currt1Row; std::vector currt2Row; for (size_t j = 0; j < permutationCommits[i].size(); j++) { Proof currProof; Scalar currb2; Scalar currt1; Scalar currt2; Twistpoint U1, U2, U3, U4; currb2.set_random(); currt1.set_random(); currt2.set_random(); U1 = g * currb2 + h * currt1; U2 = oldValues[j] * (b1 * permutations[j][i] + currb2 * power); U3 = oldValues[j] * b1 * currb2 + h * currt2; U4 = g * currt2; currProof.curvepointUniversals.push_back(U2); oracleInput << U1 << U2 << U3 << U4; currb2Row.push_back(currb2); currt1Row.push_back(currt1); currt2Row.push_back(currt2); retval.push_back(currProof); } b2.push_back(currb2Row); t1.push_back(currt1Row); t2.push_back(currt2Row); } Scalar x = oracle(oracleInput.str()); retval[0].challengeParts.push_back(x); Scalar f1 = power * x + b1; retval[0].responseParts.push_back(f1); for (size_t i = 0; i < permutationCommits.size(); i++) { for (size_t j = 0; j < permutationCommits[i].size(); j++) { size_t piIndex = i * permutationCommits.size() + j + 1; Scalar f2 = permutations[j][i] * x + b2[i][j]; Scalar z1 = permutationSeeds[j][i] * x + t1[i][j]; Scalar z2 = productSeeds[i][j] * x * x + t2[i][j]; retval[piIndex].responseParts.push_back(f2); retval[piIndex].responseParts.push_back(z1); retval[piIndex].responseParts.push_back(z2); } } return retval; } bool PrsonaBase::verify_proof_of_reordering_plus_power( const std::vector& pi, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const std::vector>& seedCommits) const { if (pi.empty()) return false; if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Twistpoint g = EL_GAMAL_GENERATOR; Twistpoint h = elGamalBlindGenerator; std::stringstream oracleInput; oracleInput << g << h; for (size_t i = 0; i < oldValues.size(); i++) oracleInput << oldValues[i]; for (size_t i = 0; i < permutationCommits.size(); i++) for (size_t j = 0; j < permutationCommits[i].size(); j++) oracleInput << permutationCommits[i][j]; for (size_t i = 0; i < productCommits.size(); i++) for (size_t j = 0; j < productCommits[i].size(); j++) oracleInput << productCommits[i][j]; for (size_t i = 0; i < seedCommits.size(); i++) for (size_t j = 0; j < seedCommits[i].size(); j++) oracleInput << seedCommits[i][j]; Scalar x = pi[0].challengeParts[0]; Scalar f1 = pi[0].responseParts[0]; for (size_t i = 0; i < permutationCommits.size(); i++) { for (size_t j = 0; j < permutationCommits[i].size(); j++) { size_t piIndex = i * permutationCommits.size() + j + 1; Twistpoint U1, U2, U3, U4; U2 = pi[piIndex].curvepointUniversals[0]; Scalar f2 = pi[piIndex].responseParts[0]; Scalar z1 = pi[piIndex].responseParts[1]; Scalar z2 = pi[piIndex].responseParts[2]; U1 = g * f2 + h * z1 - permutationCommits[j][i] * x; U3 = oldValues[j] * f1 * f2 + h * z2 - productCommits[i][j] * x * x - U2 * x; U4 = g * z2 - seedCommits[i][j] * x * x; oracleInput << U1 << U2 << U3 << U4; } } if (x != oracle(oracleInput.str())) { std::cerr << "Reordered + power things not generated by permutation matrix." << std::endl; return false; } for (size_t i = 0; i < seedCommits.size(); i++) { Twistpoint sum = seedCommits[i][0]; for (size_t j = 1; j < seedCommits[i].size(); j++) sum = sum + seedCommits[i][j]; if (sum != Twistpoint()) { std::cerr << "seed commits did not sum to 0, aborting." << std::endl; return false; } } return true; } std::vector PrsonaBase::generate_user_tally_proofs( const std::vector>& permutations, const Scalar& power, const Twistpoint& nextGenerator, const std::vector>& permutationSeeds, const std::vector>& userTallySeeds, const std::vector& currPseudonyms, const std::vector& userTallyMasks, const std::vector& userTallyMessages, const std::vector>& permutationCommits, const std::vector>& userTallyMaskCommits, const std::vector>& userTallyMessageCommits, const std::vector>& userTallySeedCommits) const { std::vector retval; if (!SERVER_IS_MALICIOUS) { retval.push_back(Proof("PROOF")); return retval; } Proof first; retval.push_back(first); Twistpoint g = EL_GAMAL_GENERATOR; Twistpoint h = elGamalBlindGenerator; std::stringstream oracleInput; oracleInput << g << h << nextGenerator; for (size_t i = 0; i < currPseudonyms.size(); i++) oracleInput << currPseudonyms[i]; for (size_t i = 0; i < userTallyMasks.size(); i++) oracleInput << userTallyMasks[i]; for (size_t i = 0; i < userTallyMessages.size(); i++) oracleInput << userTallyMessages[i]; for (size_t i = 0; i < permutationCommits.size(); i++) for (size_t j = 0; j < permutationCommits[i].size(); j++) oracleInput << permutationCommits[i][j]; for (size_t i = 0; i < userTallyMaskCommits.size(); i++) for (size_t j = 0; j < userTallyMaskCommits[i].size(); j++) oracleInput << userTallyMaskCommits[i][j]; for (size_t i = 0; i < userTallyMessageCommits.size(); i++) for (size_t j = 0; j < userTallyMessageCommits[i].size(); j++) oracleInput << userTallyMessageCommits[i][j]; for (size_t i = 0; i < userTallySeedCommits.size(); i++) for (size_t j = 0; j < userTallySeedCommits[i].size(); j++) oracleInput << userTallySeedCommits[i][j]; Scalar b1; b1.set_random(); std::vector> b2; std::vector> t1; std::vector> t2; for (size_t i = 0; i < permutationCommits.size(); i++) { std::vector currb2Row; std::vector currt1Row; std::vector currt2Row; for (size_t j = 0; j < permutationCommits[i].size(); j++) { Proof currProof; Scalar currb2; Scalar currt1; Scalar currt2; Twistpoint U1, U2, U3, U4, U5, U6, U7; currb2.set_random(); currt1.set_random(); currt2.set_random(); U1 = g * currb2 + h * currt1; U2 = userTallyMasks[j] * (b1 * permutations[j][i] + currb2 * power) + currPseudonyms[j] * (permutations[j][i] * b1 * userTallySeeds[i][j] + power * currb2 * userTallySeeds[i][j] + permutations[j][i] * power * currt2) + h * currt2; U3 = userTallyMasks[j] * (b1 * currb2) + currPseudonyms[j] * (b1 * currb2 * userTallySeeds[i][j] + permutations[j][i] * b1 * currt2 + power * currb2 * currt2); U4 = currPseudonyms[j] * (b1 * currb2 * currt2); U5 = userTallyMessages[j] * currb2 + nextGenerator * (permutations[j][i] * currt2 + userTallySeeds[i][j] * currb2) + h * currt2; U6 = nextGenerator * (currb2 * currt2); U7 = g * currt2; currProof.curvepointUniversals.push_back(U2); currProof.curvepointUniversals.push_back(U3); currProof.curvepointUniversals.push_back(U5); oracleInput << U1 << U2 << U3 << U4 << U5 << U6 << U7; currb2Row.push_back(currb2); currt1Row.push_back(currt1); currt2Row.push_back(currt2); retval.push_back(currProof); } b2.push_back(currb2Row); t1.push_back(currt1Row); t2.push_back(currt2Row); } Scalar x = oracle(oracleInput.str()); retval[0].challengeParts.push_back(x); Scalar f1 = power * x + b1; retval[0].responseParts.push_back(f1); for (size_t i = 0; i < permutationCommits.size(); i++) { for (size_t j = 0; j < permutationCommits[i].size(); j++) { size_t piIndex = i * permutationCommits.size() + j + 1; Scalar f2 = permutations[j][i] * x + b2[i][j]; Scalar z1 = permutationSeeds[j][i] * x + t1[i][j]; Scalar z2 = userTallySeeds[i][j] * x + t2[i][j]; retval[piIndex].responseParts.push_back(f2); retval[piIndex].responseParts.push_back(z1); retval[piIndex].responseParts.push_back(z2); } } return retval; } bool PrsonaBase::verify_user_tally_proofs( const std::vector& pi, const Twistpoint& nextGenerator, const std::vector& currPseudonyms, const std::vector& userTallyMasks, const std::vector& userTallyMessages, const std::vector>& permutationCommits, const std::vector>& userTallyMaskCommits, const std::vector>& userTallyMessageCommits, const std::vector>& userTallySeedCommits) const { if (pi.empty()) return false; if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Twistpoint g = EL_GAMAL_GENERATOR; Twistpoint h = elGamalBlindGenerator; std::stringstream oracleInput; oracleInput << g << h << nextGenerator; for (size_t i = 0; i < currPseudonyms.size(); i++) oracleInput << currPseudonyms[i]; for (size_t i = 0; i < userTallyMasks.size(); i++) oracleInput << userTallyMasks[i]; for (size_t i = 0; i < userTallyMessages.size(); i++) oracleInput << userTallyMessages[i]; for (size_t i = 0; i < permutationCommits.size(); i++) for (size_t j = 0; j < permutationCommits[i].size(); j++) oracleInput << permutationCommits[i][j]; for (size_t i = 0; i < userTallyMaskCommits.size(); i++) for (size_t j = 0; j < userTallyMaskCommits[i].size(); j++) oracleInput << userTallyMaskCommits[i][j]; for (size_t i = 0; i < userTallyMessageCommits.size(); i++) for (size_t j = 0; j < userTallyMessageCommits[i].size(); j++) oracleInput << userTallyMessageCommits[i][j]; for (size_t i = 0; i < userTallySeedCommits.size(); i++) for (size_t j = 0; j < userTallySeedCommits[i].size(); j++) oracleInput << userTallySeedCommits[i][j]; Scalar x = pi[0].challengeParts[0]; Scalar f1 = pi[0].responseParts[0]; for (size_t i = 0; i < permutationCommits.size(); i++) { for (size_t j = 0; j < permutationCommits[i].size(); j++) { size_t piIndex = i * permutationCommits.size() + j + 1; Twistpoint U1, U2, U3, U4, U5, U6, U7; U2 = pi[piIndex].curvepointUniversals[0]; U3 = pi[piIndex].curvepointUniversals[1]; U5 = pi[piIndex].curvepointUniversals[2]; Scalar f2 = pi[piIndex].responseParts[0]; Scalar z1 = pi[piIndex].responseParts[1]; Scalar z2 = pi[piIndex].responseParts[2]; U1 = g * f2 + h * z1 - permutationCommits[j][i] * x; U4 = userTallyMasks[j] * (f1 * f2 * x) + currPseudonyms[j] * (f1 * f2 * z2) + h * (z2 * x * x) - userTallyMaskCommits[i][j] * (x * x * x) - U2 * (x * x) - U3 * x; U6 = userTallyMessages[j] * (f2 * x) + nextGenerator * (f2 * z2) + h * (z2 * x) - userTallyMessageCommits[i][j] * (x * x) - U5 * x; U7 = g * z2 - userTallySeedCommits[i][j] * x; oracleInput << U1 << U2 << U3 << U4 << U5 << U6 << U7; } } if (x != oracle(oracleInput.str())) { std::cerr << "User tallies not generated by permutation matrix." << std::endl; return false; } for (size_t i = 0; i < userTallySeedCommits.size(); i++) { Twistpoint sum = userTallySeedCommits[i][0]; for (size_t j = 1; j < userTallySeedCommits[i].size(); j++) sum = sum + userTallySeedCommits[i][j]; if (sum != Twistpoint()) { std::cerr << "seed commits did not sum to 0, aborting." << std::endl; return false; } } return true; } template std::vector PrsonaBase::generate_proof_of_reordering( const std::vector>& permutations, const std::vector>& permutationSeeds, const std::vector>& productSeeds, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const T& otherG, const T& otherH) const { std::vector retval; if (!SERVER_IS_MALICIOUS) { retval.push_back(Proof("PROOF")); return retval; } Proof first; retval.push_back(first); Twistpoint g = EL_GAMAL_GENERATOR; Twistpoint h = elGamalBlindGenerator; std::stringstream oracleInput; oracleInput << g << h << otherG << otherH; for (size_t i = 0; i < oldValues.size(); i++) oracleInput << oldValues[i]; for (size_t i = 0; i < permutationCommits.size(); i++) for (size_t j = 0; j < permutationCommits[i].size(); j++) oracleInput << permutationCommits[i][j]; for (size_t i = 0; i < productCommits.size(); i++) for (size_t j = 0; j < productCommits[i].size(); j++) oracleInput << productCommits[i][j]; std::vector> a; std::vector> t1; std::vector> t2; for (size_t i = 0; i < permutationCommits.size(); i++) { std::vector curraRow; std::vector currt1Row; std::vector currt2Row; for (size_t j = 0; j < permutationCommits[i].size(); j++) { Proof currProof; Scalar curra; Scalar currt1; Scalar currt2; Twistpoint U1; T U2; curra.set_random(); currt1.set_random(); currt2.set_random(); U1 = g * curra + h * currt1; U2 = oldValues[j] * curra + otherH * currt2; oracleInput << U1 << U2; std::stringstream out1, out2; out1 << U1; out2 << U2; curraRow.push_back(curra); currt1Row.push_back(currt1); currt2Row.push_back(currt2); retval.push_back(currProof); } a.push_back(curraRow); t1.push_back(currt1Row); t2.push_back(currt2Row); } Scalar x = oracle(oracleInput.str()); retval[0].challengeParts.push_back(x); for (size_t i = 0; i < permutationCommits.size(); i++) { for (size_t j = 0; j < permutationCommits[i].size(); j++) { size_t piIndex = i * permutationCommits.size() + j + 1; Scalar f = permutations[j][i] * x + a[i][j]; Scalar z1 = permutationSeeds[j][i] * x + t1[i][j]; Scalar z2 = productSeeds[i][j] * x + t2[i][j]; retval[piIndex].responseParts.push_back(f); retval[piIndex].responseParts.push_back(z1); retval[piIndex].responseParts.push_back(z2); } } return retval; } template bool PrsonaBase::verify_proof_of_reordering( const std::vector& pi, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const T& otherG, const T& otherH) const { if (pi.empty()) return false; if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Twistpoint g = EL_GAMAL_GENERATOR; Twistpoint h = elGamalBlindGenerator; std::stringstream oracleInput; oracleInput << g << h << otherG << otherH; for (size_t i = 0; i < oldValues.size(); i++) oracleInput << oldValues[i]; for (size_t i = 0; i < permutationCommits.size(); i++) for (size_t j = 0; j < permutationCommits[i].size(); j++) oracleInput << permutationCommits[i][j]; for (size_t i = 0; i < productCommits.size(); i++) for (size_t j = 0; j < productCommits[i].size(); j++) oracleInput << productCommits[i][j]; Scalar x = pi[0].challengeParts[0]; for (size_t i = 0; i < permutationCommits.size(); i++) { for (size_t j = 0; j < permutationCommits[i].size(); j++) { size_t piIndex = i * permutationCommits.size() + j + 1; Twistpoint U1; T U2; Scalar f = pi[piIndex].responseParts[0]; Scalar z1 = pi[piIndex].responseParts[1]; Scalar z2 = pi[piIndex].responseParts[2]; U1 = g * f + h * z1 - permutationCommits[j][i] * x; U2 = oldValues[j] * f + otherH * z2 - productCommits[i][j] * x; oracleInput << U1 << U2; std::stringstream out1, out2; out1 << U1; out2 << U2; } } if (x != oracle(oracleInput.str())) { std::cerr << "Reordered things not generated by permutation matrix." << std::endl; return false; } return true; } /* * SERVER AGREEMENT PROOFS */ Proof PrsonaBase::generate_valid_vote_row_proof( const std::vector& commitment) const { Proof retval; if (!SERVER_IS_MALICIOUS) { retval.hbc = "PROOF"; return retval; } std::stringstream oracleInput; for (size_t i = 0; i < commitment.size(); i++) oracleInput << commitment[i]; Scalar val = oracle(oracleInput.str()); retval.responseParts.push_back(val); return retval; } Proof PrsonaBase::generate_valid_vote_matrix_proof( const std::vector>& commitment) const { Proof retval; if (!SERVER_IS_MALICIOUS) { retval.hbc = "PROOF"; return retval; } std::stringstream oracleInput; for (size_t i = 0; i < commitment.size(); i++) for (size_t j = 0; j < commitment[i].size(); j++) oracleInput << commitment[i][j]; Scalar val = oracle(oracleInput.str()); retval.responseParts.push_back(val); return retval; } Proof PrsonaBase::generate_valid_user_tally_proof( const EGCiphertext& commitment) const { Proof retval; if (!SERVER_IS_MALICIOUS) { retval.hbc = "PROOF"; return retval; } std::stringstream oracleInput; oracleInput << commitment; Scalar val = oracle(oracleInput.str()); retval.responseParts.push_back(val); return retval; } Proof PrsonaBase::generate_valid_server_tally_proof( const CurveBipoint& commitment) const { Proof retval; if (!SERVER_IS_MALICIOUS) { retval.hbc = "PROOF"; return retval; } std::stringstream oracleInput; oracleInput << commitment; Scalar val = oracle(oracleInput.str()); retval.responseParts.push_back(val); return retval; } Proof PrsonaBase::generate_valid_pseudonyms_proof( const std::vector& commitment) const { Proof retval; if (!SERVER_IS_MALICIOUS) { retval.hbc = "PROOF"; return retval; } std::stringstream oracleInput; for (size_t i = 0; i < commitment.size(); i++) oracleInput << commitment[i]; Scalar val = oracle(oracleInput.str()); retval.responseParts.push_back(val); return retval; } bool PrsonaBase::verify_valid_vote_row_proof( const std::vector& pi, const std::vector& commitment) const { if (pi.empty()) return false; if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Scalar comparison = pi[0].responseParts[0]; std::stringstream oracleInput; for (size_t i = 0; i < commitment.size(); i++) oracleInput << commitment[i]; if (oracle(oracleInput.str()) != comparison) { std::cerr << "Server's claimed value doesn't match their own commitment." << std::endl; return false; } size_t agreement = 1; for (size_t i = 1; i < pi.size(); i++) if (comparison == pi[i].responseParts[0]) agreement++; return agreement * 2 > pi.size(); } bool PrsonaBase::verify_valid_vote_matrix_proof( const std::vector& pi, const std::vector>& commitment) const { if (pi.empty()) return false; if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Scalar comparison = pi[0].responseParts[0]; std::stringstream oracleInput; for (size_t i = 0; i < commitment.size(); i++) for (size_t j = 0; j < commitment[i].size(); j++) oracleInput << commitment[i][j]; if (oracle(oracleInput.str()) != comparison) { std::cerr << "Server's claimed value doesn't match their own commitment." << std::endl; return false; } size_t agreement = 1; for (size_t i = 1; i < pi.size(); i++) if (comparison == pi[i].responseParts[0]) agreement++; return agreement * 2 > pi.size(); } bool PrsonaBase::verify_valid_user_tally_proof( const std::vector& pi, const EGCiphertext& commitment) const { if (pi.empty()) return false; if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Scalar comparison = pi[0].responseParts[0]; std::stringstream oracleInput; oracleInput << commitment; if (oracle(oracleInput.str()) != comparison) { std::cerr << "Server's claimed value doesn't match their own commitment." << std::endl; return false; } size_t agreement = 1; for (size_t i = 1; i < pi.size(); i++) if (comparison == pi[i].responseParts[0]) agreement++; return agreement * 2 > pi.size(); } bool PrsonaBase::verify_valid_server_tally_proof( const std::vector& pi, const CurveBipoint& commitment) const { if (pi.empty()) return false; if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Scalar comparison = pi[0].responseParts[0]; std::stringstream oracleInput; oracleInput << commitment; if (oracle(oracleInput.str()) != comparison) { std::cerr << "Server's claimed value doesn't match their own commitment." << std::endl; return false; } size_t agreement = 1; for (size_t i = 1; i < pi.size(); i++) if (comparison == pi[i].responseParts[0]) agreement++; return agreement * 2 > pi.size(); } bool PrsonaBase::verify_valid_pseudonyms_proof( const std::vector& pi, const std::vector& commitment) const { if (pi.empty()) return false; if (!SERVER_IS_MALICIOUS) return pi[0].hbc == "PROOF"; Scalar comparison = pi[0].responseParts[0]; std::stringstream oracleInput; for (size_t i = 0; i < commitment.size(); i++) oracleInput << commitment[i]; if (oracle(oracleInput.str()) != comparison) { std::cerr << "Server's claimed value doesn't match their own commitment." << std::endl; return false; } size_t agreement = 1; for (size_t i = 1; i < pi.size(); i++) if (comparison == pi[i].responseParts[0]) agreement++; return agreement * 2 > pi.size(); } template std::vector PrsonaBase::generate_proof_of_reordering( const std::vector>& permutations, const std::vector>& permutationSeeds, const std::vector>& productSeeds, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const Twistpoint& otherG, const Twistpoint& otherH) const; template std::vector PrsonaBase::generate_proof_of_reordering( const std::vector>& permutations, const std::vector>& permutationSeeds, const std::vector>& productSeeds, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const TwistBipoint& otherG, const TwistBipoint& otherH) const; template std::vector PrsonaBase::generate_proof_of_reordering( const std::vector>& permutations, const std::vector>& permutationSeeds, const std::vector>& productSeeds, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const CurveBipoint& otherG, const CurveBipoint& otherH) const; template bool PrsonaBase::verify_proof_of_reordering( const std::vector& pi, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const Twistpoint& otherG, const Twistpoint& otherH) const; template bool PrsonaBase::verify_proof_of_reordering( const std::vector& pi, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const TwistBipoint& otherG, const TwistBipoint& otherH) const; template bool PrsonaBase::verify_proof_of_reordering( const std::vector& pi, const std::vector& oldValues, const std::vector>& permutationCommits, const std::vector>& productCommits, const CurveBipoint& otherG, const CurveBipoint& otherH) const;