From 997262855db1f7f749f00ecbca59d722b5d1322e Mon Sep 17 00:00:00 2001 From: Ben Adida Date: Tue, 6 Jan 2009 16:06:07 -0800 Subject: [PATCH] removed election_type, added ballot_type and tally_type and added ways to create such elections and checks to ensure verification is of the right type. --- client/heliosclient.py | 4 ++-- crypto/electionalgs.py | 6 ++++++ helios/json.py | 2 +- helios/models.py | 7 ++++--- helios/templates/ballot_verifier.html | 9 +++++++-- helios/templates/election_view.html | 13 +++++++++++++ helios/templates/tally_verifier.html | 9 +++++++-- helios/templates/vote.html | 7 +++---- helios/views.py | 14 ++++++++++++-- static/helios.js | 28 +++++++++++++++++++++++---- tests/heliosclient.py | 8 ++++---- 11 files changed, 83 insertions(+), 24 deletions(-) diff --git a/client/heliosclient.py b/client/heliosclient.py index e0d3f11..b834e3e 100644 --- a/client/heliosclient.py +++ b/client/heliosclient.py @@ -35,8 +35,8 @@ def params(self): params_json = self.get("/elections/params") return algs.ElGamal.fromJSONDict(utils.from_json(params_json)) - def election_new(self, name, public_key): - election_id = self.post("/elections/new_3", {"name" : name, "public_key" : utils.to_json(public_key.toJSONDict())}) + def election_new(self, name, public_key, ballot_type = 'homomorphic', tally_type = 'homomorphic'): + election_id = self.post("/elections/new_3", {"name" : name, "public_key" : utils.to_json(public_key.toJSONDict()), "ballot_type": ballot_type, "tally_type": tally_type}) return election_id def election_get(self, election_id): diff --git a/crypto/electionalgs.py b/crypto/electionalgs.py index 323d689..865422d 100644 --- a/crypto/electionalgs.py +++ b/crypto/electionalgs.py @@ -220,6 +220,8 @@ def __init__(self): self.voting_starts_at = None self.voting_ends_at = None self.openreg = False + self.ballot_type = False + self.tally_type = False def init_tally(self): return Tally(self) @@ -231,10 +233,12 @@ def get_hash(self): def toJSONDict(self): return_value = { + 'ballot_type' : self.ballot_type, 'election_id' : self.election_id, 'name' : self.name, 'public_key' : self.pk.toJSONDict(), 'questions' : self.questions, + 'tally_type' : self.tally_type, 'voting_starts_at' : self.voting_starts_at, 'voting_ends_at' : self.voting_ends_at } @@ -256,6 +260,8 @@ def fromJSONDict(cls, d): el.voting_starts_at = d['voting_starts_at'] el.voting_ends_at = d['voting_ends_at'] el.questions = d['questions'] + el.ballot_type = d['ballot_type'] + el.tally_type = d['tally_type'] if d['public_key']: el.pk = algs.EGPublicKey.fromJSONDict(d['public_key']) diff --git a/helios/json.py b/helios/json.py index 09a2b15..2a12254 100644 --- a/helios/json.py +++ b/helios/json.py @@ -130,4 +130,4 @@ def toJSONRecurse(item): def toJSON(self): # FIXME: factor in the JSON_FIELDS for the class - return json.dumps(self.toJSONDict()) \ No newline at end of file + return dumps(self.toJSONDict()) \ No newline at end of file diff --git a/helios/models.py b/helios/models.py index af9d0c5..7649bbb 100644 --- a/helios/models.py +++ b/helios/models.py @@ -17,7 +17,7 @@ class Election(models.Model, JSONObject): # when JSON'ified - JSON_FIELDS = ['election_id', 'name', 'public_key', 'questions', 'voters_hash', 'openreg', 'voting_starts_at', 'voting_ends_at'] + JSON_FIELDS = ['ballot_type', 'election_id', 'name', 'public_key', 'questions', 'tally_type', 'voters_hash', 'openreg', 'voting_starts_at', 'voting_ends_at'] election_id = models.AutoField(primary_key=True) @@ -52,8 +52,9 @@ class Election(models.Model, JSONObject): # decryption proof, a JSON object decryption_proof = JSONField(null=True) - # type of election (homomorphic, mixnet, possibly with more detail) - election_type = models.CharField(max_length=300, default='homomorphic') + # type of election + ballot_type = models.CharField(max_length=300, default='homomorphic') + tally_type = models.CharField(max_length=300, default='homomorphic') def toJSONDict(self): # JSON fields, no need! diff --git a/helios/templates/ballot_verifier.html b/helios/templates/ballot_verifier.html index 16fb118..d6fd707 100644 --- a/helios/templates/ballot_verifier.html +++ b/helios/templates/ballot_verifier.html @@ -34,12 +34,17 @@ var encrypted_vote_json = jQuery.secureEvalJSON(audit_trail); // go get the election - Helios.get_election({'election_id' : encrypted_vote_json.election_id}, function(election_json) { + Helios.get_election({'election_id' : encrypted_vote_json.election_id}, function(election_json, election_raw_json) { // display election fingerprint - var election = HELIOS.Election.fromJSONObject(election_json); + var election = HELIOS.Election.fromJSONString(election_raw_json); var election_hash = election.get_hash(); result_append("election fingerprint is " + election_hash); + // check if the election ballot_type is homomorphic + if (election.ballot_type != "homomorphic") { + result_append("WARNING: the ballot type for this election is not homomorphic. Verification may fail because this verifier is only set up to handle homomorphic ballots."); + } + // display ballot fingerprint encrypted_vote = HELIOS.EncryptedVote.fromJSONObject(encrypted_vote_json, election); result_append("ballot fingerprint is " + encrypted_vote.get_hash()); diff --git a/helios/templates/election_view.html b/helios/templates/election_view.html index 7ce3afe..abcf395 100644 --- a/helios/templates/election_view.html +++ b/helios/templates/election_view.html @@ -40,9 +40,17 @@

Administration

{% else %} {% if election.in_progress_p %} {% if election.get_sk %} +{% ifequal election.tally_type "homomorphic" %}
  • compute tally
  • {% else %} +
  • custom tally
  • +{% endifequal %} +{% else %} +{% ifequal election.tally_type "homomorphic" %}
  • compute tally
  • +{% else %} +
  • custom tally
  • +{% endifequal %} {% endif %} {% endif %} {% endif %} @@ -101,7 +109,12 @@

    Tally

    {% endfor %} +{% ifequal election.tally_type "homomorphic" %} Audit the Election Tally
    +{% else %} +This election uses a custom tallying mechanism, which must be audited by the custom code. +{% endifequal %} +

    {% endif %} diff --git a/helios/templates/tally_verifier.html b/helios/templates/tally_verifier.html index b122151..7dd2dd2 100644 --- a/helios/templates/tally_verifier.html +++ b/helios/templates/tally_verifier.html @@ -40,11 +40,16 @@ VOTER_LIST = []; // get the election data - Helios.get_election({'election_id' : election_id}, function(election_json) { - var election = HELIOS.Election.fromJSONObject(election_json); + Helios.get_election({'election_id' : election_id}, function(election_json, election_raw_json) { + var election = HELIOS.Election.fromJSONString(election_raw_json); ELECTION = election; result_append("Election: " + election.name); + if (election.tally_type != "homomorphic") { + result_append("PROBLEM: this election is not a straight-forward homomorphic-tally election. As a result, Helios cannot currently verify it."); + return; + } + // hash it var election_hash = election.get_hash(); result_append("Fingerprint: " + election_hash); diff --git a/helios/templates/vote.html b/helios/templates/vote.html index 98554b6..a23a4e9 100644 --- a/helios/templates/vote.html +++ b/helios/templates/vote.html @@ -51,10 +51,9 @@

    Fingerprint:

    }; BOOTH.setup_election = function(election, raw_json) { - BOOTH.election = HELIOS.Election.fromJSONObject(election); - - // compute the hash from the raw JSON - BOOTH.election_hash = b64_sha1(raw_json); + // use the raw JSON for safer hash computation + BOOTH.election = HELIOS.Election.fromJSONString(raw_json); + BOOTH.election_hash = BOOTH.election.get_hash(); $('#election_hash').html(BOOTH.election_hash); diff --git a/helios/views.py b/helios/views.py index 9256d4b..dceee71 100644 --- a/helios/views.py +++ b/helios/views.py @@ -105,6 +105,11 @@ def election_new_3(request): voting_starts_at = request.POST.get('voting_starts_at', None) voting_ends_at = request.POST.get('voting_ends_at', None) + # election type is homomorphic. The type of the election determines + # how votes are tallied and verified. + ballot_type = request.POST.get('ballot_type', 'homomorphic') + tally_type = request.POST.get('tally_type', 'homomorphic') + # we need a list of admins, or at least a public key if len(trustee) == 0 and not public_key: return HttpResponseServerError('Need a list of trustees or a public key') @@ -120,7 +125,7 @@ def election_new_3(request): else: sk = None - election = Election.objects.create(election_type = 'homomorphic', name = name, + election = Election.objects.create(ballot_type = ballot_type, tally_type = tally_type, name = name, admin = get_user(request), api_client= get_api_client(request), # voting_starts_at = utils.string_to_datetime(voting_starts_at), # voting_ends_at = utils.string_to_datetime(voting_ends_at), @@ -509,6 +514,9 @@ def one_election_email_trustees(request, election): @election_admin(frozen=True) def one_election_compute_tally(request, election): + if election.tally_type != "homomorphic": + return HttpResponseRedirect(reverse(one_election_view,args=[election.election_id])) + return HttpResponse("election compute tally %s" % election.election_id) @election_admin(frozen=True) @@ -516,6 +524,9 @@ def one_election_drive_tally(request, election): """ JavaScript-based driver for the entire tallying process, now done in JavaScript. """ + if election.tally_type != "homomorphic": + return HttpResponseRedirect(reverse(one_election_view,args=[election.election_id])) + election_pk = election.public_key election_pk_json = utils.to_json(election_pk.toJSONDict()) @@ -586,7 +597,6 @@ def one_voter_delete(request, election, voter_id): @election_view(frozen=True) def one_voter_submit(request, election, voter_id): - import pdb; pdb.set_trace() election_obj = election.toElection() # this will raise an exception if the voter is bad diff --git a/static/helios.js b/static/helios.js index 81c6bcd..e51883e 100644 --- a/static/helios.js +++ b/static/helios.js @@ -72,18 +72,24 @@ HELIOS.Election = Class.extend({ // in terms of ordering the keys. FIXME: get a JSON library that orders keys properly. if (this.openreg) { return { - election_id : this.election_id, name : this.name, openreg: true, public_key: this.pk.toJSONObject(), questions : this.questions, - voting_ends_at : this.voting_ends_at, voting_starts_at : this.voting_starts_at + ballot_type: this.ballot_type, election_id : this.election_id, + name : this.name, openreg: true, public_key: this.pk.toJSONObject(), questions : this.questions, + tally_type: this.tally_type, voting_ends_at : this.voting_ends_at, voting_starts_at : this.voting_starts_at }; } else { return { - election_id : this.election_id, name : this.name, public_key: this.pk.toJSONObject(), questions : this.questions, - voters_hash : this.voters_hash, voting_ends_at : this.voting_ends_at, voting_starts_at : this.voting_starts_at + ballot_type: this.ballot_type, election_id : this.election_id, + name : this.name, public_key: this.pk.toJSONObject(), questions : this.questions, + tally_type: this.tally_type, voters_hash : this.voters_hash, voting_ends_at : this.voting_ends_at, voting_starts_at : this.voting_starts_at }; } }, get_hash: function() { + if (this.election_hash) + return this.election_hash; + + // otherwise return b64_sha1(this.toJSON()); }, @@ -93,12 +99,26 @@ HELIOS.Election = Class.extend({ } }); +HELIOS.Election.fromJSONString = function(raw_json) { + var json_object = $.secureEvalJSON(raw_json); + + // hash fix for the issue with re-json'ifying unicode chars + var election = HELIOS.Election.fromJSONObject(json_object); + election.election_hash = b64_sha1(raw_json); + + return election; +}; + HELIOS.Election.fromJSONObject = function(d) { var el = new HELIOS.Election(); el.election_id = d.election_id; el.name = d.name; el.voters_hash = d.voters_hash; el.voting_starts_at = d.voting_starts_at; el.voting_ends_at = d.voting_ends_at; el.questions = d.questions; + // stuff about the election + el.ballot_type = d.ballot_type; + el.tally_type = d.tally_type; + // empty questions if (!el.questions) el.questions = []; diff --git a/tests/heliosclient.py b/tests/heliosclient.py index b1f9755..ae5092a 100644 --- a/tests/heliosclient.py +++ b/tests/heliosclient.py @@ -15,10 +15,10 @@ # instantiate the client # modify variables here helios = heliosclient.HeliosClient({'consumer_key': 'test', 'consumer_secret': '123'}, - host = '174.129.241.146', -# host = "localhost", - port = 80, - prefix = "/helios" +# host = '174.129.241.146', + host = "localhost", + port = 8000, +# prefix = "/helios" ) print "headers:\n"