Full end-to-end voting is working using the new binary encoding scheme, ballot combination and trustee partial decryption with tallies working perfectly. This required updating the Node server as well as Django models and views to support this. Emails to voters and trustees have also been updated to be more informative and look more professional. It could probably do at this point with using email templates and in the future HTML emails.

This commit is contained in:
vince0656 2018-07-11 14:25:36 +01:00
parent 571cd723bc
commit 5b746ad406
14 changed files with 631 additions and 555 deletions

View file

@ -1,6 +1,7 @@
from __future__ import unicode_literals
import json
import uuid
from django.core.mail import send_mail
from django.db import models
@ -8,6 +9,7 @@ from django.utils import timezone
from allauthdemo.auth.models import DemoUser
class EmailUser(models.Model):
email = models.CharField(max_length=80, unique=True)
@ -35,16 +37,23 @@ class Event(models.Model):
creator = models.CharField(max_length=256, blank=True)
c_email = models.CharField(max_length=512, blank=True)
trustees = models.CharField(max_length=4096)
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# Custom helper methods
def EID_hr(self):
EID_json = json.loads(self.EID)
return EID_json['hr']
try:
EID_json = json.loads(self.EID)
return EID_json['hr']
except ValueError:
return self.EID
def EID_crypto(self):
EID_json = json.loads(self.EID)
EID_crypto_str = EID_json['crypto']
return json.loads(EID_crypto_str)
try:
EID_json = json.loads(self.EID)
EID_crypto_str = EID_json['crypto']
return json.loads(EID_crypto_str)
except ValueError:
return "None - Event not Initialised"
def duration(self):
duration_str = self.start_time_formatted()
@ -63,26 +72,55 @@ class Event(models.Model):
def end_time_formatted_utc(self):
return self.end_time.strftime("%d-%m-%y %H:%M %Z")
# Total number of options in all polls
def total_num_opts(self):
polls = self.polls.all()
count = 0
for poll in polls:
count += poll.options.all().count()
return count
def total_num_partial_decs(self):
polls = self.polls.all()
count = 0
for poll in polls:
count += PartialBallotDecryption.objects.filter(poll=poll).count()
return count
def all_part_decs_received(self):
received = False
if self.total_num_partial_decs() == self.total_num_opts() * self.users_trustees.all().count():
received = True
return received
def status(self):
status_str = ""
# Get the current date and time to compare against to establish if this is a past, current or
# future event
# future event. Prepared means the public key has been initialised
present = timezone.now()
if self.ended is False:
if present < self.start_time and self.public_key is None:
if present < self.start_time and self.prepared is False:
status_str = "Future"
elif present < self.start_time and self.public_key is not None:
elif present < self.start_time and self.prepared is True:
status_str = "Prepared"
elif present >= self.start_time and present <= self.end_time and self.public_key is not None:
elif present >= self.start_time and present <= self.end_time and self.prepared is True:
status_str = "Active"
elif present > self.end_time and self.public_key is not None:
elif present >= self.start_time and present <= self.end_time and self.prepared is False:
status_str = "Future"
elif present > self.end_time:
status_str = "Expired"
else:
if self.event_sk.all().count() == 1:
if self.all_part_decs_received():
status_str = "Decrypted"
elif self.event_sk.all().count() == 0:
else:
status_str = "Ended"
return status_str
@ -109,13 +147,12 @@ class TrusteeKey(models.Model):
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="trustee_keys")
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=255, unique=True)
#total = models.IntegerField(blank=True, null=True, default=0)
def has_started(self):
return timezone.now() >= self.start
@ -125,60 +162,60 @@ class AccessKey(models.Model):
def __unicode__(self):
return self.title
class Poll(models.Model):
question_text = models.CharField(max_length=200)
total_votes = models.IntegerField(default=0)
min_num_selections = models.IntegerField(default=0)
max_num_selections = models.IntegerField(default=1)
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="polls")
enc = models.CharField(max_length=4096, null=True)
#index = models.IntegerField()
combined_ballots = models.CharField(max_length=4096, null=True)
result_json = models.CharField(max_length=4096, null=True)
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
def __str__(self):
return self.question_text
class PollOption(models.Model):
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
question = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="options")
#index = models.IntegerField()
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 CombinedBallot(models.Model):
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="combined_ballot")
option = models.ForeignKey(PollOption, on_delete=models.CASCADE, related_name="combined_ballot")
cipher_text_c1 = models.CharField(max_length=4096)
cipher_text_c2 = models.CharField(max_length=4096)
# A partial decryption supplied by a trustee for a combined ballot that relates to a poll option
class PartialBallotDecryption(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="decryption")
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="decryption")
option = models.ForeignKey(PollOption, on_delete=models.CASCADE, related_name="decryption")
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="decryption")
text = models.CharField(max_length=4096)
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)
event = models.ForeignKey(Event, on_delete=models.CASCADE)