Untitled
unknown
plain_text
8 months ago
9.9 kB
11
Indexable
#include "../../include/pkg/election.hpp"
#include "../../include-shared/logger.hpp"
#include <crypto++/osrng.h>
#include <crypto++/nbtheory.h>
using namespace CryptoPP;
/*
Syntax to use logger:
CUSTOM_LOG(lg, debug) << "your message"
See logger.hpp for more modes besides 'debug'
*/
namespace {
src::severity_logger<logging::trivial::severity_level> lg;
}
/**
* Generate Vote and ZKP.
*/
std::pair<Vote_Ciphertext, VoteZKP_Struct>
ElectionClient::GenerateVote(CryptoPP::Integer vote, CryptoPP::Integer pk) {
//TODO: implement me!
if (vote != 0 && vote != 1) {
throw std::runtime_error("GenerateVote error: only votes of 0 or 1 are supported!");
}
AutoSeededRandomPool rng;
// Pick random r in [1..q-1]
SecByteBlock rb(DL_Q.ByteCount());
rng.GenerateBlock(rb, rb.size());
Integer r = Integer(rb, rb.size()) % DL_Q;
while (r == Integer::Zero()) {
rng.GenerateBlock(rb, rb.size());
r = Integer(rb, rb.size()) % DL_Q;
};
// a = g^r mod p
// b = (pk^r)*(g^vote) mod p
Vote_Ciphertext ciphertext;
ciphertext.a = a_exp_b_mod_c(DL_G, r, DL_P); // g^r mod p
Integer pk_to_r = a_exp_b_mod_c(pk, r, DL_P); // pk^r mod p
// If vote == 0, then g^vote = g^0 = 1
// If vote == 1, then g^vote = g
Integer g_to_v;
if (vote == Integer::One()) {
g_to_v = a_exp_b_mod_c(DL_G, Integer::One(), DL_P); // equivalent to g^1 mod p
} else {
g_to_v = Integer::One(); // simply 1
}
ciphertext.b = (pk_to_r * g_to_v) % DL_P;
VoteZKP_Struct zkp;
// Here i write some helper functions for the mod functions just to make life easier
auto modMul = [&](const Integer &x, const Integer &y){
return (x * y) % DL_P;
};
auto modExp = [&](const Integer &base, const Integer &exp){
return a_exp_b_mod_c(base, exp, DL_P);
};
auto modInv = [&](const Integer &x){
return EuclideanMultiplicativeInverse(x, DL_P);
};
auto random_in_mod_q = [&](void) {
Integer x;
do {
SecByteBlock tmp(DL_Q.ByteCount());
rng.GenerateBlock(tmp, tmp.size());
x = Integer(tmp, tmp.size()) % DL_Q;
} while (x == Integer::Zero());
return x;
};
auto bDivG = [&](const Integer &b_val){
// b * g^{-1} mod p
return modMul(b_val, modInv(DL_G));
};
const Integer &a = ciphertext.a;
const Integer &b = ciphertext.b;
Integer a0_, b0_, a1_, b1_;
Integer c0, c1;
Integer r0, r1;
if (vote == Integer::Zero()) {
// simulate proof for encryption of 1
c1 = random_in_mod_q();
Integer r1_doubleprime = random_in_mod_q();
// a1 = g^(r1'') / a^c1
{
Integer g_r1dp = modExp(DL_G, r1_doubleprime);
Integer a_c1 = modExp(a, c1);
a1_ = modMul(g_r1dp, modInv(a_c1));
}
// b1 = pk^(r1'') / [(b/g)^c1]
{
Integer pk_r1dp = modExp(pk, r1_doubleprime);
Integer b_over_g = bDivG(b); // pk^r * g^-1
Integer b_over_g_c1 = modExp(b_over_g, c1);
b1_ = modMul(pk_r1dp, modInv(b_over_g_c1));
}
//Real proof for 0 encr
Integer r0_prime = random_in_mod_q();
a0_ = modExp(DL_G, r0_prime); // g^(r0')
b0_ = modExp(pk, r0_prime); // pk^(r0')
Integer bigC = hash_vote_zkp(pk, a, b, a0_, b0_, a1_, b1_) % DL_Q;
c0 = (bigC >= c1) ? bigC - c1 : (bigC + DL_Q - c1);
c0 = c0 % DL_Q;
r0 = (r0_prime + (c0 * r)) % DL_Q;
r1 = r1_doubleprime;
}
else {
// vote is 1 then
// sim for vote being 0
c0 = random_in_mod_q();
Integer r0_doubleprime = random_in_mod_q();
// a0 = g^r0'' / a^c0
{
Integer g_r0dp = modExp(DL_G, r0_doubleprime);
Integer a_c0 = modExp(a, c0);
a0_ = modMul(g_r0dp, modInv(a_c0));
}
// b0 = pk^r0'' / [b^c0]
{
Integer pk_r0dp = modExp(pk, r0_doubleprime);
Integer b_c0 = modExp(b, c0);
b0_ = modMul(pk_r0dp, modInv(b_c0));
}
// real encr 1 proof
Integer r1_prime = random_in_mod_q();
a1_ = modExp(DL_G, r1_prime); // g^(r1')
b1_ = modExp(pk, r1_prime); // pk^(r1')
Integer bigC = hash_vote_zkp(pk, a, b, a0_, b0_, a1_, b1_) % DL_Q;
c1 = (bigC >= c0) ? bigC - c0 : (bigC + DL_Q - c0);
c1 = c1 % DL_Q;
r1 = (r1_prime + (c1 * r)) % DL_Q;
r0 = r0_doubleprime;
}
zkp.a0 = a0_;
zkp.a1 = a1_;
zkp.b0 = b0_;
zkp.b1 = b1_;
zkp.c0 = c0;
zkp.c1 = c1;
zkp.r0 = r0;
zkp.r1 = r1;
return std::make_pair(ciphertext, zkp);
}
/**
* Verify vote zkp.
*/
bool ElectionClient::VerifyVoteZKP(
std::pair<Vote_Ciphertext, VoteZKP_Struct> vote, CryptoPP::Integer pk) {
initLogger();
// TODO: implement me!
const Vote_Ciphertext &ciphertext = vote.first;
const VoteZKP_Struct &zkp = vote.second;
CryptoPP::Integer a = ciphertext.a; // g^r
CryptoPP::Integer b = ciphertext.b;
CryptoPP::Integer a0 = zkp.a0, a1 = zkp.a1,
b0 = zkp.b0, b1 = zkp.b1,
c0 = zkp.c0, c1 = zkp.c1,
r0 = zkp.r0, r1 = zkp.r1;
CryptoPP::Integer bigC = hash_vote_zkp(pk, a, b, a0, b0, a1, b1) % DL_Q;
if(((c0 + c1) % DL_Q) != bigC) {
return false;
}
// g^r0 == a0 * a^c0
CryptoPP::Integer lhs = ModularExponentiation(DL_G, r0, DL_P);
CryptoPP::Integer rhs = ( a0 * ModularExponentiation(a, c0, DL_P) ) % DL_P;
if(lhs != rhs) return false;
// pk^r0 == b0 * b^c0
lhs = ModularExponentiation(pk, r0, DL_P);
rhs = ( b0 * ModularExponentiation(b, c0, DL_P) ) % DL_P;
if(lhs != rhs) return false;
// g^r1 == a1 * a^c1
lhs = ModularExponentiation(DL_G, r1, DL_P);
rhs = ( a1 * ModularExponentiation(a, c1, DL_P) ) % DL_P;
if(lhs != rhs) return false;
CryptoPP::Integer bInvG = (b * EuclideanMultiplicativeInverse(DL_G, DL_P)) % DL_P;
lhs = ModularExponentiation(pk, r1, DL_P);
rhs = ( b1 * ModularExponentiation(bInvG, c1, DL_P) ) % DL_P;
if(lhs != rhs) return false;
return true;
}
/**
* Generate partial decryption and zkp.
*/
std::pair<PartialDecryption_Struct, DecryptionZKP_Struct>
ElectionClient::PartialDecrypt(Vote_Ciphertext combined_vote,
CryptoPP::Integer pk, CryptoPP::Integer sk) {
initLogger();
// TODO: implement me!
PartialDecryption_Struct dec;
dec.aggregate_ciphertext = combined_vote;
CryptoPP::Integer p = DL_P;
CryptoPP::Integer a = combined_vote.a;
dec.d = a_exp_b_mod_c(a, sk, p);
// 2) Build the ZKP
DecryptionZKP_Struct zkp;
// pick random r in [1..q-1]
CryptoPP::AutoSeededRandomPool rng;
CryptoPP::Integer r;
while (true) {
CryptoPP::SecByteBlock tmp(DL_Q.ByteCount());
rng.GenerateBlock(tmp, tmp.size());
CryptoPP::Integer candidate(tmp, tmp.size());
// reduce mod Q so 0 < r < Q
candidate = candidate % (DL_Q);
if (candidate > CryptoPP::Integer::Zero()) {
r = candidate;
break;
}
}
// zkp.u = a^r mod p
zkp.u = a_exp_b_mod_c(a, r, p);
// zkp.v = g^r mod p
zkp.v = a_exp_b_mod_c(DL_G, r, p);
CryptoPP::Integer c = hash_dec_zkp(pk, combined_vote.a, combined_vote.b,
zkp.u, zkp.v);
c = c % DL_Q;
// zkp.s = r + c * sk (mod Q)
zkp.s = (r + c * sk) % DL_Q;
return std::make_pair(dec, zkp);
}
/**
* Verify partial decryption zkp.
*/
bool ElectionClient::VerifyPartialDecryptZKP(
ArbiterToWorld_PartialDecryption_Message a2w_dec_s, CryptoPP::Integer pki) {
initLogger();
// TODO: implement me!
PartialDecryption_Struct dec_s = a2w_dec_s.dec;
DecryptionZKP_Struct zkp = a2w_dec_s.zkp;
Vote_Ciphertext agg = dec_s.aggregate_ciphertext;
CryptoPP::Integer a = agg.a;
CryptoPP::Integer b = agg.b;
CryptoPP::Integer d = dec_s.d;
CryptoPP::Integer p = DL_P;
//Compute challenge c
CryptoPP::Integer c = hash_dec_zkp(pki, a, b, zkp.u, zkp.v);
c = c % DL_Q;
// Check a^s == u * d^c (mod p)
CryptoPP::Integer lhs = a_exp_b_mod_c(a, zkp.s, p);
CryptoPP::Integer rhs = (zkp.u * a_exp_b_mod_c(d, c, p)) % p;
if (lhs != rhs) {
return false;
}
// g^s == v * pki^c (mod p)
lhs = a_exp_b_mod_c(DL_G, zkp.s, p);
rhs = (zkp.v * a_exp_b_mod_c(pki, c, p)) % p;
if (lhs != rhs) {
return false;
}
return true;
}
/**
* Combine votes into one using homomorphic encryption.
*/
Vote_Ciphertext ElectionClient::CombineVotes(std::vector<VoteRow> all_votes) {
initLogger();
// TODO: implement me!
Vote_Ciphertext combined;
combined.a = CryptoPP::Integer::One();
combined.b = CryptoPP::Integer::One();
CryptoPP::Integer p = DL_P;
for (auto &row : all_votes) {
// row.vote is a Vote_Ciphertext
combined.a = (combined.a * row.vote.a) % p;
combined.b = (combined.b * row.vote.b) % p;
}
return combined;
}
/**
* Combines partial decryptions and returns final vote count.
*/
CryptoPP::Integer ElectionClient::CombineResults(
Vote_Ciphertext combined_vote,
std::vector<PartialDecryptionRow> all_partial_decryptions) {
initLogger();
// TODO: implement me!
// Combine all partial dec by multiplying them
CryptoPP::Integer bigD = CryptoPP::Integer::One();
for(const auto &row : all_partial_decryptions) {
bigD = (bigD * row.dec.d) % DL_P;
}
CryptoPP::Integer numerator = combined_vote.b;
CryptoPP::Integer denominator = bigD; // = a^sk mod p
CryptoPP::Integer invDen = EuclideanMultiplicativeInverse(denominator, DL_P);
CryptoPP::Integer raw = (numerator * invDen) % DL_P; // = g^(sum_of_ones)
int maxVotes = 10000; // might need to change this depending on autograder tests idk
for(int i=0; i<= maxVotes; i++){
CryptoPP::Integer testVal =
CryptoPP::ModularExponentiation(DL_G, CryptoPP::Integer(i), DL_P);
if(testVal == raw){
// found the exponent i, and i is the number of 1-votes
return CryptoPP::Integer(i);
}
}
// In case something goes wrong...
return CryptoPP::Integer(-1);
}
Editor is loading...
Leave a Comment