diff --git a/allauthdemo/polls/models.py b/allauthdemo/polls/models.py index 581e88e..6fbabfe 100755 --- a/allauthdemo/polls/models.py +++ b/allauthdemo/polls/models.py @@ -208,6 +208,11 @@ class Ballot(models.Model): cast = models.BooleanField(default=False) +class EncBallot(models.Model): + handle = models.CharField(primary_key=True, default=uuid.uuid4, editable=False, max_length=255) + ballot = models.CharField(max_length=4096) + + # Implements the new binary encoding scheme class EncryptedVote(models.Model): ballot = models.ForeignKey(Ballot, on_delete=models.CASCADE, related_name="encrypted_vote") diff --git a/allauthdemo/polls/urls.py b/allauthdemo/polls/urls.py index 23deeff..2e9c840 100755 --- a/allauthdemo/polls/urls.py +++ b/allauthdemo/polls/urls.py @@ -20,5 +20,6 @@ urlpatterns = [ url(r'^(?P[0-9a-f-]+)/prepare/$', views.event_trustee_setup, name='prepare-event'), url(r'^(?P[0-9a-f-]+)/poll/(?P[0-9a-f-]+)/vote/$', views.event_vote, name='event-vote'), url(r'^(?P[0-9a-f-]+)/create/poll/$', login_required(views.manage_questions), name='create-poll'), - url(r'^(?P[0-9a-f-]+)/poll/(?P[0-9a-f-]+)/edit$', login_required(views.edit_poll), name='edit-poll') + url(r'^(?P[0-9a-f-]+)/poll/(?P[0-9a-f-]+)/edit$', login_required(views.edit_poll), name='edit-poll'), + url(r'^audit/$', views.vote_audit, name='vote_audit') ] diff --git a/allauthdemo/polls/views.py b/allauthdemo/polls/views.py index a09e6c2..9694e6d 100755 --- a/allauthdemo/polls/views.py +++ b/allauthdemo/polls/views.py @@ -1,6 +1,8 @@ import urllib import urllib2 import json +import logging +import base64 from django.contrib import messages from django.http import HttpResponseRedirect, HttpResponse, Http404 @@ -11,7 +13,7 @@ from django.views import generic from django.conf import settings from .forms import PollForm, OptionFormset, VoteForm, EventSetupForm, EventEditForm -from .models import Event, Poll, Ballot, EncryptedVote, TrusteeKey, PartialBallotDecryption, CombinedBallot, VoteFragment +from .models import Event, Poll, Ballot, EncBallot, EncryptedVote, TrusteeKey, PartialBallotDecryption, CombinedBallot, VoteFragment from allauthdemo.auth.models import DemoUser from .tasks import email_trustees_prep, update_EID, generate_combpk, event_ended, create_ballots @@ -110,6 +112,15 @@ def edit_poll(request, event_id, poll_id): return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id])) +def vote_audit(request): + encryptedBallot = get_object_or_404(EncBallot, handle=''+urllib.quote_plus(request.GET.get('handle', None))) + + return render(request, "polls/vote_audit.html", + { + "ballot": encryptedBallot.ballot + }) + + def event_vote(request, event_id, poll_id): event = get_object_or_404(Event, pk=event_id) @@ -172,10 +183,21 @@ def event_vote(request, event_id, poll_id): cant_vote_reason = "The event either isn't ready for voting or it has expired and therefore you cannot vote." if request.method == "POST": - data = json.loads(request.POST.lists()[0][0]) - ballot_json = data['ballot'] + ballot_json = json.loads(request.POST.get('ballot')) encrypted_votes_json = ballot_json['encryptedVotes'] + enc_ballot_json = request.POST.get('encBallot') + handle_json = request.POST.get('handle') + + # Adds or replaces the encrypted un-submitted ballot to the database for the auditor app to pick up later + if EncBallot.objects.filter(handle=handle_json).exists(): + b = EncBallot.objects.get(handle=handle_json) + b.ballot = ballot_json + b.save() + else: + b = EncBallot(handle=handle_json, ballot=enc_ballot_json) + b.save() + # Before storing the encrypted votes, we need the voter's ballot ballot, created = Ballot.objects.get_or_create(voter=email_key[0].user, poll=poll) EncryptedVote.objects.filter(ballot=ballot).delete() diff --git a/allauthdemo/templates/bases/bootstrap-jquery.html b/allauthdemo/templates/bases/bootstrap-jquery.html index f75137b..8e87f58 100755 --- a/allauthdemo/templates/bases/bootstrap-jquery.html +++ b/allauthdemo/templates/bases/bootstrap-jquery.html @@ -11,16 +11,16 @@ + - + + + diff --git a/allauthdemo/templates/polls/event_vote.html b/allauthdemo/templates/polls/event_vote.html index d186ad3..b980b5e 100755 --- a/allauthdemo/templates/polls/event_vote.html +++ b/allauthdemo/templates/polls/event_vote.html @@ -53,6 +53,7 @@ +

Poll {{ poll_num }} of {{ poll_count }}: {{object.question_text}}


@@ -124,7 +125,7 @@ diff --git a/allauthdemo/templates/polls/vote_audit.html b/allauthdemo/templates/polls/vote_audit.html new file mode 100644 index 0000000..c587f84 --- /dev/null +++ b/allauthdemo/templates/polls/vote_audit.html @@ -0,0 +1,18 @@ +{% extends "bases/bootstrap-with-nav.html" %} +{% load staticfiles %} +{% load bootstrap3 %} + +{% block content %} + + + +
+
+ +
{{ ballot }}
+ +

+
+

+
+{% endblock %}
\ No newline at end of file
diff --git a/static/js/demos2-booth.js b/static/js/demos2-booth.js
index 015493c..afdb364 100755
--- a/static/js/demos2-booth.js
+++ b/static/js/demos2-booth.js
@@ -118,7 +118,8 @@ encrypt=function(params,PK, m){
 		
 		return{
 		    C1:C1,
-		    C2:C2
+		    C2:C2,
+		    r:r
 		}
 }
 
diff --git a/static/js/event_vote.js b/static/js/event_vote.js
index d97a1e7..a68ad21 100644
--- a/static/js/event_vote.js
+++ b/static/js/event_vote.js
@@ -80,23 +80,22 @@ function isVotingInputValid() {
     return valid;
 }
 
-// Generates a blank vote as a string using the binary encoding scheme
-function genBlankVote() {
-    var vote = "";
-
-    for(var i = 0; i < OPTION_COUNT; i++) {
-        vote += "0";
-
-        if (i !== (OPTION_COUNT - 1)) {
-            vote += ",";
-        }
-    }
-
-    return vote;
-}
-
 var progressBar = document.getElementById("progress-bar");
 
+$('#gen-ballots-btn').click(function() {
+    // Ensure that the user selections are valid
+    if(isVotingInputValid()) {
+        // Hide the button
+        $(this).toggleClass('hidden');
+
+        // Inject the description progress bar which can then be updated by the encrypt btn
+        $('#progress-bar-description').toggleClass('hidden');
+        $('#progress-bar-container').toggleClass('hidden');
+
+        setTimeout(generateBallots, 25);
+    }
+});
+
 // Based on the user's vote in the current poll, this generates a ballot which
 // does not leak information about how many options the user has selected
 function generateBallot() {
@@ -156,16 +155,20 @@ function generateBallot() {
         unencryptedVote.split(',').forEach(function(fragment) {
             var cipher = encrypt(params, pk, parseInt(fragment));
 
-            // Store C1 and C2 from the cipher in the fragment
+            // Store C1, C2 and r from the cipher in the fragment
             var c1Bytes = [];
             cipher.C1.toBytes(c1Bytes);
 
             var c2Bytes = [];
             cipher.C2.toBytes(c2Bytes);
 
+            var rBytes = [];
+            cipher.r.toBytes(rBytes);
+
             encFragments.push({
                 C1 : c1Bytes.toString(),
-                C2 : c2Bytes.toString()
+                C2 : c2Bytes.toString(),
+                r : rBytes.toString()
             });
         });
 
@@ -180,29 +183,19 @@ function generateBallot() {
     };
 }
 
-$('#gen-ballots-btn').click(function() {
-    // Ensure that the user selections are valid
-    if(isVotingInputValid()) {
-        // Hide the button
-        $(this).toggleClass('hidden');
+// Generates a blank vote as a string using the binary encoding scheme
+function genBlankVote() {
+    var vote = "";
 
-        // Inject the description progress bar which can then be updated by the encrypt btn
-        $('#progress-bar-description').toggleClass('hidden');
-        $('#progress-bar-container').toggleClass('hidden');
+    for(var i = 0; i < OPTION_COUNT; i++) {
+        vote += "0";
 
-        setTimeout(generateBallotsAndShowUsr, 25);
-    }
-});
-
-function voteSuccessfullyReceived() {
-    let titleTxt = 'Vote Successfully Received';
-    let bodyText = "Thank you for voting!";
-
-    if(POLL_NUM !== POLL_COUNT) {
-        bodyText += " You can vote on the next poll by closing down this dialog and clicking 'Next Poll'.";
+        if (i !== (OPTION_COUNT - 1)) {
+            vote += ",";
+        }
     }
 
-    showDialogWithText(titleTxt, bodyText);
+    return vote;
 }
 
 var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val();
@@ -211,25 +204,6 @@ function csrfSafeMethod(method) {
     return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
 }
 
-function sendBallotToServer(selection, altHash) {
-    $.ajaxSetup({
-        beforeSend: function(xhr, settings) {
-            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
-                xhr.setRequestHeader("X-CSRFToken", CSRF);
-            }
-        }
-    });
-
-    $.ajax({
-         type : "POST",
-         url : window.location,
-         data : JSON.stringify({ ballot: selection}),
-         success : function(){
-             onAfterBallotSend(altHash);
-         }
-    });
-}
-
 var bytestostring = function(b) {
     var s = "";
     var len = b.length;
@@ -272,63 +246,24 @@ function SHA256Hash(bytes, toStr) {
     }
 }
 
-// Called once the ballot has been sent to the back-end and dialog has closed
-function onAfterBallotSend(altHash) {
-    let titleText = 'Vote Successfully Received';
-    let bodyText = "Thank you for voting! This is your copy of your ballot - make sure to save it onto your phone before closing this window.";
+function generateBallots() {
+    // Generate Ballot A and Ballot B to be displayed to the user
+    // This fn starts the process
+    var ballotA = generateBallot();
 
-    if(POLL_NUM !== POLL_COUNT) {
-        bodyText += " You can vote on the next poll by closing down this dialog and clicking 'Next Poll'.";
-    }
+    // Update the progress bar once the generation has completed
+    progressBar.setAttribute("style", "width: 50%;");
 
-    // With one ballot selected, we can display a QR code of the voter's copy
-    var modalDialog = $('#modalDialog');
-    var title = modalDialog.find('.modal-title');
-    var body = modalDialog.find('.modal-body');
-    title.text(titleText);
-    body.empty();
+    // This delay allows the execution thread to update the above CSS on the progress bar
+    setTimeout(function () {
+        var ballotB = generateBallot();
+        progressBar.setAttribute("style", "width: 100%;");
 
-    var p = document.createElement("p");
-    p.innerHTML = bodyText;
-    body.append(p);
-
-    // Generate the body of the dialog which displays the unselected ballot QR code and hash
-    var choiceGroupDiv = document.createElement('div');
-    choiceGroupDiv.setAttribute('class', 'choice-group');
-
-    var QRCodeImg = document.createElement('img');
-    QRCodeImg.setAttribute('class', 'QR-code');
-    new QRCode(QRCodeImg, altHash);
-
-    choiceGroupDiv.append(QRCodeImg);
-
-    // ----------------------------------------------
-
-    var hashGroupDiv = document.createElement('div');
-    var br = document.createElement('br');
-    hashGroupDiv.append( br );
-
-    var hash = document.createElement("span");
-    hash.innerHTML = "Hash: " + altHash;
-    hashGroupDiv.append( hash );
-
-    // -----------------------------------------------
-
-    body.append(choiceGroupDiv);
-    body.append(hashGroupDiv);
-
-    modalDialog.modal('show');
+        showFirstQRCode(ballotA, ballotB);
+    }, 150);
 }
 
-function processBallotSelection(selection, selectionHash, alt, altHash) {
-    // Dispatch the ballot to the server
-    sendBallotToServer(selection, altHash);
-
-    // Call the successfn currently with the selection hash but this may not be needed
-    //successFn(alt, altHash);
-}
-
-function showBallotChoiceDialog(ballotA, ballotB) {
+function showFirstQRCode(ballotA, ballotB) {
     var ballots = new Array(ballotA, ballotB);
     var ballotHashes = new Array(2);
 
@@ -336,28 +271,18 @@ function showBallotChoiceDialog(ballotA, ballotB) {
     for (let i = 0; i <= 1; i++)
         ballotHashes[i] = SHA256Hash(stringtobytes(JSON.stringify(ballots[i])), true);
 
-    // With the ballots and their hashes generated, we can display the ballot choice dialog
+    // With the ballots and their hashes generated, we can display the QR code of both hashes
     var modalDialog = $('#modalDialog');
     var title = modalDialog.find('.modal-title');
     var body = modalDialog.find('.modal-body');
+    var footer = modalDialog.find('.modal-footer');
+
     body.empty();
-    title.text('Please Select a Ballot');
+    title.text('Please Scan this QR Code');
 
-    // Generate the body of the dialog which consists of a button for A and for B as well as their hashes
-    var choiceGroupDiv = document.createElement('div');
-    choiceGroupDiv.setAttribute('class', 'choice-group');
-
-    var btnChoiceA = document.createElement('a');
-    btnChoiceA.setAttribute('id', 'choice-A');
-    btnChoiceA.setAttribute('class', 'btn btn-sq btn-primary');
-    btnChoiceA.innerHTML = 'A';
-    choiceGroupDiv.append(btnChoiceA);
-
-    var btnChoiceB = document.createElement('a');
-    btnChoiceB.setAttribute('id', 'choice-B');
-    btnChoiceB.setAttribute('class', 'btn btn-sq btn-warning choice');
-    btnChoiceB.innerHTML = 'B';
-    choiceGroupDiv.append(btnChoiceB);
+    var QRCodeImg = document.createElement('img');
+    QRCodeImg.setAttribute('class', 'QR-code');
+    new QRCode(QRCodeImg, ballotHashes[0] + ';' + ballotHashes[1]);
 
     // ----------------------------------------------
 
@@ -378,38 +303,153 @@ function showBallotChoiceDialog(ballotA, ballotB) {
 
     // -----------------------------------------------
 
-    body.append(choiceGroupDiv);
+    body.append(QRCodeImg);
     body.append(hashGroupDiv);
 
+    var closeButton = $('close-button');
+    closeButton.removeClass('btn-success');
+    closeButton.addClass('btn-danger');
+    closeButton.text("Close without submitting vote");
+
+    var nextButton = document.createElement('button');
+    nextButton.setAttribute('type', 'button');
+    nextButton.setAttribute('id', 'next-button');
+    nextButton.setAttribute('class', 'btn btn-default');
+    nextButton.innerHTML = "Next";
+
+    footer.prepend(nextButton);
+
+
+    modalDialog.modal('show');
+
+    $('#next-button').click(function(e) {
+        showBallotChoiceDialog(ballots);
+    });
+}
+
+function showBallotChoiceDialog(ballots) {
+    // Display the ballot choice dialog
+    var modalDialog = $('#modalDialog');
+    var title = modalDialog.find('.modal-title');
+    var body = modalDialog.find('.modal-body');
+
+    body.empty();
+    title.text('Please Select a Ballot');
+
+    // Generate the body of the dialog which consists of a button for A and for B
+    var choiceGroupDiv = document.createElement('div');
+    choiceGroupDiv.setAttribute('class', 'choice-group');
+
+    var btnChoiceA = document.createElement('a');
+    btnChoiceA.setAttribute('id', 'choice-A');
+    btnChoiceA.setAttribute('class', 'btn btn-sq btn-primary');
+    btnChoiceA.innerHTML = 'A';
+    choiceGroupDiv.append(btnChoiceA);
+
+    var btnChoiceB = document.createElement('a');
+    btnChoiceB.setAttribute('id', 'choice-B');
+    btnChoiceB.setAttribute('class', 'btn btn-sq btn-warning choice');
+    btnChoiceB.innerHTML = 'B';
+    choiceGroupDiv.append(btnChoiceB);
+
+    body.append(choiceGroupDiv);
+
     modalDialog.modal('show');
 
     // Register callback functions for the selection of either A or B
     $('#choice-A').click(function(e) {
-        processBallotSelection(ballots[0], ballotHashes[0], ballots[1], ballotHashes[1]);
+        sendBallotsToServer(ballots[0], ballots[1]);
     });
 
     $('#choice-B').click(function(e) {
-        processBallotSelection(ballots[1], ballotHashes[1], ballots[0], ballotHashes[0]);
+        sendBallotsToServer(ballots[1], ballots[0]);
     });
 }
 
-function generateBallotB(ballotA) {
-    var ballotB = generateBallot();
-    progressBar.setAttribute("style", "width: 100%;");
+function sendBallotsToServer(selection, alt) {
+    $.ajaxSetup({
+        beforeSend: function(xhr, settings) {
+            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
+                xhr.setRequestHeader("X-CSRFToken", CSRF);
+            }
+        }
+    });
 
-    showBallotChoiceDialog(ballotA, ballotB);
+    // Elliptic curve cryptography params used for encryption of encrypted vote
+    // fragments
+    var ctx = new CTX("BN254CX");
+    var n = new ctx.BIG();
+    var g1 = new ctx.ECP();
+    var g2 = new ctx.ECP2();
+
+    var parameter = $('#event-param').val();
+    var tempParams = JSON.parse(JSON.parse(parameter).crypto);
+
+    //copying the values
+    n.copy(tempParams.n);
+    g1.copy(tempParams.g1);
+    g2.copy(tempParams.g2);
+
+    var params = {
+      n:n,
+      g1:g1,
+      g2:g2
+    };
+
+    var tempPK = JSON.parse($('#comb_pk').val());
+    var pk = new ctx.ECP(0);
+    pk.copy(tempPK.PK);
+
+    var voterID = window.location.search.slice(1).split(/=(.+)/)[1];//.slice(0, -2);
+    var eventID = window.location.href.split('/')[4];
+    var pollNum = $('#poll-num').text();
+    var ballotID = encodeURIComponent(btoa(JSON.stringify({voterID: voterID, eventID: eventID, pollNum: pollNum})));
+
+    var SK = "temporary";
+    var encAlt = sjcl.encrypt(SK, JSON.stringify(alt));
+    selection = JSON.stringify(selection);
+
+    $.ajax({
+         type : "POST",
+         url : window.location,
+         data : {  handle: ballotID, encBallot: encAlt, ballot: selection },
+         success : function(){
+             onAfterBallotSend(ballotID, SK);
+         }
+    });
 }
 
-function generateBallotsAndShowUsr() {
-    // Generate Ballot A and Ballot B to be displayed to the user
-    // This fn starts the process
-    var ballotA = generateBallot();
+// Called once the ballot has been sent to the back-end and dialog has closed
+function onAfterBallotSend(ballotID, SK) {
+    let titleText = 'Vote Successfully Received';
+    let bodyText = "Thank you for voting! Your secret key is '"+SK+"'. Make sure to scan this QR code with your phone before closing this window.";
 
-    // Update the progress bar once the generation has completed
-    progressBar.setAttribute("style", "width: 50%;");
+    if(POLL_NUM !== POLL_COUNT) {
+        bodyText += " You can vote on the next poll by closing down this dialog and clicking 'Next Poll'.";
+    }
 
-    // This delay allows the execution thread to update the above CSS on the progress bar
-    setTimeout(function () {
-        generateBallotB(ballotA);
-    }, 150);
+    // With one ballot selected, we can display a QR code of the ballot ID
+    var modalDialog = $('#modalDialog');
+    var title = modalDialog.find('.modal-title');
+    var body = modalDialog.find('.modal-body');
+    title.text(titleText);
+    body.empty();
+
+    var p = document.createElement("p");
+    p.innerHTML = bodyText;
+    body.append(p);
+
+    // Generate the body of the dialog which displays the unselected ballot QR code
+    var QRCodeImg = document.createElement('img');
+    QRCodeImg.setAttribute('class', 'QR-code');
+    new QRCode(QRCodeImg, ballotID);
+
+    body.append(QRCodeImg);
+
+    var closeButton = $('#close-button');
+    closeButton.removeClass('btn-danger');
+    closeButton.addClass('btn-success');
+    closeButton.text("Close");
+
+    modalDialog.modal('show');
 }
\ No newline at end of file
diff --git a/static/js/vote_audit.js b/static/js/vote_audit.js
new file mode 100644
index 0000000..d82f9bc
--- /dev/null
+++ b/static/js/vote_audit.js
@@ -0,0 +1,17 @@
+$('#begin-test').click(function() {
+    var ballot = JSON.parse(sjcl.decrypt($('#SK').val(), $('#ballot').text()));
+    var votes = ballot['encryptedVotes'][0]['fragments'];
+    $('#ballot-content').text(JSON.stringify(votes));
+
+    var option = 0;
+    votes.forEach(function(cT) {
+        option++;
+        var encoding = "";
+        for (var i = 0; i < cT['C1'].length; i++) {
+            cipherText = "("+cT['C1']+","+cT['C2']+")";
+            var m = cT['C2'][i] / Math.pow(cT['C1'][i], cT['r'][i]);
+            encoding += (m) ? "1" : "0";
+        }
+        $('#ballot-result').text($('#ballot-result').text() + "\n\nOption "+option+": "+encoding);
+    })
+});
\ No newline at end of file