def encrypt_ballots(request: EncryptBallotsRequest = Body(...)) -> Any: """ Encrypt one or more ballots """ ballots = [ PlaintextBallot.from_json_object(ballot) for ballot in request.ballots ] description = InternalElectionDescription( ElectionDescription.from_json_object(request.description)) context = CiphertextElectionContext.from_json_object(request.context) seed_hash = read_json_object(request.seed_hash, ElementModQ) nonce: Optional[ElementModQ] = (read_json_object( request.nonce, ElementModQ) if request.nonce else None) encrypted_ballots = [] current_hash = seed_hash for ballot in ballots: encrypted_ballot = encrypt_ballot(ballot, description, context, current_hash, nonce) if not encrypted_ballot: raise HTTPException(status_code=500, detail="Ballot failed to encrypt") encrypted_ballots.append(encrypted_ballot) current_hash = get_optional(encrypted_ballot.tracking_hash) response = EncryptBallotsResponse( encrypted_ballots=[ ballot.to_json_object() for ballot in encrypted_ballots ], next_seed_hash=write_json_object(current_hash), ) return response
def build_election(self, election_creation: dict): self.election = ElectionDescription.from_json_object( complete_election_description(election_creation['description'])) if not self.election.is_valid(): raise InvalidElectionDescription() self.number_of_guardians = len(election_creation['trustees']) self.quorum = election_creation['scheme']['parameters']['quorum'] self.election_builder = ElectionBuilder(self.number_of_guardians, self.quorum, self.election)
def handle_ballot(request: AcceptBallotRequest, state: BallotBoxState) -> Any: ballot = CiphertextBallot.from_json_object(request.ballot) description = ElectionDescription.from_json_object(request.description) internal_description = InternalElectionDescription(description) context = CiphertextElectionContext.from_json_object(request.context) accepted_ballot = accept_ballot( ballot, state, internal_description, context, BallotStore(), ) return accepted_ballot
def _parse_tally_request( request: StartTallyRequest, ) -> Tuple[List[CiphertextAcceptedBallot], InternalElectionDescription, CiphertextElectionContext, ]: """ Deserialize common tally request values """ ballots = [ CiphertextAcceptedBallot.from_json_object(ballot) for ballot in request.ballots ] description = ElectionDescription.from_json_object(request.description) internal_description = InternalElectionDescription(description) context = CiphertextElectionContext.from_json_object(request.context) return (ballots, internal_description, context)
def build_election_context(request: ElectionContextRequest = Body(...)) -> Any: """ Build a CiphertextElectionContext for a given election """ description: ElectionDescription = ElectionDescription.from_json_object( request.description) elgamal_public_key: ElementModP = read_json_object( request.elgamal_public_key, ElementModP) number_of_guardians = request.number_of_guardians quorum = request.quorum context = make_ciphertext_election_context(number_of_guardians, quorum, elgamal_public_key, description.crypto_hash()) return write_json_object(context)
def decrypt_share( request: DecryptTallyShareRequest = Body(...), scheduler: Scheduler = Depends(get_scheduler), ) -> Any: """ Decrypt a single guardian's share of a tally """ description = InternalElectionDescription( ElectionDescription.from_json_object(request.description) ) context = CiphertextElectionContext.from_json_object(request.context) guardian = convert_guardian(request.guardian) tally = convert_tally(request.encrypted_tally, description, context) share = compute_decryption_share(guardian, tally, context, scheduler) return write_json_object(share)
def validate_election_description( request: ValidateElectionDescriptionRequest = Body(...), schema: Any = Depends(get_description_schema), ) -> Any: """ Validate an Election description or manifest for a given election """ success = True message = "Election description successfully validated" details = "" # Check schema schema = request.schema_override if request.schema_override else schema (schema_success, error_details) = validate_json_schema(request.description, schema) if not schema_success: success = schema_success message = "Election description did not match schema" details = error_details # Check object parse description: Optional[ElectionDescription] = None if success: try: description = ElectionDescription.from_json_object( request.description) except Exception: # pylint: disable=broad-except success = False message = "Election description could not be read from JSON" if success: if description: valid_success = description.is_valid() if not valid_success: message = "Election description was not valid well formed data" # Check return ValidationResponse(success=success, message=message, details=details)
def decrypt_tally(request: DecryptTallyRequest = Body(...)) -> Any: """ Decrypt a tally from a collection of decrypted guardian shares """ description = InternalElectionDescription( ElectionDescription.from_json_object(request.description)) context = CiphertextElectionContext.from_json_object(request.context) tally = convert_tally(request.encrypted_tally, description, context) shares = { guardian_id: read_json_object(share, TallyDecryptionShare) for guardian_id, share in request.shares.items() } full_plaintext_tally = decrypt(tally, shares, context) if not full_plaintext_tally: raise HTTPException( status_code=500, detail="Unable to decrypt tally", ) published_plaintext_tally = publish_plaintext_tally(full_plaintext_tally) return published_plaintext_tally.to_json_object()