Support null-encrypted election in web UI

This commit is contained in:
RunasSudo 2021-10-16 20:13:28 +11:00
parent adbdb21e0d
commit d44c21cbd7
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
14 changed files with 5647 additions and 2810 deletions

View File

@ -1,5 +1,5 @@
# Eos - Verifiable elections
# Copyright © 2017-2019 RunasSudo (Yingtong Li)
# Copyright © 2017-2021 RunasSudo (Yingtong Li)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -30,6 +30,9 @@ class NullEncryptedAnswer(EncryptedAnswer):
def decrypt(self):
return None, self.answer
def deaudit(self):
return self
class Ballot(EmbeddedObject):
#_id = UUIDField()
encrypted_answers = EmbeddedObjectListField()
@ -219,6 +222,10 @@ class Election(TopLevelObject):
questions = EmbeddedObjectListField()
results = EmbeddedObjectListField(is_hashed=False)
def can_audit(self):
"""Can prepared votes be audited?"""
return False
def verify(self):
#__pragma__('skip')
from eos.core.hashing import SHA256

View File

@ -1,5 +1,5 @@
# Eos - Verifiable elections
# Copyright © 2017-18 RunasSudo (Yingtong Li)
# Copyright © 2017-2021 RunasSudo (Yingtong Li)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -38,7 +38,7 @@ class ElectionTestCase(EosTestCase):
def test_run_election(self):
# Set up election
election = Election()
election.workflow = WorkflowBase()
election.workflow = BaseWorkflow()
# Check _instance
self.assertEqual(election.workflow._instance, (election, 'workflow'))

View File

@ -1,5 +1,5 @@
# Eos - Verifiable elections
# Copyright © 2017-18 RunasSudo (Yingtong Li)
# Copyright © 2017-2021 RunasSudo (Yingtong Li)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -58,7 +58,12 @@ class WorkflowTask(EmbeddedObject):
def are_dependencies_met(self):
for depends_on_desc in self.depends_on:
for depends_on_task in self.workflow.get_tasks(depends_on_desc):
depends_on_tasks = list(self.workflow.get_tasks(depends_on_desc))
if len(depends_on_tasks) == 0:
return False
for depends_on_task in depends_on_tasks:
if depends_on_task.status is not WorkflowTaskStatus.EXITED:
return False
return True
@ -184,7 +189,9 @@ class TaskReleaseResults(WorkflowTask):
# Concrete workflows
# ==================
class WorkflowBase(Workflow):
class BaseWorkflow(Workflow):
"""Base workflow, with no encryption"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

View File

@ -1,5 +1,5 @@
# Eos - Verifiable elections
# Copyright © 2017 RunasSudo (Yingtong Li)
# Copyright © 2017-2021 RunasSudo (Yingtong Li)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -230,7 +230,12 @@ class PSRElection(Election):
public_key = EmbeddedObjectField(SEGPublicKey)
mixing_trustees = EmbeddedObjectListField()
def can_audit(self):
"""Overrides Election.can_audit"""
return True
def verify(self):
"""Overrides Election.verify"""
# Verify ballots
super().verify()

View File

@ -1,6 +1,6 @@
/*
Eos - Verifiable elections
Copyright © 2017-2019 RunasSudo (Yingtong Li)
Copyright © 2017-2021 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@ -22,6 +22,7 @@ isLibrariesLoaded = false;
eosjs = null;
function generateEncryptedVote(election, answers, should_do_fingerprint) {
if (election._name === 'eos.psr.election.PSRElection') {
encrypted_answers = [];
for (var q_num = 0; q_num < answers.length; q_num++) {
answer_json = answers[q_num];
@ -33,6 +34,22 @@ function generateEncryptedVote(election, answers, should_do_fingerprint) {
postMessage({
encrypted_answers: encrypted_answers
});
} else if (election._name === 'eos.base.election.Election') {
encrypted_answers = [];
for (var q_num = 0; q_num < answers.length; q_num++) {
answer_json = answers[q_num];
answer = eosjs.eos.core.objects.EosObject.deserialise_and_unwrap(answer_json, null);
encrypted_answer = eosjs.eos.base.election.NullEncryptedAnswer();
encrypted_answer.answer = answer;
encrypted_answers.push(eosjs.eos.core.objects.EosObject.serialise_and_wrap(encrypted_answer, null));
}
postMessage({
encrypted_answers: encrypted_answers
});
} else {
throw "Don't know how to encrypt ballots in election of type " + election._name;
}
}
onmessage = function(msg) {

View File

@ -1,6 +1,6 @@
{#
Eos - Verifiable elections
Copyright © 2017-2019 RunasSudo (Yingtong Li)
Copyright © 2017-2021 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@ -32,9 +32,17 @@
{% elif template == 'booth/audit.html' %}
{% set menuindex = 4 %}
{% elif template == 'booth/cast.html' %}
{% if election.can_audit() %}
{% set menuindex = 5 %}
{% else %}
{% set menuindex = 4 %}
{% endif %}
{% elif template == 'booth/complete.html' %}
{% if election.can_audit() %}
{% set menuindex = 6 %}
{% else %}
{% set menuindex = 5 %}
{% endif %}
{% endif %}
{% macro menuitem(index, text) %}
@ -50,9 +58,14 @@
{{ menuitem(1, "Welcome") }}
{{ menuitem(2, "Select") }}
{{ menuitem(3, "Review") }}
{% if election.can_audit() %}
{{ menuitem(4, "Audit") }}
{{ menuitem(5, "Cast") }}
{{ menuitem(6, "Finish") }}
{% else %}
{{ menuitem(4, "Cast") }}
{{ menuitem(5, "Finish") }}
{% endif %}
</ul>
<div class="ui container">

View File

@ -2,7 +2,7 @@
{#
Eos - Verifiable elections
Copyright © 2017-2019 RunasSudo (Yingtong Li)
Copyright © 2017-2021 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@ -20,7 +20,7 @@
{% block content %}
<div id="cast_prompt">
<p>Your vote has <span class="superem">not</span> yet been cast. Please make a note of your ballot fingerprint, <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(ballot).hash_as_b64(true) }}</span>.</p>
<p>Your vote has <span class="superem">not</span> yet been cast.{% if election.can_audit() %} Please make a note of your ballot fingerprint, <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(ballot).hash_as_b64(true) }}</span>.{% endif %}</p>
<div class="ui negative message">
<p>Your vote has <span class="superem">not</span> yet been cast. Please follow the instructions to continue.</p>
@ -69,10 +69,12 @@
{% endblock %}
{% block after %}
{% if election.can_audit() %}
<div class="ui tiny message" style="margin-top: 3em;">
<div class="header">Information for advanced users</div>
<p>Your full ballot fingerprint is <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(ballot).hash_as_b64() }}</span>.</p>
</div>
{% endif %}
<script>
$(".message .close").on("click", function() {

View File

@ -2,7 +2,7 @@
{#
Eos - Verifiable elections
Copyright © 2017-2019 RunasSudo (Yingtong Li)
Copyright © 2017-2021 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@ -23,7 +23,9 @@
<p>Your vote has <span class="superem">not</span> yet been cast. Please follow the instructions to continue.</p>
</div>
{% if election.can_audit() %}
<p>Please make a note of your ballot fingerprint, <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(ballot).hash_as_b64(true) }}</span>. Please retain a copy of your ballot fingerprint – you can use it to verify that your vote has been counted correctly. You may <a href="#" onclick="window.print();return false;">print this page</a> as a receipt if you wish.</p>
{% endif %}
<p>To continue, copy and paste the ballot below and provide it to the election administrator.</p>
@ -39,10 +41,12 @@
{% endblock %}
{% block after %}
{% if election.can_audit() %}
<div class="ui tiny message" style="margin-top: 3em;">
<div class="header">Information for advanced users</div>
<p>Your full ballot fingerprint is <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(ballot).hash_as_b64() }}</span>.</p>
</div>
{% endif %}
{% endblock %}
{% block help %}

View File

@ -2,7 +2,7 @@
{#
Eos - Verifiable elections
Copyright © 2017-2019 RunasSudo (Yingtong Li)
Copyright © 2017-2021 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@ -26,13 +26,16 @@
<div class="content">
<div class="header">Smart ballot tracker</div>
<p>This smart ballot tracker confirms that {{ voter.py_name }} cast a vote in the election {{ election.py_name }} at {{ vote.cast_at }}.</p>
{% if election.can_audit() %}
<p>Ballot fingerprint: <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(vote.ballot).hash_as_b64(true) }}</span></p>
{% endif %}
</div>
</div>
{% if election.can_audit() %}
<p>Please check that the ballot fingerprint above matches the ballot fingerprint you recorded earlier.</p>
<p>To confirm that your ballot was cast correctly, please go to the <a href="{{ election_base_url }}view/ballots">‘Voters and ballots’ page</a> for the {{ election.kind }} or click ‘Finish’, and confirm that the above ballot fingerprint appears next to your name.</p>
{% endif %}
{% endblock %}
{% block buttons %}
@ -40,10 +43,12 @@
{% endblock %}
{% block after %}
{% if election.can_audit() %}
<div class="ui tiny message" style="margin-top: 3em;">
<div class="header">Information for advanced users</div>
<p>Your full ballot fingerprint is <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(vote.ballot).hash_as_b64() }}</span>.</p>
</div>
{% endif %}
{% endblock %}
{% block help %}

View File

@ -2,7 +2,7 @@
{#
Eos - Verifiable elections
Copyright © 2017-2019 RunasSudo (Yingtong Li)
Copyright © 2017-2021 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@ -30,8 +30,12 @@
{% include templates[selection_model_view_map[election.questions.__getitem__(loop.index0)._name]["selections_review"]] %}
{% endfor %}
{% if election.can_audit() %}
<p>If you are happy with your selections, then make a note of your ballot fingerprint, <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(ballot).hash_as_b64(true) }}</span>.</p>
<p>Click ‘Continue’, and you will be able to log in to cast your vote.</p>
{% else %}
<p>If you are happy with your selections, then click ‘Continue’, and you will be able to log in to cast your vote.</p>
{% endif %}
{% endblock %}
{% block buttons %}
@ -40,11 +44,13 @@
{% endblock %}
{% block after %}
{% if election.can_audit() %}
<div class="ui tiny message" style="margin-top: 3em;">
<div class="header">Information for advanced users</div>
<p>Your ballot fingerprint is <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(ballot).hash_as_b64() }}</span>.</p>
<p>If you would like to audit your ballot, <a href="#" onclick="nextTemplate(1);">click here</a></p>
</div>
{% endif %}
{% endblock %}
{% block help %}

View File

@ -2,7 +2,7 @@
{#
Eos - Verifiable elections
Copyright © 2017-2019 RunasSudo (Yingtong Li)
Copyright © 2017-2021 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@ -30,8 +30,12 @@
{% include templates[selection_model_view_map[election.questions.__getitem__(loop.index0)._name]["selections_review"]] %}
{% endfor %}
{% if election.can_audit() %}
<p>If you are happy with your selections, then make a note of your ballot fingerprint, <span class="hash">{{ eosjs.eos.core.hashing.SHA256().update_obj(ballot).hash_as_b64(true) }}</span>.</p>
<p>Click ‘Continue’, and you will be able to copy your pre-poll ballot to provide to the election administrator.</p>
{% else %}
<p>If you are happy with your selections, then click ‘Continue’, and you will be able to copy your pre-poll ballot to provide to the election administrator.</p>
{% endif %}
{% endblock %}
{% block buttons %}

View File

@ -212,12 +212,14 @@
}
});
templates['booth/welcome.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/selections.html');
}
});
templates['booth/selections.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
if (fromLeft) {
@ -237,12 +239,14 @@
}
});
templates['booth/review_prepoll.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/audit.html', {ballot: booth.ballot});
}
});
templates['booth/audit.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/cast_prepoll.html', {ballot: booth.ballot});
@ -257,18 +261,21 @@
}
});
templates['booth/review.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/audit.html', {ballot: booth.ballot});
}
});
templates['booth/audit.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/cast.html', {ballot: booth.ballot, is_cast: false});
}
});
templates['booth/cast.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/complete.html', {voter: booth.voter, vote: booth.vote});
@ -287,6 +294,7 @@
}
});
templates['booth/cast.html'] = null;
boothTasks.append({
activate: function(fromLeft) {
showTemplate('booth/complete.html', {voter: booth.voter, vote: booth.vote});

8236
package-lock.json generated

File diff suppressed because it is too large Load Diff

9
package.json Normal file
View File

@ -0,0 +1,9 @@
{
"dependencies": {
"@babel/cli": "^7.15.7",
"@babel/core": "^7.15.8",
"@babel/preset-env": "^7.15.8",
"babelify": "^10.0.0",
"browserify": "^17.0.0"
}
}