|
@@ -28,6 +28,11 @@ mpz_class log2(mpz_class x)
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
+mpz_class bit(mpz_class x)
|
|
|
+{
|
|
|
+ return x > 0 ? 1 : 0;
|
|
|
+}
|
|
|
+
|
|
|
/********************
|
|
|
* PUBLIC FUNCTIONS *
|
|
|
********************/
|
|
@@ -84,29 +89,37 @@ Curvepoint PrsonaClient::get_short_term_public_key(Proof &pi) const
|
|
|
* SERVER INTERACTIONS
|
|
|
*/
|
|
|
|
|
|
-// Generate a new vote vector to give to the servers
|
|
|
-// (@replace controls which votes are actually being updated and which are not)
|
|
|
+/* Generate a new vote vector to give to the servers
|
|
|
+ * @replaces controls which votes are actually being updated and which are not
|
|
|
+ *
|
|
|
+ * You may really want to make currentEncryptedVotes a member variable,
|
|
|
+ * but it doesn't behave correctly when adding new clients after this one. */
|
|
|
std::vector<CurveBipoint> PrsonaClient::make_votes(
|
|
|
Proof& pi,
|
|
|
- const std::vector<Scalar>& vote,
|
|
|
- const std::vector<bool>& replace)
|
|
|
+ const std::vector<CurveBipoint>& currentEncryptedVotes,
|
|
|
+ const std::vector<Scalar>& votes,
|
|
|
+ const std::vector<bool>& replaces)
|
|
|
{
|
|
|
std::vector<CurveBipoint> retval;
|
|
|
|
|
|
- for (size_t i = 0; i < vote.size(); i++)
|
|
|
+ if (!verify_valid_votes_proof(pi, currentEncryptedVotes))
|
|
|
+ {
|
|
|
+ std::cerr << "Could not verify proof of valid votes." << std::endl;
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (size_t i = 0; i < votes.size(); i++)
|
|
|
{
|
|
|
CurveBipoint currScore;
|
|
|
- if (replace[i])
|
|
|
- serverPublicKey.encrypt(currScore, vote[i]);
|
|
|
+ if (replaces[i])
|
|
|
+ serverPublicKey.encrypt(currScore, votes[i]);
|
|
|
else
|
|
|
currScore = serverPublicKey.rerandomize(currentEncryptedVotes[i]);
|
|
|
|
|
|
retval.push_back(currScore);
|
|
|
}
|
|
|
|
|
|
- currentEncryptedVotes = retval;
|
|
|
-
|
|
|
- pi = generate_vote_proof(retval, vote);
|
|
|
+ pi = generate_vote_proof(retval, votes);
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
@@ -130,32 +143,21 @@ void PrsonaClient::receive_vote_tally(
|
|
|
decrypt_score(score);
|
|
|
}
|
|
|
|
|
|
-// Receive a new encrypted vote vector from the servers (each epoch)
|
|
|
-void PrsonaClient::receive_encrypted_votes(
|
|
|
- const Proof& pi, const std::vector<CurveBipoint>& votes)
|
|
|
-{
|
|
|
- if (!verify_valid_votes_proof(pi, votes))
|
|
|
- {
|
|
|
- std::cerr << "Could not verify proof of valid votes." << std::endl;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- currentEncryptedVotes = votes;
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* REPUTATION PROOFS
|
|
|
*/
|
|
|
|
|
|
-// TO BE UPDATED WITH THING IAN SHOWED ME IN MEETING FOR DISJUNCTION
|
|
|
+// A pretty straightforward range proof (generation)
|
|
|
std::vector<Proof> PrsonaClient::generate_reputation_proof(
|
|
|
const Scalar& threshold) const
|
|
|
{
|
|
|
std::vector<Proof> retval;
|
|
|
|
|
|
+ // Don't even try if the user asks to make an illegitimate proof
|
|
|
if (threshold > currentScore)
|
|
|
return retval;
|
|
|
|
|
|
+ // Base case
|
|
|
if (!CLIENT_IS_MALICIOUS)
|
|
|
{
|
|
|
Proof currProof;
|
|
@@ -165,78 +167,79 @@ std::vector<Proof> PrsonaClient::generate_reputation_proof(
|
|
|
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(generate_ownership_proof());
|
|
|
|
|
|
+ // The value we're actually using in our proof
|
|
|
mpz_class proofVal = currentScore.curveSub(threshold).toInt();
|
|
|
- mpz_class proofBits = log2(currentEncryptedVotes.size() * MAX_ALLOWED_VOTE - threshold.toInt());
|
|
|
+ // Top of the range in our proof determined by what scores are even possible
|
|
|
+ mpz_class proofBits =
|
|
|
+ log2(
|
|
|
+ servers->get_num_clients() * 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(Scalar());
|
|
|
+ masksPerBit.push_back(inversePrivateKey);
|
|
|
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)));
|
|
|
+ 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;
|
|
|
- std::stringstream oracleInput;
|
|
|
- oracleInput << currentFreshGenerator << EL_GAMAL_BLIND_GENERATOR;
|
|
|
+ Curvepoint g, h, c, c_a, c_b;
|
|
|
+ g = currentEncryptedScore.mask;
|
|
|
+ h = EL_GAMAL_BLIND_GENERATOR;
|
|
|
|
|
|
- mpz_class currBit = proofVal & (1 << i);
|
|
|
+ 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];
|
|
|
|
|
|
- Curvepoint currentCommitment = currentFreshGenerator * masksPerBit[i] + EL_GAMAL_BLIND_GENERATOR * Scalar(currBit);
|
|
|
- currProof.partialUniversals.push_back(currentCommitment);
|
|
|
- oracleInput << currentCommitment;
|
|
|
+ c = g * r + h * m;
|
|
|
+ currProof.partialUniversals.push_back(c);
|
|
|
|
|
|
- if (currBit)
|
|
|
- {
|
|
|
- Scalar u_0, c, c_0, c_1, z_0, z_1;
|
|
|
- u_0.set_random();
|
|
|
- c_1.set_random();
|
|
|
- z_1.set_random();
|
|
|
-
|
|
|
- Curvepoint U_0 = currentFreshGenerator * u_0;
|
|
|
- Curvepoint U_1 = currentFreshGenerator * z_1 - currentCommitment * c_1 + EL_GAMAL_BLIND_GENERATOR;
|
|
|
- currProof.initParts.push_back(U_0);
|
|
|
- currProof.initParts.push_back(U_1);
|
|
|
- oracleInput << U_0 << U_1;
|
|
|
-
|
|
|
- c = oracle(oracleInput.str());
|
|
|
- c_0 = c.curveSub(c_1);
|
|
|
- z_0 = c_0.curveMult(masksPerBit[i]).curveAdd(u_0);
|
|
|
-
|
|
|
- currProof.challengeParts.push_back(c_0);
|
|
|
- currProof.challengeParts.push_back(c_1);
|
|
|
- currProof.responseParts.push_back(z_0);
|
|
|
- currProof.responseParts.push_back(z_1);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Scalar u_1, c, c_0, c_1, z_0, z_1;
|
|
|
- u_1.set_random();
|
|
|
- c_0.set_random();
|
|
|
- z_0.set_random();
|
|
|
-
|
|
|
- Curvepoint U_0 = currentFreshGenerator * z_0 - currentCommitment * c_0;
|
|
|
- Curvepoint U_1 = currentFreshGenerator * u_1;
|
|
|
- currProof.initParts.push_back(U_0);
|
|
|
- currProof.initParts.push_back(U_1);
|
|
|
- oracleInput << U_0 << U_1;
|
|
|
-
|
|
|
- c = oracle(oracleInput.str());
|
|
|
- c_1 = c.curveSub(c_0);
|
|
|
- z_1 = c_1.curveMult(masksPerBit[i]).curveAdd(u_1);
|
|
|
-
|
|
|
- currProof.challengeParts.push_back(c_0);
|
|
|
- currProof.challengeParts.push_back(c_1);
|
|
|
- currProof.responseParts.push_back(z_0);
|
|
|
- currProof.responseParts.push_back(z_1);
|
|
|
- }
|
|
|
+ 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);
|
|
|
}
|
|
@@ -244,56 +247,90 @@ std::vector<Proof> PrsonaClient::generate_reputation_proof(
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-// TO BE UPDATED WITH THING IAN SHOWED ME IN MEETING FOR DISJUNCTION
|
|
|
+// A pretty straightforward range proof (verification)
|
|
|
bool PrsonaClient::verify_reputation_proof(
|
|
|
const std::vector<Proof>& pi,
|
|
|
const Curvepoint& shortTermPublicKey,
|
|
|
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;
|
|
|
+ }
|
|
|
|
|
|
+ // 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], shortTermPublicKey))
|
|
|
+ {
|
|
|
+ std::cerr << "Schnorr proof failed, aborting." << std::endl;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get the encrypted score in question from the servers
|
|
|
+ Proof serverProof;
|
|
|
+ EGCiphertext encryptedScore =
|
|
|
+ servers->get_current_tally(serverProof, shortTermPublicKey);
|
|
|
+
|
|
|
+ // Rough for the prover but if the server messes up,
|
|
|
+ // no way to prove the thing anyways
|
|
|
+ if (!verify_valid_tally_proof(serverProof, encryptedScore))
|
|
|
+ {
|
|
|
+ std::cerr << "Server error prevented proof from working, 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++)
|
|
|
{
|
|
|
- X = X + pi[i].partialUniversals[0] * Scalar(1 << (i - 1));
|
|
|
+ Curvepoint c, g, h;
|
|
|
+ c = pi[i].partialUniversals[0];
|
|
|
+ g = encryptedScore.mask;
|
|
|
+ h = EL_GAMAL_BLIND_GENERATOR;
|
|
|
|
|
|
- std::stringstream oracleInput;
|
|
|
- oracleInput << currentFreshGenerator << EL_GAMAL_BLIND_GENERATOR << pi[i].partialUniversals[0];
|
|
|
- oracleInput << pi[i].initParts[0] << pi[i].initParts[1];
|
|
|
+ X = X + c * Scalar(1 << (i - 1));
|
|
|
|
|
|
- Scalar c = oracle(oracleInput.str());
|
|
|
+ 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];
|
|
|
|
|
|
- if (c != pi[i].challengeParts[0] + pi[i].challengeParts[1])
|
|
|
- return false;
|
|
|
+ // 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;
|
|
|
|
|
|
- if (currentFreshGenerator * pi[i].responseParts[0] != pi[i].initParts[0] + pi[i].partialUniversals[0] * pi[i].challengeParts[0])
|
|
|
- return false;
|
|
|
+ std::stringstream oracleInput;
|
|
|
+ oracleInput << g << h << c << c_a << c_b;
|
|
|
|
|
|
- if (currentFreshGenerator * pi[i].responseParts[1] != pi[i].initParts[1] + pi[i].partialUniversals[0] * pi[i].challengeParts[1] - EL_GAMAL_BLIND_GENERATOR)
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- Proof serverProof;
|
|
|
- EGCiphertext encryptedScore = servers->get_current_tally(serverProof, shortTermPublicKey);
|
|
|
-
|
|
|
- if (!verify_valid_tally_proof(serverProof, encryptedScore))
|
|
|
- return false;
|
|
|
-
|
|
|
Scalar negThreshold;
|
|
|
negThreshold = Scalar(0).curveSub(threshold);
|
|
|
|
|
|
- Curvepoint scoreCommitment = encryptedScore.encryptedMessage + EL_GAMAL_BLIND_GENERATOR * negThreshold;
|
|
|
- if (X != scoreCommitment)
|
|
|
- return false;
|
|
|
+ Curvepoint scoreCommitment =
|
|
|
+ encryptedScore.encryptedMessage +
|
|
|
+ EL_GAMAL_BLIND_GENERATOR * negThreshold;
|
|
|
+
|
|
|
+ return X == scoreCommitment;
|
|
|
+}
|
|
|
|
|
|
- return true;
|
|
|
+Scalar PrsonaClient::get_score() const
|
|
|
+{
|
|
|
+ return currentScore;
|
|
|
}
|
|
|
|
|
|
/*********************
|
|
@@ -364,7 +401,7 @@ Proof PrsonaClient::generate_ownership_proof() const
|
|
|
Scalar z = r.curveAdd(c.curveMult(longTermPrivateKey));
|
|
|
|
|
|
retval.basic = "PROOF";
|
|
|
- retval.initParts.push_back(u);
|
|
|
+ retval.challengeParts.push_back(c);
|
|
|
retval.responseParts.push_back(z);
|
|
|
|
|
|
return retval;
|
|
@@ -377,15 +414,15 @@ bool PrsonaClient::verify_ownership_proof(
|
|
|
if (!CLIENT_IS_MALICIOUS)
|
|
|
return pi.basic == "PROOF";
|
|
|
|
|
|
- Curvepoint u = pi.initParts[0];
|
|
|
+ Scalar c = pi.challengeParts[0];
|
|
|
+ Scalar z = pi.responseParts[0];
|
|
|
+
|
|
|
+ Curvepoint u = currentFreshGenerator * z - shortTermPublicKey * c;
|
|
|
|
|
|
std::stringstream oracleInput;
|
|
|
oracleInput << currentFreshGenerator << shortTermPublicKey << u;
|
|
|
- Scalar c = oracle(oracleInput.str());
|
|
|
-
|
|
|
- Scalar z = pi.responseParts[0];
|
|
|
-
|
|
|
- return (currentFreshGenerator * z) == (shortTermPublicKey * c + u);
|
|
|
+
|
|
|
+ return c == oracle(oracleInput.str());
|
|
|
}
|
|
|
|
|
|
/*
|