From 2e61ca7b945398ef1129a050a8c3c640bb779a2e Mon Sep 17 00:00:00 2001 From: Rumperuu Date: Mon, 16 Jul 2018 10:52:39 +0100 Subject: [PATCH 1/6] Updated README --- README.md | 76 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index c59b7f4..a4d62c0 100755 --- a/README.md +++ b/README.md @@ -1,62 +1,78 @@ # DEMOS2 -Prototype Django-based e-voting application, to demonstrate DEMOS2's client-side encryption e-voting. +Prototype Django-based e-voting application, to demonstrate DEMOS2's client-side +encryption e-voting. -The previous repository for DEMOS2 by Carey Williams can be found at: https://github.com/CareyJWilliams/DEMOS2 +The previous repository for DEMOS2 by Carey Williams can be found at: +https://github.com/CareyJWilliams/DEMOS2 + +## Setup ### Main Application Dependencies - Python: Version 2.7 (Anything higher than this will not currently work) - Python packages: Specified in 'requirements.txt' - PyCharm will detect these dependencies and offer installation - MySQL Server: Community Ed will do - initialised with legacy password authentication - New MySQL DB User: Default username and password specified in 'aullauthdemo/settings.py' - New MySQL DB: demos2 (also specified in 'aullauthdemo/settings.py') - make sure to set the charset to UTF8 +* Python: Version 2.7 (anything higher than this will not currently work) +* Python packages: Specified in `requirements.txt` - PyCharm will detect these dependencies and offer installation +* MySQL Server: Community Edition (initialise with 'legacy password authentication') +* New MySQL DB User: Default username and password specified in `aullauthdemo/settings.py` +* New MySQL DB: `demos2` (also specified in `aullauthdemo/settings.py`) - make sure to set the charset to 'UTF8' -Finally, with all the above dependencies in place, you can simply issue the following command to initialise the DB: +### Database setup - python manage.py migrate +After installing the above dependencies, issue the following command to initialise the DB: -'aullauthdemo/settings.py' specifies the Google reCAPTCHA site key and private key which will need changing when deployed -onto a new domain. There is also a DOMAIN setting within the file that needs updating during deployment as things -like email functionality depend on this setting for correct URL generation during event preparation etc. +``` +python manage.py migrate +``` + +The first run will produce a `django.db.utils.IntegrityError: (1215, 'Cannot add foreign key constraint')` error. Issue the command a second time and it will complete successfully. + +### Settings + +`allauthdemo/settings.py` specifies the Google reCAPTCHA site key and private key, which will need changing when deployed onto a new domain. +There is also a `DOMAIN` setting within the file that needs updating during deployment as things like email functionality depend on this setting for correct URL generation during event preparation etc. Emails from the application are currently sent from the following email account which can be updated within the settings: - demos2.no.reply@gmail.com +``` +demos2.no.reply@gmail.com +``` ### NodeJS Dependencies -The Node.js crypto server depends on the milagro-crypto-js and express modules. A package.json file can be found in the -Node/ directory with these dependency requirements and therefore from this folder you can run: +The Node.js crypto server depends on the `milagro-crypto-js` and `express` modules. A `package.json` file can be found in the `Node/` directory with these dependency requirements and therefore from this folder you can run: - npm install +``` +npm install +``` Once the dependencies have been installed, you can then run the node server as per the below instructions. +## InstructionsRunning + ### Step 1: Running the Python app and creating a new user account You can run the server with the following command: - python manage.py runserver +``` +python manage.py runserver +``` -The application will then be available at: +The application will then be available at `127.0.0.1:8000`. - 127.0.0.1:8000 - -You can then click on 'Join' to create a new user account. Currently, a server error is thrown when you create a new -email account saying something like 'Too Many Attempts'. Rest assured that the account will have been created. Navigate -back to the home page and you should be able to log in. This will hopefully be fixed in a future version. +You can then click on 'Join' to create a new user account. Currently, a server error is thrown when you create a new email account saying something like 'Too Many Attempts'. Rest assured that the account will have been created. Navigate back to the home page and you should be able to log in. This will hopefully be fixed in a future version. ### Step 2: Running Celery -Celery is used to run tasks asynchronously and the DEMOS2 application can't run without this application. A bash script -called 'start_celery_worker.sh' is provided to make starting a worker as easy as possible: +Celery is used to run tasks asynchronously and the DEMOS2 application can't run without this application. A bash script called `start_celery_worker.sh` is provided to make starting a worker as easy as possible: - ./start_celery_worker.sh +``` +./start_celery_worker.sh +``` -### Step 3: Running the NodeJS Server +### Step 3: Running the Node.js Server -The NodeJS server exposes a lot of cryptographic operations that the application depends on throughout. To run the -server, issue the following command line request from the Node/ folder: +The Node.js server exposes a lot of cryptographic operations that the application depends on throughout. To run the server, issue the following command line request from the `Node/` folder: - node index.js +``` +node index.js +``` From 78d771405ead0400172ae7b3e638a181bd891696 Mon Sep 17 00:00:00 2001 From: Rumperuu Date: Mon, 16 Jul 2018 10:53:16 +0100 Subject: [PATCH 2/6] Type Fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a4d62c0..5104910 100755 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ npm install Once the dependencies have been installed, you can then run the node server as per the below instructions. -## InstructionsRunning +## Instructions ### Step 1: Running the Python app and creating a new user account From 77508479b4dab3959adf7825273cd641000d9eab Mon Sep 17 00:00:00 2001 From: Rumperuu Date: Mon, 23 Jul 2018 14:08:17 +0100 Subject: [PATCH 3/6] Added QR codes to ballot selection, and a couple other minor chchanges --- Node/package.json | 2 +- Node/test/demos2-booth.js | 1 + README.md | 19 +++-- allauthdemo/polls/crypto_rpc.py | 1 - allauthdemo/polls/tasks.py | 2 +- .../templates/bases/bootstrap-jquery.html | 1 + static/js/create-event-poll.js | 12 +-- static/js/event_vote.js | 85 ++++++++++++++----- static/js/qrcode.min.js | 1 + 9 files changed, 81 insertions(+), 43 deletions(-) create mode 100644 static/js/qrcode.min.js diff --git a/Node/package.json b/Node/package.json index be592f5..3133702 100644 --- a/Node/package.json +++ b/Node/package.json @@ -1,5 +1,5 @@ { - "name": "Node DEMOS2 Cryptography Server", + "name": "node-demos2-cryptography-server", "version": "1.0.0", "description": "Exposes Cryptographic Functionality from Various Endpoints", "main": "index.js", diff --git a/Node/test/demos2-booth.js b/Node/test/demos2-booth.js index 6416c52..bd63803 100755 --- a/Node/test/demos2-booth.js +++ b/Node/test/demos2-booth.js @@ -35,6 +35,7 @@ gpGen = function(){ B.rcopy(ctx.ROM_CURVE.CURVE_Pyb); var Qy=new ctx.FP2(0); Qy.bset(A,B); var Q=new ctx.ECP2(); + var Q=new ctx.ECP2(); Q.setxy(Qy,Qy); return{ diff --git a/README.md b/README.md index 5104910..b543030 100755 --- a/README.md +++ b/README.md @@ -59,9 +59,17 @@ python manage.py runserver The application will then be available at `127.0.0.1:8000`. -You can then click on 'Join' to create a new user account. Currently, a server error is thrown when you create a new email account saying something like 'Too Many Attempts'. Rest assured that the account will have been created. Navigate back to the home page and you should be able to log in. This will hopefully be fixed in a future version. +You can then click on 'Join' to create a new user account. -### Step 2: Running Celery +### Step 2: Running the Node.js Server + +The Node.js server exposes a lot of cryptographic operations that the application depends on throughout. To run the server, issue the following command line request from the `Node/` folder: + +``` +node index.js +``` + +### Step 3: Running Celery Celery is used to run tasks asynchronously and the DEMOS2 application can't run without this application. A bash script called `start_celery_worker.sh` is provided to make starting a worker as easy as possible: @@ -69,10 +77,3 @@ Celery is used to run tasks asynchronously and the DEMOS2 application can't run ./start_celery_worker.sh ``` -### Step 3: Running the Node.js Server - -The Node.js server exposes a lot of cryptographic operations that the application depends on throughout. To run the server, issue the following command line request from the `Node/` folder: - -``` -node index.js -``` diff --git a/allauthdemo/polls/crypto_rpc.py b/allauthdemo/polls/crypto_rpc.py index b2d804f..35e124a 100755 --- a/allauthdemo/polls/crypto_rpc.py +++ b/allauthdemo/polls/crypto_rpc.py @@ -30,7 +30,6 @@ def param(): jsondict = json.load(urllib2.urlopen(url)) return json.dumps(jsondict) - def combpk(pks): url = 'http://localhost:8080/cmpkstring' diff --git a/allauthdemo/polls/tasks.py b/allauthdemo/polls/tasks.py index ebe53ad..277d0ad 100755 --- a/allauthdemo/polls/tasks.py +++ b/allauthdemo/polls/tasks.py @@ -16,7 +16,7 @@ from .crypto_rpc import param, combpk, add_ciphers, get_tally The following tasks were re-implemented by Thomas Smith: generate_event_param, tally_results, generate_combpk, generate_enc - This file was also updated by Vincent de Almeida + This file was also updated by Vincent de Almeida and Ben Goldsworthy ''' # Will store the result of the initial cal to param() from .cpp_calls diff --git a/allauthdemo/templates/bases/bootstrap-jquery.html b/allauthdemo/templates/bases/bootstrap-jquery.html index e19f8b0..f75137b 100755 --- a/allauthdemo/templates/bases/bootstrap-jquery.html +++ b/allauthdemo/templates/bases/bootstrap-jquery.html @@ -17,6 +17,7 @@ integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"> + diff --git a/static/js/create-event-poll.js b/static/js/create-event-poll.js index 84c8e6a..cd8f080 100755 --- a/static/js/create-event-poll.js +++ b/static/js/create-event-poll.js @@ -235,16 +235,12 @@ function isEventTimingsValid() { var valid = true; var helpBlockId = "event-timings-error-block"; - // Extract the string val from the vote start and end input controls - var start_date_time = $('#vote-start-input').val(); - var end_date_time = $('#vote-end-input').val(); - - // Convert the string vals to Date objects - var start_dateObj = new Date(start_date_time); - var end_dateObj = new Date(end_date_time); + // Extract the dates from the vote start and end input controls + var start_date_time = $('#vote-start-input').data('DateTimePicker').date(); + var end_date_time = $('#vote-end-input').data('DateTimePicker').date(); // Ensure that the start date is before the end date and that the end date is after the start date - if(!(start_dateObj < end_dateObj && end_dateObj > start_dateObj)) { + if(!(start_date_time < end_date_time && end_date_time > start_date_time)) { checkAndAddError({ error: "The start date must be before the end date and the end after the start date.", helpBlockId: "event-timings-error-block" diff --git a/static/js/event_vote.js b/static/js/event_vote.js index 6de7a42..d97a1e7 100644 --- a/static/js/event_vote.js +++ b/static/js/event_vote.js @@ -211,7 +211,7 @@ function csrfSafeMethod(method) { return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } -function sendBallotToServer(ballot) { +function sendBallotToServer(selection, altHash) { $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { @@ -223,9 +223,9 @@ function sendBallotToServer(ballot) { $.ajax({ type : "POST", url : window.location, - data : JSON.stringify({ ballot: ballot}), + data : JSON.stringify({ ballot: selection}), success : function(){ - voteSuccessfullyReceived(); + onAfterBallotSend(altHash); } }); } @@ -272,30 +272,69 @@ function SHA256Hash(bytes, toStr) { } } -// FAO Ben: Called once the ballot has been sent to the back-end and dialog has closed -function onAfterBallotSend() { - // TODO: FAO Ben: Implement QR func here. - // TODO: Currently, there is a dialog already implemented in the event_vote.html page which is - // TODO: used for voting error information but could be used to display the QR code using JS in - // TODO: a similar way that showBallotChoiceDialog does. +// 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."; + + if(POLL_NUM !== POLL_COUNT) { + bodyText += " You can vote on the next poll by closing down this dialog and clicking 'Next Poll'."; + } + + // 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(); + + 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'); } -function processBallotSelection(selection, selectionHash, successFn) { +function processBallotSelection(selection, selectionHash, alt, altHash) { // Dispatch the ballot to the server - sendBallotToServer(selection); - - // Close the choice selection dialog - var modalDialog = $('#modalDialog'); - modal.modal('hide'); + sendBallotToServer(selection, altHash); // Call the successfn currently with the selection hash but this may not be needed - successFn(selectionHash); + //successFn(alt, altHash); } function showBallotChoiceDialog(ballotA, ballotB) { - // Output hashes of the 2 ballots - const BALLOT_A_HASH = SHA256Hash(stringtobytes(JSON.stringify(ballotA)), true); - const BALLOT_B_HASH = SHA256Hash(stringtobytes(JSON.stringify(ballotB)), true); + var ballots = new Array(ballotA, ballotB); + var ballotHashes = new Array(2); + + // Hash both ballots and store + 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 var modalDialog = $('#modalDialog'); @@ -327,14 +366,14 @@ function showBallotChoiceDialog(ballotA, ballotB) { hashGroupDiv.append( br ); var hashA = document.createElement("span"); - hashA.innerHTML = "Hash A: " + BALLOT_A_HASH; + hashA.innerHTML = "Hash A: " + ballotHashes[0]; hashGroupDiv.append( hashA ); var br2 = document.createElement('br'); hashGroupDiv.append( br2 ); var hashB = document.createElement("span"); - hashB.innerHTML = "Hash B: " + BALLOT_B_HASH; + hashB.innerHTML = "Hash B: " + ballotHashes[1]; hashGroupDiv.append( hashB ); // ----------------------------------------------- @@ -346,11 +385,11 @@ function showBallotChoiceDialog(ballotA, ballotB) { // Register callback functions for the selection of either A or B $('#choice-A').click(function(e) { - processBallotSelection(ballotA, BALLOT_A_HASH, onAfterBallotSend); + processBallotSelection(ballots[0], ballotHashes[0], ballots[1], ballotHashes[1]); }); $('#choice-B').click(function(e) { - processBallotSelection(ballotB, BALLOT_B_HASH, onAfterBallotSend); + processBallotSelection(ballots[1], ballotHashes[1], ballots[0], ballotHashes[0]); }); } diff --git a/static/js/qrcode.min.js b/static/js/qrcode.min.js new file mode 100644 index 0000000..993e88f --- /dev/null +++ b/static/js/qrcode.min.js @@ -0,0 +1 @@ +var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j=0?p.get(q):0}}for(var r=0,m=0;mm;m++)for(var j=0;jm;m++)for(var j=0;j=0;)b^=f.G15<=0;)b^=f.G18<>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;cf;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=[''],h=0;d>h;h++){g.push("");for(var i=0;d>i;i++)g.push('');g.push("")}g.push("
"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}(); \ No newline at end of file From 89533a54b1027a999535da19933a78b683411998 Mon Sep 17 00:00:00 2001 From: Rumperuu Date: Thu, 16 Aug 2018 22:17:16 +0100 Subject: [PATCH 4/6] Going home --- allauthdemo/polls/models.py | 5 + allauthdemo/polls/urls.py | 3 +- allauthdemo/polls/views.py | 28 +- .../templates/bases/bootstrap-jquery.html | 8 +- allauthdemo/templates/polls/event_vote.html | 3 +- allauthdemo/templates/polls/vote_audit.html | 18 + static/js/demos2-booth.js | 3 +- static/js/event_vote.js | 322 ++++++++++-------- static/js/vote_audit.js | 17 + 9 files changed, 256 insertions(+), 151 deletions(-) create mode 100644 allauthdemo/templates/polls/vote_audit.html create mode 100644 static/js/vote_audit.js 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

From 9ea59723a52a6184ef8ee14607ff717e38c7bdfc Mon Sep 17 00:00:00 2001
From: Rumperuu 
Date: Wed, 29 Aug 2018 14:29:27 +0100
Subject: [PATCH 5/6] I really ought to be using branches

---
 allauthdemo/polls/views.py |  6 +++---
 static/js/event_vote.js    |  4 +++-
 static/js/vote_audit.js    | 43 ++++++++++++++++++++++++++------------
 3 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/allauthdemo/polls/views.py b/allauthdemo/polls/views.py
index 9694e6d..c498fbe 100755
--- a/allauthdemo/polls/views.py
+++ b/allauthdemo/polls/views.py
@@ -113,11 +113,11 @@ def edit_poll(request, event_id, poll_id):
 
 
 def vote_audit(request):
-    encryptedBallot = get_object_or_404(EncBallot, handle=''+urllib.quote_plus(request.GET.get('handle', None)))
+    encrypted_ballot = get_object_or_404(EncBallot, handle=''+urllib.quote_plus(request.GET.get('handle', None)))
 
     return render(request, "polls/vote_audit.html",
                   {
-                      "ballot": encryptedBallot.ballot
+                      "ballot": encrypted_ballot.ballot
                   })
 
 
@@ -192,7 +192,7 @@ def event_vote(request, event_id, poll_id):
         # 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.ballot = enc_ballot_json
             b.save()
         else:
             b = EncBallot(handle=handle_json, ballot=enc_ballot_json)
diff --git a/static/js/event_vote.js b/static/js/event_vote.js
index a68ad21..05946f5 100644
--- a/static/js/event_vote.js
+++ b/static/js/event_vote.js
@@ -319,7 +319,6 @@ function showFirstQRCode(ballotA, ballotB) {
 
     footer.prepend(nextButton);
 
-
     modalDialog.modal('show');
 
     $('#next-button').click(function(e) {
@@ -450,6 +449,9 @@ function onAfterBallotSend(ballotID, SK) {
     closeButton.removeClass('btn-danger');
     closeButton.addClass('btn-success');
     closeButton.text("Close");
+    if(POLL_NUM == POLL_COUNT) {
+        $('#next-button').hide();
+    }
 
     modalDialog.modal('show');
 }
\ No newline at end of file
diff --git a/static/js/vote_audit.js b/static/js/vote_audit.js
index d82f9bc..9e33fc1 100644
--- a/static/js/vote_audit.js
+++ b/static/js/vote_audit.js
@@ -1,17 +1,34 @@
 $('#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);
-    })
+    var votes = ballot['encryptedVotes'];
+    $('#ballot-content').text(JSON.stringify(votes));
+    var voteNum = 0, optionNum = 0;
+
+    // For each encrypted vote within the ballot...
+    votes.forEach(function(vote) {
+        voteNum++;
+        $('#ballot-result').text($('#ballot-result').text() + "Vote " + voteNum + ": \n ");
+
+        // For each encrypted fragment within the vote (i.e. the presence of a vote for each option)...
+        vote['fragments'].forEach(function(fragment) {
+            optionNum++;
+            $('#ballot-result').text($('#ballot-result').text() + "Option " + optionNum + ": \n  ");
+
+            var encoding = "";
+            console.log(fragment);
+
+            // For each pair of values in C1 and C2 and the randomness r, test whether C2/(C1)^r = g^0 or g^1, and
+            // record g's exponent.
+            for (var i = 0; i < fragment['C1'].length; i++) {
+                cipherText = "("+fragment['C1']+","+fragment['C2']+")";
+                var m = fragment['C2'][i] / Math.pow(fragment['C1'][i], fragment['r'][i]);
+                encoding += (m) ? "1" : "0";
+            }
+
+            // Somehow, this string of 1s and 0s  here needs to become _one_ 1 or 0 to signify whether the option was
+            // voted for or not.
+            $('#ballot-result').text($('#ballot-result').text() + encoding + "\n ");
+        });
+    });
 });
\ No newline at end of file

From de517c19c5195951f68bcbbcbf860a5df9c7d67a Mon Sep 17 00:00:00 2001
From: Rumperuu 
Date: Wed, 29 Aug 2018 14:44:37 +0100
Subject: [PATCH 6/6] Ready for merge

---
 Node/test/demos2-booth.js       |  1 -
 README.md                       | 12 +++++++-----
 allauthdemo/polls/crypto_rpc.py |  1 +
 static/js/create-event-poll.js  | 12 ++++++++----
 4 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/Node/test/demos2-booth.js b/Node/test/demos2-booth.js
index bd63803..6416c52 100755
--- a/Node/test/demos2-booth.js
+++ b/Node/test/demos2-booth.js
@@ -35,7 +35,6 @@ gpGen = function(){
         B.rcopy(ctx.ROM_CURVE.CURVE_Pyb);
 		var Qy=new ctx.FP2(0); Qy.bset(A,B);
 		var Q=new ctx.ECP2();
-		var Q=new ctx.ECP2();
 		Q.setxy(Qy,Qy);
 
 		return{
diff --git a/README.md b/README.md
index b543030..c977460 100755
--- a/README.md
+++ b/README.md
@@ -10,11 +10,13 @@ https://github.com/CareyJWilliams/DEMOS2
 
 ### Main Application Dependencies
 
-* Python: Version 2.7 (anything higher than this will not currently work)
-* Python packages: Specified in `requirements.txt` - PyCharm will detect these dependencies and offer installation
-* MySQL Server: Community Edition (initialise with 'legacy password authentication')
-* New MySQL DB User: Default username and password specified in `aullauthdemo/settings.py`
-* New MySQL DB: `demos2` (also specified in `aullauthdemo/settings.py`) - make sure to set the charset to 'UTF8'
+* Python
+   * Version 2.7; anything higher than this will not currently work
+   * All Python packages specified in `requirements.txt`; PyCharm will detect these dependencies and offer installation
+* MySQL Server: Community Edition
+   * Initialise with 'legacy password authentication'
+   * Create a new MySQL DB user with the default username and password specified in `aullauthdemo/settings.py`
+   * Create a new MySQL DB called `demos2` (also specified in `aullauthdemo/settings.py`), making sure to set the charset to 'UTF8'
 
 ### Database setup
 
diff --git a/allauthdemo/polls/crypto_rpc.py b/allauthdemo/polls/crypto_rpc.py
index 35e124a..b2d804f 100755
--- a/allauthdemo/polls/crypto_rpc.py
+++ b/allauthdemo/polls/crypto_rpc.py
@@ -30,6 +30,7 @@ def param():
     jsondict = json.load(urllib2.urlopen(url))
     return json.dumps(jsondict)
 
+
 def combpk(pks):
     url = 'http://localhost:8080/cmpkstring'
 
diff --git a/static/js/create-event-poll.js b/static/js/create-event-poll.js
index cd8f080..fa41893 100755
--- a/static/js/create-event-poll.js
+++ b/static/js/create-event-poll.js
@@ -235,12 +235,16 @@ function isEventTimingsValid() {
     var valid = true;
     var helpBlockId = "event-timings-error-block";
 
-    // Extract the dates from the vote start and end input controls
-    var start_date_time = $('#vote-start-input').data('DateTimePicker').date();
-    var end_date_time = $('#vote-end-input').data('DateTimePicker').date();
+    // Extract the string val from the vote start and end input controls
+    var start_date_time = $('#vote-start-input').val();
+    var end_date_time = $('#vote-end-input').val();
+    
+    // Convert the string vals to Date objects
+    var start_dateObj = new Date(start_date_time);
+    var end_dateObj = new Date(end_date_time);
 
     // Ensure that the start date is before the end date and that the end date is after the start date
-    if(!(start_date_time < end_date_time && end_date_time > start_date_time)) {
+    if(!(start_dateObj < end_dateObj && end_dateObj > start_dateObj)) {
         checkAndAddError({
            error: "The start date must be before the end date and the end after the start date.",
            helpBlockId: "event-timings-error-block"