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 %}—{% 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 %}—{% 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]] != ""} - - <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]] != ""} + +<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