Skip to content

Commit

Permalink
full voting loop working with self-managed election
Browse files Browse the repository at this point in the history
  • Loading branch information
benadida committed Dec 29, 2008
1 parent dbc55ea commit 38c73b3
Show file tree
Hide file tree
Showing 5 changed files with 308 additions and 30 deletions.
33 changes: 28 additions & 5 deletions helios/models.py
Expand Up @@ -123,6 +123,30 @@ def set_result(self, tally_d, proof_d):
self.result = tally_d
self.decryption_proof = proof_d

@property
def pretty_result(self):
if not self.result:
return None

election_obj = self.toElection()
raw_result = self.result
prettified_result = []

# loop through questions
for i in range(len(election_obj.questions)):
q = election_obj.questions[i]
pretty_question = []

# go through answers
for j in range(len(q['answers'])):
a = q['answers'][j]
count = raw_result[i][j]
pretty_question.append({'answer': a, 'count': count})

prettified_result.append({'question': q['short_name'], 'answers': pretty_question})

return prettified_result

def get_first_uncounted_voter(self):
"""
Return the voter that hasn't been counted yet, in order of cast_id
Expand Down Expand Up @@ -305,15 +329,14 @@ class Voter(models.Model, JSONObject):
def selectByEmailOrOpenID(cls, election, email, openid_url):
email_voter = openid_voter = None

## FIXME
if email:
email_voter = cls.selectByKeys({'election': election, 'email': email})
email_voter = cls.objects.get(election = election, email = email)

if openid_url:
openid_voter = cls.selectByKeys({'election': election, 'openid_url': openid_url})
openid_voter = cls.objects.get(election = election, openid_url= openid_url)

# two voters, not the same?
if email_voter and openid_voter and email_voter.voter_id != openid_voter.voter_id:
if email_voter and openid_voter and email_voter != openid_voter:
raise Exception("problem matching openid and email")

return email_voter or openid_voter
Expand Down Expand Up @@ -361,7 +384,7 @@ def compute_vote_hash(self):
return vote_hash

def get_vote(self):
vote_dict = utils.from_json(self.vote or "null")
vote_dict = self.vote

# null vote
if not vote_dict or vote_dict == "":
Expand Down
178 changes: 178 additions & 0 deletions helios/templates/drive_tally.html
@@ -0,0 +1,178 @@
{% extends "base.html" %}
{% block content %}
<script language="javascript">
Helios.setup();

ELECTION_PK = ElGamal.PublicKey.fromJSONObject({{election_pk_json|safe}});
var ELECTION_SK = null;

{% if election_sk %}
ELECTION_SK = ElGamal.SecretKey.fromJSONObject({{election_sk_json|safe}});
{% endif %}

$(document).ready(function() {
if (ELECTION_SK != null) {
$('#sk_section').hide();
}
});

function load_election_and_ballots(election_id) {
// BALLOTS by voter key
ELECTION = null;
ELECTION_ID = election_id;
BALLOTS = {};
BALLOTS_LOADED = {};
VOTER_LIST = [];

// get the election data
Helios.get_election({'election_id' : election_id}, function(election_json) {
var election = HELIOS.Election.fromJSONObject(election_json);
ELECTION = election;

// get the voters
Helios.get_election_voters({'election_id' : election_id}, function(voters) {
VOTER_LIST = voters;
$(voters).each(function(v_num, v) {
// download each voter's data, stuffing into BALLOTS
Helios.get_election_voter({'election_id' : election_id, 'voter_id' : v.voter_id}, function(voter) {
BALLOTS[voter.voter_id] = HELIOS.EncryptedVote.fromJSONObject(voter.vote, ELECTION);
BALLOTS_LOADED[voter.voter_id] = true;
});
});

when_ready_verify_ballots_and_tally();
});
});
}

function when_ready_verify_ballots_and_tally() {
var tally = verify_ballots_and_tally();

if (!tally) {
setTimeout('when_ready_verify_ballots_and_tally()', 2000);
} else {
// store the tally
$.post('/elections/{{election.election_id}}/set_tally', {tally: jQuery.toJSON(tally)}, function(result) {
if (result == "success" || result == "SUCCESS") {
alert('tally and proof done and uploaded');
document.location = "./view"
}
});
}
}

var NUM_VOTES = 0;

function increment_vote_tally_count() {
NUM_VOTES += 1;
$('#num_votes').html(NUM_VOTES);
}

function verify_ballots_and_tally() {
// check that ballots are here
for (var i=0; i<VOTER_LIST.length; i++) {
if (BALLOTS_LOADED[VOTER_LIST[i].voter_id] == null) {
// not all ballots have been downloaded
return false;
}
}

// initialize the tallies
var TALLY = [];
$(ELECTION.questions).each(function(qnum, q) {
TALLY[qnum] = $(q.answers).map(function(anum, a) {
return 1;
});
});

// voter hashes
var voters = [];

var VALID_P = true;

// we need to keep track of the values of g^{voter_num} for decryption
var DISCRETE_LOGS = {};
var CURRENT_EXP = 0;
var CURRENT_RESULT = BigInt.ONE;
DISCRETE_LOGS[CURRENT_RESULT.toString()] = CURRENT_EXP;

// all ballots are here
$(VOTER_LIST).each(function(vnum, voter) {
var ballot = BALLOTS[voter.voter_id];

// non-voter?
if (ballot == null) {
return;
}

// this ballot will count, so let's increment the discrete log pre-computation mapping.
CURRENT_EXP += 1;
CURRENT_RESULT = CURRENT_RESULT.multiply(ELECTION.pk.g).mod(ELECTION.pk.p);
DISCRETE_LOGS[CURRENT_RESULT.toString()] = CURRENT_EXP;

var voter_hash = ballot.get_hash();

// verify the ballot
var this_vote_result = ballot.verifyProofs(ELECTION.pk, function(answer_num, choice_num, result, choice) {
if (choice_num != null) {
// keep track of tally
TALLY[answer_num][choice_num] = choice.multiply(TALLY[answer_num][choice_num]);
}
});

VALID_P = VALID_P && this_vote_result;

increment_vote_tally_count();
});

// are we okay?
if (!VALID_P)
return false;

var result= [];
var result_proof= [];

// decrypt the tallies
$(TALLY).each(function(q_num, q_tally) {
result[q_num] = [];
result_proof[q_num] = [];
$(q_tally).each(function(choice_num, choice_tally) {
var one_choice_result = ELECTION_SK.decryptAndProve(choice_tally, ElGamal.fiatshamir_challenge_generator);
result[q_num][choice_num] = DISCRETE_LOGS[one_choice_result.plaintext.getM().toString()];
result_proof[q_num][choice_num] = one_choice_result.proof;
});
});

return {'result': result, 'result_proof' : result_proof};
}

function do_tally() {
if (ELECTION_SK == null) {
ELECTION_SK = ElGamal.SecretKey.fromJSONObject($.secureEvalJSON($('#sk_textarea').val()));
$('#sk_section').hide();
}

load_election_and_ballots('{{election.election_id}}');
}
</script>
<h2 class="title">{{election.name}} -- Drive Tally</h2>

<p>
This page will tally the election and set the result back with the server.
</p>

<p align="center" style="font-size:16pt;">
<span id="num_votes">0</span> tallied.
</p>

<div id="sk_section">
<form onsubmit="return false;">
Your Secret Key:<br />
<textarea id="sk_textarea" cols="60" rows="5"></textarea>
</form>
</div>

<p id="tally_section">
<button onclick="do_tally();">Tally!</button>
</p>
{% endblock %}
22 changes: 10 additions & 12 deletions helios/templates/election_view.html
Expand Up @@ -11,7 +11,7 @@ <h3>Administration</h3>
{% if election.in_progress_p %}
<b>Election in Progress</b>
{% else %}
{% if election.get_result %}
{% if election.result %}
<b>Election Done</b>
{% else %}
<b>Election Done, Ready for Tally</b>
Expand Down Expand Up @@ -48,7 +48,7 @@ <h3>Administration</h3>
{% endif %}

{% if election.encrypted_tally %}
{% if result %}
{% if election.result %}
{% else %}
<li> <a href="decrypt_and_prove">decrypt and prove</a></li>
{% endif %}
Expand Down Expand Up @@ -81,7 +81,7 @@ <h3>Administration</h3>

<p ><a style="font-size: 18pt;" target="_blank" href="vote">Vote in this election</a> &nbsp;&nbsp;&nbsp;[<a style="font-size: 12pt;" target="_blank" href="../single_ballot_verifier">Audit a Single Ballot</a>] &nbsp; &nbsp; [<a style="font-size: 12pt;" href="bboard">Bulletin Board of Cast Votes</a>]
<br />
{% if result %}
{% if election.result %}
(the tally has already been computed, but you can view the voting interface anyways.)
{% endif %}

Expand All @@ -90,18 +90,16 @@ <h3>Administration</h3>
{% endif %}

<br clear="right" />
{%if result %}
{%if election.result %}
<h2>Tally</h2>
#for $i in range(len($election_obj.questions))
#set $q = $election_obj.questions[$i]
<b>$q.short_name</b>:
{% for question in election.pretty_result %}
<b>{{question.question}}</b>:
<ul>
#for $j in range(len($q.answers))
#set $a = $q.answers[$j]
<li> $a: $result[$i][$j]
#end for
{% for answer in question.answers %}
<li> {{answer.answer}}: {{answer.count}}
{% endfor %}
</ul>
#end for
{% endfor %}

<a style="font-size: 14pt;" target="_blank" href="../verifier">Audit the Election Tally</a><br />
<br /><br />
Expand Down

0 comments on commit 38c73b3

Please sign in to comment.