From df0d2c59286455ea1a2271f61aeb810572eeb321 Mon Sep 17 00:00:00 2001
From: millosolomillo <marcomillo@gmail.com>
Date: Wed, 21 Aug 2019 20:54:09 +0200
Subject: [PATCH] Revert "Update pull from benadida repository. (#3)" (#4)

This reverts commit b52a755d493f68262dd498e4266469798319eda7.
---
 .gitignore                                    |   3 +-
 .travis.yml                                   |   3 -
 helios/__init__.py                            |   2 +
 helios/crypto/utils.py                        |  10 +-
 helios/templates/election_keygenerator.html   |  35 +--
 helios/templates/trustee_check_sk.html        |   2 +-
 .../templates/trustee_decrypt_and_prove.html  |  14 +-
 helios/templates/voters_list.html             |  14 +-
 helios/urls.py                                |   6 +-
 helios/utils.py                               |  14 +-
 helios/views.py                               |  80 +++---
 .../djangofb/default_app/urls.py              |   2 +-
 helios_auth/auth_systems/google.py            |   4 +-
 helios_auth/security/__init__.py              |   1 -
 helios_auth/urls.py                           |  40 ++-
 heliosbooth/boothworker-single.js             |  12 +-
 heliosbooth/css/forms.css                     |   2 +-
 heliosbooth/js/jscrypto/random.js             |   3 +-
 heliosbooth/templates/question.html           |  19 +-
 heliosverifier/css/forms.css                  |   2 +-
 heliosverifier/verify.html                    | 243 +++++++++---------
 requirements.txt                              |   6 +-
 reset.sh                                      |   1 -
 runtime.txt                                   |   2 +-
 server_ui/glue.py                             |  35 ++-
 server_ui/media/boothcss/forms.css            |   2 +-
 server_ui/templates/email/cast_vote_body.txt  |  14 -
 .../templates/email/cast_vote_subject.txt     |   1 -
 server_ui/templates/index.html                |   2 +-
 server_ui/urls.py                             |   4 +-
 30 files changed, 264 insertions(+), 314 deletions(-)
 delete mode 100644 server_ui/templates/email/cast_vote_body.txt
 delete mode 100644 server_ui/templates/email/cast_vote_subject.txt

diff --git a/.gitignore b/.gitignore
index a2dca2b..681947f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,5 +6,4 @@ media/*
 venv
 celerybeat-*
 env.sh
-.cache
-.idea/
\ No newline at end of file
+.cache
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 6b93e8a..be482e4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,6 +12,3 @@ addons:
   postgresql: "9.3"
 before_script:
   - psql -c 'create database helios;' -U postgres
-before_install:
-  - export BOTO_CONFIG=/dev/null
-
diff --git a/helios/__init__.py b/helios/__init__.py
index 3a516d7..06f0514 100644
--- a/helios/__init__.py
+++ b/helios/__init__.py
@@ -1,5 +1,7 @@
+
 from django.conf import settings
 from django.core.urlresolvers import reverse
+from helios.views import election_shortcut
 
 TEMPLATE_BASE = settings.HELIOS_TEMPLATE_BASE or "helios/templates/base.html"
 
diff --git a/helios/crypto/utils.py b/helios/crypto/utils.py
index 258f413..dd395a5 100644
--- a/helios/crypto/utils.py
+++ b/helios/crypto/utils.py
@@ -1,7 +1,7 @@
 """
 Crypto Utils
 """
-import hashlib
+
 import hmac, base64, json
 
 from hashlib import sha256
@@ -21,11 +21,3 @@ def to_json(d):
 def from_json(json_str):
   if not json_str: return None
   return json.loads(json_str)
-
-
-def do_hmac(k,s):
-  """
-  HMAC a value with a key, hex output
-  """
-  mac = hmac.new(k, s, hashlib.sha1)
-  return mac.hexdigest()
\ No newline at end of file
diff --git a/helios/templates/election_keygenerator.html b/helios/templates/election_keygenerator.html
index 4521fc9..60a2c55 100644
--- a/helios/templates/election_keygenerator.html
+++ b/helios/templates/election_keygenerator.html
@@ -76,23 +76,11 @@ function show_sk() {
 }
 
 function download_sk() {
-    $('#pk_content').show();
-    $('#sk_content').html(jQuery.toJSON(SECRET_KEY));
-}
-
-function download_sk_to_file(filename) {
-    var element = document.createElement('a');
-    element.setAttribute('href','data:text/plain;charset=utf-8,'+ encodeURIComponent(jQuery.toJSON(SECRET_KEY)));
-    element.setAttribute('download', filename);
-    element.style.display = 'none';
-    document.body.appendChild(element);
-    element.click();
-    document.body.removeChild(element);
+    UTILS.open_window_with_content(jQuery.toJSON(SECRET_KEY), "application/json");
 }
 
 function show_pk() {
     $('#sk_download').hide();
-    $('#pk_content').hide();
     $('#pk_hash').show();
     $('#pk_form').show();
 }
@@ -113,7 +101,7 @@ function show_pk() {
 
 <span id="buttons"><button onclick="generate_keypair(); return false;" id="generate_button">Generate Election Keys</button></span>
 
-<br />
+<br /><br />
 If you've already generated a keypair, you can <a href="javascript:show_key_reuse()">reuse it</a>.
 </p>
 
@@ -138,24 +126,11 @@ Your key has been generated, but you may choose to<br /><a href="javascript:clea
 </span>
 
 <p>
-    <button style="font-size:16pt;" onclick="download_sk(); $('#pk_link').show();">Show my secret key</button>
+    <button style="font-size:16pt;" onclick="download_sk(); $('#pk_link').show();">Save your secret key</button>
 </p>
-</div>
 
-<div style="display:none;" id="pk_content">
-    <p>Bellow is your trustee secret key content. Please copy its content and save it securely. <br>
-       You can also click to dowload it to a file.
-       And please don't lose it! Otherwise it will not be possible to decrypt the election tally.<br>
-    </p>
-    <textarea id="sk_content" rows="5" wrap="soft" cols="50" style="height: 25em;"></textarea>
-</div>
-
-<div style="display:none;" id="pk_link">
-<p>
-<a id="download_to_file" href="javascript:download_sk_to_file('trustee_key_for_{{election.name}}.txt');">download private key to a file</a>
-</p>
-<p>
-  <a href="javascript:show_pk();">ok, I've saved the key, let's move on</a>
+<p style="display: none;" id="pk_link">
+  <a href="javascript:show_pk();">ok, I've saved the key, let's move on</a>.
 </p>
 </div>
 
diff --git a/helios/templates/trustee_check_sk.html b/helios/templates/trustee_check_sk.html
index 81afe42..f6b615e 100644
--- a/helios/templates/trustee_check_sk.html
+++ b/helios/templates/trustee_check_sk.html
@@ -59,7 +59,7 @@ To verify that you have the right secret key, paste it here:
 <p>
 
 <form onsubmit="check_sk(this.secret_key.value); this.secret_key.value=''; return false;">
-<textarea name="secret_key" cols="60" rows ="5" wrap="soft" style="height: 25em;">
+<textarea name="secret_key" cols="60" rows ="5" wrap="soft">
 </textarea>
 <br />
 <input type="submit" value="check" />
diff --git a/helios/templates/trustee_decrypt_and_prove.html b/helios/templates/trustee_decrypt_and_prove.html
index 05153b8..d4cbe43 100644
--- a/helios/templates/trustee_decrypt_and_prove.html
+++ b/helios/templates/trustee_decrypt_and_prove.html
@@ -157,7 +157,7 @@ function reset() {
 
       <form onsubmit="return false;">
           <h3>FIRST STEP: enter your secret key</h3>
-          <textarea id="sk_textarea" cols="60" rows="5" style="height: 25em;"></textarea>
+          <textarea id="sk_textarea" cols="60" rows="5"></textarea>
       </form>
       <p id="tally_section">
           <button onclick="do_tally();">Generate partial decryption</button>
@@ -187,11 +187,13 @@ function reset() {
           When you're ready, you can submit this result to the server.
       </p>
       Your partial decryption:<br />
-      <p>
-          <textarea id="result_textarea" cols="60" rows="5" wrap="soft" style="height: 25em;"></textarea>
-          <button onclick="submit_result();">Upload decryption factors to server</button>
-      </p>
-      <p><a href="javascript:reset()">reset and restart decryption process</a></p>
+      <form action="javascript:submit_result();">
+          <textarea id="result_textarea" cols="60" rows="5" wrap="soft"></textarea><br /><br />
+          <input type="submit" value="Upload decryption factors to server" />
+      </form>
+      <br />
+      <a href="javascript:reset()">reset and restart decryption process</a>
+      <br />
   </div>
   
   <div id="done_div">
diff --git a/helios/templates/voters_list.html b/helios/templates/voters_list.html
index fffe57f..1409770 100644
--- a/helios/templates/voters_list.html
+++ b/helios/templates/voters_list.html
@@ -112,11 +112,6 @@ Voters {{voters_page.start_index}} - {{voters_page.end_index}} (of {{total_voter
 <table class="pretty">
 <tr>
 {% if admin_p or not election.use_voter_aliases %}
-{% if admin_p %}
-<th style="width: 80px;">Actions</th>
-<th>Login</th>
-<th>Email Address</th>
-{% endif %}
 <th>Name</th>
 {% endif %}
 
@@ -128,22 +123,19 @@ Voters {{voters_page.start_index}} - {{voters_page.end_index}} (of {{total_voter
 {% for voter in voters %}
 <tr>
 {% if admin_p or not election.use_voter_aliases %}
+<td>
 {% if admin_p %}
-<td style="white-space: nowrap;">
 {% if election.frozen_at %}
 [<a href="{% url "helios.views.voters_email" election.uuid %}?voter_id={{voter.voter_login_id}}">email</a>]
 {% endif %}
 [<a onclick="return confirm('are you sure you want to remove {{voter.name}} ?');" href="{% url "helios.views.voter_delete" election.uuid voter.uuid %}">x</a>]
-</td>
-<td>{{voter.voter_login_id}}</td>
-<td>{{voter.voter_email}}</td>
 {% endif %}
-<td><img class="small-logo" src="/static/auth/login-icons/{{voter.voter_type}}.png" alt="{{voter.voter_type}}" /> {{voter.name}}</td>
+<img class="small-logo" src="/static/auth/login-icons/{{voter.voter_type}}.png" alt="{{voter.voter_type}}" /> {{voter.name}}</td>
 {% endif %}
 {% if election.use_voter_aliases %}
 <td>{{voter.alias}}</td>
 {% endif %}
-<td><tt style="font-size: 1.4em;">{% if voter.vote_hash %}{{voter.vote_hash}} <span style="font-size:0.8em;">[<a href="{% url "helios.views.castvote_shortcut" vote_tinyhash=voter.vote_tinyhash %}">view</a>]</span>{% else %}&mdash;{% endif %}</tt></td>
+<td><tt style="font-size: 1.4em;;">{% if voter.vote_hash %}{{voter.vote_hash}} <span style="font-size:0.8em;">[<a href="{% url "helios.views.castvote_shortcut" vote_tinyhash=voter.vote_tinyhash %}">view</a>]</span>{% else %}&mdash;{% endif %}</tt></td>
 </tr>
 {% endfor %}
 </table>
diff --git a/helios/urls.py b/helios/urls.py
index 8effba1..67c0077 100644
--- a/helios/urls.py
+++ b/helios/urls.py
@@ -1,8 +1,12 @@
 # -*- coding: utf-8 -*-
-from django.conf.urls import patterns, include
+from django.conf.urls import *
+
+from django.conf import settings
 
 from views import *
 
+urlpatterns = None
+
 urlpatterns = patterns('',
   (r'^autologin$', admin_autologin),
   (r'^testcookie$', test_cookie),
diff --git a/helios/utils.py b/helios/utils.py
index 03095a7..d053dc1 100644
--- a/helios/utils.py
+++ b/helios/utils.py
@@ -5,7 +5,9 @@ Ben Adida - ben@adida.net
 2005-04-11
 """
 
-import urllib, re, datetime, string
+import urllib, re, sys, datetime, urlparse, string
+
+import boto.ses
 
 # utils from helios_auth, too
 from helios_auth.utils import *
@@ -13,6 +15,14 @@ from helios_auth.utils import *
 from django.conf import settings
   
 import random, logging
+import hashlib, hmac, base64
+
+def do_hmac(k,s):
+  """
+  HMAC a value with a key, hex output
+  """
+  mac = hmac.new(k, s, hashlib.sha1)
+  return mac.hexdigest()
 
 
 def split_by_length(str, length, rejoin_with=None):
@@ -157,7 +167,7 @@ def one_val_raw_sql(raw_sql, values=[]):
   """
   for a simple aggregate
   """
-  from django.db import connection
+  from django.db import connection, transaction
   cursor = connection.cursor()
 
   cursor.execute(raw_sql, values)
diff --git a/helios/views.py b/helios/views.py
index 3337459..2959463 100644
--- a/helios/views.py
+++ b/helios/views.py
@@ -6,41 +6,41 @@ Ben Adida (ben@adida.net)
 """
 
 from django.core.urlresolvers import reverse
+from django.core.mail import send_mail
 from django.core.paginator import Paginator
 from django.core.exceptions import PermissionDenied
-from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseForbidden
+from django.http import *
 from django.db import transaction, IntegrityError
 
+from mimetypes import guess_type
+
 from validate_email import validate_email
 
-import urllib, os, base64
+import csv, urllib, os, base64
 
 from crypto import algs, electionalgs, elgamal
 from crypto import utils as cryptoutils
 from workflows import homomorphic
-from helios import utils, VOTERS_EMAIL, VOTERS_UPLOAD
-from view_utils import SUCCESS, FAILURE, return_json, render_template, render_template_raw
+from helios import utils as helios_utils
+from view_utils import *
 
-from helios_auth.security import check_csrf, login_required, get_user, save_in_session_across_logouts
+from helios_auth.security import *
 from helios_auth.auth_systems import AUTH_SYSTEMS, can_list_categories
 from helios_auth.models import AuthenticationExpired
 
+from helios import security
 from helios_auth import views as auth_views
 
 import tasks
 
-from security import (election_view, election_admin,
-                      trustee_check, set_logged_in_trustee,
-                      can_create_election, user_can_see_election, get_voter,
-                      user_can_admin_election, user_can_feature_election)
+from security import *
+from helios_auth.security import get_user, save_in_session_across_logouts
 
 import uuid, datetime
-import logging
 
-from models import User, Election, CastVote, Voter, VoterFile, Trustee, AuditedBallot
-import datatypes
+from models import *
 
-import forms
+import forms, signals
 
 # Parameters for everything
 ELGAMAL_PARAMS = elgamal.Cryptosystem()
@@ -196,7 +196,7 @@ def election_new(request):
       election_params = dict(election_form.cleaned_data)
       
       # is the short name valid
-      if utils.urlencode(election_params['short_name']) == election_params['short_name']:
+      if helios_utils.urlencode(election_params['short_name']) == election_params['short_name']:      
         election_params['uuid'] = str(uuid.uuid1())
         election_params['cast_url'] = settings.SECURE_URL_HOST + reverse(one_election_cast, args=[election_params['uuid']])
       
@@ -293,8 +293,8 @@ def election_badge(request, election):
 @election_view()
 def one_election_view(request, election):
   user = get_user(request)
-  admin_p = user_can_admin_election(user, election)
-  can_feature_p = user_can_feature_election(user, election)
+  admin_p = security.user_can_admin_election(user, election)
+  can_feature_p = security.user_can_feature_election(user, election)
   
   notregistered = False
   eligible_p = True
@@ -383,7 +383,7 @@ def list_trustees(request, election):
 def list_trustees_view(request, election):
   trustees = Trustee.get_by_election(election)
   user = get_user(request)
-  admin_p = user_can_admin_election(user, election)
+  admin_p = security.user_can_admin_election(user, election)
   
   return render_template(request, 'list_trustees', {'election': election, 'trustees': trustees, 'admin_p':admin_p})
   
@@ -424,9 +424,14 @@ def trustee_login(request, election_short_name, trustee_email, trustee_secret):
       if trustee.secret == trustee_secret:
         set_logged_in_trustee(request, trustee)
         return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(trustee_home, args=[election.uuid, trustee.uuid]))
-    # bad secret or no such trustee
-    raise Http404("Trustee not recognized.")
-  raise Http404("No election {} found.".format(election_short_name))
+      else:
+        # bad secret, we'll let that redirect to the front page
+        pass
+    else:
+      # no such trustee
+      raise Http404
+
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + "/")
 
 @election_admin()
 def trustee_send_url(request, election, trustee_uuid):
@@ -446,7 +451,7 @@ Your trustee dashboard is at
 Helios  
 """ % (election.name, url)
 
-  utils.send_email(settings.SERVER_EMAIL, ["%s <%s>" % (trustee.name, trustee.email)], 'your trustee homepage for %s' % election.name, body)
+  helios_utils.send_email(settings.SERVER_EMAIL, ["%s <%s>" % (trustee.name, trustee.email)], 'your trustee homepage for %s' % election.name, body)
 
   logging.info("URL %s " % url)
   return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(list_trustees_view, args = [election.uuid]))
@@ -471,7 +476,7 @@ def trustee_upload_pk(request, election, trustee):
     if not trustee.public_key.verify_sk_proof(trustee.pok, algs.DLog_challenge_generator):
       raise Exception("bad pok for this public key")
     
-    trustee.public_key_hash = cryptoutils.hash_b64(utils.to_json(trustee.public_key.toJSONDict()))
+    trustee.public_key_hash = utils.hash_b64(utils.to_json(trustee.public_key.toJSONDict()))
 
     trustee.save()
     
@@ -507,7 +512,8 @@ def encrypt_ballot(request, election):
   perform the ballot encryption given answers_json, a JSON'ified list of list of answers
   (list of list because each question could have a list of answers if more than one.)
   """
-  answers = utils.from_json(request.POST['answers_json'])
+  # FIXME: maybe make this just request.POST at some point?
+  answers = utils.from_json(request.REQUEST['answers_json'])
   ev = homomorphic.EncryptedVote.fromElectionAndAnswers(election, answers)
   return ev.ld_object.includeRandomness().toJSONDict()
     
@@ -546,13 +552,7 @@ def password_voter_login(request, election):
   """
   
   # the URL to send the user to after they've logged in
-  if request.method == "GET" and 'return_url' in request.GET:
-      return_url = request.GET['return_url']
-  elif request. method == "POST" and 'return_url' in request.POST:
-      return_url = request.POST['return_url']
-  else:
-      return_url = reverse(one_election_cast_confirm, args=[election.uuid])
-
+  return_url = request.REQUEST.get('return_url', reverse(one_election_cast_confirm, args=[election.uuid]))
   bad_voter_login = (request.GET.get('bad_voter_login', "0") == "1")
 
   if request.method == "GET":
@@ -568,7 +568,7 @@ def password_voter_login(request, election):
                             'password_login_form': password_login_form,
                             'bad_voter_login' : bad_voter_login})
   
-  login_url = request.GET.get('login_url', None)
+  login_url = request.REQUEST.get('login_url', None)
 
   if not login_url:
     # login depending on whether this is a private election
@@ -923,7 +923,7 @@ def one_election_set_featured(request, election):
   """
 
   user = get_user(request)
-  if not user_can_feature_election(user, election):
+  if not security.user_can_feature_election(user, election):
     raise PermissionDenied()
 
   featured_p = bool(int(request.GET['featured_p']))
@@ -987,7 +987,7 @@ def one_election_copy(request, election):
 def one_election_questions(request, election):
   questions_json = utils.to_json(election.questions)
   user = get_user(request)
-  admin_p = user_can_admin_election(user, election)
+  admin_p = security.user_can_admin_election(user, election)
 
   return render_template(request, 'election_questions', {'election': election, 'questions_json' : questions_json, 'admin_p': admin_p})
 
@@ -1191,7 +1191,7 @@ def voters_list_pretty(request, election):
     order_by = 'alias'
 
   user = get_user(request)
-  admin_p = user_can_admin_election(user, election)
+  admin_p = security.user_can_admin_election(user, election)
 
   categories = None
   eligibility_category_id = None
@@ -1204,7 +1204,7 @@ def voters_list_pretty(request, election):
     return user_reauth(request, user)
   
   # files being processed
-  voter_files = election.voterfile_set.all().order_by('-uploaded_at')
+  voter_files = election.voterfile_set.all()
 
   # load a bunch of voters
   # voters = Voter.get_by_election(election, order_by=order_by)
@@ -1224,9 +1224,9 @@ def voters_list_pretty(request, election):
   return render_template(request, 'voters_list', 
                          {'election': election, 'voters_page': voters_page,
                           'voters': voters_page.object_list, 'admin_p': admin_p, 
-                          'email_voters': VOTERS_EMAIL,
+                          'email_voters': helios.VOTERS_EMAIL,
                           'limit': limit, 'total_voters': total_voters,
-                          'upload_p': VOTERS_UPLOAD, 'q' : q,
+                          'upload_p': helios.VOTERS_UPLOAD, 'q' : q,
                           'voter_files': voter_files,
                           'categories': categories,
                           'eligibility_category_id' : eligibility_category_id})
@@ -1330,7 +1330,7 @@ def voters_upload_cancel(request, election):
 
 @election_admin(frozen=True)
 def voters_email(request, election):
-  if not VOTERS_EMAIL:
+  if not helios.VOTERS_EMAIL:
     return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
   TEMPLATES = [
     ('vote', 'Time to Vote'),
@@ -1339,11 +1339,11 @@ def voters_email(request, election):
     ('result', 'Election Result')
     ]
 
-  template = request.GET.get('template', 'vote')
+  template = request.REQUEST.get('template', 'vote')
   if not template in [t[0] for t in TEMPLATES]:
     raise Exception("bad template")
 
-  voter_id = request.GET.get('voter_id', None)
+  voter_id = request.REQUEST.get('voter_id', None)
 
   if voter_id:
     voter = Voter.get_by_election_and_voter_id(election, voter_id)
diff --git a/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py b/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py
index 5e793a6..8501844 100644
--- a/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py
+++ b/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py
@@ -1,4 +1,4 @@
-from django.conf.urls import *
+from django.conf.urls.defaults import *
 
 urlpatterns = patterns('{{ project }}.{{ app }}.views',
     (r'^$', 'canvas'),
diff --git a/helios_auth/auth_systems/google.py b/helios_auth/auth_systems/google.py
index 7caa32f..b6eb57c 100644
--- a/helios_auth/auth_systems/google.py
+++ b/helios_auth/auth_systems/google.py
@@ -50,11 +50,11 @@ def get_user_info_after_auth(request):
   # get the nice name
   http = httplib2.Http(".cache")
   http = credentials.authorize(http)
-  (resp_headers, content) = http.request("https://people.googleapis.com/v1/people/me?personFields=names", "GET")
+  (resp_headers, content) = http.request("https://www.googleapis.com/plus/v1/people/me", "GET")
 
   response = json.loads(content)
 
-  name = response['names'][0]['displayName']
+  name = response['displayName']
   
   # watch out, response also contains email addresses, but not sure whether thsoe are verified or not
   # so for email address we will only look at the id_token
diff --git a/helios_auth/security/__init__.py b/helios_auth/security/__init__.py
index facc0c3..b036067 100644
--- a/helios_auth/security/__init__.py
+++ b/helios_auth/security/__init__.py
@@ -10,7 +10,6 @@ from functools import update_wrapper
 from django.http import HttpResponse, Http404, HttpResponseRedirect
 from django.core.exceptions import *
 from django.conf import settings
-from django.http import HttpResponseNotAllowed
 
 import oauth
 
diff --git a/helios_auth/urls.py b/helios_auth/urls.py
index 11d1013..e4dca39 100644
--- a/helios_auth/urls.py
+++ b/helios_auth/urls.py
@@ -1,33 +1,31 @@
-
 """
 Authentication URLs
 
 Ben Adida (ben@adida.net)
 """
 
-from django.conf.urls import url
+from django.conf.urls import *
 
-import views
-from settings import AUTH_ENABLED_AUTH_SYSTEMS
+from views import *
+from auth_systems.password import password_login_view, password_forgotten_view
+from auth_systems.twitter import follow_view
 
-urlpatterns = [
+urlpatterns = patterns('',
     # basic static stuff
-    url(r'^$', views.index),
-    url(r'^logout$', views.logout),
-    url(r'^start/(?P<system_name>.*)$', views.start),
+    (r'^$', index),
+    (r'^logout$', logout),
+    (r'^start/(?P<system_name>.*)$', start),
     # weird facebook constraint for trailing slash
-    url(r'^after/$', views.after),
-    url(r'^why$', views.perms_why),
-    url(r'^after_intervention$', views.after_intervention),
-]
+    (r'^after/$', after),
+    (r'^why$', perms_why),
+    (r'^after_intervention$', after_intervention),
+    
+    ## should make the following modular
 
-# password auth
-if 'password' in AUTH_ENABLED_AUTH_SYSTEMS:
-    from auth_systems.password import password_login_view, password_forgotten_view
-    urlpatterns.append(url(r'^password/login', password_login_view))
-    urlpatterns.append(url(r'^password/forgot', password_forgotten_view))
+    # password auth
+    (r'^password/login', password_login_view),
+    (r'^password/forgot', password_forgotten_view),
 
-# twitter
-if 'twitter' in AUTH_ENABLED_AUTH_SYSTEMS:
-    from auth_systems.twitter import follow_view
-    urlpatterns.append(url(r'^twitter/follow', follow_view))
+    # twitter
+    (r'^twitter/follow', follow_view),
+)
diff --git a/heliosbooth/boothworker-single.js b/heliosbooth/boothworker-single.js
index 1c87986..edb1deb 100644
--- a/heliosbooth/boothworker-single.js
+++ b/heliosbooth/boothworker-single.js
@@ -49,16 +49,12 @@ function do_encrypt(message) {
 // receive either
 // a) an election and an integer position of the question
 // that this worker will be used to encrypt
-// {'type': 'setup', 'election': election_json}
+// {'type': 'setup', 'question_num' : 2, 'election' : election_json}
 //
 // b) an answer that needs encrypting
-// {'type': 'encrypt', 'q_num': 2, 'id': id, 'answer': answer_json}
+// {'type': 'encrypt', 'answer' : answer_json}
 //
 self.onmessage = function(event) {
     // dispatch to method
-    if (event.data.type === "setup") {
-        do_setup(event.data);
-	} else if (event.data.type === "encrypt") {
-        do_encrypt(event.data);
-    }
-};
+    self['do_' + event.data.type](event.data);
+}
diff --git a/heliosbooth/css/forms.css b/heliosbooth/css/forms.css
index ae4cbb7..ee1a141 100644
--- a/heliosbooth/css/forms.css
+++ b/heliosbooth/css/forms.css
@@ -7,7 +7,7 @@ form.prettyform label,input,textarea,select {
     line-height: 1.8;
 }
 
-form.prettyform label:not(.answer) {
+form.prettyform label {
     display: block;
     text-align: right;
     float: left;
diff --git a/heliosbooth/js/jscrypto/random.js b/heliosbooth/js/jscrypto/random.js
index 8e363a6..dd69f9d 100644
--- a/heliosbooth/js/jscrypto/random.js
+++ b/heliosbooth/js/jscrypto/random.js
@@ -26,9 +26,10 @@ Random.getRandomInteger = function(max) {
   var bit_length = max.bitLength();
   Random.setupGenerator();
   var random;
-  random = sjcl.random.randomWords(Math.ceil(bit_length / 32) + 2, 6);
+  random = sjcl.random.randomWords(Math.ceil(bit_length / 32)+2, 0);
   // we get a bit array instead of a BigInteger in this case
   var rand_bi = new BigInt(sjcl.codec.hex.fromBits(random), 16);
   return rand_bi.mod(max);
+  return BigInt._from_java_object(random).mod(max);
 };
 
diff --git a/heliosbooth/templates/question.html b/heliosbooth/templates/question.html
index 8945e45..776eefd 100644
--- a/heliosbooth/templates/question.html
+++ b/heliosbooth/templates/question.html
@@ -25,19 +25,14 @@ as many as you approve of
 </p>
 
 {#foreach $T.question.answers as answer}
-<div id="answer_label_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}">
-  <input type="checkbox" class="ballot_answer" id="answer_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}" name="answer_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}" value="yes" onclick="BOOTH.click_checkbox({$T.question_num}, {$T.answer_ordering[$T.answer$index]}, this.checked);" />
+<div id="answer_label_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}"><input type="checkbox" class="ballot_answer" id="answer_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}" name="answer_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}" value="yes" onclick="BOOTH.click_checkbox({$T.question_num}, {$T.answer_ordering[$T.answer$index]}, this.checked);" /> {$T.question.answers[$T.answer_ordering[$T.answer$index]]}
 
-  <label class="answer" for="answer_{$T.question_num}_{$T.answer_ordering[$T.answer$index]}">
-    {$T.question.answers[$T.answer_ordering[$T.answer$index]]}
-
-    {#if $T.question.answer_urls && $T.question.answer_urls[$T.answer_ordering[$T.answer$index]] && $T.question.answer_urls[$T.answer_ordering[$T.answer$index]] != ""}
-      &nbsp;&nbsp;
-      <span style="font-size: 12pt;">
-        [<a target="_blank" href="{$T.question.answer_urls[$T.answer_ordering[$T.answer$index]]}" rel="noopener noreferrer">more info</a>]
-      </span>
-    {#/if}
-  </label>
+{#if $T.question.answer_urls && $T.question.answer_urls[$T.answer_ordering[$T.answer$index]] && $T.question.answer_urls[$T.answer_ordering[$T.answer$index]] != ""}
+&nbsp;&nbsp;
+<span style="font-size: 12pt;">
+[<a target="_blank" href="{$T.question.answer_urls[$T.answer_ordering[$T.answer$index]]}" rel="noopener noreferrer">more info</a>]
+</span>
+{#/if}
 </div>
 {#/for}
 
diff --git a/heliosverifier/css/forms.css b/heliosverifier/css/forms.css
index ae4cbb7..ee1a141 100644
--- a/heliosverifier/css/forms.css
+++ b/heliosverifier/css/forms.css
@@ -7,7 +7,7 @@ form.prettyform label,input,textarea,select {
     line-height: 1.8;
 }
 
-form.prettyform label:not(.answer) {
+form.prettyform label {
     display: block;
     text-align: right;
     float: left;
diff --git a/heliosverifier/verify.html b/heliosverifier/verify.html
index 0e06b17..344a277 100644
--- a/heliosverifier/verify.html
+++ b/heliosverifier/verify.html
@@ -97,158 +97,151 @@ function load_election_and_ballots(election_url) {
     
     // the hash will be computed within the setup function call now
     $.get(election_url, function(raw_json) {
-        try {
-            election = HELIOS.Election.fromJSONString(raw_json);
-            result_append("loaded election: " + election.name);
-            result_append("election fingerprint: " + election.get_hash());
-
-            var tally = [];
+        election = HELIOS.Election.fromJSONString(raw_json);
+        result_append("loaded election: " + election.name);
+        result_append("election fingerprint: " + election.get_hash());
+        
+        var tally = [];
 
-            $(election.questions).each(function(qnum, q) {
-                if (q.tally_type != "homomorphic") {
-                  result_append("PROBLEM: this election is not a straight-forward homomorphic-tally election. As a result, Helios cannot currently verify it.");
-                  return;
-                }
+        $(election.questions).each(function(qnum, q) {
+            if (q.tally_type != "homomorphic") {
+              result_append("PROBLEM: this election is not a straight-forward homomorphic-tally election. As a result, Helios cannot currently verify it.");
+              return;
+            }
 
-                tally[qnum] = $(q.answers).map(function(anum, a) {
-                    return 1;
-                });
+            tally[qnum] = $(q.answers).map(function(anum, a) {
+                return 1;
             });
+        });
 
-            result_append("loading list of voters...");
+        result_append("loading list of voters...");
+        
+        // load voter list
+        load_ballot_list(election_url, [], null, function(ballot_list) {
+            result_append("loaded voter list, now loading ballots for each..");
             
-            // load voter list
-            load_ballot_list(election_url, [], null, function(ballot_list) {
-                result_append("loaded voter list, now loading ballots for each..");
+            // load all ballots
+            load_ballots(election_url, ballot_list, [], function(ballots) {
+                result_append("");
+                result_append("<h3>Ballots</h3>");
+                // now load each ballot
+                $(ballots).each(function(i, cast_vote){
+
+                    if (cast_vote.vote == null)
+                      return;
+                      
+                    var vote = HELIOS.EncryptedVote.fromJSONObject(cast_vote.vote, election);
+                    result_append("Voter #" + (i+1));
+                    result_append("-- UUID: " + cast_vote.voter_uuid);
+                    result_append("-- Ballot Tracking Number: " + vote.get_hash());
+
+                    vote.verifyProofs(election.public_key, function(answer_num, choice_num, result, choice) {
+                        overall_result = overall_result && result;
+                        if (choice_num != null) {
+                            // keep track of tally
+                            tally[answer_num][choice_num] = choice.multiply(tally[answer_num][choice_num]);
+
+                            result_append("Question #" + (answer_num+1) + ", Option #" + (choice_num+1) + " -- " + pretty_result(result));                            
+                        } else {
+                            result_append("Question #" + (answer_num+1) + " OVERALL -- " + pretty_result(result));            
+                        }
+                    });
 
-                // load all ballots
-                load_ballots(election_url, ballot_list, [], function(ballots) {
                     result_append("");
-                    result_append("<h3>Ballots</h3>");
-                    // now load each ballot
-                    $(ballots).each(function(i, cast_vote){
-
-                        if (cast_vote.vote == null)
-                          return;
-
-                        var vote = HELIOS.EncryptedVote.fromJSONObject(cast_vote.vote, election);
-                        result_append("Voter #" + (i+1));
-                        result_append("-- UUID: " + cast_vote.voter_uuid);
-                        result_append("-- Ballot Tracking Number: " + vote.get_hash());
-
-                        vote.verifyProofs(election.public_key, function(answer_num, choice_num, result, choice) {
-                            overall_result = overall_result && result;
-                            if (choice_num != null) {
-                                // keep track of tally
-                                tally[answer_num][choice_num] = choice.multiply(tally[answer_num][choice_num]);
-
-                                result_append("Question #" + (answer_num+1) + ", Option #" + (choice_num+1) + " -- " + pretty_result(result));
-                            } else {
-                                result_append("Question #" + (answer_num+1) + " OVERALL -- " + pretty_result(result));
-                            }
-                        });
-
-                        result_append("");
-                    });
+                });
 
-                    // get the election result
-                    $.get(election_url + "/result", function(result) {
-                        var results = $.secureEvalJSON(result);
+                // get the election result
+                $.get(election_url + "/result", function(result) {
+                    var results = $.secureEvalJSON(result);
 
-                        // get the trustees and proofs
-                        $.get(election_url + "/trustees/", function(trustees_json) {
-                           trustees = $.secureEvalJSON(trustees_json);
+                    // get the trustees and proofs
+                    $.get(election_url + "/trustees/", function(trustees_json) {
+                       trustees = $.secureEvalJSON(trustees_json);
 
-                           // create the Helios objects
-                           trustees = $(trustees).map(function(i, trustee) {return HELIOS.Trustee.fromJSONObject(trustee)});
+                       // create the Helios objects
+                       trustees = $(trustees).map(function(i, trustee) {return HELIOS.Trustee.fromJSONObject(trustee)});
 
-                           // the public key that we'll check
-                           var combined_key = 1;
+                       // the public key that we'll check
+                       var combined_key = 1;
 
-                           result_append("<h3>Trustees</h3>");
-                           // verify the keys
-                           $(trustees).each(function(i, trustee) {
-                              result_append("Trustee #" + (i+1) + ": " + trustee.email);
-                              if (trustee.public_key.verifyKnowledgeOfSecretKey(trustee.pok, ElGamal.fiatshamir_dlog_challenge_generator)) {
-                                  result_append("-- PK " + trustee.public_key_hash + " -- VERIFIED.");
+                       result_append("<h3>Trustees</h3>");
+                       // verify the keys
+                       $(trustees).each(function(i, trustee) {
+                          result_append("Trustee #" + (i+1) + ": " + trustee.email);
+                          if (trustee.public_key.verifyKnowledgeOfSecretKey(trustee.pok, ElGamal.fiatshamir_dlog_challenge_generator)) {
+                              result_append("-- PK " + trustee.public_key_hash + " -- VERIFIED.");
 
-                                  // FIXME check the public key hash
-                              } else {
-                                  result_append("==== ERROR for PK of trustee " + trustee.email);
-                                  overall_result = false;
-                              }
+                              // FIXME check the public key hash
+                          } else {
+                              result_append("==== ERROR for PK of trustee " + trustee.email);
+                              overall_result = false;
+                          }
 
-                              combined_key = trustee.public_key.multiply(combined_key);
+                          combined_key = trustee.public_key.multiply(combined_key);
 
-                              result_append("");
-                           });
+                          result_append("");
+                       });
+
+                       // verify the combination of the keys into the final public key
+                       if (combined_key.equals(election.public_key)) {
+                           result_append("election public key CORRECTLY FORMED");
+                       } else {
+                           result_append("==== ERROR, election public key doesn't match");
+                           overall_result = false;
+                       }
+
+                       result_append("<h3>Tally</h3>");
 
-                           // verify the combination of the keys into the final public key
-                           if (combined_key.equals(election.public_key)) {
-                               result_append("election public key CORRECTLY FORMED");
-                           } else {
-                               result_append("==== ERROR, election public key doesn't match");
-                               overall_result = false;
-                           }
-
-                           result_append("<h3>Tally</h3>");
-
-                           $(tally).each(function(q_num, q) {
-                               result_append("Question #" + (q_num+1) + ": " + election.questions[q_num].short_name);
-                               $(q).each(function(a_num, a) {
-                                   var plaintext = new ElGamal.Plaintext(election.public_key.g.modPow(BigInt.fromInt(results[q_num][a_num]), election.public_key.p), election.public_key);
-
-                                   var check = true;
-                                   result_append("Answer #" + (a_num + 1) + ": " + election.questions[q_num].answers[a_num] + " - COUNT = " + results[q_num][a_num]);
-
-                                   var decryption_factors = [];
-
-                                   // go through the trustees' decryption factors and verify each one
-                                   $(trustees).each(function(t_num, trustee) {
-                                       if (trustee.public_key.verifyDecryptionFactor(a, trustee.decryption_factors[q_num][a_num],
-                                                       trustee.decryption_proofs[q_num][a_num], ElGamal.fiatshamir_challenge_generator)) {
-                                           result_append("-- Trustee " + trustee.email + ": decryption factor verifies");
-                                       } else {
-                                           result_append("==== ERROR with Trustee " + trustee.email + ": decryption factor does not verify");
-                                           check= false;
-                                           overall_result = false;
-                                       }
-
-                                       decryption_factors.push(trustee.decryption_factors[q_num][a_num]);
-                                   });
-
-                                   // recheck decryption factors
-                                   var expected_value = election.public_key.g.modPow(BigInt.fromInt(results[q_num][a_num]), election.public_key.p);                        
-                                   var recomputed_value = a.decrypt(decryption_factors).getM();
-                                   if (expected_value.equals(recomputed_value)) {
+                       $(tally).each(function(q_num, q) {
+                           result_append("Question #" + (q_num+1) + ": " + election.questions[q_num].short_name);
+                           $(q).each(function(a_num, a) {
+                               var plaintext = new ElGamal.Plaintext(election.public_key.g.modPow(BigInt.fromInt(results[q_num][a_num]), election.public_key.p), election.public_key);
+
+                               var check = true;
+                               result_append("Answer #" + (a_num + 1) + ": " + election.questions[q_num].answers[a_num] + " - COUNT = " + results[q_num][a_num]);
+
+                               var decryption_factors = [];
+
+                               // go through the trustees' decryption factors and verify each one
+                               $(trustees).each(function(t_num, trustee) {
+                                   if (trustee.public_key.verifyDecryptionFactor(a, trustee.decryption_factors[q_num][a_num],
+                                                   trustee.decryption_proofs[q_num][a_num], ElGamal.fiatshamir_challenge_generator)) {
+                                       result_append("-- Trustee " + trustee.email + ": decryption factor verifies");
                                    } else {
-                                       check = false;
+                                       result_append("==== ERROR with Trustee " + trustee.email + ": decryption factor does not verify");
+                                       check= false;
                                        overall_result = false;
                                    }
 
-                                   result_append("-" + pretty_result(check));
+                                   decryption_factors.push(trustee.decryption_factors[q_num][a_num]);
                                });
 
+                               // recheck decryption factors
+                               var expected_value = election.public_key.g.modPow(BigInt.fromInt(results[q_num][a_num]), election.public_key.p);                        
+                               var recomputed_value = a.decrypt(decryption_factors).getM();
+                               if (expected_value.equals(recomputed_value)) {
+                               } else {
+                                   check = false;
+                                   overall_result = false;
+                               }
+
+                               result_append("-" + pretty_result(check));
                            });
 
-                           result_append("<h3>FINAL RESULT</h3>");
+                       });
 
-                           if (overall_result) {
-                             result_append("ELECTION FULLY VERIFIED -- SUCCESS!");
-                           } else {
-                             result_append("VERIFICATION FAILED");
-                           }
-                        });
+                       result_append("<h3>FINAL RESULT</h3>");
+        
+                       if (overall_result) {
+                         result_append("ELECTION FULLY VERIFIED -- SUCCESS!");
+                       } else {
+                         result_append("VERIFICATION FAILED");
+                       }
                     });
-                });
-
+                });                
             });
-    } catch (error) {
-        result_append("<p>It appears that you are trying to verify a private election.</p>");
-        result_append('<p>You can log in as a valid voter or log in as the election admin.</p>');
-        result_append('<a class="btn" href="' + election_url + '">Log in as a valid voter </a>');
-        result_append('<a class="btn" href="/auth/?return_url=/verifier/verify.html?election_url=' + election_url + '">Log in as the election admin</a>');
-    }
+            
+        });
     });
         
 }
diff --git a/requirements.txt b/requirements.txt
index 237e75c..77a8496 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-Django==1.8.19
+Django==1.8.18
 anyjson==0.3.3
 celery==3.1.18
 django-celery==3.1.16
@@ -10,8 +10,8 @@ pyparsing==1.5.7
 python-dateutil>=1.5
 python-openid==2.2.5
 wsgiref==0.1.2
-gunicorn==19.9
-requests==2.21.0
+gunicorn==19.3
+requests==2.7.0
 unicodecsv==0.9.0
 dj_database_url==0.3.0
 django-sslify==0.2.7
diff --git a/reset.sh b/reset.sh
index 52141e1..f7ad853 100755
--- a/reset.sh
+++ b/reset.sh
@@ -1,5 +1,4 @@
 #!/bin/bash
-set -e  # Exit immediately if a command exits with a non-zero status.
 dropdb helios
 createdb helios
 python manage.py syncdb
diff --git a/runtime.txt b/runtime.txt
index f27f1cc..2b55d15 100644
--- a/runtime.txt
+++ b/runtime.txt
@@ -1 +1 @@
-python-2.7.15
+python-2.7.10
diff --git a/server_ui/glue.py b/server_ui/glue.py
index 9b3b5b3..bcb0995 100644
--- a/server_ui/glue.py
+++ b/server_ui/glue.py
@@ -5,7 +5,6 @@ Glue some events together
 from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.conf import settings
-from helios.view_utils import render_template_raw
 import helios.views, helios.signals
 
 import views
@@ -13,18 +12,30 @@ import views
 def vote_cast_send_message(user, voter, election, cast_vote, **kwargs):
   ## FIXME: this doesn't work for voters that are not also users
   # prepare the message
-  subject_template = 'email/cast_vote_subject.txt'
-  body_template = 'email/cast_vote_body.txt'
+  subject = "%s - vote cast" % election.name
   
-  extra_vars = {
-    'election' : election,
-    'voter': voter,
-    'cast_vote': cast_vote,
-    'cast_vote_url': helios.views.get_castvote_url(cast_vote),
-    'custom_subject' : "%s - vote cast" % election.name
-  }
-  subject = render_template_raw(None, subject_template, extra_vars)
-  body = render_template_raw(None, body_template, extra_vars)
+  body = """
+You have successfully cast a vote in
+
+  %s
+  
+Your ballot is archived at:
+
+  %s
+""" % (election.name, helios.views.get_castvote_url(cast_vote))
+  
+  if election.use_voter_aliases:
+    body += """
+
+This election uses voter aliases to protect your privacy.
+Your voter alias is : %s    
+""" % voter.alias
+
+  body += """
+
+--
+%s
+""" % settings.SITE_TITLE  
   
   # send it via the notification system associated with the auth system
   user.send_message(subject, body)
diff --git a/server_ui/media/boothcss/forms.css b/server_ui/media/boothcss/forms.css
index ae4cbb7..ee1a141 100644
--- a/server_ui/media/boothcss/forms.css
+++ b/server_ui/media/boothcss/forms.css
@@ -7,7 +7,7 @@ form.prettyform label,input,textarea,select {
     line-height: 1.8;
 }
 
-form.prettyform label:not(.answer) {
+form.prettyform label {
     display: block;
     text-align: right;
     float: left;
diff --git a/server_ui/templates/email/cast_vote_body.txt b/server_ui/templates/email/cast_vote_body.txt
deleted file mode 100644
index e248c36..0000000
--- a/server_ui/templates/email/cast_vote_body.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Dear {{voter.name}},
-
-You have successfully cast a vote in {{election.name}}.
-
-Your ballot is archived at: {{cast_vote_url}}
-
-{% if election.use_voter_aliases %}
-This election uses voter aliases to protect your privacy.
-Your voter alias is: {{voter.alias}}.
-{% endif %}
-
---
-
-Helios
diff --git a/server_ui/templates/email/cast_vote_subject.txt b/server_ui/templates/email/cast_vote_subject.txt
deleted file mode 100644
index a7929f1..0000000
--- a/server_ui/templates/email/cast_vote_subject.txt
+++ /dev/null
@@ -1 +0,0 @@
-{{custom_subject|safe}}
diff --git a/server_ui/templates/index.html b/server_ui/templates/index.html
index e31ac19..2b0a0e2 100644
--- a/server_ui/templates/index.html
+++ b/server_ui/templates/index.html
@@ -24,7 +24,7 @@ Helios elections are:
 </ul>
 
 <p>
-More than <b>2,000,000 votes</b> have been cast using Helios.
+More than <b>100,000 votes</b> have been cast using Helios.
 </p>
 
 {% if create_p %}
diff --git a/server_ui/urls.py b/server_ui/urls.py
index 0711bd7..d71e04d 100644
--- a/server_ui/urls.py
+++ b/server_ui/urls.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
-from django.conf.urls import patterns
+from django.conf.urls import *
 
-from views import home, about, docs, faq, privacy
+from views import *
 
 urlpatterns = patterns('',
   (r'^$', home),
-- 
GitLab