|
@@ -0,0 +1,842 @@
|
|
|
+#include <iostream>
|
|
|
+
|
|
|
+#include "base.hpp"
|
|
|
+
|
|
|
+extern const scalar_t bn_n;
|
|
|
+extern const curvepoint_fp_t bn_curvegen;
|
|
|
+
|
|
|
+/* 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. */
|
|
|
+Curvepoint PrsonaBase::EL_GAMAL_GENERATOR = Curvepoint();
|
|
|
+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 = Curvepoint(bn_curvegen);
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+Curvepoint PrsonaBase::get_blinding_generator() const
|
|
|
+{
|
|
|
+ return elGamalBlindGenerator;
|
|
|
+}
|
|
|
+
|
|
|
+Curvepoint PrsonaBase::get_blinding_generator(std::vector<Proof>& pi) const
|
|
|
+{
|
|
|
+ pi = elGamalBlindGeneratorProof;
|
|
|
+
|
|
|
+ return elGamalBlindGenerator;
|
|
|
+}
|
|
|
+
|
|
|
+/***********************
|
|
|
+ * PROTECTED FUNCTIONS *
|
|
|
+ ***********************/
|
|
|
+
|
|
|
+/*
|
|
|
+ * PRIVATE ELEMENT SETTER
|
|
|
+ */
|
|
|
+
|
|
|
+bool PrsonaBase::set_EG_blind_generator(
|
|
|
+ const std::vector<Proof>& pi,
|
|
|
+ const Curvepoint& currGenerator,
|
|
|
+ size_t numServers)
|
|
|
+{
|
|
|
+ if (!verify_generator_proof(pi, currGenerator, numServers))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ elGamalBlindGeneratorProof = pi;
|
|
|
+ elGamalBlindGenerator = currGenerator;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * SCHNORR PROOFS
|
|
|
+ */
|
|
|
+
|
|
|
+Proof PrsonaBase::schnorr_generation(
|
|
|
+ const Curvepoint& generator,
|
|
|
+ const Curvepoint& commitment,
|
|
|
+ const Scalar& log) const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ std::stringstream oracleInput;
|
|
|
+
|
|
|
+ Scalar r;
|
|
|
+ r.set_random();
|
|
|
+
|
|
|
+ Curvepoint U = generator * r;
|
|
|
+ oracleInput << generator << commitment << U;
|
|
|
+
|
|
|
+ Scalar c = oracle(oracleInput.str());
|
|
|
+ Scalar z = r.curveAdd(c.curveMult(log));
|
|
|
+
|
|
|
+ retval.challengeParts.push_back(c);
|
|
|
+ retval.responseParts.push_back(z);
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+bool PrsonaBase::schnorr_verification(
|
|
|
+ const Curvepoint& generator,
|
|
|
+ const Curvepoint& commitment,
|
|
|
+ const Scalar& c,
|
|
|
+ const Scalar& z) const
|
|
|
+{
|
|
|
+ Curvepoint U = generator * z - commitment * c;
|
|
|
+
|
|
|
+ std::stringstream oracleInput;
|
|
|
+ oracleInput << generator << commitment << U;
|
|
|
+
|
|
|
+ return c == oracle(oracleInput.str());
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * OWNERSHIP PROOFS
|
|
|
+ */
|
|
|
+
|
|
|
+// Prove ownership of the short term public key
|
|
|
+Proof PrsonaBase::generate_ownership_proof(
|
|
|
+ const Curvepoint& generator,
|
|
|
+ const Curvepoint& commitment,
|
|
|
+ const Scalar& log) const
|
|
|
+{
|
|
|
+ if (!CLIENT_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ Proof retval;
|
|
|
+ retval.basic = "PROOF";
|
|
|
+
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ return schnorr_generation(generator, commitment, log);
|
|
|
+}
|
|
|
+
|
|
|
+bool PrsonaBase::verify_ownership_proof(
|
|
|
+ const Proof& pi,
|
|
|
+ const Curvepoint& generator,
|
|
|
+ const Curvepoint& commitment) const
|
|
|
+{
|
|
|
+ if (!CLIENT_IS_MALICIOUS)
|
|
|
+ return pi.basic == "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 Curvepoint& currGenerator,
|
|
|
+ const Scalar& seed) const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+ if (!CLIENT_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ Curvepoint nextGenerator = currGenerator * seed;
|
|
|
+ retval = schnorr_generation(currGenerator, nextGenerator, seed);
|
|
|
+
|
|
|
+ retval.partialUniversals.push_back(currGenerator);
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+bool PrsonaBase::verify_generator_proof(
|
|
|
+ const std::vector<Proof>& pi,
|
|
|
+ const Curvepoint& 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].basic == "PROOF";
|
|
|
+
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pi[0].partialUniversals[0] != EL_GAMAL_GENERATOR)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ for (size_t i = 0; i < pi.size(); i++)
|
|
|
+ {
|
|
|
+ Curvepoint generator = pi[i].partialUniversals[0];
|
|
|
+ Curvepoint commitment = (i == pi.size() - 1 ?
|
|
|
+ currGenerator :
|
|
|
+ pi[i + 1].partialUniversals[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<Proof> 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<Proof> 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.curveSub(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<Scalar> 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].curveSub(currMask.curveMult(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;
|
|
|
+ Curvepoint 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.partialUniversals.push_back(c);
|
|
|
+
|
|
|
+ c_a = g * s + h * a;
|
|
|
+
|
|
|
+ Scalar am = a.curveMult(m);
|
|
|
+ c_b = g * t + h * am;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ Scalar mx = m.curveMult(x);
|
|
|
+ f = mx.curveAdd(a);
|
|
|
+
|
|
|
+ Scalar rx = r.curveMult(x);
|
|
|
+ z_a = rx.curveAdd(s);
|
|
|
+
|
|
|
+ Scalar x_f = x.curveSub(f);
|
|
|
+ Scalar r_x_f = r.curveMult(x_f);
|
|
|
+ z_b = r_x_f.curveAdd(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<Proof>& pi,
|
|
|
+ const Curvepoint& generator,
|
|
|
+ const Curvepoint& 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].basic == "PROOF";
|
|
|
+
|
|
|
+ Scalar c, z;
|
|
|
+ c = pi[0].challengeParts[0];
|
|
|
+ z = pi[0].responseParts[0];
|
|
|
+
|
|
|
+ // User should be able to prove they are who they say they are
|
|
|
+ if (!schnorr_verification(generator, owner, c, z))
|
|
|
+ {
|
|
|
+ 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.
|
|
|
+ Curvepoint X;
|
|
|
+ for (size_t i = 1; i < pi.size(); i++)
|
|
|
+ {
|
|
|
+ Curvepoint c, g, h;
|
|
|
+ c = pi[i].partialUniversals[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
|
|
|
+ Curvepoint c_a, c_b;
|
|
|
+ c_a = g * z_a + h * f - c * x;
|
|
|
+ Scalar x_f = x.curveSub(f);
|
|
|
+ c_b = g * z_b - c * x_f;
|
|
|
+
|
|
|
+ std::stringstream oracleInput;
|
|
|
+ oracleInput << g << h << c << c_a << c_b;
|
|
|
+
|
|
|
+ if (oracle(oracleInput.str()) != pi[i].challengeParts[0])
|
|
|
+ {
|
|
|
+ std::cerr << "0 or 1 proof failed at index " << i << " of " << pi.size() - 1 << ", aborting." << std::endl;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Scalar negThreshold;
|
|
|
+ negThreshold = Scalar(0).curveSub(threshold);
|
|
|
+
|
|
|
+ Curvepoint scoreCommitment =
|
|
|
+ commitment.encryptedMessage +
|
|
|
+ elGamalBlindGenerator * negThreshold;
|
|
|
+
|
|
|
+ return X == scoreCommitment;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * VALID VOTE PROOFS
|
|
|
+ */
|
|
|
+
|
|
|
+std::vector<Proof> PrsonaBase::generate_vote_proof(
|
|
|
+ const Proof& ownershipProof,
|
|
|
+ const CurveBipoint& g,
|
|
|
+ const CurveBipoint& h,
|
|
|
+ const std::vector<bool>& replaces,
|
|
|
+ const std::vector<CurveBipoint>& oldEncryptedVotes,
|
|
|
+ const std::vector<CurveBipoint>& newEncryptedVotes,
|
|
|
+ const std::vector<Scalar>& seeds,
|
|
|
+ const std::vector<Scalar>& votes) const
|
|
|
+{
|
|
|
+ std::vector<Proof> 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];
|
|
|
+
|
|
|
+ /* 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 c_r, z_r, a, s, t_1, t_2;
|
|
|
+ c_r.set_random();
|
|
|
+ z_r.set_random();
|
|
|
+ a.set_random();
|
|
|
+ s.set_random();
|
|
|
+ t_1.set_random();
|
|
|
+ t_2.set_random();
|
|
|
+
|
|
|
+ CurveBipoint U = h * z_r +
|
|
|
+ oldEncryptedVotes[i] * c_r -
|
|
|
+ newEncryptedVotes[i] * c_r;
|
|
|
+
|
|
|
+ CurveBipoint C_a = g * a + h * s;
|
|
|
+
|
|
|
+ Scalar power = (a.curveAdd(a)).curveMult(votes[i].curveMult(votes[i]));
|
|
|
+ power =
|
|
|
+ power.curveSub((a.curveAdd(a).curveAdd(a)).curveMult(votes[i]));
|
|
|
+ CurveBipoint C_b = g * power + h * t_1;
|
|
|
+ currProof.partialUniversals.push_back(C_b[0]);
|
|
|
+ currProof.partialUniversals.push_back(C_b[1]);
|
|
|
+
|
|
|
+ CurveBipoint C_c = g * a.curveMult(a.curveMult(votes[i])) +
|
|
|
+ h * t_2;
|
|
|
+
|
|
|
+ oracleInput << U << C_a << C_b << C_c;
|
|
|
+
|
|
|
+ Scalar c = oracle(oracleInput.str());
|
|
|
+ Scalar c_n = c.curveSub(c_r);
|
|
|
+ currProof.challengeParts.push_back(c_r);
|
|
|
+ currProof.challengeParts.push_back(c_n);
|
|
|
+
|
|
|
+ Scalar f = (votes[i].curveMult(c_n)).curveAdd(a);
|
|
|
+ Scalar z_na = (seeds[i].curveMult(c_n)).curveAdd(s);
|
|
|
+
|
|
|
+ Scalar t_1_c_n_t_2 = (t_1.curveMult(c_n)).curveAdd(t_2);
|
|
|
+ Scalar f_c_n = f.curveSub(c_n);
|
|
|
+ Scalar c_n2_f = c_n.curveAdd(c_n).curveSub(f);
|
|
|
+ Scalar z_nb =
|
|
|
+ (seeds[i].curveMult(f_c_n).curveMult(c_n2_f)).curveAdd(
|
|
|
+ t_1_c_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,
|
|
|
+ c_n, z_na, z_nb, f;
|
|
|
+ u.set_random();
|
|
|
+ commitmentLambda_1.set_random();
|
|
|
+ commitmentLambda_2.set_random();
|
|
|
+ c_n.set_random();
|
|
|
+ z_na.set_random();
|
|
|
+ z_nb.set_random();
|
|
|
+ f.set_random();
|
|
|
+
|
|
|
+ CurveBipoint U = h * u;
|
|
|
+
|
|
|
+ CurveBipoint C_a = g * f +
|
|
|
+ h * z_na -
|
|
|
+ newEncryptedVotes[i] * c_n;
|
|
|
+
|
|
|
+ CurveBipoint C_b = g * commitmentLambda_1 + h * commitmentLambda_2;
|
|
|
+ currProof.partialUniversals.push_back(C_b[0]);
|
|
|
+ currProof.partialUniversals.push_back(C_b[1]);
|
|
|
+
|
|
|
+ Scalar f_c_n = f.curveSub(c_n);
|
|
|
+ Scalar c_n2_f = c_n.curveAdd(c_n).curveSub(f);
|
|
|
+ CurveBipoint C_c =
|
|
|
+ h * z_nb -
|
|
|
+ newEncryptedVotes[i] * f_c_n.curveMult(c_n2_f) -
|
|
|
+ C_b * c_n;
|
|
|
+
|
|
|
+ oracleInput << U << C_a << C_b << C_c;
|
|
|
+
|
|
|
+ Scalar c = oracle(oracleInput.str());
|
|
|
+ Scalar c_r = c.curveSub(c_n);
|
|
|
+ currProof.challengeParts.push_back(c_r);
|
|
|
+ currProof.challengeParts.push_back(c_n);
|
|
|
+
|
|
|
+ Scalar z_r = u.curveAdd(c_r.curveMult(seeds[i]));
|
|
|
+ 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 CurveBipoint& g,
|
|
|
+ const CurveBipoint& h,
|
|
|
+ const std::vector<Proof>& pi,
|
|
|
+ const std::vector<CurveBipoint>& oldEncryptedVotes,
|
|
|
+ const std::vector<CurveBipoint>& newEncryptedVotes,
|
|
|
+ const Curvepoint& freshGenerator,
|
|
|
+ const Curvepoint& 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].basic == "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;
|
|
|
+ Curvepoint C_b_0, C_b_1;
|
|
|
+ C_b_0 = pi[i].partialUniversals[0];
|
|
|
+ C_b_1 = pi[i].partialUniversals[1];
|
|
|
+
|
|
|
+ CurveBipoint C_b(C_b_0, C_b_1);
|
|
|
+
|
|
|
+ Scalar c_r, c_n, z_r, f, z_na, z_nb;
|
|
|
+ c_r = pi[i].challengeParts[0];
|
|
|
+ c_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];
|
|
|
+
|
|
|
+ CurveBipoint U, C_a, C_c;
|
|
|
+ U = h * z_r +
|
|
|
+ oldEncryptedVotes[voteIndex] * c_r -
|
|
|
+ newEncryptedVotes[voteIndex] * c_r;
|
|
|
+ C_a = g * f + h * z_na - newEncryptedVotes[voteIndex] * c_n;
|
|
|
+
|
|
|
+ Scalar f_c_n = f.curveSub(c_n);
|
|
|
+ Scalar c_n2_f = c_n.curveAdd(c_n).curveSub(f);
|
|
|
+ C_c = h * z_nb -
|
|
|
+ newEncryptedVotes[voteIndex] * f_c_n.curveMult(c_n2_f) -
|
|
|
+ C_b * c_n;
|
|
|
+
|
|
|
+ std::stringstream oracleInput;
|
|
|
+ oracleInput << g << h
|
|
|
+ << oldEncryptedVotes[voteIndex] << newEncryptedVotes[voteIndex]
|
|
|
+ << U << C_a << C_b << C_c;
|
|
|
+
|
|
|
+ if (oracle(oracleInput.str()) != c_r.curveAdd(c_n))
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * EPOCH PROOFS
|
|
|
+ */
|
|
|
+
|
|
|
+bool PrsonaBase::verify_update_proof(
|
|
|
+ const Proof& pi) const
|
|
|
+{
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * SERVER AGREEMENT PROOFS
|
|
|
+ */
|
|
|
+
|
|
|
+Proof PrsonaBase::generate_valid_default_tally_proof() const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+Proof PrsonaBase::generate_valid_fresh_generator_proof() const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+Proof PrsonaBase::generate_votes_valid_proof() const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+Proof PrsonaBase::generate_proof_of_added_user() const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+Proof PrsonaBase::generate_score_proof() const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+Proof PrsonaBase::generate_proof_of_correct_tally() const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+Proof PrsonaBase::generate_proof_of_correct_sum() const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+Proof PrsonaBase::generate_proof_of_shuffle() const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+Proof PrsonaBase::generate_valid_pseudonyms_proof() const
|
|
|
+{
|
|
|
+ Proof retval;
|
|
|
+
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ {
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ retval.basic = "PROOF";
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+bool PrsonaBase::verify_valid_tally_proof(const Proof& pi) const
|
|
|
+{
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+}
|
|
|
+
|
|
|
+bool PrsonaBase::verify_score_proof(const Proof& pi) const
|
|
|
+{
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+}
|
|
|
+
|
|
|
+bool PrsonaBase::verify_default_tally_proof(const Proof& pi) const
|
|
|
+{
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+}
|
|
|
+
|
|
|
+bool PrsonaBase::verify_default_votes_proof(const Proof& pi) const
|
|
|
+{
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+}
|
|
|
+
|
|
|
+bool PrsonaBase::verify_valid_votes_proof(const Proof& pi) const
|
|
|
+{
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+}
|
|
|
+
|
|
|
+bool PrsonaBase::verify_valid_pseudonyms_proof(const Proof& pi) const
|
|
|
+{
|
|
|
+ if (!SERVER_IS_MALICIOUS)
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+
|
|
|
+ return pi.basic == "PROOF";
|
|
|
+}
|