def add(request): if request.method == 'POST': # If the form has been submitted... form = KeyForm(request.POST) # A form bound to the POST data if form.is_valid(): # All validation rules pass # Process the data in form.cleaned_data # ... instances = form.save(commit=False) public_key_encrypt = algs.EGPublicKey.from_dict( utils.from_json(instances.public_key_encrypt)) pok_encrypt = algs.DLogProof.from_dict( utils.from_json(instances.pok_encrypt)) public_key_signing = algs.EGPublicKey.from_dict( utils.from_json(instances.public_key_signing)) pok_signing = algs.DLogProof.from_dict( utils.from_json(instances.pok_signing)) if public_key_encrypt.verify_sk_proof(pok_encrypt, algs.DLog_challenge_generator): pass else: return HttpResponse('Wrong pok_encrypt') if public_key_signing.verify_sk_proof(pok_signing, algs.DLog_challenge_generator): pass else: return HttpResponse('Wrong pok_signing') form.save() # Redirect after POST return HttpResponseRedirect('/bulletin_board/communication_keys/view') else: form = KeyForm() # An unbound form template = loader.get_template('bulletin_board/add_key.html') context = Context() return render_template(request, 'add_key', {})
def public_key_form(request): if request.method == "POST": # get the public key and the hash, and add it key = Key() key.name = request.POST['name'] key.email = request.POST['email'] public_key_and_proof_enc = utils.from_json( request.POST['public_key_json_enc']) public_key_enc = algs.EGPublicKey.fromJSONDict( public_key_and_proof_enc['public_key']) pok_enc = algs.DLogProof.fromJSONDict(public_key_and_proof_enc['pok']) # verify the pok if not public_key_enc.verify_sk_proof(pok_enc, algs.DLog_challenge_generator): raise Exception("bad pok for public key encrypting") key.public_key_encrypt = utils.to_json(public_key_enc.to_dict()) key.pok_encrypt = utils.to_json(pok_enc.to_dict()) key.public_key_encrypt_hash = cryptoutils.hash_b64( key.public_key_encrypt) public_key_and_proof_sign = utils.from_json( request.POST['public_key_json_sign']) public_key_sign = algs.EGPublicKey.fromJSONDict( public_key_and_proof_sign['public_key']) pok_sign = algs.DLogProof.fromJSONDict( public_key_and_proof_sign['pok']) # verify the pok if not public_key_sign.verify_sk_proof(pok_sign, algs.DLog_challenge_generator): raise Exception("bad pok for public key signing") key.public_key_signing = utils.to_json(public_key_sign.to_dict()) key.pok_signing = utils.to_json(pok_sign.to_dict()) key.public_key_signing_hash = cryptoutils.hash_b64( key.public_key_signing) key.save() # send a note to admin try: election.admin.send_message( "pk upload, " + "%s uploaded a pk for communication." % (key.name)) except: # oh well, no message sent pass return HttpResponseRedirect('/bulletin_board/') """ A key generator with the current params, like the trustee home but without a specific election. """ eg_params_json = utils.to_json(ELGAMAL_PARAMS_LD_OBJECT.toJSONDict()) return render_template(request, "election_publickeygenerator", {'eg_params_json': eg_params_json})
def trustee_upload_pk(request, election, trustee): if request.method == "POST": # get the public key and the hash, and add it public_key_and_proof = utils.from_json(request.POST['public_key_json']) trustee.public_key = algs.EGPublicKey.fromJSONDict( public_key_and_proof['public_key']) trustee.pok = algs.DLogProof.fromJSONDict(public_key_and_proof['pok']) # verify the pok 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 = utils.hash_b64( utils.to_json(trustee.public_key.toJSONDict())) trustee.save() # send a note to admin try: election.admin.send_message( "%s - trustee pk upload" % election.name, "trustee %s (%s) uploaded a pk." % (trustee.name, trustee.email)) except: # oh well, no message sent pass return HttpResponseRedirect(reverse(trustee_home, args=[election.uuid, trustee.uuid]))
def _cast_vote(self, election, voter, answers): answers = [answers] self._setup_voter_login(voter) response = self.app.post("/helios/elections/%s/encrypt-ballot" % election.uuid, { 'answers_json': utils.to_json(answers)}) self.assertContains(response, "answers") # parse it as an encrypted vote with randomness, and make sure randomness is there the_ballot = utils.from_json(response.testbody) assert the_ballot['answers'][0].has_key('randomness'), "no randomness" # parse it as an encrypted vote, and re-serialize it ballot = datatypes.LDObject.fromDict(utils.from_json(response.testbody), 'phoebus/EncryptedVote') encrypted_vote = ballot.serialize() response = self.app.post("/helios/elections/%s/cast" % election.uuid, { 'encrypted_vote': encrypted_vote}) response = self.app.get("/helios/elections/%s/cast_confirm" % election.uuid) response.form.submit() response = self.app.get("/helios/elections/%s/cast_done" % election.uuid)
def download_data(request, election_id, receiver_id): election = Election.objects.get(id=election_id) scheme = Thresholdscheme.objects.get(election=election) n = scheme.n k = scheme.k shares_list = Signed_Encrypted_Share.objects.filter( election_id=election_id).filter(receiver_id=receiver_id).order_by('signer_id') trustees = Trustee.objects.filter(election=election) if (len(trustees) == scheme.n): pk_list_string = [] for i in range(len(trustees)): pk_list_string.append(trustees[i].key) else: pk_list_string = None pk_list = [] pk_id_list = [] for i in range(len(pk_list_string)): pk_list.append(elgamal.PublicKey.from_dict( utils.from_json(pk_list_string[i].public_key_signing))) pk_id_list.append(pk_list_string[i].id) string = '' string = string + utils.to_json({'p': p, 'q': q, 'g': g, 'ground_1': scheme.ground_1, 'ground_2': scheme.ground_2, 'k': scheme.k, 'n': n}) + '\n' for i in range(len(pk_list)): string = string + str(pk_id_list[i]) + '\n' string = string + utils.to_json(pk_list[i].to_dict()) + '\n' for i in range(len(shares_list)): share = shares_list[i] string = string + utils.to_json({'share': share.share, 'election_id': share.election_id, 'signer': share.signer, 'signer_id': share.signer_id, 'receiver': share.receiver, 'receiver_id': share.receiver_id}) string = string + '\n' file = open( 'bulletin_board/files/data_receiver_' + str(receiver_id) + '.txt', "w") file.write(string) file.close() file = open( 'bulletin_board/files/data_receiver_' + str(receiver_id) + '.txt', "r") mimetype = mimetypes.guess_type( 'data_receiver_' + str(receiver_id) + '.txt')[0] if not mimetype: mimetype = "application/octet-stream" response = HttpResponse(file.read(), mimetype=mimetype) response[ "Content-Disposition"] = "attachment; filename=%s" % os.path.split('data_receiver.txt')[1] return response
def _do_tally(self, election_id): # log back in as administrator self.setup_login() # encrypted tally response = self.client.post( "/helios/elections/%s/compute_tally" % election_id, {"csrf_token": self.client.session['csrf_token']}) self.assertRedirects(response, "/helios/elections/%s/view" % election_id) # should trigger helios decryption automatically self.assertNotEquals( models.Election.objects.get( uuid=election_id).get_helios_trustee().decryption_proofs, None) # combine decryptions response = self.client.post( "/helios/elections/%s/combine_decryptions" % election_id, { "csrf_token": self.client.session['csrf_token'], }) # after tallying, we now go back to election_view self.assertRedirects(response, "/helios/elections/%s/view" % election_id) # check that we can't get the tally yet response = self.client.get("/helios/elections/%s/result" % election_id) self.assertStatusCode(response, 403) # release response = self.client.post( "/helios/elections/%s/release_result" % election_id, { "csrf_token": self.client.session['csrf_token'], }) # check that tally matches response = self.client.get("/helios/elections/%s/result" % election_id) self.assertEquals(utils.from_json(response.content), [[0, 1]])
def _cast_ballot( self, election_id, username, password, need_login=True, check_user_logged_in=False, ): """ check_user_logged_in looks for the "you're already logged" message """ # vote by preparing a ballot via the server-side encryption response = self.app.post( "/helios/elections/%s/encrypt-ballot" % election_id, params={"answers_json": utils.to_json([[1]])}, ) assert "answers" in response.content.decode() # parse it as an encrypted vote with randomness, and make sure randomness is there the_ballot = utils.from_json(response.testbody) assert the_ballot["answers"][0].has_key("randomness"), "no randomness" assert len(the_ballot["answers"][0] ["randomness"]) == 2, "not enough randomness" # parse it as an encrypted vote, and re-serialize it ballot = datatypes.LDObject.fromDict(utils.from_json( response.testbody), type_hint="legacy/EncryptedVote") encrypted_vote = ballot.serialize() # cast the ballot response = self.app.post( "/helios/elections/%s/cast" % election_id, params={"encrypted_vote": encrypted_vote}, ) self.assertRedirects( response, "%s/helios/elections/%s/cast_confirm" % (settings.SECURE_URL_HOST, election_id), ) cast_confirm_page = response.follow() if need_login: if check_user_logged_in: self.assertContains(cast_confirm_page, "You are logged in as") self.assertContains(cast_confirm_page, "requires election-specific credentials") # set the form login_form = cast_confirm_page.form login_form["voter_id"] = username login_form["password"] = password response = login_form.submit() else: # here we should be at the cast-confirm page and logged in self.assertContains(cast_confirm_page, "CAST this ballot") # confirm the vote, now with the actual form cast_form = cast_confirm_page.form if "status_update" in cast_form.fields.keys(): cast_form["status_update"] = False response = cast_form.submit() self.assertRedirects( response, "%s/helios/elections/%s/cast_done" % (settings.URL_HOST, election_id), ) # at this point an email should have gone out to the user # at position num_messages after, since that was the len() before we cast this ballot email_message = mail.outbox[len(mail.outbox) - 1] url = re.search("https?://[^/]+(/[^ \n]*)", email_message.body).group(1) # check that we can get at that URL if not need_login: # confusing piece: if need_login is True, that means it was a public election # that required login before casting a ballot. # so if need_login is False, it was a private election, and we do need to re-login here # we need to re-login if it's a private election, because all data, including ballots # is otherwise private login_page = self.app.get( "/helios/elections/%s/password_voter_login" % election_id) # if we redirected, that's because we can see the page, I think if login_page.status_int != 302: login_form = login_page.form # try with extra spaces login_form["voter_id"] = " " + username + " " login_form["password"] = "******" + password + " " login_form.submit() response = self.app.get(url, auto_follow=True) assert ballot.hash in response.content.decode() assert html_escape(encrypted_vote) in response.content.decode() # if we request the redirect to cast_done, the voter should be logged out, but not the user response = self.app.get("/helios/elections/%s/cast_done" % election_id)
def _setup_complete_election(self, election_params=None): "do the setup part of a whole election" # REPLACE with params? self.setup_login(from_scratch=True) # create the election full_election_params = { "short_name": "test-complete", "name": "Test Complete", "description": "A complete election test", "election_type": "referendum", "use_voter_aliases": "0", "use_advanced_audit_features": "1", "private_p": "False", "csrf_token": self.client.session["csrf_token"], } # override with the given full_election_params.update(election_params or {}) response = self.client.post("/helios/elections/new", full_election_params) # we are redirected to the election, let's extract the ID out of the URL election_id = re.search("/elections/([^/]+)/", str(response["Location"])) self.assertIsNotNone( election_id, "Election id not found in redirect: %s" % str(response["Location"]), ) election_id = election_id.group(1) # helios is automatically added as a trustee # check that helios is indeed a trustee response = self.client.get("/helios/elections/%s/trustees/view" % election_id) assert "Trustee #1" in response.content.decode() # add a few voters with an improperly placed email address FILE = "helios/fixtures/voter-badfile.csv" voters_file = open(FILE) response = self.client.post( "/helios/elections/%s/voters/upload" % election_id, {"voters_file": voters_file}, ) voters_file.close() assert "HOLD ON" in response.content.decode() # add a few voters, via file upload # this file now includes a UTF-8 encoded unicode character # yes I know that's not how you spell Ernesto. # I just needed some unicode quickly. FILE = "helios/fixtures/voter-file.csv" voters_file = open(FILE) response = self.client.post( "/helios/elections/%s/voters/upload" % election_id, {"voters_file": voters_file}, ) voters_file.close() assert "first few rows of this file" in response.content.decode() # now we confirm the upload response = self.client.post( "/helios/elections/%s/voters/upload" % election_id, {"confirm_p": "1"}) self.assertRedirects(response, "/helios/elections/%s/voters/list" % election_id) # and we want to check that there are now voters response = self.client.get("/helios/elections/%s/voters/" % election_id) NUM_VOTERS = 4 self.assertEquals(len(utils.from_json(response.content)), NUM_VOTERS) # let's get a single voter single_voter = models.Election.objects.get( uuid=election_id).voter_set.all()[0] response = self.client.get("/helios/elections/%s/voters/%s" % (election_id, single_voter.uuid)) assert '"uuid": "%s"' % single_voter.uuid in response.content.decode() response = self.client.get("/helios/elections/%s/voters/foobar" % election_id) self.assertStatusCode(response, 404) # add questions response = self.client.post( "/helios/elections/%s/save_questions" % election_id, { "questions_json": utils.to_json([{ "answer_urls": ["http://example.com", None], "answers": ["Alice", "Bob"], "choice_type": "approval", "max": 1, "min": 0, "question": "Who should be president?", "result_type": "absolute", "short_name": "Who should be president?", "tally_type": "homomorphic", }]), "csrf_token": self.client.session["csrf_token"], }, ) assert "SUCCESS" in response.content.decode() # freeze election response = self.client.post( "/helios/elections/%s/freeze" % election_id, {"csrf_token": self.client.session["csrf_token"]}, ) self.assertRedirects(response, "/helios/elections/%s/view" % election_id) # email the voters num_messages_before = len(mail.outbox) response = self.client.post( "/helios/elections/%s/voters/email" % election_id, { "csrf_token": self.client.session["csrf_token"], "subject": "your password", "body": "time to vote", "suppress_election_links": "0", "send_to": "all", }, ) self.assertRedirects(response, "/helios/elections/%s/view" % election_id) num_messages_after = len(mail.outbox) self.assertEquals(num_messages_after - num_messages_before, NUM_VOTERS) email_message = mail.outbox[num_messages_before] assert "your password" in email_message.subject, "bad subject in email" # get the username and password username = re.search("voter ID: (.*)", email_message.body).group(1) password = re.search("password: (.*)", email_message.body).group(1) # now log out as administrator self.clear_login() self.assertEquals(self.client.session.has_key("user"), False) # return the voter username and password to vote return election_id, username, password
def test_get_election_voters_raw(self): response = self.client.get("/helios/elections/%s/voters/" % self.election.uuid, follow=False) assert (len(utils.from_json( response.content.decode())) == self.election.num_voters)
def add_encrypted_shares(request, election_id, signature=None): ELGAMAL_PARAMS = algs.ElGamal() ELGAMAL_PARAMS.p = p ELGAMAL_PARAMS.q = q ELGAMAL_PARAMS.g = g election = Election.objects.filter(id=election_id)[0] trustees = Trustee.objects.filter(election=election).order_by('id') scheme = Thresholdscheme.objects.filter(election=election)[0] n = scheme.n pk_list = [] for i in range(len(trustees)): pk_list.append(trustees[i].key) if len(pk_list) != n: return HttpResponse('The number of public keys for communication must equal: ' + str(n)) # If the form has been submitted... if (request.method == 'POST')or(signature): form = SignatureForm(request.POST) # A form bound to the POST data if (form.is_valid())or(signature): # All validation rules pass # Process the data in form.cleaned_data # ... if(signature == None): instances = form.save(commit=False) signature = instances.signature secret_key_sig = algs.EGSecretKey.from_dict( utils.from_json(signature)) #signer_id = 1 for j in range(len(pk_list)): pk = pk_list[j] if (pow(g, secret_key_sig.x, p) == elgamal.PublicKey.from_dict(utils.from_json(pk.public_key_signing)).y): signer = pk.name signer_id = pk.id trustee_signer_id = trustees[j].id break if not signer: return HttpResponse('Your signature doesnt belong to the election: ' + election.name) if (len(Signed_Encrypted_Share.objects.filter(signer_id=signer_id).filter(election_id=election_id)) > 0): return render_template(request, 'shares_already_uploaded', {'signer': signer, 'election': election}) s = Utils.random_mpz_lt(q) t = Utils.random_mpz_lt(q) shares = scheme.share_verifiably(s, t, ELGAMAL_PARAMS) if len(pk_list) == len(shares): # f=open('Encrypted_shares'+str(trustee_id)+'.txt','w') for i in range(len(trustees)): trustee_temp = trustees[i] key = trustee_temp.key receiver = key.name receiver_id = key.id share = shares[i] share_string = cryptoutils.to_json_js(share.to_dict()) if(share.point_s.x_value != trustee_temp.id): return HttpResponse('Shares have wrong x_coordinate') encry_share = share.encrypt( algs.EGPublicKey.from_dict(utils.from_json(key.public_key_encrypt))) sig = share.sign(secret_key_sig, p, q, g) signed_encry_share = thresholdalgs.Signed_Encrypted_Share( sig, encry_share) encry_share = Signed_Encrypted_Share() encry_share.share = utils.to_json( signed_encry_share.to_dict()) pk_sign = Key.objects.filter(id=signer_id)[0] if(sig.verify(share_string, algs.EGPublicKey.from_dict(utils.from_json(pk_sign.public_key_signing)), p, q, g)): encry_share.signer = pk_sign.name encry_share.signer_id = signer_id encry_share.receiver = receiver encry_share.receiver_id = receiver_id encry_share.election_id = election_id encry_share.trustee_receiver_id = trustees[i].id encry_share.trustee_signer_id = trustee_signer_id encry_share.save() else: # Dit doet hij return HttpResponse('Wrong Signature') if (len(Signed_Encrypted_Share.objects.filter(signer_id=signer_id).filter(election_id=election.id)) == scheme.n): signer_key = Key.objects.get(id=signer_id) signer_trustee = Trustee.objects.filter( key=signer_key)[0] signer_trustee.added_encrypted_shares = True signer_trustee.save() return HttpResponseRedirect('/bulletin_board/elections/' + str(election.id) + '/') else: return HttpResponse('pk_list and shares havent the same length') return render_template() else: form = SignatureForm() # An unbound form template = loader.get_template('bulletin_board/signature_form.html') context = Context() return render_template(request, 'signature_form', {'election': election})
def election_get(self, election_id): return electionalgs.Election.fromJSONDict(utils.from_json(self.get("/elections/%s/" % election_id)))
def params(self): params_json = self.get("/elections/params") return algs.ElGamal.fromJSONDict(utils.from_json(params_json))
""" # give the base path for importing import sys sys.path.append('../') from helios import utils from crypto import algs, electionalgs # parse an election election = electionalgs.Election.fromJSONDict(utils.from_json(""" {"election_id": "agxoZWxpb3N2b3RpbmdyDgsSCEVsZWN0aW9uGDUM", "name": "Testing", "pk": {"g": "68111451286792593845145063691659993410221812806874234365854504719057401858372594942893291581957322023471947260828209362467690671421429979048643907159864269436501403220400197614308904460547529574693875218662505553938682573554719632491024304637643868603338114042760529545510633271426088675581644231528918421974", "p": "169989719781940995935039590956086833929670733351333885026079217526937746166790934510618940073906514429409914370072173967782198129423558224854191320917329420870526887804017711055077916007496804049206725568956610515399196848621653907978580213217522397058071043503404700268425750722626265208099856407306527012763", "q": "84994859890970497967519795478043416964835366675666942513039608763468873083395467255309470036953257214704957185036086983891099064711779112427095660458664710435263443902008855527538958003748402024603362784478305257699598424310826953989290106608761198529035521751702350134212875361313132604049928203653263506381", "y": "138965420538060877725598288443038715549505041934920830506963758871983703536539489932164465777433596361969779587970636705737864807597351351075991866746398793736744305399650068324779832920449571128406381195057599494539495719265448905682934458434094839365843432367426280317832018301235396448313025835566537081578"}, "questions": [{"answers": ["ice-cream", "cake"], "max": 1, "question": "ice-cream or cake?", "short_name": "dessert"}], "voters_hash": "2nMJu6KQ5nNJxySS6vwvCtx\/mbI", "voting_ends_at": null, "voting_starts_at": null} """)) print election.hash # ballot #1 - 6nGjE9hDIJh+gJLbBs+V8gCnqwM, ice-cream ballot_1 = electionalgs.EncryptedVote.fromJSONDict(utils.from_json(""" {"answers": [{"choices": [{"alpha": "79423156875790789386636380711411557382011404179785862301851615012301997738581616233606306565176776628181638207312074846014426085937540815198122397466359879439087155788105406682550892189407933604393108898921993667488924252715342976726016778309376458081564159805006567274567646092947902027698679865295747216740", "beta": "143572946708780195227746097638279374240715557430362133497355213227909252587541440720698294397928771371398316440609397662289105847496010523722521262815906074191163698789679619483819131879040287456403168217586341933589428747564875966367655575868585312270511247141253264850857205009342714491750000543927983547067"}, {"alpha": "24649237060119825898230579770062433501527408953598772450873437762686027284229586224876293478401349759892180283537796569765680781002239651357595392751435996613998837243105555985368958866562000845697200263898545146523028797479534681009418642713785547377151966355257490739865674397955223055114479655315692948265",
# give the base path for importing import sys sys.path.append('../') from helios import utils from crypto import algs, electionalgs # parse an election election = electionalgs.Election.fromJSONDict( utils.from_json(""" {"election_id": "agxoZWxpb3N2b3RpbmdyDgsSCEVsZWN0aW9uGDUM", "name": "Testing", "pk": {"g": "68111451286792593845145063691659993410221812806874234365854504719057401858372594942893291581957322023471947260828209362467690671421429979048643907159864269436501403220400197614308904460547529574693875218662505553938682573554719632491024304637643868603338114042760529545510633271426088675581644231528918421974", "p": "169989719781940995935039590956086833929670733351333885026079217526937746166790934510618940073906514429409914370072173967782198129423558224854191320917329420870526887804017711055077916007496804049206725568956610515399196848621653907978580213217522397058071043503404700268425750722626265208099856407306527012763", "q": "84994859890970497967519795478043416964835366675666942513039608763468873083395467255309470036953257214704957185036086983891099064711779112427095660458664710435263443902008855527538958003748402024603362784478305257699598424310826953989290106608761198529035521751702350134212875361313132604049928203653263506381", "y": "138965420538060877725598288443038715549505041934920830506963758871983703536539489932164465777433596361969779587970636705737864807597351351075991866746398793736744305399650068324779832920449571128406381195057599494539495719265448905682934458434094839365843432367426280317832018301235396448313025835566537081578"}, "questions": [{"answers": ["ice-cream", "cake"], "max": 1, "question": "ice-cream or cake?", "short_name": "dessert"}], "voters_hash": "2nMJu6KQ5nNJxySS6vwvCtx\/mbI", "voting_ends_at": null, "voting_starts_at": null} """)) print election.hash # ballot #1 - 6nGjE9hDIJh+gJLbBs+V8gCnqwM, ice-cream ballot_1 = electionalgs.EncryptedVote.fromJSONDict( utils.from_json(""" {"answers": [{"choices": [{"alpha": "79423156875790789386636380711411557382011404179785862301851615012301997738581616233606306565176776628181638207312074846014426085937540815198122397466359879439087155788105406682550892189407933604393108898921993667488924252715342976726016778309376458081564159805006567274567646092947902027698679865295747216740", "beta": "143572946708780195227746097638279374240715557430362133497355213227909252587541440720698294397928771371398316440609397662289105847496010523722521262815906074191163698789679619483819131879040287456403168217586341933589428747564875966367655575868585312270511247141253264850857205009342714491750000543927983547067"}, {"alpha":