Example #1
0
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', {})
Example #2
0
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})
Example #3
0
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]))
Example #4
0
    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)
Example #5
0
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
Example #6
0
    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]])
Example #7
0
    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)
Example #8
0
    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
Example #9
0
 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)
Example #10
0
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))
Example #13
0
"""

# 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",
Example #14
0
# 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":