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
208
Node/index.js
208
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 express = require('express');
|
||||
var Buffer = require('buffer').Buffer;
|
||||
var CTX = require('milagro-crypto-js')
|
||||
var app = express();
|
||||
/*
|
||||
var cors = require('cors')
|
||||
app.use(cors());
|
||||
*/
|
||||
var CTX = require('milagro-crypto-js');
|
||||
|
||||
var express = require('express');
|
||||
var bodyParser = require("body-parser");
|
||||
var app = express();
|
||||
|
||||
// Express server configuration
|
||||
app.use(express.static('test'));
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(bodyParser.json());
|
||||
|
||||
//default test
|
||||
app.get('/', function(request, response){
|
||||
|
||||
var data = {
|
||||
message: 'hello world',
|
||||
value: 5
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//response.send('Hey there'+request.ip);
|
||||
|
@ -40,13 +43,13 @@ app.get('/param', function(request, response){
|
|||
console.log('Generated Param:' + param);
|
||||
response.json(param);
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
//combine public keys and return the full combined one - JSON Version
|
||||
app.get('/combpk', function(request, response){
|
||||
|
||||
console.log('\nEndpoint /combpk called');
|
||||
|
||||
var partials = request.query['PK']
|
||||
var partials = request.query['PK'];
|
||||
|
||||
var parsed = [];
|
||||
|
||||
|
@ -57,45 +60,40 @@ app.get('/combpk', function(request, response){
|
|||
parsed.push(JSON.parse(partials[i]));
|
||||
}
|
||||
|
||||
var PK = combine(parsed);
|
||||
var PK = combine_pks(parsed);
|
||||
response.json(PK);
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
//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 partials = request.query['PK']
|
||||
//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 partials = request.body.PKs;
|
||||
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--) {
|
||||
console.log('PK' +i+ ': '+partials[i]);
|
||||
var bytes = Buffer.from(partials[i].split(','), 'hex');
|
||||
console.log(bytes)
|
||||
var pk = new ctx.ECP.fromBytes(bytes);
|
||||
parsed.push(pk);
|
||||
console.log('PK' + i + ': ' + partials[i]);
|
||||
var bytes = Buffer.from(partials[i].split(','), 'hex');
|
||||
var pk = new ctx.ECP.fromBytes(bytes);
|
||||
parsed.push(pk);
|
||||
}
|
||||
}
|
||||
else if(noOfKeys == 1)
|
||||
else if(partials.length === 1)
|
||||
{
|
||||
console.log("Combining just one key");
|
||||
var bytes = Buffer.from(partials.split(','), 'hex');
|
||||
console.log(bytes);
|
||||
console.log("Combining just one public key...");
|
||||
var bytes = Buffer.from(partials[0].split(','), 'hex');
|
||||
var pk = new ctx.ECP.fromBytes(bytes);
|
||||
parsed.push(pk);
|
||||
}
|
||||
|
||||
response.json(combine(parsed));
|
||||
})
|
||||
response.json(combine_pks(parsed));
|
||||
});
|
||||
|
||||
|
||||
//addition function on homomorphically encrypted variables
|
||||
|
@ -155,20 +153,19 @@ app.get('/addec', function(request, response){
|
|||
|
||||
|
||||
response.json(add(parsed));
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
|
||||
//tally partially decrypted ciphertexts
|
||||
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 paramString = request.query['param'];//event group parameter in JSON
|
||||
var partialsStrings = request.query['decs'];//array of partial decryption(s) in bytes
|
||||
var ciphertextString = request.query['cipher'];//ciphertext being decrypted in JSON
|
||||
|
||||
//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 n = new ctx.BIG();
|
||||
var g1 = new ctx.ECP();
|
||||
|
@ -183,47 +180,117 @@ app.get('/tally', function(request, response){
|
|||
n:n,
|
||||
g1:g1,
|
||||
g2:g2
|
||||
}
|
||||
};
|
||||
|
||||
//re-build partial decryptions
|
||||
var partials = []
|
||||
var partials = [];
|
||||
if(amount == partialsStrings.length)
|
||||
{
|
||||
console.log(amount + " partial decryptions");
|
||||
for(var i = 0; i < partialsStrings.length; i++)
|
||||
{
|
||||
var bytes = Buffer.from(partialsStrings[i].split(','), 'hex');
|
||||
|
||||
var dec = {
|
||||
D:new ctx.ECP.fromBytes(bytes)
|
||||
}
|
||||
};
|
||||
|
||||
partials.push(dec);
|
||||
}
|
||||
}
|
||||
else if(amount == 1)
|
||||
{
|
||||
console.log("Only one partial decryption received")
|
||||
console.log(paramString)
|
||||
console.log("\nOnly one partial decryption received\n");
|
||||
console.log(JSON.parse(paramString).crypto + "\n");
|
||||
|
||||
var bytes = Buffer.from(partialsStrings.split(','), 'hex');
|
||||
var dec = {
|
||||
D:new ctx.ECP.fromBytes(bytes)
|
||||
}
|
||||
D : new ctx.ECP.fromBytes(bytes)
|
||||
};
|
||||
|
||||
partials.push(dec);
|
||||
}
|
||||
|
||||
//re-build combined ciphertext
|
||||
var tempCipher = JSON.parse(ciphertextString);
|
||||
|
||||
cipher = {
|
||||
var cipher = {
|
||||
C1: new ctx.ECP(),
|
||||
C2: new ctx.ECP()
|
||||
}
|
||||
};
|
||||
|
||||
cipher.C1.copy(tempCipher.C1);
|
||||
cipher.C2.copy(tempCipher.C2);
|
||||
|
||||
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 host = server.address().address;
|
||||
|
@ -277,7 +344,7 @@ gpGen = function(){
|
|||
g1:P,
|
||||
g2:Q
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//creates ElGamal public and secret key
|
||||
|
@ -304,12 +371,12 @@ keyGen=function(params){
|
|||
PK:pk,
|
||||
SK:sk
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//combine multiple public key together
|
||||
//the input is an array of PKs
|
||||
combine=function(PKs){
|
||||
combine_pks=function(PKs){
|
||||
var ctx = new CTX("BN254CX");
|
||||
var pk=new ctx.ECP();
|
||||
//copy the first pk
|
||||
|
@ -319,11 +386,25 @@ combine=function(PKs){
|
|||
pk.add(PKs[i]);
|
||||
}
|
||||
|
||||
return{
|
||||
PK:pk
|
||||
return {
|
||||
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
|
||||
encrypt=function(params,PK, m){
|
||||
|
@ -356,7 +437,7 @@ encrypt=function(params,PK, m){
|
|||
C1:C1,
|
||||
C2:C2
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//add ciphertexts
|
||||
|
@ -380,7 +461,7 @@ add=function(Ciphers){
|
|||
C1:s1,
|
||||
C2:s2
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//ElGamal decryption
|
||||
|
@ -410,9 +491,7 @@ decrypt=function(params,SK, C){
|
|||
return{
|
||||
M: "Error"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
//ElGamal partial decryption
|
||||
|
@ -424,8 +503,7 @@ partDec=function(SK, C){
|
|||
return{
|
||||
D: D
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -446,24 +524,24 @@ tally=function(params,Ds, C){
|
|||
gM.copy(C.C2);
|
||||
gM.sub(D);
|
||||
|
||||
//search for message by brute force
|
||||
//search for message by brute force
|
||||
var B;
|
||||
for (j = 0; j < 1000; j++) {
|
||||
for (var j = 0; j < 1000; j++) {
|
||||
//use D as temp var
|
||||
B = new ctx.BIG(j);
|
||||
D = ctx.PAIR.G1mul(params.g1,B);
|
||||
if (D.equals(gM))
|
||||
return{
|
||||
M:j
|
||||
M: j
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return{
|
||||
M: "Error"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -14,9 +14,10 @@
|
|||
"url": "https://github.com/vincentmdealmeida/DEMOS2"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Bingsheng Zang, Thomas Smith",
|
||||
"author": "Bingsheng Zang, Thomas Smith, Vincent de Almeida",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.3",
|
||||
"express": "^4.16.3",
|
||||
"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 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:
|
||||
-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():
|
||||
url = 'http://localhost:8080/param' # RPC URL
|
||||
url = 'http://localhost:8080/param'
|
||||
jsondict = json.load(urllib2.urlopen(url))
|
||||
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)
|
||||
jsondict = json.load(urllib2.urlopen(url+querystring))
|
||||
print(json.dumps(jsondict))
|
||||
return json.dumps(jsondict)
|
||||
def combpk(pks):
|
||||
url = 'http://localhost:8080/cmpkstring'
|
||||
|
||||
data = {}
|
||||
data['PKs'] = pks
|
||||
|
||||
return send_post_req(url, data)
|
||||
|
||||
|
||||
def addec(amount, ciphers):
|
||||
url = 'http://localhost:8080/addec' # RPC URL
|
||||
url = 'http://localhost:8080/addec'
|
||||
querystring = '?number='+str(amount)
|
||||
c1s = ciphers['c1s']
|
||||
c2s = ciphers['c2s']
|
||||
|
@ -42,23 +53,44 @@ def addec(amount, ciphers):
|
|||
print(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)
|
||||
testquerystring += '¶m='+str(param)
|
||||
# Deprecated functionality and has been superseded by get_tally
|
||||
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):
|
||||
querystring += "&decs="+str(value)
|
||||
testquerystring += "&decs="+str(value)
|
||||
|
||||
querystring += '&cipher=' + urllib2.quote(str(cipher))
|
||||
testquerystring += '&cipher=' + str(cipher)
|
||||
|
||||
print(url+querystring)
|
||||
print(url+testquerystring)
|
||||
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()
|
||||
end_time = models.DateTimeField()
|
||||
prepared = models.BooleanField(default=False)
|
||||
ended = models.BooleanField(default=False)
|
||||
public_key = models.CharField(null=True, blank=False, max_length=1024)
|
||||
title = models.CharField(max_length=1024)
|
||||
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)
|
||||
trustees = models.CharField(max_length=4096)
|
||||
|
||||
# Custom helper methods
|
||||
def EID_hr(self):
|
||||
EID_json = json.loads(self.EID)
|
||||
return EID_json['hr']
|
||||
|
@ -68,15 +70,36 @@ class Event(models.Model):
|
|||
# future event
|
||||
present = timezone.now()
|
||||
|
||||
if present >= self.start_time and present <= self.end_time:
|
||||
status_str = "Active"
|
||||
elif present > self.end_time:
|
||||
status_str = "Expired"
|
||||
elif present < self.start_time:
|
||||
status_str = "Future"
|
||||
if self.ended is False:
|
||||
if present < self.start_time and self.public_key is None:
|
||||
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
|
||||
|
||||
'''
|
||||
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):
|
||||
return self.title
|
||||
|
||||
|
@ -84,12 +107,12 @@ class Event(models.Model):
|
|||
class TrusteeKey(models.Model):
|
||||
event = models.ForeignKey(Event, 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):
|
||||
event = models.ForeignKey(Event, 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)
|
||||
|
||||
|
@ -115,20 +138,6 @@ class Poll(models.Model):
|
|||
def __str__(self):
|
||||
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):
|
||||
choice_text = models.CharField(max_length=200)
|
||||
votes = models.IntegerField(default=0)
|
||||
|
@ -138,6 +147,35 @@ class PollOption(models.Model):
|
|||
def __str__(self):
|
||||
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):
|
||||
index = models.IntegerField(default=0)
|
||||
email = models.CharField(max_length=100, blank=False, null=False)
|
||||
|
|
|
@ -5,14 +5,11 @@ import json
|
|||
from os import urandom
|
||||
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 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
|
||||
|
@ -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
|
||||
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():
|
||||
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
|
||||
'''
|
||||
|
@ -64,19 +79,31 @@ def email_trustees_prep(trustees, event):
|
|||
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()
|
||||
def email_voters_a_key(voters, event):
|
||||
def email_voters_vote_url(voters, event):
|
||||
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:
|
||||
# Generate a key and create an AccessKey object
|
||||
key = gen_access_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())
|
||||
|
@ -93,6 +120,83 @@ def update_EID(event):
|
|||
event.EID = json.dumps(EID)
|
||||
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()
|
||||
def tally_results(event):
|
||||
for poll in event.polls.all():
|
||||
|
@ -101,39 +205,42 @@ def tally_results(event):
|
|||
decs.append(dec.text)
|
||||
amount = len(decs)
|
||||
result = tally(amount, event.EID, decs, poll.enc)
|
||||
send_mail(
|
||||
'Your Results:',
|
||||
poll.question_text + ": " + result,
|
||||
'from@example.com',
|
||||
["fake@fake.com"],
|
||||
fail_silently=False,
|
||||
)
|
||||
|
||||
# TODO: Email organisers using email_user method?
|
||||
|
||||
print(poll.question_text + ": " + result)
|
||||
|
||||
@task()
|
||||
def generate_combpk(event):
|
||||
pks = list()
|
||||
|
||||
for tkey in event.trustee_keys.all():
|
||||
pks.append(str(tkey.key))
|
||||
amount = len(pks)
|
||||
event.public_key = combpk(amount, pks)
|
||||
|
||||
event.public_key = combpk(pks)
|
||||
|
||||
event.prepared = True
|
||||
event.save()
|
||||
|
||||
@task
|
||||
def generate_enc(poll):
|
||||
c1s = list()#c1 components of ciphertexts
|
||||
c2s = list()#c1 components of ciphertexts
|
||||
# c1 and c2 components of ciphertexts
|
||||
c1s = list()
|
||||
c2s = list()
|
||||
|
||||
for ballot in poll.ballots.all():
|
||||
if (ballot.cast):
|
||||
if ballot.cast:
|
||||
c1s.append(str(ballot.cipher_text_c1))
|
||||
c2s.append(str(ballot.cipher_text_c2))
|
||||
|
||||
ciphers = {
|
||||
'c1s':c1s,
|
||||
'c2s':c2s
|
||||
'c1s': c1s,
|
||||
'c2s': c2s
|
||||
}
|
||||
amount = len(c1s)
|
||||
poll.enc = addec(amount, ciphers)
|
||||
|
||||
count = len(c1s)
|
||||
|
||||
poll.enc = addec(count, ciphers)
|
||||
poll.save()
|
||||
|
||||
|
||||
|
|
|
@ -5,5 +5,17 @@ register = template.Library()
|
|||
#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
|
||||
@register.filter
|
||||
def get_ballot_value(value):
|
||||
return pow(10, value-1)
|
||||
def get_ballot_value(option_no, options_count):
|
||||
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.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
from . import views
|
||||
|
||||
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 = [
|
||||
url(r'^vote/(?P<poll_id>[0-9]+)/$', views.test_poll_vote, name='vote-poll'),
|
||||
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'^$', login_required(views.EventListView.as_view()), name='index'),
|
||||
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<event_id>[0-9]+)/prepare/$', login_required(views.event_trustee_setup), name='prepare-event'),
|
||||
url(r'^(?P<event_id>[0-9]+)/encrypt/$', login_required(views.event_addec), name='enc-event'),
|
||||
url(r'^(?P<pk>[0-9]+)/launch/$', views.EventDetailLaunchView.as_view(), name='launch-event'),
|
||||
url(r'^edit/(?P<event_id>[0-9]+)/$', login_required(views.edit_event), name='edit-event'),
|
||||
url(r'^delete/(?P<event_id>[0-9]+)/$', login_required(views.del_event), name='del-event'),
|
||||
url(r'^(?P<pk>[0-9]+)/$', login_required(views.EventDetailView.as_view()), name='view-event'),
|
||||
url(r'^(?P<pk>[0-9]+)/polls/$', login_required(views.EventDetailPollsView.as_view()), name='event-polls'),
|
||||
url(r'^(?P<pk>[0-9]+)/entities/$', login_required(views.EventDetailEntitiesView.as_view()), name='event-entities'),
|
||||
url(r'^(?P<pk>[0-9]+)/advanced/$', login_required(views.EventDetailAdvancedView.as_view()), name='event-advanced'),
|
||||
url(r'^(?P<event_id>[0-9]+)/end/$', login_required(views.event_end), name='end-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]+)/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<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'),
|
||||
url(r'^(?P<event_id>[0-9]+)/poll/(?P<poll_num>[0-9]+)/edit$', login_required(views.edit_poll), name='edit-poll')
|
||||
]
|
||||
|
|
|
@ -539,12 +539,9 @@ class EventModelAdaptor:
|
|||
# Extract the list of trustees
|
||||
trustees_list = self.form_data.pop('trustee-email-input')
|
||||
|
||||
for trustee in trustees_list:
|
||||
if trustee != '':
|
||||
if EmailUser.objects.filter(email=trustee).exists():
|
||||
self.trustees.append(EmailUser.objects.filter(email=trustee).get())
|
||||
else:
|
||||
self.trustees.append(EmailUser(email=trustee))
|
||||
for trustee_email in trustees_list:
|
||||
if trustee_email != '':
|
||||
self.trustees.append(trustee_email)
|
||||
|
||||
# Extract the email list of voters
|
||||
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:
|
||||
if voter_email != '':
|
||||
if EmailUser.objects.filter(email=voter_email).exists():
|
||||
self.voters.append(EmailUser.objects.filter(email=voter_email).get())
|
||||
else:
|
||||
self.voters.append(EmailUser(email=voter_email))
|
||||
self.voters.append(voter_email)
|
||||
|
||||
# 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,
|
||||
end_time=self.ends_at,
|
||||
title=self.event_name,
|
||||
EID=self.identifier,
|
||||
creator=self.user.first_name + ' ' + self.user.last_name,
|
||||
creator=creator,
|
||||
c_email=self.user.email,
|
||||
trustees=voters_csv_string)
|
||||
|
||||
|
@ -629,20 +630,21 @@ class EventModelAdaptor:
|
|||
# so it can just be added
|
||||
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:
|
||||
if not EmailUser.objects.filter(email=trustee.email).exists():
|
||||
trustee.save()
|
||||
user, created = EmailUser.objects.get_or_create(email=trustee)
|
||||
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
|
||||
# Additionally, generating the AccessKey for voters
|
||||
# Add the list of voters to the event
|
||||
db_voters = list()
|
||||
for voter in self.voters:
|
||||
if not EmailUser.objects.filter(email=voter.email).exists():
|
||||
voter.save()
|
||||
user, created = EmailUser.objects.get_or_create(email=voter)
|
||||
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
|
||||
# This can only be done at this point as the event has been persisted
|
||||
|
|
|
@ -2,25 +2,23 @@ import urllib
|
|||
import urllib2
|
||||
import json
|
||||
|
||||
from io import StringIO
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponseRedirect, HttpResponse, Http404
|
||||
from django.http.response import HttpResponseNotAllowed
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import get_object_or_404, render, render_to_response
|
||||
from django.utils import timezone
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.views import generic
|
||||
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 .models import Event, Poll, PollOption, EmailUser, Ballot, TrusteeKey, Decryption
|
||||
from .forms import PollForm, OptionFormset, VoteForm, EventSetupForm, EventEditForm
|
||||
from .models import Event, Poll, Ballot, EncryptedVote, TrusteeKey, TrusteeSK
|
||||
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
|
||||
|
||||
|
||||
class EventListView(generic.ListView):
|
||||
|
||||
model = Event
|
||||
|
@ -30,6 +28,7 @@ class EventListView(generic.ListView):
|
|||
#context['now'] = timezone.now()
|
||||
return context
|
||||
|
||||
|
||||
class EventDetailView(generic.DetailView):
|
||||
template_name="polls/event_detail_details.html"
|
||||
model = Event
|
||||
|
@ -37,38 +36,32 @@ class EventDetailView(generic.DetailView):
|
|||
def get_context_data(self, **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['decrypted'] = self.object.status() == "Decrypted"
|
||||
|
||||
#context['now'] = timezone.now()
|
||||
return context
|
||||
|
||||
|
||||
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):
|
||||
template_name="polls/event_detail_launch.html"
|
||||
class EventDetailEntitiesView(EventDetailView):
|
||||
template_name = "polls/event_detail_entities.html"
|
||||
|
||||
|
||||
class EventDetailAdvancedView(EventDetailView):
|
||||
template_name = "polls/event_detail_advanced.html"
|
||||
|
||||
|
||||
class PollDetailView(generic.View):
|
||||
|
||||
model = Poll
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(PollDetailView, self).get_context_data(**kwargs)
|
||||
#context['now'] = timezone.now()
|
||||
context['form'] = VoteForm(instance=self.object)
|
||||
context['poll_count'] = self.object.event.polls.all().count()
|
||||
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):
|
||||
try:
|
||||
|
@ -80,78 +73,114 @@ def util_get_poll_by_event_index(event, poll_num):
|
|||
return None
|
||||
return poll
|
||||
|
||||
|
||||
def edit_poll(request, event_id, poll_num):
|
||||
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)
|
||||
|
||||
if (poll == None):
|
||||
raise Http404("Poll does not exist")
|
||||
|
||||
form = PollForm(instance=poll, prefix="main")
|
||||
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})
|
||||
if request.method == 'GET':
|
||||
form = PollForm(instance=poll, prefix="main")
|
||||
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})
|
||||
elif request.method == 'POST':
|
||||
form = PollForm(request.POST, instance=poll, prefix="main")
|
||||
|
||||
def view_poll(request, event_id, poll_num):
|
||||
#return HttpResponse(param("012345"))
|
||||
#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"))
|
||||
#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 form.is_valid():
|
||||
form.save()
|
||||
|
||||
formset = OptionFormset(request.POST, instance=poll, prefix="formset_options")
|
||||
|
||||
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)
|
||||
if (not event.prepared):
|
||||
|
||||
if not event.prepared:
|
||||
messages.add_message(request, messages.WARNING, "This Event isn\'t ready for voting yet.")
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
|
||||
event_poll_count = event.polls.all().count()
|
||||
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)
|
||||
|
||||
if (poll == None):
|
||||
raise Http404("Poll does not exist")
|
||||
if poll is None:
|
||||
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 suceeded in the util function
|
||||
poll_num = int(poll_num) # now known to be safe as it succeeded in the util function
|
||||
|
||||
if (poll_num > 1):
|
||||
if 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)
|
||||
|
||||
access_key = request.GET.get('key', None)
|
||||
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 = Ballot.objects.filter(voter=email_key[0].user, poll=poll)
|
||||
if (ballot.exists() and ballot[0].cast):
|
||||
has_voted = True
|
||||
|
||||
if (access_key and email_key.exists()): #or (can_vote(request.user, event))
|
||||
ballot = None
|
||||
if email_key.exists() and event.voters.filter(email=email_key[0].user.email).exists():
|
||||
# Passing this test means the user can vote
|
||||
voter_email = email_key[0].user.email
|
||||
can_vote = True
|
||||
|
||||
if (request.method == "POST"):
|
||||
form = VoteForm(request.POST, instance=poll)
|
||||
if (email_key.exists()):
|
||||
#return HttpResponse(email_key[0].key)
|
||||
ballot = Ballot.objects.get_or_create(voter=email_key[0].user, poll=poll)[0]
|
||||
# Check whether this is the first time a user is voting
|
||||
ballot = Ballot.objects.filter(voter=email_key[0].user, poll=poll)
|
||||
if ballot.exists() and ballot[0].cast:
|
||||
has_voted = True
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, "You don\'t have permission to vote in this event.")
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
|
||||
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:
|
||||
return HttpResponse("Voted successfully!") # finished all polls in event
|
||||
if request.method == "POST":
|
||||
if ballot is None:
|
||||
ballot = Ballot.objects.get_or_create(voter=email_key[0].user, poll=poll)
|
||||
|
||||
return render(request, "polls/poll_detail.html",
|
||||
{"object": poll, "poll_num": poll_num , "event": event, "form": form, "poll_count": event.polls.all().count(),
|
||||
"prev_index": prev_poll_index , "next_index": next_poll_index,
|
||||
"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):
|
||||
# Obtain the event and the event preparation access key that's been supplied
|
||||
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 -
|
||||
# voters therefore need to be informed
|
||||
if event.trustee_keys.count() == event.users_trustees.count():
|
||||
create_ballots.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)
|
||||
|
||||
# This re-direct may not be appropriate for trustees that don't have logins
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
else:
|
||||
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?
|
||||
messages.add_message(request, messages.WARNING, 'You do not have permission to access: ' + request.path)
|
||||
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)
|
||||
for poll in event.polls.all():
|
||||
generate_enc.delay(poll)
|
||||
return HttpResponse("Generating enc.")
|
||||
|
||||
if not event.ended:
|
||||
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):
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
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.')
|
||||
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):
|
||||
|
||||
|
@ -262,7 +305,7 @@ def manage_questions(request, event_id):
|
|||
formset = OptionFormset(request.POST, prefix="formset_organiser", instance=poll)
|
||||
if formset.is_valid():
|
||||
formset.save()
|
||||
#create_ballots.delay(poll)
|
||||
create_ballots_for_poll.delay(poll)
|
||||
messages.add_message(request, messages.SUCCESS, 'Poll created successfully')
|
||||
return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id]))
|
||||
|
||||
|
@ -274,6 +317,7 @@ def manage_questions(request, event_id):
|
|||
else:
|
||||
return HttpResponseNotAllowed()
|
||||
|
||||
|
||||
def render_invalid(request, events, demo_users, invalid_fields):
|
||||
return render(request,
|
||||
"polls/create_event.html",
|
||||
|
@ -285,6 +329,7 @@ def render_invalid(request, events, demo_users, invalid_fields):
|
|||
"invalid_fields": invalid_fields
|
||||
})
|
||||
|
||||
|
||||
def create_event(request):
|
||||
# Obtain context data for the rendering of the html template and validation
|
||||
events = Event.objects.all()
|
||||
|
@ -347,6 +392,7 @@ def create_event(request):
|
|||
else:
|
||||
return HttpResponseNotAllowed()
|
||||
|
||||
|
||||
def edit_event(request, event_id):
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
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})
|
||||
#trustee_formset = TrusteeFormSet(request.POST, prefix="formset_trustee", instance=event)
|
||||
|
||||
#class CreatePoll(generic.View):
|
||||
|
||||
def del_event(request, event_id):
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
|
@ -392,9 +437,4 @@ def del_event(request, event_id):
|
|||
return render(request, "polls/del_event.html", {"event_title": event.title, "event_id": event.id})
|
||||
elif request.method == "POST":
|
||||
event.delete()
|
||||
return HttpResponseRedirect(reverse('polls:index'))
|
||||
|
||||
def can_vote(user, event):
|
||||
if event.voters.filter(email=user.email).exists():
|
||||
return True
|
||||
return False
|
||||
return HttpResponseRedirect(reverse('polls:index'))
|
|
@ -14,7 +14,7 @@
|
|||
<hr>
|
||||
|
||||
<div class="row">
|
||||
|
||||
{% bootstrap_messages %}
|
||||
{% if socialaccount_providers %}
|
||||
<div class="col-md-5 col-lg-5">
|
||||
{% include "allauth/account/provider_panel.html" with process="login" %}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
crossorigin="anonymous"></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/decrypt_event.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>
|
||||
|
@ -74,15 +75,19 @@
|
|||
|
||||
//new 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 g1 = new ctx.ECP();
|
||||
var g2 = new ctx.ECP2();
|
||||
|
||||
var param = $('#event-param').val();
|
||||
//console.log(param);
|
||||
var parameter = $('#event-param').val();
|
||||
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
|
||||
|
||||
var tempParams = JSON.parse(param);
|
||||
//copying the values
|
||||
n.copy(tempParams.n);
|
||||
g1.copy(tempParams.g1);
|
||||
|
@ -92,26 +97,55 @@
|
|||
n:n,
|
||||
g1:g1,
|
||||
g2:g2
|
||||
}
|
||||
|
||||
var tempPK = JSON.parse($('#comb_pk').val());
|
||||
};
|
||||
|
||||
var tempPK = JSON.parse($('#comb_pk').val());
|
||||
var pk = new ctx.ECP(0);
|
||||
pk.copy(tempPK.PK);
|
||||
var answer = $('#poll-options').val();
|
||||
console.log(answer);
|
||||
var cipher = encrypt(params, pk, answer);
|
||||
|
||||
var c1Bytes = [];
|
||||
cipher.C1.toBytes(c1Bytes);
|
||||
var c2Bytes = [];
|
||||
cipher.C2.toBytes(c2Bytes);
|
||||
|
||||
$('#id_cipher_text_c1').val(c1Bytes.toString());
|
||||
$('#id_cipher_text_c2').val(c2Bytes.toString());
|
||||
// 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 = [];
|
||||
cipher.C1.toBytes(c1Bytes);
|
||||
|
||||
var c2Bytes = [];
|
||||
cipher.C2.toBytes(c2Bytes);
|
||||
|
||||
// Inject hidden input controls into the form that represents a single ballot
|
||||
var c1Input = document.createElement("input");
|
||||
c1Input.setAttribute("type", "hidden");
|
||||
c1Input.setAttribute("name", "cipher_c1_frag_" + i);
|
||||
c1Input.setAttribute("value", c1Bytes.toString());
|
||||
|
||||
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
|
||||
|
@ -149,7 +183,7 @@
|
|||
|
||||
//new function
|
||||
demosEncrypt.generateKeys = function() {
|
||||
parameter = $("#event-param").val();
|
||||
var parameter = $("#event-param").val();
|
||||
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
|
||||
//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 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-theme.min.css">
|
||||
|
|
|
@ -7,24 +7,24 @@
|
|||
{% block content %}
|
||||
|
||||
<div class="container">
|
||||
<h1>Event: {{event.title}}</h1>
|
||||
<h2>Trustee Decrypt</h2>
|
||||
<h2>Trustee Event Decryption for Event '{{ event.title }}'</h2>
|
||||
<hr/>
|
||||
<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">
|
||||
<input id="secret-key" class="textinput textInput form-control" type="text"></input>
|
||||
<p>Use your secret key to generate a decrypted cipher</p>
|
||||
<button id="keygen-btn" onclick="demosEncrypt.decryptCipher()" class="btn btn-default">Decrypt</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Encrypted Ciphers</div>
|
||||
<div class="panel panel-body">
|
||||
{% load crispy_forms_tags %}
|
||||
<form method="post" action="" class="">
|
||||
{% crispy formset helper %}
|
||||
<input class="btn btn-default" type="submit" value="Submit" disabled>
|
||||
</form>
|
||||
<form id="sk-form" method="POST">
|
||||
{% csrf_token %}
|
||||
<input id="secret-key" name="secret-key" class="textinput textInput form-control" type="text"/>
|
||||
<div class="alert alert-info" role="alert" style="margin-top: 0.75em;">
|
||||
Your secret key will be used to decrypt the event and get a vote tally for every poll.
|
||||
</div>
|
||||
<label for="files_sk_upload" class="btn btn-primary">
|
||||
<span class="glyphicon glyphicon-cloud-upload"></span>
|
||||
Upload Key
|
||||
</label>
|
||||
<input type="file" id="files_sk_upload" name="file" class="btn-info">
|
||||
<input type="submit" value="Submit" class="btn btn-success"/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,12 +9,22 @@
|
|||
{% if is_organiser %}
|
||||
<div>
|
||||
<!-- 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>
|
||||
</div>
|
||||
<!-- Edit Button -->
|
||||
<div class="col-xs-5 col-sm-3 col-md-2 marginTopEditButton">
|
||||
<a href="{% url 'polls:edit-event' event.id %}" class="btn btn-primary" style="float: right;">
|
||||
<div class="col-xs-5 col-sm-3 col-md-3 marginTopEditButton">
|
||||
{% if object.has_received_votes and object.ended == False %}
|
||||
<a href="{% url 'polls:end-event' event.id %}" class="btn btn-danger" style="float: right;">
|
||||
<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
|
||||
</a>
|
||||
</div>
|
||||
|
@ -40,11 +50,11 @@
|
|||
<a href="{% url 'polls:event-polls' event.id %}"><strong>Polls ({{ object.polls.count }})</strong></a>
|
||||
</li>
|
||||
<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>
|
||||
{% if is_organiser %}
|
||||
<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>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
{% block event_content %}
|
||||
{% if object.polls.all %}
|
||||
{% for poll in object.polls.all %}
|
||||
<h3>Poll: {{ poll.question_text }} (<a href="{% url 'polls: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/>
|
||||
<h4>Poll Options:</h4>
|
||||
<ul class="list-group">
|
||||
|
|
|
@ -46,9 +46,13 @@
|
|||
</td>
|
||||
<td class="text-center">
|
||||
<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 == '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 }}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
<div class="container">
|
||||
<h2>Trustee Event Setup for Event '{{ event.title }}'</h2>
|
||||
<hr/>
|
||||
<h4>Key Generation For: {{ user_email }}</h4>
|
||||
<br/>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Step 1: Generate Your Secret Key</strong></div>
|
||||
<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 */
|
||||
|
||||
.statusBtn {
|
||||
width: 74px;
|
||||
width: 89px;
|
||||
}
|
||||
|
||||
.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