Merge pull request #10 from vincentmdealmeida/BinaryEncoding
Implemented full end-to-end encryption and decryption of an event usi…
This commit is contained in:
commit
571cd723bc
23 changed files with 809 additions and 446 deletions
188
Node/index.js
188
Node/index.js
|
@ -1,30 +1,33 @@
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
||||||
Code by Thomas Smith
|
Code by Bingsheng Zhang, Thomas Smith, Vincent de Almeida
|
||||||
|
|
||||||
|
Dependencies can be found in 'package.json' and installed using 'npm install'
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var port = 8080;
|
var port = 8080;
|
||||||
|
|
||||||
var express = require('express');
|
|
||||||
var Buffer = require('buffer').Buffer;
|
var Buffer = require('buffer').Buffer;
|
||||||
var CTX = require('milagro-crypto-js')
|
var CTX = require('milagro-crypto-js');
|
||||||
var app = express();
|
|
||||||
/*
|
|
||||||
var cors = require('cors')
|
|
||||||
app.use(cors());
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
var express = require('express');
|
||||||
|
var bodyParser = require("body-parser");
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
// Express server configuration
|
||||||
app.use(express.static('test'));
|
app.use(express.static('test'));
|
||||||
|
app.use(bodyParser.urlencoded({ extended: false }));
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
//default test
|
//default test
|
||||||
app.get('/', function(request, response){
|
app.get('/', function(request, response){
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
message: 'hello world',
|
message: 'hello world',
|
||||||
value: 5
|
value: 5
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
//response.send('Hey there'+request.ip);
|
//response.send('Hey there'+request.ip);
|
||||||
|
@ -40,13 +43,13 @@ app.get('/param', function(request, response){
|
||||||
console.log('Generated Param:' + param);
|
console.log('Generated Param:' + param);
|
||||||
response.json(param);
|
response.json(param);
|
||||||
|
|
||||||
})
|
});
|
||||||
|
|
||||||
//combine public keys and return the full combined one - JSON Version
|
//combine public keys and return the full combined one - JSON Version
|
||||||
app.get('/combpk', function(request, response){
|
app.get('/combpk', function(request, response){
|
||||||
|
console.log('\nEndpoint /combpk called');
|
||||||
|
|
||||||
|
var partials = request.query['PK'];
|
||||||
var partials = request.query['PK']
|
|
||||||
|
|
||||||
var parsed = [];
|
var parsed = [];
|
||||||
|
|
||||||
|
@ -57,45 +60,40 @@ app.get('/combpk', function(request, response){
|
||||||
parsed.push(JSON.parse(partials[i]));
|
parsed.push(JSON.parse(partials[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
var PK = combine(parsed);
|
var PK = combine_pks(parsed);
|
||||||
response.json(PK);
|
response.json(PK);
|
||||||
|
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
//byte array version
|
//byte array version
|
||||||
app.get('/cmpkstring', function(request, response){
|
app.post('/cmpkstring', function(request, response){
|
||||||
|
console.log('\nEndpoint /cmpkstring called');
|
||||||
var ctx = new CTX("BN254CX");
|
var ctx = new CTX("BN254CX");
|
||||||
|
|
||||||
var partials = request.query['PK']
|
var partials = request.body.PKs;
|
||||||
//if there is only one key, partials will be an array of the individual bytes
|
|
||||||
//if more than one, it will be an array of arrays
|
|
||||||
//we need to factor for this in code
|
|
||||||
var noOfKeys = request.query['number'];
|
|
||||||
var parsed = [];
|
var parsed = [];
|
||||||
|
|
||||||
if(noOfKeys == partials.length)//if we're submitting more than one key
|
if(partials.length > 1)//if we're submitting more than one key
|
||||||
{
|
{
|
||||||
console.log('Combining' + noOfKeys + " keys...");
|
console.log('Combining ' + partials.length + " public keys into one...");
|
||||||
for (var i = partials.length - 1; i >= 0; i--) {
|
for (var i = partials.length - 1; i >= 0; i--) {
|
||||||
console.log('PK' + i + ': ' + partials[i]);
|
console.log('PK' + i + ': ' + partials[i]);
|
||||||
var bytes = Buffer.from(partials[i].split(','), 'hex');
|
var bytes = Buffer.from(partials[i].split(','), 'hex');
|
||||||
console.log(bytes)
|
|
||||||
var pk = new ctx.ECP.fromBytes(bytes);
|
var pk = new ctx.ECP.fromBytes(bytes);
|
||||||
parsed.push(pk);
|
parsed.push(pk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(noOfKeys == 1)
|
else if(partials.length === 1)
|
||||||
{
|
{
|
||||||
console.log("Combining just one key");
|
console.log("Combining just one public key...");
|
||||||
var bytes = Buffer.from(partials.split(','), 'hex');
|
var bytes = Buffer.from(partials[0].split(','), 'hex');
|
||||||
console.log(bytes);
|
|
||||||
var pk = new ctx.ECP.fromBytes(bytes);
|
var pk = new ctx.ECP.fromBytes(bytes);
|
||||||
parsed.push(pk);
|
parsed.push(pk);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.json(combine(parsed));
|
response.json(combine_pks(parsed));
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
//addition function on homomorphically encrypted variables
|
//addition function on homomorphically encrypted variables
|
||||||
|
@ -155,20 +153,19 @@ app.get('/addec', function(request, response){
|
||||||
|
|
||||||
|
|
||||||
response.json(add(parsed));
|
response.json(add(parsed));
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//tally partially decrypted ciphertexts
|
//tally partially decrypted ciphertexts
|
||||||
app.get('/tally', function(request, response){
|
app.get('/tally', function(request, response){
|
||||||
console.log("called tally");
|
console.log("\nEndpoint /tally called");
|
||||||
var amount = request.query['number'];//number of decryptions taking in
|
var amount = request.query['number'];//number of decryptions taking in
|
||||||
var paramString = request.query['param'];//event group parameter in JSON
|
var paramString = request.query['param'];//event group parameter in JSON
|
||||||
var partialsStrings = request.query['decs'];//array of partial decryption(s) in bytes
|
var partialsStrings = request.query['decs'];//array of partial decryption(s) in bytes
|
||||||
var ciphertextString = request.query['cipher'];//ciphertext being decrypted in JSON
|
var ciphertextString = request.query['cipher'];//ciphertext being decrypted in JSON
|
||||||
|
|
||||||
//re-build parameters
|
//re-build parameters
|
||||||
var tempParams = JSON.parse(paramString);
|
var tempParams = JSON.parse(JSON.parse(paramString).crypto);
|
||||||
var ctx = new CTX("BN254CX"); //new context we can use
|
var ctx = new CTX("BN254CX"); //new context we can use
|
||||||
var n = new ctx.BIG();
|
var n = new ctx.BIG();
|
||||||
var g1 = new ctx.ECP();
|
var g1 = new ctx.ECP();
|
||||||
|
@ -183,47 +180,117 @@ app.get('/tally', function(request, response){
|
||||||
n:n,
|
n:n,
|
||||||
g1:g1,
|
g1:g1,
|
||||||
g2:g2
|
g2:g2
|
||||||
}
|
};
|
||||||
|
|
||||||
//re-build partial decryptions
|
//re-build partial decryptions
|
||||||
var partials = []
|
var partials = [];
|
||||||
if(amount == partialsStrings.length)
|
if(amount == partialsStrings.length)
|
||||||
{
|
{
|
||||||
console.log(amount + " partial decryptions");
|
console.log(amount + " partial decryptions");
|
||||||
for(var i = 0; i < partialsStrings.length; i++)
|
for(var i = 0; i < partialsStrings.length; i++)
|
||||||
{
|
{
|
||||||
var bytes = Buffer.from(partialsStrings[i].split(','), 'hex');
|
var bytes = Buffer.from(partialsStrings[i].split(','), 'hex');
|
||||||
|
|
||||||
var dec = {
|
var dec = {
|
||||||
D:new ctx.ECP.fromBytes(bytes)
|
D:new ctx.ECP.fromBytes(bytes)
|
||||||
}
|
};
|
||||||
|
|
||||||
partials.push(dec);
|
partials.push(dec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(amount == 1)
|
else if(amount == 1)
|
||||||
{
|
{
|
||||||
console.log("Only one partial decryption received")
|
console.log("\nOnly one partial decryption received\n");
|
||||||
console.log(paramString)
|
console.log(JSON.parse(paramString).crypto + "\n");
|
||||||
|
|
||||||
var bytes = Buffer.from(partialsStrings.split(','), 'hex');
|
var bytes = Buffer.from(partialsStrings.split(','), 'hex');
|
||||||
var dec = {
|
var dec = {
|
||||||
D : new ctx.ECP.fromBytes(bytes)
|
D : new ctx.ECP.fromBytes(bytes)
|
||||||
}
|
};
|
||||||
|
|
||||||
partials.push(dec);
|
partials.push(dec);
|
||||||
}
|
}
|
||||||
|
|
||||||
//re-build combined ciphertext
|
//re-build combined ciphertext
|
||||||
var tempCipher = JSON.parse(ciphertextString);
|
var tempCipher = JSON.parse(ciphertextString);
|
||||||
|
|
||||||
cipher = {
|
var cipher = {
|
||||||
C1: new ctx.ECP(),
|
C1: new ctx.ECP(),
|
||||||
C2: new ctx.ECP()
|
C2: new ctx.ECP()
|
||||||
}
|
};
|
||||||
|
|
||||||
cipher.C1.copy(tempCipher.C1);
|
cipher.C1.copy(tempCipher.C1);
|
||||||
cipher.C2.copy(tempCipher.C2);
|
cipher.C2.copy(tempCipher.C2);
|
||||||
|
|
||||||
response.json(tally(params, partials, cipher))
|
response.json(tally(params, partials, cipher))
|
||||||
})
|
});
|
||||||
|
|
||||||
|
app.post('/comb_sks', function(request, response){
|
||||||
|
console.log("\nEndpoint /comb_sks called");
|
||||||
|
const SKsAsStrings = request.body.SKs;
|
||||||
|
|
||||||
|
// Parse and combine the secret keys
|
||||||
|
var ctx = new CTX("BN254CX");
|
||||||
|
var parsedSKs = [];
|
||||||
|
|
||||||
|
for(var i = 0; i < SKsAsStrings.length; i++) {
|
||||||
|
var skBytes = SKsAsStrings[i].split(",");
|
||||||
|
parsedSKs.push(new ctx.BIG.fromBytes(skBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Combining " + parsedSKs.length + " SKs...");
|
||||||
|
var SKBytes = [];
|
||||||
|
combine_sks(parsedSKs).SK.toBytes(SKBytes);
|
||||||
|
|
||||||
|
response.send(SKBytes.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/get_tally', function(request, response){
|
||||||
|
const COUNT = request.body.count;
|
||||||
|
const TEMP_PARAMS = JSON.parse(JSON.parse(request.body.param).crypto);
|
||||||
|
const C1s = request.body.ciphers.c1s;
|
||||||
|
const C2s = request.body.ciphers.c2s;
|
||||||
|
const SK = request.body.sk;
|
||||||
|
|
||||||
|
console.log("\nFrom /get_tally - C1 array length (num of voters for the opt): " + C1s.length);
|
||||||
|
|
||||||
|
//re-build parameters
|
||||||
|
var ctx = new CTX("BN254CX"); //new context we can use
|
||||||
|
var n = new ctx.BIG();
|
||||||
|
var g1 = new ctx.ECP();
|
||||||
|
var g2 = new ctx.ECP2();
|
||||||
|
|
||||||
|
n.copy(TEMP_PARAMS.n);
|
||||||
|
g1.copy(TEMP_PARAMS.g1);
|
||||||
|
g2.copy(TEMP_PARAMS.g2);
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
n:n,
|
||||||
|
g1:g1,
|
||||||
|
g2:g2
|
||||||
|
};
|
||||||
|
|
||||||
|
//rebuild our secret key
|
||||||
|
var skBytes = SK.split(",");
|
||||||
|
var sk = new ctx.BIG.fromBytes(skBytes);
|
||||||
|
|
||||||
|
var tally = 0;
|
||||||
|
|
||||||
|
for(var i = 0; i < COUNT; i++) {
|
||||||
|
var c1Bytes = Buffer.from(C1s[i].split(','), 'hex');
|
||||||
|
var newC1 = new ctx.ECP.fromBytes(c1Bytes);
|
||||||
|
|
||||||
|
var c2Bytes = Buffer.from(C2s[i].split(','), 'hex');
|
||||||
|
var newC2 = new ctx.ECP.fromBytes(c2Bytes);
|
||||||
|
|
||||||
|
var cipher = {C1: newC1, C2: newC2};
|
||||||
|
tally += decrypt(params, sk, cipher).M;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Tally: " + tally + "\n");
|
||||||
|
|
||||||
|
response.send("" + tally);
|
||||||
|
});
|
||||||
|
|
||||||
var server = app.listen(port, function(){
|
var server = app.listen(port, function(){
|
||||||
var host = server.address().address;
|
var host = server.address().address;
|
||||||
|
@ -277,7 +344,7 @@ gpGen = function(){
|
||||||
g1:P,
|
g1:P,
|
||||||
g2:Q
|
g2:Q
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
//creates ElGamal public and secret key
|
//creates ElGamal public and secret key
|
||||||
|
@ -304,12 +371,12 @@ keyGen=function(params){
|
||||||
PK:pk,
|
PK:pk,
|
||||||
SK:sk
|
SK:sk
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
//combine multiple public key together
|
//combine multiple public key together
|
||||||
//the input is an array of PKs
|
//the input is an array of PKs
|
||||||
combine=function(PKs){
|
combine_pks=function(PKs){
|
||||||
var ctx = new CTX("BN254CX");
|
var ctx = new CTX("BN254CX");
|
||||||
var pk=new ctx.ECP();
|
var pk=new ctx.ECP();
|
||||||
//copy the first pk
|
//copy the first pk
|
||||||
|
@ -322,8 +389,22 @@ combine=function(PKs){
|
||||||
return {
|
return {
|
||||||
PK : pk
|
PK : pk
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Written by Vincent de Almeida: Combines multiple secret keys together
|
||||||
|
// The SKs in the SKs array should already have been initialised using 'new ctx.BIG.fromBytes()'
|
||||||
|
combine_sks=function(SKs) {
|
||||||
|
// 'add' the rest of the sks to the first
|
||||||
|
var sk = SKs[0];
|
||||||
|
|
||||||
|
for(var i = 1; i < SKs.length; i++) {
|
||||||
|
sk.add(SKs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
SK: sk
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//ElGamal encryption
|
//ElGamal encryption
|
||||||
encrypt=function(params,PK, m){
|
encrypt=function(params,PK, m){
|
||||||
|
@ -356,7 +437,7 @@ encrypt=function(params,PK, m){
|
||||||
C1:C1,
|
C1:C1,
|
||||||
C2:C2
|
C2:C2
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
//add ciphertexts
|
//add ciphertexts
|
||||||
|
@ -380,7 +461,7 @@ add=function(Ciphers){
|
||||||
C1:s1,
|
C1:s1,
|
||||||
C2:s2
|
C2:s2
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
//ElGamal decryption
|
//ElGamal decryption
|
||||||
|
@ -410,9 +491,7 @@ decrypt=function(params,SK, C){
|
||||||
return{
|
return{
|
||||||
M: "Error"
|
M: "Error"
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//ElGamal partial decryption
|
//ElGamal partial decryption
|
||||||
|
@ -424,8 +503,7 @@ partDec=function(SK, C){
|
||||||
return{
|
return{
|
||||||
D: D
|
D: D
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -448,7 +526,7 @@ tally=function(params,Ds, C){
|
||||||
|
|
||||||
//search for message by brute force
|
//search for message by brute force
|
||||||
var B;
|
var B;
|
||||||
for (j = 0; j < 1000; j++) {
|
for (var j = 0; j < 1000; j++) {
|
||||||
//use D as temp var
|
//use D as temp var
|
||||||
B = new ctx.BIG(j);
|
B = new ctx.BIG(j);
|
||||||
D = ctx.PAIR.G1mul(params.g1,B);
|
D = ctx.PAIR.G1mul(params.g1,B);
|
||||||
|
@ -457,13 +535,13 @@ tally=function(params,Ds, C){
|
||||||
M: j
|
M: j
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
return{
|
return{
|
||||||
M: "Error"
|
M: "Error"
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,10 @@
|
||||||
"url": "https://github.com/vincentmdealmeida/DEMOS2"
|
"url": "https://github.com/vincentmdealmeida/DEMOS2"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Bingsheng Zang, Thomas Smith",
|
"author": "Bingsheng Zang, Thomas Smith, Vincent de Almeida",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"body-parser": "^1.18.3",
|
||||||
"express": "^4.16.3",
|
"express": "^4.16.3",
|
||||||
"milagro-crypto-js": "git+https://github.com/milagro-crypto/milagro-crypto-js.git"
|
"milagro-crypto-js": "git+https://github.com/milagro-crypto/milagro-crypto-js.git"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import os
|
|
||||||
import shlex
|
|
||||||
import subprocess
|
|
||||||
import json
|
import json
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
|
@ -10,26 +7,40 @@ All functions in this file have been re-implemenented by Thomas Smith
|
||||||
|
|
||||||
File then updated by Vincent de Almeida. Changes include:
|
File then updated by Vincent de Almeida. Changes include:
|
||||||
-Update filename to 'crypto_rpc' to reflect the RPC nature of the methods
|
-Update filename to 'crypto_rpc' to reflect the RPC nature of the methods
|
||||||
|
-Modified RPC calls that send data to POST requests to avoid large query URLs
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def send_post_req(url, data):
|
||||||
|
data = json.dumps(data)
|
||||||
|
|
||||||
|
# Create a request specifying the Content-Type
|
||||||
|
req = urllib2.Request(url, data, {'Content-Type': 'application/json'})
|
||||||
|
f = urllib2.urlopen(req)
|
||||||
|
response = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def param():
|
def param():
|
||||||
url = 'http://localhost:8080/param' # RPC URL
|
url = 'http://localhost:8080/param'
|
||||||
jsondict = json.load(urllib2.urlopen(url))
|
jsondict = json.load(urllib2.urlopen(url))
|
||||||
return json.dumps(jsondict)
|
return json.dumps(jsondict)
|
||||||
|
|
||||||
def combpk(amount, pks):
|
|
||||||
url = 'http://localhost:8080/cmpkstring' # RPC URL
|
|
||||||
querystring = '?number='+str(amount)
|
|
||||||
for pk in pks:
|
|
||||||
querystring += '&PK='+pk
|
|
||||||
|
|
||||||
print(url+querystring)
|
def combpk(pks):
|
||||||
jsondict = json.load(urllib2.urlopen(url+querystring))
|
url = 'http://localhost:8080/cmpkstring'
|
||||||
print(json.dumps(jsondict))
|
|
||||||
return json.dumps(jsondict)
|
data = {}
|
||||||
|
data['PKs'] = pks
|
||||||
|
|
||||||
|
return send_post_req(url, data)
|
||||||
|
|
||||||
|
|
||||||
def addec(amount, ciphers):
|
def addec(amount, ciphers):
|
||||||
url = 'http://localhost:8080/addec' # RPC URL
|
url = 'http://localhost:8080/addec'
|
||||||
querystring = '?number='+str(amount)
|
querystring = '?number='+str(amount)
|
||||||
c1s = ciphers['c1s']
|
c1s = ciphers['c1s']
|
||||||
c2s = ciphers['c2s']
|
c2s = ciphers['c2s']
|
||||||
|
@ -42,23 +53,44 @@ def addec(amount, ciphers):
|
||||||
print(json.dumps(jsondict))
|
print(json.dumps(jsondict))
|
||||||
return json.dumps(jsondict)
|
return json.dumps(jsondict)
|
||||||
|
|
||||||
def tally(amount, param, decs, cipher):
|
|
||||||
url = 'http://localhost:8080/tally' # RPC URL
|
|
||||||
querystring = '?number='+str(amount)
|
|
||||||
querystring += '¶m='+urllib2.quote(str(param))
|
|
||||||
|
|
||||||
testquerystring = '?number='+str(amount)
|
# Deprecated functionality and has been superseded by get_tally
|
||||||
testquerystring += '¶m='+str(param)
|
def tally(amount, group_param, decs, cipher):
|
||||||
|
url = 'http://localhost:8080/tally'
|
||||||
|
querystring = '?number='+str(amount)
|
||||||
|
querystring += '¶m='+urllib2.quote(str(group_param))
|
||||||
|
|
||||||
for i, value in enumerate(decs):
|
for i, value in enumerate(decs):
|
||||||
querystring += "&decs="+str(value)
|
querystring += "&decs="+str(value)
|
||||||
testquerystring += "&decs="+str(value)
|
|
||||||
|
|
||||||
querystring += '&cipher=' + urllib2.quote(str(cipher))
|
querystring += '&cipher=' + urllib2.quote(str(cipher))
|
||||||
testquerystring += '&cipher=' + str(cipher)
|
|
||||||
|
|
||||||
print(url+querystring)
|
|
||||||
print(url+testquerystring)
|
|
||||||
jsondict = json.load(urllib2.urlopen(url+querystring))
|
jsondict = json.load(urllib2.urlopen(url+querystring))
|
||||||
print('tally: ' + str(jsondict['M']))
|
|
||||||
return str(jsondict['M'])
|
return str(jsondict['M'])
|
||||||
|
|
||||||
|
|
||||||
|
def combine_sks(sks):
|
||||||
|
url = 'http://localhost:8080/comb_sks'
|
||||||
|
|
||||||
|
# Construct POST data
|
||||||
|
data = {}
|
||||||
|
data['SKs'] = sks
|
||||||
|
|
||||||
|
# Return the new combined SK
|
||||||
|
return send_post_req(url, data)
|
||||||
|
|
||||||
|
|
||||||
|
def get_tally(count, ciphers, sk, group_param):
|
||||||
|
url = 'http://localhost:8080/get_tally'
|
||||||
|
|
||||||
|
# Construct POST data
|
||||||
|
data = {}
|
||||||
|
data['count'] = count
|
||||||
|
data['ciphers'] = ciphers
|
||||||
|
data['sk'] = sk
|
||||||
|
data['param'] = group_param
|
||||||
|
|
||||||
|
# Return the tally of votes for the option
|
||||||
|
return send_post_req(url, data)
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ class Event(models.Model):
|
||||||
start_time = models.DateTimeField()
|
start_time = models.DateTimeField()
|
||||||
end_time = models.DateTimeField()
|
end_time = models.DateTimeField()
|
||||||
prepared = models.BooleanField(default=False)
|
prepared = models.BooleanField(default=False)
|
||||||
|
ended = models.BooleanField(default=False)
|
||||||
public_key = models.CharField(null=True, blank=False, max_length=1024)
|
public_key = models.CharField(null=True, blank=False, max_length=1024)
|
||||||
title = models.CharField(max_length=1024)
|
title = models.CharField(max_length=1024)
|
||||||
EID = models.CharField(max_length=2048, blank=True)
|
EID = models.CharField(max_length=2048, blank=True)
|
||||||
|
@ -35,6 +36,7 @@ class Event(models.Model):
|
||||||
c_email = models.CharField(max_length=512, blank=True)
|
c_email = models.CharField(max_length=512, blank=True)
|
||||||
trustees = models.CharField(max_length=4096)
|
trustees = models.CharField(max_length=4096)
|
||||||
|
|
||||||
|
# Custom helper methods
|
||||||
def EID_hr(self):
|
def EID_hr(self):
|
||||||
EID_json = json.loads(self.EID)
|
EID_json = json.loads(self.EID)
|
||||||
return EID_json['hr']
|
return EID_json['hr']
|
||||||
|
@ -68,15 +70,36 @@ class Event(models.Model):
|
||||||
# future event
|
# future event
|
||||||
present = timezone.now()
|
present = timezone.now()
|
||||||
|
|
||||||
if present >= self.start_time and present <= self.end_time:
|
if self.ended is False:
|
||||||
status_str = "Active"
|
if present < self.start_time and self.public_key is None:
|
||||||
elif present > self.end_time:
|
|
||||||
status_str = "Expired"
|
|
||||||
elif present < self.start_time:
|
|
||||||
status_str = "Future"
|
status_str = "Future"
|
||||||
|
elif present < self.start_time and self.public_key is not None:
|
||||||
|
status_str = "Prepared"
|
||||||
|
elif present >= self.start_time and present <= self.end_time and self.public_key is not None:
|
||||||
|
status_str = "Active"
|
||||||
|
elif present > self.end_time and self.public_key is not None:
|
||||||
|
status_str = "Expired"
|
||||||
|
else:
|
||||||
|
if self.event_sk.all().count() == 1:
|
||||||
|
status_str = "Decrypted"
|
||||||
|
elif self.event_sk.all().count() == 0:
|
||||||
|
status_str = "Ended"
|
||||||
|
|
||||||
return status_str
|
return status_str
|
||||||
|
|
||||||
|
'''
|
||||||
|
The result applies to all polls for an event so True will only be returned when votes have
|
||||||
|
been received for every poll.
|
||||||
|
'''
|
||||||
|
def has_received_votes(self):
|
||||||
|
received_votes = True
|
||||||
|
|
||||||
|
for poll in self.polls.all():
|
||||||
|
if Ballot.objects.filter(poll=poll, cast=True).count() == 0:
|
||||||
|
received_votes = False
|
||||||
|
|
||||||
|
return received_votes
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
@ -84,12 +107,12 @@ class Event(models.Model):
|
||||||
class TrusteeKey(models.Model):
|
class TrusteeKey(models.Model):
|
||||||
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="trustee_keys")
|
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="trustee_keys")
|
||||||
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="trustee_keys")
|
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="trustee_keys")
|
||||||
key = models.CharField(max_length=1024, unique=True) # ideally composite key here, but django doesn't really support yet
|
key = models.CharField(max_length=255, unique=True)
|
||||||
|
|
||||||
class AccessKey(models.Model):
|
class AccessKey(models.Model):
|
||||||
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="keys")
|
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="keys")
|
||||||
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="keys")
|
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="keys")
|
||||||
key = models.CharField(max_length=1024, unique=True) # ideally composite key here, but django doesn't really support yet
|
key = models.CharField(max_length=255, unique=True)
|
||||||
|
|
||||||
#total = models.IntegerField(blank=True, null=True, default=0)
|
#total = models.IntegerField(blank=True, null=True, default=0)
|
||||||
|
|
||||||
|
@ -115,20 +138,6 @@ class Poll(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.question_text
|
return self.question_text
|
||||||
|
|
||||||
class Decryption(models.Model):
|
|
||||||
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="decryptions")
|
|
||||||
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="decryptions")
|
|
||||||
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="decryptions")
|
|
||||||
text = models.CharField(max_length=1024)
|
|
||||||
|
|
||||||
#some modification to this class
|
|
||||||
class Ballot(models.Model):
|
|
||||||
voter = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="ballots")
|
|
||||||
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="ballots")
|
|
||||||
cipher_text_c1 = models.CharField(max_length=4096)#the encryption system uses two byte strings
|
|
||||||
cipher_text_c2 = models.CharField(max_length=4096)
|
|
||||||
cast = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
class PollOption(models.Model):
|
class PollOption(models.Model):
|
||||||
choice_text = models.CharField(max_length=200)
|
choice_text = models.CharField(max_length=200)
|
||||||
votes = models.IntegerField(default=0)
|
votes = models.IntegerField(default=0)
|
||||||
|
@ -138,6 +147,35 @@ class PollOption(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.choice_text
|
return self.choice_text
|
||||||
|
|
||||||
|
class Decryption(models.Model):
|
||||||
|
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="decryptions")
|
||||||
|
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="decryptions")
|
||||||
|
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="decryptions")
|
||||||
|
text = models.CharField(max_length=1024)
|
||||||
|
|
||||||
|
class TrusteeSK(models.Model):
|
||||||
|
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="trustee_sk")
|
||||||
|
trustee = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="trustee_sk")
|
||||||
|
key = models.CharField(max_length=1024)
|
||||||
|
|
||||||
|
class EventSK(models.Model):
|
||||||
|
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="event_sk")
|
||||||
|
key = models.CharField(max_length=1024)
|
||||||
|
|
||||||
|
class Ballot(models.Model):
|
||||||
|
voter = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="ballots")
|
||||||
|
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="ballots")
|
||||||
|
cast = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
# Implements the new binary encoding scheme
|
||||||
|
class EncryptedVote(models.Model):
|
||||||
|
ballot = models.ForeignKey(Ballot, on_delete=models.CASCADE, related_name="encrypted_vote")
|
||||||
|
|
||||||
|
class VoteFragment(models.Model):
|
||||||
|
encrypted_vote = models.ForeignKey(EncryptedVote, on_delete=models.CASCADE, related_name="fragment")
|
||||||
|
cipher_text_c1 = models.CharField(max_length=4096)
|
||||||
|
cipher_text_c2 = models.CharField(max_length=4096)
|
||||||
|
|
||||||
class Organiser(models.Model):
|
class Organiser(models.Model):
|
||||||
index = models.IntegerField(default=0)
|
index = models.IntegerField(default=0)
|
||||||
email = models.CharField(max_length=100, blank=False, null=False)
|
email = models.CharField(max_length=100, blank=False, null=False)
|
||||||
|
|
|
@ -5,14 +5,11 @@ import json
|
||||||
from os import urandom
|
from os import urandom
|
||||||
from celery import task
|
from celery import task
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.core.validators import EmailValidator
|
|
||||||
from django.core.mail import send_mail
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from allauthdemo.polls.models import AccessKey
|
from allauthdemo.polls.models import AccessKey, Ballot, Decryption, TrusteeSK, EventSK
|
||||||
|
|
||||||
from .crypto_rpc import param, combpk, addec, tally
|
from .crypto_rpc import param, combpk, addec, tally, get_tally, combine_sks
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Goal: This py file defines celery tasks that can be initiated
|
Goal: This py file defines celery tasks that can be initiated
|
||||||
|
@ -25,25 +22,43 @@ from .crypto_rpc import param, combpk, addec, tally
|
||||||
# Will store the result of the initial cal to param() from .cpp_calls
|
# Will store the result of the initial cal to param() from .cpp_calls
|
||||||
group_param = None
|
group_param = None
|
||||||
|
|
||||||
def is_valid_email(email):
|
|
||||||
try:
|
|
||||||
valid_email = EmailValidator(whitelist=None)
|
|
||||||
valid_email(email)
|
|
||||||
return True
|
|
||||||
except ValidationError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@task()
|
|
||||||
def create_ballots(poll):
|
|
||||||
for voter in poll.event.voters.all():
|
|
||||||
ballot = poll.ballots.create(voter=voter, poll=poll)
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Will generate a key for accessing either the event preparation page or the voting page
|
Helper functions
|
||||||
|
|
||||||
|
gen_access_key - Will generate an a key for accessing either the event preparation page, voting page and decryption page
|
||||||
'''
|
'''
|
||||||
def gen_access_key():
|
def gen_access_key():
|
||||||
return base64.urlsafe_b64encode(urandom(16)).decode('utf-8')
|
return base64.urlsafe_b64encode(urandom(16)).decode('utf-8')
|
||||||
|
|
||||||
|
def email_trustees_dec(event):
|
||||||
|
email_subject = "Event Ballot Decryption for '" + event.title + "'"
|
||||||
|
|
||||||
|
# Plain text email - this could be replaced for a HTML-based email in the future
|
||||||
|
email_body = "Please visit the following URL to submit your trustee secret key to begin event decryption:\n\n"
|
||||||
|
url_base = "http://" + settings.DOMAIN + "/event/" + str(event.pk) + "/decrypt/?key="
|
||||||
|
email_body = email_body + url_base
|
||||||
|
|
||||||
|
for trustee in event.users_trustees.all():
|
||||||
|
# Generate a key and create an AccessKey object
|
||||||
|
key = gen_access_key()
|
||||||
|
AccessKey.objects.create(user=trustee, event=event, key=key)
|
||||||
|
|
||||||
|
trustee.send_email(email_subject, email_body + key)
|
||||||
|
|
||||||
|
@task()
|
||||||
|
def create_ballots(event):
|
||||||
|
voters = event.voters.all()
|
||||||
|
|
||||||
|
for poll in event.polls.all():
|
||||||
|
for voter in voters:
|
||||||
|
ballot = poll.ballots.create(voter=voter, poll=poll)
|
||||||
|
|
||||||
|
@task()
|
||||||
|
def create_ballots_for_poll(poll):
|
||||||
|
for voter in poll.event.voters.all():
|
||||||
|
ballot = poll.ballots.create(voter=voter, poll=poll)
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Emails an event preparation URL containing an access key for all of the trustees for an event
|
Emails an event preparation URL containing an access key for all of the trustees for an event
|
||||||
'''
|
'''
|
||||||
|
@ -64,19 +79,31 @@ def email_trustees_prep(trustees, event):
|
||||||
trustee.send_email(email_subject, email_body + key)
|
trustee.send_email(email_subject, email_body + key)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Emails the access keys for all of the voters for an event
|
Emails a URL containing an access key for all of the voters for an event
|
||||||
'''
|
'''
|
||||||
@task()
|
@task()
|
||||||
def email_voters_a_key(voters, event):
|
def email_voters_vote_url(voters, event):
|
||||||
email_subject = "Voting Access for Event '" + event.title + "'"
|
email_subject = "Voting Access for Event '" + event.title + "'"
|
||||||
email_body = 'Key: '
|
|
||||||
|
# Plain text email - this could be replaced for a HTML-based email in the future
|
||||||
|
email_body_base = "Please visit the following URL in order to vote on the event '" + event.title + "':\n\n"
|
||||||
|
url_base = "http://" + settings.DOMAIN + "/event/" + str(event.pk) + "/poll/1/vote/?key="
|
||||||
|
email_body_base = email_body_base + url_base
|
||||||
|
|
||||||
|
duration_info = "\n\nYou can vote between the following dates and times:\n"
|
||||||
|
duration_info = duration_info + "Start: " + event.start_time_formatted_utc() + "\n"
|
||||||
|
duration_info = duration_info + "End: " + event.end_time_formatted_utc()
|
||||||
|
|
||||||
for voter in voters:
|
for voter in voters:
|
||||||
# Generate a key and create an AccessKey object
|
# Generate a key and create an AccessKey object
|
||||||
key = gen_access_key()
|
key = gen_access_key()
|
||||||
AccessKey.objects.create(user=voter, event=event, key=key)
|
AccessKey.objects.create(user=voter, event=event, key=key)
|
||||||
|
|
||||||
voter.send_email(email_subject, email_body + key)
|
# Update the email body to incl the access key as well as the duration information
|
||||||
|
email_body = str(email_body_base + key)
|
||||||
|
email_body = email_body + duration_info
|
||||||
|
|
||||||
|
voter.send_email(email_subject, email_body)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Updates the EID of an event to contain 2 event IDs: a human readable one (hr) and a crypto one (GP from param())
|
Updates the EID of an event to contain 2 event IDs: a human readable one (hr) and a crypto one (GP from param())
|
||||||
|
@ -93,6 +120,83 @@ def update_EID(event):
|
||||||
event.EID = json.dumps(EID)
|
event.EID = json.dumps(EID)
|
||||||
event.save()
|
event.save()
|
||||||
|
|
||||||
|
@task()
|
||||||
|
def event_ended(event):
|
||||||
|
# Email all trustees to request their secret keys
|
||||||
|
email_trustees_dec(event)
|
||||||
|
|
||||||
|
@task()
|
||||||
|
def gen_event_sk_and_dec(event):
|
||||||
|
trustee_sks = TrusteeSK.objects.filter(event=event)
|
||||||
|
t_sks_count = len(trustee_sks)
|
||||||
|
|
||||||
|
# Combine SKs if there's more than one
|
||||||
|
event_sk = None
|
||||||
|
if t_sks_count == 1:
|
||||||
|
event_sk = trustee_sks.get().key
|
||||||
|
else:
|
||||||
|
t_sks_str_list = list()
|
||||||
|
|
||||||
|
for t_sk in trustee_sks:
|
||||||
|
t_sks_str_list.append(t_sk.key)
|
||||||
|
|
||||||
|
event_sk = combine_sks(t_sks_str_list)
|
||||||
|
|
||||||
|
EventSK.objects.create(event=event, key=event_sk)
|
||||||
|
|
||||||
|
# With the event sk created, we can decrypt the event
|
||||||
|
decrypt_and_tally(event)
|
||||||
|
|
||||||
|
@task()
|
||||||
|
def decrypt_and_tally(event):
|
||||||
|
polls = event.polls.all()
|
||||||
|
sk = EventSK.objects.filter(event=event).get().key
|
||||||
|
|
||||||
|
for i in range(len(polls)):
|
||||||
|
poll = polls[i]
|
||||||
|
result = str("")
|
||||||
|
result += "{\"name\": \"" + poll.question_text + "\","
|
||||||
|
|
||||||
|
# get num of opts and ballots
|
||||||
|
options = poll.options.all()
|
||||||
|
opt_count = len(options)
|
||||||
|
ballots = Ballot.objects.filter(poll=poll, cast=True)
|
||||||
|
|
||||||
|
result += "\"options\": ["
|
||||||
|
for j in range(opt_count):
|
||||||
|
# Collect all fragments for this opt
|
||||||
|
frags_c1 = list()
|
||||||
|
frags_c2 = list()
|
||||||
|
|
||||||
|
for ballot in ballots:
|
||||||
|
enc_vote = ballot.encrypted_vote.get()
|
||||||
|
|
||||||
|
if enc_vote is not None:
|
||||||
|
fragments = enc_vote.fragment.all()
|
||||||
|
frags_c1.append(fragments[j].cipher_text_c1)
|
||||||
|
frags_c2.append(fragments[j].cipher_text_c2)
|
||||||
|
|
||||||
|
ciphers = {
|
||||||
|
'c1s': frags_c1,
|
||||||
|
'c2s': frags_c2
|
||||||
|
}
|
||||||
|
|
||||||
|
count = len(frags_c1)
|
||||||
|
votes = get_tally(count, ciphers, sk, event.EID)
|
||||||
|
|
||||||
|
result += "{\"option\": \"" + str(options[j].choice_text) + "\", \"votes\": " + str(votes) + "}"
|
||||||
|
|
||||||
|
if j != (opt_count-1):
|
||||||
|
result += ","
|
||||||
|
|
||||||
|
result += "]}"
|
||||||
|
|
||||||
|
if i != (len(polls) - 1):
|
||||||
|
result += ","
|
||||||
|
|
||||||
|
poll.enc = result
|
||||||
|
poll.save()
|
||||||
|
|
||||||
@task()
|
@task()
|
||||||
def tally_results(event):
|
def tally_results(event):
|
||||||
for poll in event.polls.all():
|
for poll in event.polls.all():
|
||||||
|
@ -101,39 +205,42 @@ def tally_results(event):
|
||||||
decs.append(dec.text)
|
decs.append(dec.text)
|
||||||
amount = len(decs)
|
amount = len(decs)
|
||||||
result = tally(amount, event.EID, decs, poll.enc)
|
result = tally(amount, event.EID, decs, poll.enc)
|
||||||
send_mail(
|
|
||||||
'Your Results:',
|
# TODO: Email organisers using email_user method?
|
||||||
poll.question_text + ": " + result,
|
|
||||||
'from@example.com',
|
|
||||||
["fake@fake.com"],
|
|
||||||
fail_silently=False,
|
|
||||||
)
|
|
||||||
print(poll.question_text + ": " + result)
|
print(poll.question_text + ": " + result)
|
||||||
|
|
||||||
@task()
|
@task()
|
||||||
def generate_combpk(event):
|
def generate_combpk(event):
|
||||||
pks = list()
|
pks = list()
|
||||||
|
|
||||||
for tkey in event.trustee_keys.all():
|
for tkey in event.trustee_keys.all():
|
||||||
pks.append(str(tkey.key))
|
pks.append(str(tkey.key))
|
||||||
amount = len(pks)
|
|
||||||
event.public_key = combpk(amount, pks)
|
event.public_key = combpk(pks)
|
||||||
|
|
||||||
event.prepared = True
|
event.prepared = True
|
||||||
event.save()
|
event.save()
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def generate_enc(poll):
|
def generate_enc(poll):
|
||||||
c1s = list()#c1 components of ciphertexts
|
# c1 and c2 components of ciphertexts
|
||||||
c2s = list()#c1 components of ciphertexts
|
c1s = list()
|
||||||
|
c2s = list()
|
||||||
|
|
||||||
for ballot in poll.ballots.all():
|
for ballot in poll.ballots.all():
|
||||||
if (ballot.cast):
|
if ballot.cast:
|
||||||
c1s.append(str(ballot.cipher_text_c1))
|
c1s.append(str(ballot.cipher_text_c1))
|
||||||
c2s.append(str(ballot.cipher_text_c2))
|
c2s.append(str(ballot.cipher_text_c2))
|
||||||
|
|
||||||
ciphers = {
|
ciphers = {
|
||||||
'c1s': c1s,
|
'c1s': c1s,
|
||||||
'c2s': c2s
|
'c2s': c2s
|
||||||
}
|
}
|
||||||
amount = len(c1s)
|
|
||||||
poll.enc = addec(amount, ciphers)
|
count = len(c1s)
|
||||||
|
|
||||||
|
poll.enc = addec(count, ciphers)
|
||||||
poll.save()
|
poll.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,5 +5,17 @@ register = template.Library()
|
||||||
#get a value for additively homomorphic encryption ballots
|
#get a value for additively homomorphic encryption ballots
|
||||||
#we can't do maths in the template normally so a filter is a way around it
|
#we can't do maths in the template normally so a filter is a way around it
|
||||||
@register.filter
|
@register.filter
|
||||||
def get_ballot_value(value):
|
def get_ballot_value(option_no, options_count):
|
||||||
return pow(10, value-1)
|
ballot_value = ""
|
||||||
|
|
||||||
|
for i in range(options_count):
|
||||||
|
|
||||||
|
if (i+1) == option_no:
|
||||||
|
ballot_value = ballot_value + "1"
|
||||||
|
else:
|
||||||
|
ballot_value = ballot_value + "0"
|
||||||
|
|
||||||
|
if not i == (options_count-1):
|
||||||
|
ballot_value = ballot_value + ","
|
||||||
|
|
||||||
|
return ballot_value
|
||||||
|
|
|
@ -1,37 +1,24 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
app_name = 'polls'
|
app_name = 'polls'
|
||||||
|
|
||||||
#urlpatterns = [
|
|
||||||
# url(r'^$', views.index, name='index'),
|
|
||||||
# ex: /polls/5/
|
|
||||||
# url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
|
|
||||||
# ex: /polls/5/results/
|
|
||||||
# url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
|
|
||||||
# ex: /polls/5/vote/
|
|
||||||
# url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
|
|
||||||
#]
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^vote/(?P<poll_id>[0-9]+)/$', views.test_poll_vote, name='vote-poll'),
|
url(r'^$', login_required(views.EventListView.as_view()), name='index'),
|
||||||
url(r'^(?P<pk>[0-9]+)/$', views.EventDetailView.as_view(), name='view-event'),
|
|
||||||
url(r'^(?P<pk>[0-9]+)/polls$', views.EventDetailPollsView.as_view(), name='event-polls'),
|
|
||||||
url(r'^(?P<pk>[0-9]+)/organisers$', views.EventDetailOrganisersView.as_view(), name='event-organisers'),
|
|
||||||
url(r'^$', views.EventListView.as_view(), name='index'),
|
|
||||||
url(r'^create/$', login_required(views.create_event), name='create-event'),
|
url(r'^create/$', login_required(views.create_event), name='create-event'),
|
||||||
url(r'^(?P<event_id>[0-9]+)/decrypt/$', login_required(views.event_trustee_decrypt), name='decrypt-event'),
|
url(r'^(?P<pk>[0-9]+)/$', login_required(views.EventDetailView.as_view()), name='view-event'),
|
||||||
url(r'^(?P<event_id>[0-9]+)/prepare/$', login_required(views.event_trustee_setup), name='prepare-event'),
|
url(r'^(?P<pk>[0-9]+)/polls/$', login_required(views.EventDetailPollsView.as_view()), name='event-polls'),
|
||||||
url(r'^(?P<event_id>[0-9]+)/encrypt/$', login_required(views.event_addec), name='enc-event'),
|
url(r'^(?P<pk>[0-9]+)/entities/$', login_required(views.EventDetailEntitiesView.as_view()), name='event-entities'),
|
||||||
url(r'^(?P<pk>[0-9]+)/launch/$', views.EventDetailLaunchView.as_view(), name='launch-event'),
|
url(r'^(?P<pk>[0-9]+)/advanced/$', login_required(views.EventDetailAdvancedView.as_view()), name='event-advanced'),
|
||||||
url(r'^edit/(?P<event_id>[0-9]+)/$', login_required(views.edit_event), name='edit-event'),
|
url(r'^(?P<event_id>[0-9]+)/end/$', login_required(views.event_end), name='end-event'),
|
||||||
url(r'^delete/(?P<event_id>[0-9]+)/$', login_required(views.del_event), name='del-event'),
|
url(r'^(?P<event_id>[0-9]+)/results/$', login_required(views.results), name='event-results'),
|
||||||
|
url(r'^(?P<event_id>[0-9]+)/edit/$', login_required(views.edit_event), name='edit-event'),
|
||||||
|
url(r'^(?P<event_id>[0-9]+)/delete/$', login_required(views.del_event), name='del-event'),
|
||||||
|
url(r'^(?P<event_id>[0-9]+)/decrypt/$', views.event_trustee_decrypt, name='decrypt-event'),
|
||||||
|
url(r'^(?P<event_id>[0-9]+)/prepare/$', views.event_trustee_setup, name='prepare-event'),
|
||||||
|
url(r'^(?P<event_id>[0-9]+)/poll/(?P<poll_num>[0-9]+)/vote/$', views.event_vote, name='event-vote'),
|
||||||
url(r'^(?P<event_id>[0-9]+)/create/poll/$', login_required(views.manage_questions), name='create-poll'),
|
url(r'^(?P<event_id>[0-9]+)/create/poll/$', login_required(views.manage_questions), name='create-poll'),
|
||||||
url(r'^(?P<event_id>[0-9]+)/poll/(?P<poll_num>[0-9]+)/$', login_required(views.view_poll), name='view-poll'),
|
url(r'^(?P<event_id>[0-9]+)/poll/(?P<poll_num>[0-9]+)/edit$', login_required(views.edit_poll), name='edit-poll')
|
||||||
url(r'^(?P<event_id>[0-9]+)/poll/(?P<poll_num>[0-9]+)/edit$', login_required(views.edit_poll), name='edit-poll'),
|
|
||||||
#url(r'^(?P<pk>[0-9]+)/$', login_required(views.DetailView.as_view()), name='detail'),
|
|
||||||
#url(r'^(?P<pk>[0-9]+)/results/$', login_required(views.ResultsView.as_view()), name='results'),
|
|
||||||
#url(r'^(?P<question_id>[0-9]+)/vote/$', login_required(views.vote), name='vote'),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -539,12 +539,9 @@ class EventModelAdaptor:
|
||||||
# Extract the list of trustees
|
# Extract the list of trustees
|
||||||
trustees_list = self.form_data.pop('trustee-email-input')
|
trustees_list = self.form_data.pop('trustee-email-input')
|
||||||
|
|
||||||
for trustee in trustees_list:
|
for trustee_email in trustees_list:
|
||||||
if trustee != '':
|
if trustee_email != '':
|
||||||
if EmailUser.objects.filter(email=trustee).exists():
|
self.trustees.append(trustee_email)
|
||||||
self.trustees.append(EmailUser.objects.filter(email=trustee).get())
|
|
||||||
else:
|
|
||||||
self.trustees.append(EmailUser(email=trustee))
|
|
||||||
|
|
||||||
# Extract the email list of voters
|
# Extract the email list of voters
|
||||||
voters_csv_string = self.form_data.pop('voters-list-input')[0].replace(' ', '')
|
voters_csv_string = self.form_data.pop('voters-list-input')[0].replace(' ', '')
|
||||||
|
@ -552,17 +549,21 @@ class EventModelAdaptor:
|
||||||
|
|
||||||
for voter_email in voters_email_list:
|
for voter_email in voters_email_list:
|
||||||
if voter_email != '':
|
if voter_email != '':
|
||||||
if EmailUser.objects.filter(email=voter_email).exists():
|
self.voters.append(voter_email)
|
||||||
self.voters.append(EmailUser.objects.filter(email=voter_email).get())
|
|
||||||
else:
|
|
||||||
self.voters.append(EmailUser(email=voter_email))
|
|
||||||
|
|
||||||
# Create the Event model object - this does not persist it to the DB
|
# Create the Event model object - this does not persist it to the DB
|
||||||
|
creator = ""
|
||||||
|
if self.user.first_name is not None:
|
||||||
|
creator += self.user.first_name + " "
|
||||||
|
|
||||||
|
if self.user.last_name is not None:
|
||||||
|
creator += self.user.last_name
|
||||||
|
|
||||||
self.event = Event(start_time=self.starts_at,
|
self.event = Event(start_time=self.starts_at,
|
||||||
end_time=self.ends_at,
|
end_time=self.ends_at,
|
||||||
title=self.event_name,
|
title=self.event_name,
|
||||||
EID=self.identifier,
|
EID=self.identifier,
|
||||||
creator=self.user.first_name + ' ' + self.user.last_name,
|
creator=creator,
|
||||||
c_email=self.user.email,
|
c_email=self.user.email,
|
||||||
trustees=voters_csv_string)
|
trustees=voters_csv_string)
|
||||||
|
|
||||||
|
@ -629,20 +630,21 @@ class EventModelAdaptor:
|
||||||
# so it can just be added
|
# so it can just be added
|
||||||
self.event.users_organisers = self.organisers
|
self.event.users_organisers = self.organisers
|
||||||
|
|
||||||
# Add the list of trustees to the event, making sure they're instantiated
|
# Add the list of trustees to the event
|
||||||
|
db_trustees = list()
|
||||||
for trustee in self.trustees:
|
for trustee in self.trustees:
|
||||||
if not EmailUser.objects.filter(email=trustee.email).exists():
|
user, created = EmailUser.objects.get_or_create(email=trustee)
|
||||||
trustee.save()
|
db_trustees.append(user)
|
||||||
|
|
||||||
self.event.users_trustees = self.trustees
|
self.event.users_trustees = db_trustees
|
||||||
|
|
||||||
# Add the list of voters to the event, making sure they're instantiated
|
# Add the list of voters to the event
|
||||||
# Additionally, generating the AccessKey for voters
|
db_voters = list()
|
||||||
for voter in self.voters:
|
for voter in self.voters:
|
||||||
if not EmailUser.objects.filter(email=voter.email).exists():
|
user, created = EmailUser.objects.get_or_create(email=voter)
|
||||||
voter.save()
|
db_voters.append(user)
|
||||||
|
|
||||||
self.event.voters = self.voters
|
self.event.voters = db_voters
|
||||||
|
|
||||||
# Extract all the poll data for the event and associated poll option data
|
# Extract all the poll data for the event and associated poll option data
|
||||||
# This can only be done at this point as the event has been persisted
|
# This can only be done at this point as the event has been persisted
|
||||||
|
|
|
@ -2,25 +2,23 @@ import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from io import StringIO
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import HttpResponseRedirect, HttpResponse, Http404
|
from django.http import HttpResponseRedirect, HttpResponse, Http404
|
||||||
from django.http.response import HttpResponseNotAllowed
|
from django.http.response import HttpResponseNotAllowed
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.shortcuts import get_object_or_404, render, render_to_response
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.utils import timezone
|
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import serializers
|
|
||||||
|
|
||||||
from .forms import EventForm, PollForm, OptionFormset, QuestionFormset, OrganiserFormSet, TrusteeFormSet, VoteForm, EventSetupForm, EventEditForm, DecryptionFormset, DecryptionFormSetHelper
|
from .forms import PollForm, OptionFormset, VoteForm, EventSetupForm, EventEditForm
|
||||||
from .models import Event, Poll, PollOption, EmailUser, Ballot, TrusteeKey, Decryption
|
from .models import Event, Poll, Ballot, EncryptedVote, TrusteeKey, TrusteeSK
|
||||||
from allauthdemo.auth.models import DemoUser
|
from allauthdemo.auth.models import DemoUser
|
||||||
|
|
||||||
from .tasks import email_trustees_prep, update_EID, generate_combpk, generate_enc, tally_results
|
from .tasks import email_trustees_prep, update_EID, generate_combpk, event_ended, create_ballots, create_ballots_for_poll, email_voters_vote_url, gen_event_sk_and_dec
|
||||||
|
|
||||||
from .utils.EventModelAdaptor import EventModelAdaptor
|
from .utils.EventModelAdaptor import EventModelAdaptor
|
||||||
|
|
||||||
|
|
||||||
class EventListView(generic.ListView):
|
class EventListView(generic.ListView):
|
||||||
|
|
||||||
model = Event
|
model = Event
|
||||||
|
@ -30,6 +28,7 @@ class EventListView(generic.ListView):
|
||||||
#context['now'] = timezone.now()
|
#context['now'] = timezone.now()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class EventDetailView(generic.DetailView):
|
class EventDetailView(generic.DetailView):
|
||||||
template_name="polls/event_detail_details.html"
|
template_name="polls/event_detail_details.html"
|
||||||
model = Event
|
model = Event
|
||||||
|
@ -37,38 +36,32 @@ class EventDetailView(generic.DetailView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(EventDetailView, self).get_context_data(**kwargs)
|
context = super(EventDetailView, self).get_context_data(**kwargs)
|
||||||
context['is_organiser'] = ((not self.request.user.is_anonymous()) and (self.object.users_organisers.filter(email=self.request.user.email).exists()))
|
context['is_organiser'] = ((not self.request.user.is_anonymous()) and (self.object.users_organisers.filter(email=self.request.user.email).exists()))
|
||||||
|
context['decrypted'] = self.object.status() == "Decrypted"
|
||||||
|
|
||||||
#context['now'] = timezone.now()
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class EventDetailPollsView(EventDetailView):
|
class EventDetailPollsView(EventDetailView):
|
||||||
template_name = "polls/event_detail_polls.html"
|
template_name = "polls/event_detail_polls.html"
|
||||||
|
|
||||||
class EventDetailOrganisersView(EventDetailView):
|
|
||||||
template_name="polls/event_detail_organisers.html"
|
|
||||||
|
|
||||||
class EventDetailLaunchView(EventDetailView):
|
class EventDetailEntitiesView(EventDetailView):
|
||||||
template_name="polls/event_detail_launch.html"
|
template_name = "polls/event_detail_entities.html"
|
||||||
|
|
||||||
|
|
||||||
|
class EventDetailAdvancedView(EventDetailView):
|
||||||
|
template_name = "polls/event_detail_advanced.html"
|
||||||
|
|
||||||
|
|
||||||
class PollDetailView(generic.View):
|
class PollDetailView(generic.View):
|
||||||
|
|
||||||
model = Poll
|
model = Poll
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(PollDetailView, self).get_context_data(**kwargs)
|
context = super(PollDetailView, self).get_context_data(**kwargs)
|
||||||
#context['now'] = timezone.now()
|
|
||||||
context['form'] = VoteForm(instance=self.object)
|
context['form'] = VoteForm(instance=self.object)
|
||||||
context['poll_count'] = self.object.event.polls.all().count()
|
context['poll_count'] = self.object.event.polls.all().count()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
#my_value = self.kwargs.get('key', 'default_value')
|
|
||||||
|
|
||||||
def test_poll_detail(request, event_id, poll_num, key=None):
|
|
||||||
context = {}
|
|
||||||
context['form'] = VoteForm(instance=self.object)
|
|
||||||
context['poll_count'] = self.object.event.polls.all().count()
|
|
||||||
return render(request, "polls/event_setup.html", context)
|
|
||||||
|
|
||||||
def util_get_poll_by_event_index(event, poll_num):
|
def util_get_poll_by_event_index(event, poll_num):
|
||||||
try:
|
try:
|
||||||
|
@ -80,78 +73,114 @@ def util_get_poll_by_event_index(event, poll_num):
|
||||||
return None
|
return None
|
||||||
return poll
|
return poll
|
||||||
|
|
||||||
|
|
||||||
def edit_poll(request, event_id, poll_num):
|
def edit_poll(request, event_id, poll_num):
|
||||||
event = get_object_or_404(Event, pk=event_id)
|
event = get_object_or_404(Event, pk=event_id)
|
||||||
event_poll_count = event.polls.all().count()
|
|
||||||
poll = util_get_poll_by_event_index(event, poll_num)
|
poll = util_get_poll_by_event_index(event, poll_num)
|
||||||
|
|
||||||
if (poll == None):
|
if (poll == None):
|
||||||
raise Http404("Poll does not exist")
|
raise Http404("Poll does not exist")
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
form = PollForm(instance=poll, prefix="main")
|
form = PollForm(instance=poll, prefix="main")
|
||||||
formset = OptionFormset(instance=poll, prefix="formset_options")
|
formset = OptionFormset(instance=poll, prefix="formset_options")
|
||||||
return render(request, "polls/generic_form.html", {'form_title': "Edit Poll: " + poll.question_text, 'form': form, 'option_formset': formset})
|
return render(request, "polls/generic_form.html", {'form_title': "Edit Poll: " + poll.question_text, 'form': form, 'option_formset': formset})
|
||||||
|
elif request.method == 'POST':
|
||||||
|
form = PollForm(request.POST, instance=poll, prefix="main")
|
||||||
|
|
||||||
def view_poll(request, event_id, poll_num):
|
if form.is_valid():
|
||||||
#return HttpResponse(param("012345"))
|
form.save()
|
||||||
#return HttpResponse(combpk(param("012345"), "ABzqvL+pqTi+DNLLRcM62RwCoaZTaXVbOs3sk4fc0+Dc 0 AAaQd6S1x+bcgnkDp2ev5mTt34ICQdZIzP9GaqG4x5sy 0" "ABhQay9jI4pZvkAETNwfo8iwJ8eBMkjqplqAiu/FZxMy 0 ABPxj0jVj3rt0VW54iv4tV02gYtujnR41t5gf97asrPs 0 ABfoiW03bsYIUgfAThmjurmOViKy9L89vfkIavhQIblm 1 ABhQay9jI4pZvkAETNwfo8iwJ8eBMkjqplqAiu/FZxMy 0 ABPxj0jVj3rt0VW54iv4tV02gYtujnR41t5gf97asrPs 0 ABfoiW03bsYIUgfAThmjurmOViKy9L89vfkIavhQIblm 1 ABhQay9jI4pZvkAETNwfo8iwJ8eBMkjqplqAiu/FZxMy 0 ABPxj0jVj3rt0VW54iv4tV02gYtujnR41t5gf97asrPs 0 ABfoiW03bsYIUgfAThmjurmOViKy9L89vfkIavhQIblm 1"))
|
|
||||||
#return HttpResponse(addec("ACMW70Yj3+mJ/FO+6VOSDGYPYHf7NoTXdpInbfzUqYpH 0 ABV4Mo496B0FW3AW/7gY6Fs+oz6BwfwilonMYeriUyV/ 0 AAg+bdGhs3sxSxAc/wcKdBNUy+el8A2b4yVYShNOb8uX 0 AAspJbn5V2AaY4CgLkzCkHwUWbC5nyxrBzw+o4Az8HVM 1 ABKI7o5Yhgi44XwpFnPpLnH0/czbXA8y5vM4ucV8vojo 1 AAwVrT9+dcQsqRZYoI7+QsJvWOgd7JaJpfI6envmC2jU 1 ABIZO0DK4OrdROD805of6iRk2RenonGYmo2qG2IB1sj/ 1 ACMUHQdjGN0wyCd2AgDHMk9u0TpnywNVtamHWopGho8L 0 ABNT5lbE4siC3QklQXRvTwSQPwtme91+UrIr9iXT3y84 1 ABib0mmQ9ZVCrErqFwDgoRp3jHPpjHGQR2vsMVlwM+vI 0 ABvf3cg1NSS8fn6EKJNnTomeoflcEY1WBxkPPKrBBFl+ 0 ACBUZAtolN4HNh+mw4jLZuHzD+/rYHKR5av16PUc6BJF 0", "2"))
|
formset = OptionFormset(request.POST, instance=poll, prefix="formset_options")
|
||||||
#return HttpResponse(tally("ACNQLLQlh+lNm1Dc+X+dEI0ECVLTkxRHjRnzX1OA+HtW 0 AAWOsUZK/G/cjhUee/gPAXop3Bc0CTVG3iDdQxD6+XqV 0", "ACNQLLQlh+lNm1Dc+X+dEI0ECVLTkxRHjRnzX1OA+HtW 0 0 2", "2"))
|
|
||||||
|
if formset.is_valid():
|
||||||
|
formset.save()
|
||||||
|
return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id]))
|
||||||
|
|
||||||
|
|
||||||
|
def event_vote(request, event_id, poll_num):
|
||||||
event = get_object_or_404(Event, pk=event_id)
|
event = get_object_or_404(Event, pk=event_id)
|
||||||
if (not event.prepared):
|
|
||||||
|
if not event.prepared:
|
||||||
messages.add_message(request, messages.WARNING, "This Event isn\'t ready for voting yet.")
|
messages.add_message(request, messages.WARNING, "This Event isn\'t ready for voting yet.")
|
||||||
return HttpResponseRedirect(reverse("user_home"))
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
|
|
||||||
event_poll_count = event.polls.all().count()
|
event_poll_count = event.polls.all().count()
|
||||||
prev_poll_index, next_poll_index = False, False
|
prev_poll_index, next_poll_index = False, False
|
||||||
can_vote, has_voted, voter_email, vote_count = False, False, "", 0
|
can_vote, has_voted, voter_email = False, False, ""
|
||||||
poll = util_get_poll_by_event_index(event, poll_num)
|
poll = util_get_poll_by_event_index(event, poll_num)
|
||||||
|
|
||||||
if (poll == None):
|
if poll is None:
|
||||||
raise Http404("Poll does not exist")
|
messages.add_message(request, messages.ERROR, "There was an error loading the voting page.")
|
||||||
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
|
|
||||||
form = VoteForm(instance=poll)
|
poll_num = int(poll_num) # now known to be safe as it succeeded in the util function
|
||||||
poll_num = int(poll_num) # now known to be safe as it suceeded in the util function
|
|
||||||
|
|
||||||
if (poll_num > 1):
|
if poll_num > 1:
|
||||||
prev_poll_index = (poll_num - 1)
|
prev_poll_index = (poll_num - 1)
|
||||||
if (poll_num < event_poll_count):
|
if poll_num < event_poll_count:
|
||||||
next_poll_index = (poll_num + 1)
|
next_poll_index = (poll_num + 1)
|
||||||
|
|
||||||
access_key = request.GET.get('key', None)
|
access_key = request.GET.get('key', None)
|
||||||
email_key = event.keys.filter(key=access_key)
|
email_key = event.keys.filter(key=access_key)
|
||||||
vote_count = Ballot.objects.filter(poll=poll, cast=True).count()
|
|
||||||
|
|
||||||
if (email_key.exists() and event.voters.filter(email=email_key[0].user.email).exists()):
|
ballot = None
|
||||||
ballot = Ballot.objects.filter(voter=email_key[0].user, poll=poll)
|
if email_key.exists() and event.voters.filter(email=email_key[0].user.email).exists():
|
||||||
if (ballot.exists() and ballot[0].cast):
|
# Passing this test means the user can vote
|
||||||
has_voted = True
|
|
||||||
|
|
||||||
if (access_key and email_key.exists()): #or (can_vote(request.user, event))
|
|
||||||
voter_email = email_key[0].user.email
|
voter_email = email_key[0].user.email
|
||||||
can_vote = True
|
can_vote = True
|
||||||
|
|
||||||
if (request.method == "POST"):
|
# Check whether this is the first time a user is voting
|
||||||
form = VoteForm(request.POST, instance=poll)
|
ballot = Ballot.objects.filter(voter=email_key[0].user, poll=poll)
|
||||||
if (email_key.exists()):
|
if ballot.exists() and ballot[0].cast:
|
||||||
#return HttpResponse(email_key[0].key)
|
has_voted = True
|
||||||
ballot = Ballot.objects.get_or_create(voter=email_key[0].user, poll=poll)[0]
|
|
||||||
|
|
||||||
if (form.is_valid()):
|
|
||||||
ballot.cipher_text_c1 = request.POST["cipher_text_c1"]
|
|
||||||
ballot.cipher_text_c2 = request.POST["cipher_text_c2"]
|
|
||||||
ballot.cast = True
|
|
||||||
ballot.save()
|
|
||||||
if (next_poll_index):
|
|
||||||
return HttpResponseRedirect(reverse('polls:view-poll', kwargs={'event_id': event.id, 'poll_num': next_poll_index }) + "?key=" + email_key[0].key)
|
|
||||||
else:
|
else:
|
||||||
return HttpResponse("Voted successfully!") # finished all polls in event
|
messages.add_message(request, messages.ERROR, "You don\'t have permission to vote in this event.")
|
||||||
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
|
|
||||||
return render(request, "polls/poll_detail.html",
|
if request.method == "POST":
|
||||||
{"object": poll, "poll_num": poll_num , "event": event, "form": form, "poll_count": event.polls.all().count(),
|
if ballot is None:
|
||||||
"prev_index": prev_poll_index , "next_index": next_poll_index,
|
ballot = Ballot.objects.get_or_create(voter=email_key[0].user, poll=poll)
|
||||||
"can_vote": can_vote, "voter_email": voter_email, "has_voted": has_voted, "vote_count": vote_count
|
|
||||||
|
# Will store the fragments of the encoding scheme that define the vote
|
||||||
|
encrypted_vote = EncryptedVote.objects.get_or_create(ballot=ballot[0])[0]
|
||||||
|
|
||||||
|
# Clear any existing fragments - a voter changing their vote
|
||||||
|
encrypted_vote.fragment.all().delete()
|
||||||
|
|
||||||
|
# Add in the new ciphers
|
||||||
|
fragment_count = int(request.POST['vote_frag_count'])
|
||||||
|
for i in range(fragment_count):
|
||||||
|
i_str = str(i)
|
||||||
|
|
||||||
|
cipher_c1 = request.POST['cipher_c1_frag_' + i_str]
|
||||||
|
cipher_c2 = request.POST['cipher_c2_frag_' + i_str]
|
||||||
|
|
||||||
|
encrypted_vote.fragment.create(encrypted_vote=encrypted_vote,
|
||||||
|
cipher_text_c1=cipher_c1,
|
||||||
|
cipher_text_c2=cipher_c2)
|
||||||
|
|
||||||
|
ballot[0].cast = True
|
||||||
|
ballot[0].save()
|
||||||
|
|
||||||
|
if next_poll_index:
|
||||||
|
return HttpResponseRedirect(reverse('polls:event-vote', kwargs={'event_id': event.id, 'poll_num': next_poll_index }) + "?key=" + email_key[0].key)
|
||||||
|
else:
|
||||||
|
# The user has finished voting in the event
|
||||||
|
success_msg = 'You have successfully cast your vote(s)!'
|
||||||
|
messages.add_message(request, messages.SUCCESS, success_msg)
|
||||||
|
|
||||||
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
|
|
||||||
|
return render(request, "polls/event_vote.html",
|
||||||
|
{
|
||||||
|
"object": poll, "poll_num": poll_num, "event": event, "poll_count": event.polls.all().count(),
|
||||||
|
"prev_index": prev_poll_index, "next_index": next_poll_index, "min_selection": poll.min_num_selections,
|
||||||
|
"max_selection": poll.max_num_selections, "can_vote": can_vote, "voter_email": voter_email,
|
||||||
|
"has_voted": has_voted
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def event_trustee_setup(request, event_id):
|
def event_trustee_setup(request, event_id):
|
||||||
# Obtain the event and the event preparation access key that's been supplied
|
# Obtain the event and the event preparation access key that's been supplied
|
||||||
event = get_object_or_404(Event, pk=event_id)
|
event = get_object_or_404(Event, pk=event_id)
|
||||||
|
@ -178,68 +207,82 @@ def event_trustee_setup(request, event_id):
|
||||||
# The event will now be ready to receive votes on the various polls that have been defined -
|
# The event will now be ready to receive votes on the various polls that have been defined -
|
||||||
# voters therefore need to be informed
|
# voters therefore need to be informed
|
||||||
if event.trustee_keys.count() == event.users_trustees.count():
|
if event.trustee_keys.count() == event.users_trustees.count():
|
||||||
|
create_ballots.delay(event)
|
||||||
generate_combpk.delay(event)
|
generate_combpk.delay(event)
|
||||||
# TODO: Create Celery task that generates voting URLs for voters as well as creates the ballots
|
email_voters_vote_url.delay(event.voters.all(), event)
|
||||||
|
|
||||||
success_msg = 'You have successfully submitted your public key for this event'
|
success_msg = 'You have successfully submitted your public key for this event!'
|
||||||
messages.add_message(request, messages.SUCCESS, success_msg)
|
messages.add_message(request, messages.SUCCESS, success_msg)
|
||||||
|
|
||||||
# This re-direct may not be appropriate for trustees that don't have logins
|
|
||||||
return HttpResponseRedirect(reverse("user_home"))
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
else:
|
else:
|
||||||
form = EventSetupForm()
|
form = EventSetupForm()
|
||||||
return render(request, "polls/event_setup.html", {"event": event, "form": form})
|
return render(request, "polls/event_setup.html", {"event": event, "form": form, "user_email": email_key[0].user.email})
|
||||||
|
|
||||||
#if no key or is invalid?
|
#if no key or is invalid?
|
||||||
messages.add_message(request, messages.WARNING, 'You do not have permission to access: ' + request.path)
|
messages.add_message(request, messages.WARNING, 'You do not have permission to access: ' + request.path)
|
||||||
return HttpResponseRedirect(reverse("user_home"))
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
|
|
||||||
def event_addec(request, event_id):
|
|
||||||
|
def event_end(request, event_id):
|
||||||
event = get_object_or_404(Event, pk=event_id)
|
event = get_object_or_404(Event, pk=event_id)
|
||||||
for poll in event.polls.all():
|
|
||||||
generate_enc.delay(poll)
|
if not event.ended:
|
||||||
return HttpResponse("Generating enc.")
|
event_ended.delay(event)
|
||||||
|
|
||||||
|
# Mark the event as ended
|
||||||
|
event.ended = True
|
||||||
|
event.save()
|
||||||
|
|
||||||
|
return HttpResponseRedirect(reverse('polls:view-event', args=[event_id]))
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a JSONed version of the results
|
||||||
|
def results(request, event_id):
|
||||||
|
event = get_object_or_404(Event, pk=event_id)
|
||||||
|
polls = event.polls.all()
|
||||||
|
|
||||||
|
results = ""
|
||||||
|
results += "{\"polls\":["
|
||||||
|
for poll in polls:
|
||||||
|
results += poll.enc
|
||||||
|
|
||||||
|
results += "]}"
|
||||||
|
|
||||||
|
return HttpResponse(results)
|
||||||
|
|
||||||
|
|
||||||
def event_trustee_decrypt(request, event_id):
|
def event_trustee_decrypt(request, event_id):
|
||||||
event = get_object_or_404(Event, pk=event_id)
|
event = get_object_or_404(Event, pk=event_id)
|
||||||
access_key = request.GET.get('key', None)
|
access_key = request.GET.get('key', None)
|
||||||
if (access_key):
|
|
||||||
email_key = event.keys.filter(key=access_key)
|
|
||||||
if (email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists()):
|
|
||||||
if (Decryption.objects.filter(event=event, user=email_key[0].user).exists()):
|
|
||||||
messages.add_message(request, messages.WARNING, 'You have already provided your decryptions for this event')
|
|
||||||
#if (event.decryptions.count() == (event.polls.count() * event.users_trustees.count())):
|
|
||||||
# tally_results.delay(event) # all keys are in
|
|
||||||
return HttpResponseRedirect(reverse("user_home"))
|
|
||||||
elif (request.method == "GET"):
|
|
||||||
initial = []
|
|
||||||
for poll in event.polls.all():
|
|
||||||
initial.append({'text': poll.enc })
|
|
||||||
formset = DecryptionFormset(initial=initial)
|
|
||||||
else:
|
|
||||||
formset = DecryptionFormset(request.POST)
|
|
||||||
data = []
|
|
||||||
for form in formset:
|
|
||||||
if form.is_valid():
|
|
||||||
data.append(form.cleaned_data.get('text'))
|
|
||||||
if (len(data) == event.polls.count()):
|
|
||||||
for dec, poll in zip(data, event.polls.all()):
|
|
||||||
Decryption.objects.get_or_create(user=email_key[0].user, event=event, poll=poll, text=dec)
|
|
||||||
messages.add_message(request, messages.SUCCESS, 'Decryption complete.')
|
|
||||||
if (event.decryptions.count() == (event.polls.count() * event.users_trustees.count())):
|
|
||||||
tally_results.delay(event) # all keys are in
|
|
||||||
else:
|
|
||||||
messages.add_message(request, messages.ERROR, 'You didn\'t provide decryptions for every poll. Please try again.')
|
|
||||||
return HttpResponseRedirect(reverse("user_home"))
|
|
||||||
return render(request, "polls/event_decrypt.html", {"event": event, "formset": formset, "helper": DecryptionFormSetHelper() })
|
|
||||||
|
|
||||||
|
if access_key:
|
||||||
|
email_key = event.keys.filter(key=access_key)
|
||||||
|
|
||||||
|
if email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists():
|
||||||
|
if TrusteeSK.objects.filter(event=event, trustee=email_key[0].user).exists():
|
||||||
|
messages.add_message(request, messages.WARNING, 'You have already provided your decryption key for this event')
|
||||||
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
|
elif request.method == "GET":
|
||||||
|
return render(request, "polls/event_decrypt.html", {"event": event, "user_email": email_key[0].user.email})
|
||||||
|
elif request.method == "POST":
|
||||||
|
sk = request.POST['secret-key']
|
||||||
|
|
||||||
|
TrusteeSK.objects.create(event=event,
|
||||||
|
trustee=email_key[0].user,
|
||||||
|
key=sk)
|
||||||
|
|
||||||
|
if event.trustee_sk.count() == event.users_trustees.count():
|
||||||
|
# Generate the event SK and decrypt the event to tally the results
|
||||||
|
gen_event_sk_and_dec.delay(event)
|
||||||
|
|
||||||
|
messages.add_message(request, messages.SUCCESS, 'Your secret key has been successfully submitted')
|
||||||
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
|
|
||||||
|
# Without an access key, the client does not have permission to access this page
|
||||||
messages.add_message(request, messages.WARNING, 'You do not have permission to decrypt this Event.')
|
messages.add_message(request, messages.WARNING, 'You do not have permission to decrypt this Event.')
|
||||||
return HttpResponseRedirect(reverse("user_home"))
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
|
|
||||||
def test_poll_vote(request, poll_id):
|
|
||||||
poll = get_object_or_404(Poll, pk=poll_id)
|
|
||||||
form = VoteForm(instance=poll)
|
|
||||||
return render(request, "polls/vote_poll.html", {"vote_form": form, "poll": poll})
|
|
||||||
|
|
||||||
def manage_questions(request, event_id):
|
def manage_questions(request, event_id):
|
||||||
|
|
||||||
|
@ -262,7 +305,7 @@ def manage_questions(request, event_id):
|
||||||
formset = OptionFormset(request.POST, prefix="formset_organiser", instance=poll)
|
formset = OptionFormset(request.POST, prefix="formset_organiser", instance=poll)
|
||||||
if formset.is_valid():
|
if formset.is_valid():
|
||||||
formset.save()
|
formset.save()
|
||||||
#create_ballots.delay(poll)
|
create_ballots_for_poll.delay(poll)
|
||||||
messages.add_message(request, messages.SUCCESS, 'Poll created successfully')
|
messages.add_message(request, messages.SUCCESS, 'Poll created successfully')
|
||||||
return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id]))
|
return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id]))
|
||||||
|
|
||||||
|
@ -274,6 +317,7 @@ def manage_questions(request, event_id):
|
||||||
else:
|
else:
|
||||||
return HttpResponseNotAllowed()
|
return HttpResponseNotAllowed()
|
||||||
|
|
||||||
|
|
||||||
def render_invalid(request, events, demo_users, invalid_fields):
|
def render_invalid(request, events, demo_users, invalid_fields):
|
||||||
return render(request,
|
return render(request,
|
||||||
"polls/create_event.html",
|
"polls/create_event.html",
|
||||||
|
@ -285,6 +329,7 @@ def render_invalid(request, events, demo_users, invalid_fields):
|
||||||
"invalid_fields": invalid_fields
|
"invalid_fields": invalid_fields
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def create_event(request):
|
def create_event(request):
|
||||||
# Obtain context data for the rendering of the html template and validation
|
# Obtain context data for the rendering of the html template and validation
|
||||||
events = Event.objects.all()
|
events = Event.objects.all()
|
||||||
|
@ -347,6 +392,7 @@ def create_event(request):
|
||||||
else:
|
else:
|
||||||
return HttpResponseNotAllowed()
|
return HttpResponseNotAllowed()
|
||||||
|
|
||||||
|
|
||||||
def edit_event(request, event_id):
|
def edit_event(request, event_id):
|
||||||
event = get_object_or_404(Event, pk=event_id)
|
event = get_object_or_404(Event, pk=event_id)
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
|
@ -384,7 +430,6 @@ def edit_event(request, event_id):
|
||||||
return render(request, "polls/generic_form.html", {"form_title": "Edit Event: " + event.title, "form": form}) #"organiser_formset": organiser_formset, "trustee_formset": trustee_formset})
|
return render(request, "polls/generic_form.html", {"form_title": "Edit Event: " + event.title, "form": form}) #"organiser_formset": organiser_formset, "trustee_formset": trustee_formset})
|
||||||
#trustee_formset = TrusteeFormSet(request.POST, prefix="formset_trustee", instance=event)
|
#trustee_formset = TrusteeFormSet(request.POST, prefix="formset_trustee", instance=event)
|
||||||
|
|
||||||
#class CreatePoll(generic.View):
|
|
||||||
|
|
||||||
def del_event(request, event_id):
|
def del_event(request, event_id):
|
||||||
event = get_object_or_404(Event, pk=event_id)
|
event = get_object_or_404(Event, pk=event_id)
|
||||||
|
@ -393,8 +438,3 @@ def del_event(request, event_id):
|
||||||
elif request.method == "POST":
|
elif request.method == "POST":
|
||||||
event.delete()
|
event.delete()
|
||||||
return HttpResponseRedirect(reverse('polls:index'))
|
return HttpResponseRedirect(reverse('polls:index'))
|
||||||
|
|
||||||
def can_vote(user, event):
|
|
||||||
if event.voters.filter(email=user.email).exists():
|
|
||||||
return True
|
|
||||||
return False
|
|
|
@ -14,7 +14,7 @@
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
{% bootstrap_messages %}
|
||||||
{% if socialaccount_providers %}
|
{% if socialaccount_providers %}
|
||||||
<div class="col-md-5 col-lg-5">
|
<div class="col-md-5 col-lg-5">
|
||||||
{% include "allauth/account/provider_panel.html" with process="login" %}
|
{% include "allauth/account/provider_panel.html" with process="login" %}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
<script src="{% static 'js/papaparse.min.js' %}" type="text/javascript"></script>
|
<script src="{% static 'js/papaparse.min.js' %}" type="text/javascript"></script>
|
||||||
<script src="{% static 'js/create-event-poll.js' %}" type="text/javascript"></script>
|
<script src="{% static 'js/create-event-poll.js' %}" type="text/javascript"></script>
|
||||||
|
<script src="{% static 'js/decrypt_event.js' %}" type="text/javascript"></script>
|
||||||
<script src="{% static 'js/encrypt.js' %}" type="text/javascript"></script>
|
<script src="{% static 'js/encrypt.js' %}" type="text/javascript"></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="{% static 'js/core/rand.js' %}"></script>
|
<script type="text/javascript" src="{% static 'js/core/rand.js' %}"></script>
|
||||||
|
@ -74,15 +75,19 @@
|
||||||
|
|
||||||
//new function
|
//new function
|
||||||
demosEncrypt.encryptAndSubmit = function() {
|
demosEncrypt.encryptAndSubmit = function() {
|
||||||
var ctx = new CTX("BN254CX"); //new context we can use
|
// Disable the enc and submit button to prevent fn from being called twice
|
||||||
|
$('#keygen-btn').prop("disabled", true);
|
||||||
|
|
||||||
|
// Elliptic curve cryptography params used for encryption of encrypted vote
|
||||||
|
// fragments
|
||||||
|
var ctx = new CTX("BN254CX");
|
||||||
var n = new ctx.BIG();
|
var n = new ctx.BIG();
|
||||||
var g1 = new ctx.ECP();
|
var g1 = new ctx.ECP();
|
||||||
var g2 = new ctx.ECP2();
|
var g2 = new ctx.ECP2();
|
||||||
|
|
||||||
var param = $('#event-param').val();
|
var parameter = $('#event-param').val();
|
||||||
//console.log(param);
|
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
|
||||||
|
|
||||||
var tempParams = JSON.parse(param);
|
|
||||||
//copying the values
|
//copying the values
|
||||||
n.copy(tempParams.n);
|
n.copy(tempParams.n);
|
||||||
g1.copy(tempParams.g1);
|
g1.copy(tempParams.g1);
|
||||||
|
@ -92,27 +97,56 @@
|
||||||
n:n,
|
n:n,
|
||||||
g1:g1,
|
g1:g1,
|
||||||
g2:g2
|
g2:g2
|
||||||
}
|
};
|
||||||
|
|
||||||
var tempPK = JSON.parse($('#comb_pk').val());
|
var tempPK = JSON.parse($('#comb_pk').val());
|
||||||
|
|
||||||
var pk = new ctx.ECP(0);
|
var pk = new ctx.ECP(0);
|
||||||
pk.copy(tempPK.PK);
|
pk.copy(tempPK.PK);
|
||||||
var answer = $('#poll-options').val();
|
|
||||||
console.log(answer);
|
|
||||||
var cipher = encrypt(params, pk, answer);
|
|
||||||
|
|
||||||
|
// Obtain the user's selection (their vote) and encrypt the fragments of the binary encoding
|
||||||
|
const selection = $('#poll-options').val();
|
||||||
|
const selectionFragments = selection.split(',');
|
||||||
|
|
||||||
|
var cipherForm = document.getElementById("cipher-form");
|
||||||
|
|
||||||
|
for(var i = 0; i < selectionFragments.length; i++) {
|
||||||
|
// Encrypt this fragment for the selection
|
||||||
|
var cipher = encrypt(params, pk, parseInt(selectionFragments[i]));
|
||||||
|
|
||||||
|
// Store C1 and C2 from the cipher in 2 arrays
|
||||||
var c1Bytes = [];
|
var c1Bytes = [];
|
||||||
cipher.C1.toBytes(c1Bytes);
|
cipher.C1.toBytes(c1Bytes);
|
||||||
|
|
||||||
var c2Bytes = [];
|
var c2Bytes = [];
|
||||||
cipher.C2.toBytes(c2Bytes);
|
cipher.C2.toBytes(c2Bytes);
|
||||||
|
|
||||||
$('#id_cipher_text_c1').val(c1Bytes.toString());
|
// Inject hidden input controls into the form that represents a single ballot
|
||||||
$('#id_cipher_text_c2').val(c2Bytes.toString());
|
var c1Input = document.createElement("input");
|
||||||
|
c1Input.setAttribute("type", "hidden");
|
||||||
|
c1Input.setAttribute("name", "cipher_c1_frag_" + i);
|
||||||
|
c1Input.setAttribute("value", c1Bytes.toString());
|
||||||
|
|
||||||
$('#cipher-form').submit();
|
var c2Input = document.createElement("input");
|
||||||
|
c2Input.setAttribute("type", "hidden");
|
||||||
|
c2Input.setAttribute("name", "cipher_c2_frag_" + i);
|
||||||
|
c2Input.setAttribute("value", c2Bytes.toString());
|
||||||
|
|
||||||
|
cipherForm.appendChild(c1Input);
|
||||||
|
cipherForm.appendChild(c2Input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inject a final input control into the form which specifies the number of fragments
|
||||||
|
// That make up an encrypted vote
|
||||||
|
var fragCountInput = document.createElement("input");
|
||||||
|
fragCountInput.setAttribute("type", "hidden");
|
||||||
|
fragCountInput.setAttribute("name", "vote_frag_count");
|
||||||
|
fragCountInput.setAttribute("value", "" + selectionFragments.length);
|
||||||
|
cipherForm.appendChild(fragCountInput);
|
||||||
|
|
||||||
|
// Submit the encrypted vote to the server
|
||||||
|
$('#cipher-form').submit();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
//new function
|
//new function
|
||||||
demosEncrypt.decryptCipher = function() {
|
demosEncrypt.decryptCipher = function() {
|
||||||
|
@ -149,7 +183,7 @@
|
||||||
|
|
||||||
//new function
|
//new function
|
||||||
demosEncrypt.generateKeys = function() {
|
demosEncrypt.generateKeys = function() {
|
||||||
parameter = $("#event-param").val();
|
var parameter = $("#event-param").val();
|
||||||
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
|
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
|
||||||
//the full objects need to be initalised as per the library, then copy the values we need into it
|
//the full objects need to be initalised as per the library, then copy the values we need into it
|
||||||
//I follow Bingsheng's code as to what objects are used in the parameter object
|
//I follow Bingsheng's code as to what objects are used in the parameter object
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<title>{% block title %}dẽmos 2{% endblock %}</title>
|
<title>{% block title %}DĒMOS 2{% endblock %}</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
|
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
|
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
|
||||||
|
|
|
@ -7,23 +7,23 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Event: {{event.title}}</h1>
|
<h2>Trustee Event Decryption for Event '{{ event.title }}'</h2>
|
||||||
<h2>Trustee Decrypt</h2>
|
<hr/>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Secret Key</div>
|
<div class="panel-heading"><strong>Submit your Secret Key as '{{ user_email }}'</strong></div>
|
||||||
<div class="panel panel-body">
|
<div class="panel panel-body">
|
||||||
<input id="secret-key" class="textinput textInput form-control" type="text"></input>
|
<form id="sk-form" method="POST">
|
||||||
<p>Use your secret key to generate a decrypted cipher</p>
|
{% csrf_token %}
|
||||||
<button id="keygen-btn" onclick="demosEncrypt.decryptCipher()" class="btn btn-default">Decrypt</button>
|
<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;">
|
||||||
|
Your secret key will be used to decrypt the event and get a vote tally for every poll.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<label for="files_sk_upload" class="btn btn-primary">
|
||||||
<div class="panel panel-default">
|
<span class="glyphicon glyphicon-cloud-upload"></span>
|
||||||
<div class="panel-heading">Encrypted Ciphers</div>
|
Upload Key
|
||||||
<div class="panel panel-body">
|
</label>
|
||||||
{% load crispy_forms_tags %}
|
<input type="file" id="files_sk_upload" name="file" class="btn-info">
|
||||||
<form method="post" action="" class="">
|
<input type="submit" value="Submit" class="btn btn-success"/>
|
||||||
{% crispy formset helper %}
|
|
||||||
<input class="btn btn-default" type="submit" value="Submit" disabled>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,12 +9,22 @@
|
||||||
{% if is_organiser %}
|
{% if is_organiser %}
|
||||||
<div>
|
<div>
|
||||||
<!-- Heading -->
|
<!-- Heading -->
|
||||||
<div class="col-xs-7 col-sm-9 col-md-10">
|
<div class="col-xs-7 col-sm-9 col-md-9">
|
||||||
<h2>Event: {{object.title}}</h2>
|
<h2>Event: {{object.title}}</h2>
|
||||||
</div>
|
</div>
|
||||||
<!-- Edit Button -->
|
<!-- Edit Button -->
|
||||||
<div class="col-xs-5 col-sm-3 col-md-2 marginTopEditButton">
|
<div class="col-xs-5 col-sm-3 col-md-3 marginTopEditButton">
|
||||||
<a href="{% url 'polls:edit-event' event.id %}" class="btn btn-primary" style="float: right;">
|
{% if object.has_received_votes and object.ended == False %}
|
||||||
|
<a href="{% url 'polls:end-event' event.id %}" 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;">
|
||||||
|
<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;">
|
||||||
<span class="fa fa-pencil"></span> Edit
|
<span class="fa fa-pencil"></span> Edit
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,11 +50,11 @@
|
||||||
<a href="{% url 'polls:event-polls' event.id %}"><strong>Polls ({{ object.polls.count }})</strong></a>
|
<a href="{% url 'polls:event-polls' event.id %}"><strong>Polls ({{ object.polls.count }})</strong></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="{% block event_nav_organisers %}{% endblock %}">
|
<li class="{% block event_nav_organisers %}{% endblock %}">
|
||||||
<a href="{% url 'polls:event-organisers' event.id %}"><strong>Entities</strong></a>
|
<a href="{% url 'polls:event-entities' event.id %}"><strong>Entities</strong></a>
|
||||||
</li>
|
</li>
|
||||||
{% if is_organiser %}
|
{% if is_organiser %}
|
||||||
<li class="{% block event_nav_launch %}{% endblock %}">
|
<li class="{% block event_nav_launch %}{% endblock %}">
|
||||||
<a href="{% url 'polls:launch-event' event.id %}"><strong>Advanced</strong></a>
|
<a href="{% url 'polls:event-advanced' event.id %}"><strong>Advanced</strong></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
{% block event_content %}
|
{% block event_content %}
|
||||||
{% if object.polls.all %}
|
{% if object.polls.all %}
|
||||||
{% for poll in object.polls.all %}
|
{% for poll in object.polls.all %}
|
||||||
<h3>Poll: {{ poll.question_text }} (<a href="{% url 'polls:view-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.id poll_num=forloop.counter %}">Edit</a>)</h3>
|
||||||
<br/>
|
<br/>
|
||||||
<h4>Poll Options:</h4>
|
<h4>Poll Options:</h4>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
|
|
|
@ -46,9 +46,13 @@
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<div class="btn statusBtn
|
<div class="btn statusBtn
|
||||||
{% if event.status == 'Expired' %}btn-danger{% endif %}
|
{% if event.status == 'Future' %}btn-info{% endif %}
|
||||||
|
{% if event.status == 'Prepared' %}btn-info{% endif %}
|
||||||
{% if event.status == 'Active' %}btn-success{% endif %}
|
{% if event.status == 'Active' %}btn-success{% endif %}
|
||||||
{% if event.status == 'Future' %}btn-info{% endif %}">
|
{% if event.status == 'Expired' %}btn-danger{% endif %}
|
||||||
|
{% if event.status == 'Ended' %}btn-danger{% endif %}
|
||||||
|
{% if event.status == 'Decrypted' %}btn-primary{% endif %}
|
||||||
|
">
|
||||||
{{ event.status }}
|
{{ event.status }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>Trustee Event Setup for Event '{{ event.title }}'</h2>
|
<h2>Trustee Event Setup for Event '{{ event.title }}'</h2>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
<h4>Key Generation For: {{ user_email }}</h4>
|
||||||
|
<br/>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><strong>Step 1: Generate Your Secret Key</strong></div>
|
<div class="panel-heading"><strong>Step 1: Generate Your Secret Key</strong></div>
|
||||||
<div class="panel panel-body">
|
<div class="panel panel-body">
|
||||||
|
|
81
allauthdemo/templates/polls/event_vote.html
Executable file
81
allauthdemo/templates/polls/event_vote.html
Executable file
|
@ -0,0 +1,81 @@
|
||||||
|
{% extends "bases/bootstrap-with-nav.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block app_js_vars %}
|
||||||
|
var option_count = {{ object.options.count }};
|
||||||
|
{% 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/>
|
||||||
|
|
||||||
|
<!-- 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 %}
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
{% endblock %}
|
|
@ -1,85 +0,0 @@
|
||||||
{% extends "bases/bootstrap-with-nav.html" %}
|
|
||||||
{% load staticfiles %}
|
|
||||||
{% load bootstrap3 %}
|
|
||||||
|
|
||||||
{% block app_js_vars %}
|
|
||||||
|
|
||||||
|
|
||||||
var option_count = {{ object.options.count }};
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<input id="event-param" type="text" value="{{event.EID}}" hidden></input>
|
|
||||||
<input id="comb_pk" type="text" value="{{event.public_key}}" hidden></input>
|
|
||||||
|
|
||||||
<h1>Poll: {{object.question_text}}</h1>
|
|
||||||
<span>Poll {{ poll_num }} of {{ poll_count }} in Event: <a href="{% url 'polls:view-event' object.event.id %}">{{ object.event.title }}</a></span>
|
|
||||||
<div class="panel panel-body">
|
|
||||||
{% if prev_index %}
|
|
||||||
<a href="{% url 'polls:view-poll' 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:view-poll' 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>
|
|
||||||
<a href="{% url 'polls:edit-poll' event_id=object.event.id poll_num=poll_num %}"><span class="fa fa-pencil"></span> Edit Poll</a>
|
|
||||||
{% if object.options.all %}
|
|
||||||
<h3>Options</h3>
|
|
||||||
<p> {{ vote_count }} vote(s) have been cast</p>
|
|
||||||
{% if can_vote %}
|
|
||||||
{% if has_voted %}
|
|
||||||
<p>You have already voted in this poll. Resubmitting the form will change your vote.</p>
|
|
||||||
{% endif %}
|
|
||||||
<p>Voting as {{ voter_email }} -- Do NOT share this url</p>
|
|
||||||
{% load crispy_forms_tags %}
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Options</div>
|
|
||||||
<div class="panel panel-body">
|
|
||||||
<select class="radio-inline select form-control" id="poll-options" name="options">
|
|
||||||
{% load custom_filters_tags %}
|
|
||||||
{% for option in object.options.all %}
|
|
||||||
<option value="{{forloop.counter|get_ballot_value}}">{{ option.choice_text }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<button id="keygen-btn" onclick="demosEncrypt.encryptAndSubmit()" class="btn btn-default">Encrypt & Submit</button>
|
|
||||||
<form id="cipher-form" method="post" action="" class="">
|
|
||||||
{% crispy form %}
|
|
||||||
{% csrf_token %}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="alert alert-warning" role="alert">
|
|
||||||
<p>You do not have permission to vote in this Event.</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<p>No options are available.</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
POLL ENC {{ object.enc }}
|
|
||||||
|
|
||||||
{% if form.errors %}
|
|
||||||
{% for field in form %}
|
|
||||||
{% for error in field.errors %}
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
<strong>{{ error|escape }}</strong>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
{% for error in form.non_field_errors %}
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
<strong>{{ error|escape }}</strong>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
|
@ -157,7 +157,7 @@ input[type="file"] {
|
||||||
/* Events List page / Events Detail */
|
/* Events List page / Events Detail */
|
||||||
|
|
||||||
.statusBtn {
|
.statusBtn {
|
||||||
width: 74px;
|
width: 89px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.marginTopEventList {
|
.marginTopEventList {
|
||||||
|
|
20
static/js/decrypt_event.js
Normal file
20
static/js/decrypt_event.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
function processFileSKChange(event) {
|
||||||
|
var files = event.target.files;
|
||||||
|
|
||||||
|
if(files !== undefined
|
||||||
|
&& files[0] !== undefined) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = function(e) {
|
||||||
|
$('input#secret-key').val(reader.result);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsText(files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var filesHandleSK = document.getElementById('files_sk_upload');
|
||||||
|
|
||||||
|
if(filesHandleSK) {
|
||||||
|
filesHandleSK.addEventListener('change', processFileSKChange, false);
|
||||||
|
}
|
Reference in a new issue