Full end-to-end voting is working using the new binary encoding scheme, ballot combination and trustee partial decryption with tallies working perfectly. This required updating the Node server as well as Django models and views to support this. Emails to voters and trustees have also been updated to be more informative and look more professional. It could probably do at this point with using email templates and in the future HTML emails.
This commit is contained in:
parent
571cd723bc
commit
5b746ad406
14 changed files with 631 additions and 555 deletions
|
@ -56,7 +56,6 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
{% block app_js_vars %}
|
||||
{% endblock %}
|
||||
|
||||
|
@ -73,8 +72,33 @@
|
|||
|
||||
*/
|
||||
|
||||
dropDownFragsNotZero = function(frags) {
|
||||
var valid = false;
|
||||
|
||||
for(var i = 0; i < frags.length; i++) {
|
||||
var frag = frags[i];
|
||||
|
||||
if(frag !== "0") {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
};
|
||||
|
||||
//new function
|
||||
demosEncrypt.encryptAndSubmit = function() {
|
||||
// Drop down option selection validation
|
||||
if(min_selections === 1 && max_selections === 1) {
|
||||
var fragments = $('#poll-options').val().split(",");
|
||||
|
||||
if(!dropDownFragsNotZero(fragments)) {
|
||||
alert("You have to select an option in order to vote.");
|
||||
return;
|
||||
}
|
||||
} // TODO: Checkbox validation goes here
|
||||
|
||||
// Disable the enc and submit button to prevent fn from being called twice
|
||||
$('#keygen-btn').prop("disabled", true);
|
||||
|
||||
|
@ -147,9 +171,16 @@
|
|||
$('#cipher-form').submit();
|
||||
};
|
||||
|
||||
function getBytes(arr) {
|
||||
for(var i = 0; i < arr.length; i++) {
|
||||
arr[i] = parseInt(arr[i]);
|
||||
}
|
||||
|
||||
return new Uint8Array(arr);
|
||||
}
|
||||
|
||||
//new function
|
||||
demosEncrypt.decryptCipher = function() {
|
||||
demosEncrypt.decryptSubmitCiphers = function() {
|
||||
var skString = $('#secret-key').val();
|
||||
if (!skString) {
|
||||
alert("Please enter your secret key");
|
||||
|
@ -158,28 +189,32 @@
|
|||
//rebuild our secret key
|
||||
var ctx = new CTX("BN254CX");
|
||||
var skBytes = skString.split(",");
|
||||
var sk =new ctx.BIG.fromBytes(skBytes);
|
||||
var sk = new ctx.BIG.fromBytes(skBytes);
|
||||
|
||||
var inputs = $("form input[type=text]");
|
||||
|
||||
inputs.each(function() { //for each ciphertext to decrypt
|
||||
var ciphertext = {
|
||||
C1: new ctx.ECP(),
|
||||
C2: new ctx.ECP()
|
||||
}
|
||||
C1: null,
|
||||
C2: null
|
||||
};
|
||||
|
||||
var temp = JSON.parse($(this).val());
|
||||
ciphertext.C1.copy(temp.C1);
|
||||
ciphertext.C2.copy(temp.C2);
|
||||
|
||||
var partial = partDec(sk, ciphertext);//returns an object containing an ECP()
|
||||
var c1Bytes = getBytes(temp.C1.split(','));
|
||||
ciphertext.C1 = new ctx.ECP.fromBytes(c1Bytes);
|
||||
|
||||
var c2Bytes = getBytes(temp.C2.split(','));
|
||||
ciphertext.C2 = new ctx.ECP.fromBytes(c2Bytes);
|
||||
|
||||
// Perform partial decryption where the method returns an object containing an ECP()
|
||||
var partial = partDec(sk, ciphertext);
|
||||
|
||||
var bytes = [];
|
||||
partial.D.toBytes(bytes);
|
||||
$(this).val(bytes.toString());//submit in byte array form
|
||||
})
|
||||
|
||||
$('input[type=submit]').prop("disabled", false);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//new function
|
||||
demosEncrypt.generateKeys = function() {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{% extends "bases/bootstrap-with-nav.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load bootstrap3 %}
|
||||
{% comment %} is it safe really? {% endcomment %}
|
||||
{% block sk-file-name %}{{ event.title|safe }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -11,21 +10,42 @@
|
|||
<hr/>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Submit your Secret Key as '{{ user_email }}'</strong></div>
|
||||
<div class="panel panel-body">
|
||||
<form id="sk-form" method="POST">
|
||||
{% csrf_token %}
|
||||
<input id="secret-key" name="secret-key" class="textinput textInput form-control" type="text"/>
|
||||
<div class="alert alert-info" role="alert" style="margin-top: 0.75em;">
|
||||
<div class="panel panel-body">
|
||||
<input id="secret-key" name="secret-key" class="textinput textInput form-control" type="text" disabled/>
|
||||
<div class="alert alert-info" role="alert" style="margin-top: 0.75em;">
|
||||
Your secret key will be used to decrypt the event and get a vote tally for every poll.
|
||||
</div>
|
||||
<label for="files_sk_upload" class="btn btn-primary">
|
||||
<span class="glyphicon glyphicon-cloud-upload"></span>
|
||||
It won't be sent to the server.
|
||||
</div>
|
||||
<label for="files_sk_upload" class="btn btn-primary">
|
||||
<span class="glyphicon glyphicon-cloud-upload"></span>
|
||||
Upload Key
|
||||
</label>
|
||||
<input type="file" id="files_sk_upload" name="file" class="btn-info">
|
||||
<input type="submit" value="Submit" class="btn btn-success"/>
|
||||
</form>
|
||||
</div>
|
||||
</label>
|
||||
<input type="file" id="files_sk_upload" name="file" class="btn-info">
|
||||
</div>
|
||||
<br/>
|
||||
<div class="panel-heading"><strong>Ciphers</strong></div>
|
||||
<div class="panel panel-body">
|
||||
<form id="cipher-form" method="POST">
|
||||
{% csrf_token %}
|
||||
{% for opts_ciphers in poll_ciphers %}
|
||||
{% for cipher in opts_ciphers %}
|
||||
<input id="cipher"
|
||||
name="poll-{{ forloop.parentloop.counter0 }}-cipher-{{ forloop.counter0 }}"
|
||||
class="textinput textInput form-control"
|
||||
type="text"
|
||||
value="{ "C1": "{{ cipher.C1 }}", "C2": "{{ cipher.C2 }}" }"
|
||||
/>
|
||||
|
||||
<br/>
|
||||
{% endfor %}
|
||||
<br/>
|
||||
{% endfor %}
|
||||
<button id="decrypt-btn"
|
||||
onclick="demosEncrypt.decryptSubmitCiphers()"
|
||||
class="btn btn-success">
|
||||
Decrypt & Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -15,16 +15,16 @@
|
|||
<!-- Edit Button -->
|
||||
<div class="col-xs-5 col-sm-3 col-md-3 marginTopEditButton">
|
||||
{% if object.has_received_votes and object.ended == False %}
|
||||
<a href="{% url 'polls:end-event' event.id %}" class="btn btn-danger" style="float: right;">
|
||||
<a href="{% url 'polls:end-event' event.uuid %}" class="btn btn-danger" style="float: right;">
|
||||
<span class="glyphicon glyphicon-stop"></span> End
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if decrypted == True and object.ended == True %}
|
||||
<a href="{% url 'polls:event-results' event.id %}" class="btn btn-success" style="float: right;">
|
||||
<a href="{% url 'polls:event-results' event.uuid %}" class="btn btn-success" style="float: right;">
|
||||
<span class="glyphicon glyphicon-stats"></span> Results
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{% url 'polls:edit-event' event.id %}" class="btn btn-primary" style="float: right; margin-right: 0.4em;">
|
||||
<a href="{% url 'polls:edit-event' event.uuid %}" class="btn btn-primary" style="float: right; margin-right: 0.4em;">
|
||||
<span class="fa fa-pencil"></span> Edit
|
||||
</a>
|
||||
</div>
|
||||
|
@ -44,17 +44,17 @@
|
|||
<br/>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="{% block event_nav_details %}{% endblock %}">
|
||||
<a href="{% url 'polls:view-event' event.id %}"><strong>Summary</strong></a>
|
||||
<a href="{% url 'polls:view-event' event.uuid %}"><strong>Summary</strong></a>
|
||||
</li>
|
||||
<li class="{% block event_nav_polls %}{% endblock %}">
|
||||
<a href="{% url 'polls:event-polls' event.id %}"><strong>Polls ({{ object.polls.count }})</strong></a>
|
||||
<a href="{% url 'polls:event-polls' event.uuid %}"><strong>Polls ({{ object.polls.count }})</strong></a>
|
||||
</li>
|
||||
<li class="{% block event_nav_organisers %}{% endblock %}">
|
||||
<a href="{% url 'polls:event-entities' event.id %}"><strong>Entities</strong></a>
|
||||
<a href="{% url 'polls:event-entities' event.uuid %}"><strong>Entities</strong></a>
|
||||
</li>
|
||||
{% if is_organiser %}
|
||||
<li class="{% block event_nav_launch %}{% endblock %}">
|
||||
<a href="{% url 'polls:event-advanced' event.id %}"><strong>Advanced</strong></a>
|
||||
<a href="{% url 'polls:event-advanced' event.uuid %}"><strong>Advanced</strong></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
{% block event_content %}
|
||||
{% if object.polls.all %}
|
||||
{% for poll in object.polls.all %}
|
||||
<h3>Poll: {{ poll.question_text }} (<a href="{% url 'polls:edit-poll' event_id=event.id poll_num=forloop.counter %}">Edit</a>)</h3>
|
||||
<h3>Poll: {{ poll.question_text }} (<a href="{% url 'polls:edit-poll' event_id=event.uuid poll_id=poll.uuid %}">Edit</a>)</h3>
|
||||
<br/>
|
||||
<h4>Poll Options:</h4>
|
||||
<ul class="list-group">
|
||||
|
@ -21,6 +21,6 @@
|
|||
<p>No polls are available for this Event.</p>
|
||||
{% endif %}
|
||||
{% if is_organiser %}
|
||||
<a href="{% url 'polls:create-poll' event.id %}" class="btn btn-default" role="button">Add Poll</a>
|
||||
<a href="{% url 'polls:create-poll' event.uuid %}" class="btn btn-default" role="button">Add Poll</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</div>
|
||||
{% if object_list %}
|
||||
<div class="form-group">
|
||||
<table id="trustees-input-table" class="table table-hover marginTopEventList">
|
||||
<table id="event-list-table" class="table table-hover marginTopEventList">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">Event</th>
|
||||
|
@ -33,14 +33,14 @@
|
|||
<tbody>
|
||||
{% for event in object_list %}
|
||||
<tr>
|
||||
<td class="text-center"><a href="{% url 'polls:view-event' event.id %}">{{ event.title }}</a></td>
|
||||
<td class="text-center"><a href="{% url 'polls:view-event' event.uuid %}">{{ event.title }}</a></td>
|
||||
<td class="text-center">{{ event.duration }}</td>
|
||||
<td class="text-center">{{ event.polls.count }}</td>
|
||||
<td class="text-center">
|
||||
<a href="{% url 'polls:edit-event' event.id %}">
|
||||
<a href="{% url 'polls:edit-event' event.uuid %}">
|
||||
<span class="btn btn-default glyphicon glyphicon-pencil"></span>
|
||||
</a>
|
||||
<a href="{% url 'polls:del-event' event.id %}">
|
||||
<a href="{% url 'polls:del-event' event.uuid %}">
|
||||
<span class="btn btn-default glyphicon glyphicon-trash"></span>
|
||||
</a>
|
||||
</td>
|
||||
|
|
|
@ -3,79 +3,85 @@
|
|||
{% load bootstrap3 %}
|
||||
|
||||
{% block app_js_vars %}
|
||||
var option_count = {{ object.options.count }};
|
||||
var option_count = {{ object.options.count }};
|
||||
var min_selections = {{ min_selection }};
|
||||
var max_selections = {{ max_selection }};
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="container">
|
||||
<input id="event-param" type="text" value="{{event.EID}}" hidden/>
|
||||
<input id="comb_pk" type="text" value="{{event.public_key}}" hidden/>
|
||||
{% if can_vote %}
|
||||
<!-- Hidden fields -->
|
||||
<input id="event-param" type="text" value="{{event.EID}}" hidden/>
|
||||
<input id="comb_pk" type="text" value="{{event.public_key}}" hidden/>
|
||||
|
||||
<!-- TODO: Add warning not to share the URL-->
|
||||
<h2>Event Voting Page for the Event '{{ object.event.title }}'</h2>
|
||||
<div class="alert alert-warning" role="alert" style="margin-top: 1em;">
|
||||
You are voting as: <strong>{{ voter_email }}</strong> - Ensure this is correct and don't share this URL!
|
||||
</div>
|
||||
<span><strong>Voting status:</strong>
|
||||
{% if has_voted %}
|
||||
Voted - Re-Submitting will Change your Vote
|
||||
{% else %}
|
||||
Not Voted
|
||||
{% endif %}
|
||||
</span>
|
||||
<br/>
|
||||
<span><strong>Number of polls for this event:</strong> {{ poll_count }}</span>
|
||||
<br/>
|
||||
<br/>
|
||||
<span><strong>Instructions:</strong>
|
||||
You will be shown each poll for this event one by one where you will need to make a selection for the current
|
||||
poll before moving onto the next poll. <strong>For this specific poll</strong> you need to make a <strong>
|
||||
minimum</strong> of {{ min_selection }} option selection(s) and a <strong>maximum</strong> of
|
||||
{{ max_selection }}. Please make your choice below.
|
||||
</span>
|
||||
<div class="panel panel-body">
|
||||
{% if prev_index %}
|
||||
<a href="{% url 'polls:event-vote' event_id=object.event.id poll_num=prev_index %}" class="btn" role="button">
|
||||
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if next_index %}
|
||||
<a href="{% url 'polls:event-vote' event_id=object.event.id poll_num=next_index %}" class="btn" role="button">
|
||||
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if object.options.all %}
|
||||
<h3>Poll {{ poll_num }} of {{ poll_count }}: {{object.question_text}}</h3>
|
||||
{% if can_vote %}
|
||||
{% load crispy_forms_tags %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Options</strong></div>
|
||||
<div class="panel panel-body">
|
||||
<select class="radio-inline select form-control" id="poll-options" name="options">
|
||||
{% load custom_filters_tags %}
|
||||
<option value="{{ -1|get_ballot_value:object.options.all.count }}">Please Select...</option>
|
||||
{% for option in object.options.all %}
|
||||
<option value="{{forloop.counter|get_ballot_value:object.options.all.count}}">{{ option.choice_text }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<hr/>
|
||||
<button id="keygen-btn" onclick="demosEncrypt.encryptAndSubmit()" class="btn btn-primary">Submit</button>
|
||||
<form id="cipher-form" method="post" action="" class="">
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>You don't have permission to vote in this event.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p>No options are available.</p>
|
||||
{% endif %}
|
||||
<!-- Event info and instructions -->
|
||||
<h2>Event Voting Page for the Event '{{ object.event.title }}'</h2>
|
||||
<hr/>
|
||||
<div class="alert alert-warning" role="alert" style="margin-top: 1em;">
|
||||
You are voting as: <strong>{{ voter_email }}</strong> - Ensure this is correct and don't share this URL!
|
||||
</div>
|
||||
<span><strong>Voting status:</strong>
|
||||
{% if has_voted %}
|
||||
Voted - Re-Submitting will Change your Vote
|
||||
{% else %}
|
||||
Not Voted
|
||||
{% endif %}
|
||||
</span>
|
||||
<br/>
|
||||
<span><strong>Number of polls for this event:</strong> {{ poll_count }}</span>
|
||||
<br/>
|
||||
<br/>
|
||||
<span><strong>Instructions:</strong>
|
||||
You will be shown each poll for this event one by one where you will need to make a selection for the current
|
||||
poll before moving onto the next poll. <strong>For this specific poll</strong> you need to make a <strong>
|
||||
minimum</strong> of {{ min_selection }} option selection(s) and a <strong>maximum</strong> of
|
||||
{{ max_selection }}. <br/><br/>Please make your choice below.
|
||||
</span>
|
||||
|
||||
<!-- Poll Voting Section -->
|
||||
<h3>Poll {{ poll_num }} of {{ poll_count }}: {{object.question_text}}</h3>
|
||||
<hr/>
|
||||
|
||||
{% load crispy_forms_tags %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Options</strong></div>
|
||||
<div class="panel panel-body">
|
||||
<select class="radio-inline select form-control" id="poll-options" name="options">
|
||||
{% load custom_filters_tags %}
|
||||
<option value="{{ -1|get_ballot_value:object.options.all.count }}">Please Select...</option>
|
||||
{% for option in object.options.all %}
|
||||
<option value="{{forloop.counter|get_ballot_value:object.options.all.count}}">{{ option.choice_text }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<hr/>
|
||||
<button id="keygen-btn" onclick="demosEncrypt.encryptAndSubmit()" class="btn btn-primary">Submit</button>
|
||||
<form id="cipher-form" method="post" action="" class="">
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Poll Navigation -->
|
||||
<div class="panel panel-body">
|
||||
{% if prev_uuid %}
|
||||
<a href="{% url 'polls:event-vote' event_id=object.event.uuid poll_id=prev_uuid %}?key={{ a_key }}" class="btn btn-danger"
|
||||
role="button">
|
||||
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span> Previous Poll
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if next_uuid %}
|
||||
<a href="{% url 'polls:event-vote' event_id=object.event.uuid poll_id=next_uuid %}?key={{ a_key }}" class="btn btn-primary"
|
||||
role="button" style="float: right;">
|
||||
Next Poll <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %} <!-- for: { if can_vote %} -->
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<p>{{ cant_vote_reason }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
Reference in a new issue