コード例 #1
0
    def test_decrypt_selection_with_all_guardians_present(self):
        # Arrange
        available_guardians = self.guardians

        # find the first selection
        first_contest = list(self.ciphertext_tally.contests.values())[0]
        first_selection = list(first_contest.selections.values())[0]

        print(first_contest.object_id)
        print(first_selection.object_id)

        # precompute decryption shares for specific selection for the guardians
        shares: Dict[GUARDIAN_ID,
                     Tuple[ELECTION_PUBLIC_KEY, DecryptionShare]] = {
                         guardian.id: (
                             guardian.share_election_public_key().key,
                             compute_decryption_share(
                                 guardian._election_keys,
                                 self.ciphertext_tally,
                                 self.context,
                             ).contests[first_contest.object_id].selections[
                                 first_selection.object_id],
                         )
                         for guardian in available_guardians
                     }

        # Act
        result = decrypt_selection_with_decryption_shares(
            first_selection, shares, self.context.crypto_extended_base_hash)

        # Assert
        self.assertIsNotNone(result)
        self.assertEqual(
            self.expected_plaintext_tally[first_selection.object_id],
            result.tally)
コード例 #2
0
    def test_decrypt_selection_all_present(self):
        # Arrange

        # find the first selection
        first_contest = [contest for contest in self.ciphertext_tally.cast.values()][0]
        first_selection = list(first_contest.tally_selections.values())[0]

        # precompute decryption shares for the guardians
        first_share = compute_decryption_share(
            self.guardians[0], self.ciphertext_tally, self.context
        )
        second_share = compute_decryption_share(
            self.guardians[1], self.ciphertext_tally, self.context
        )
        third_share = compute_decryption_share(
            self.guardians[2], self.ciphertext_tally, self.context
        )

        # build type: Dict[GUARDIAN_ID, Tuple[ELECTION_PUBLIC_KEY, TallyDecryptionShare]]
        shares = {
            self.guardians[0].object_id: (
                self.guardians[0].share_election_public_key().key,
                first_share.contests[first_contest.object_id].selections[
                    first_selection.object_id
                ],
            ),
            self.guardians[1].object_id: (
                self.guardians[1].share_election_public_key().key,
                second_share.contests[first_contest.object_id].selections[
                    first_selection.object_id
                ],
            ),
            self.guardians[2].object_id: (
                self.guardians[2].share_election_public_key().key,
                third_share.contests[first_contest.object_id].selections[
                    first_selection.object_id
                ],
            ),
        }

        # act
        result = decrypt_selection_with_decryption_shares(
            first_selection, shares, self.context.crypto_extended_base_hash
        )

        # assert
        self.assertIsNotNone(result)
        self.assertEqual(
            self.expected_plaintext_tally[first_selection.object_id], result.tally
        )
コード例 #3
0
    def process_message(self, message_type: str, message: dict,
                        context: Context):
        context.shares[message['guardian_id']] = message
        if len(context.shares) == context.number_of_guardians:
            tally_shares = self._prepare_shares_for_decryption(context.shares)

            results: Dict[CONTEST_ID, PlaintextTallyContest] = {}

            for contest in context.tally.cast.values():
                selections: Dict[SELECTION_ID, PlaintextTallySelection] = dict(
                    pair_with_object_id(
                        decrypt_selection_with_decryption_shares(
                            selection, tally_shares[selection.object_id],
                            context.election_context.crypto_extended_base_hash)
                    ) for selection in contest.tally_selections.values())

                results[contest.object_id] = PlaintextTallyContest(
                    contest.object_id, selections)

            return serialize(results)
コード例 #4
0
    def test_compute_compensated_selection(self):
        """
        demonstrates the complete workflow for computing a comepnsated decryption share
        For one selection. It is useful for verifying that the workflow is correct
        """
        # Arrange
        first_selection = [
            selection for contest in self.ciphertext_tally.cast.values()
            for selection in contest.tally_selections.values()
        ][0]

        # Compute lagrange coefficients for the guardians that are present
        lagrange_0 = compute_lagrange_coefficient(
            self.guardians[0].sequence_order,
            *[self.guardians[1].sequence_order],
        )
        lagrange_1 = compute_lagrange_coefficient(
            self.guardians[1].sequence_order,
            *[self.guardians[0].sequence_order],
        )

        print(
            f"lagrange: sequence_orders: ({self.guardians[0].sequence_order}, {self.guardians[1].sequence_order}, {self.guardians[2].sequence_order})\n"
        )

        print(lagrange_0)
        print(lagrange_1)

        # compute their shares
        share_0 = compute_decryption_share_for_selection(
            self.guardians[0], first_selection, self.context)

        share_1 = compute_decryption_share_for_selection(
            self.guardians[1], first_selection, self.context)

        self.assertIsNotNone(share_0)
        self.assertIsNotNone(share_1)

        # compute compensations shares for the missing guardian
        compensation_0 = compute_compensated_decryption_share_for_selection(
            self.guardians[0],
            self.guardians[2].object_id,
            first_selection,
            self.context,
            identity_auxiliary_decrypt,
        )

        compensation_1 = compute_compensated_decryption_share_for_selection(
            self.guardians[1],
            self.guardians[2].object_id,
            first_selection,
            self.context,
            identity_auxiliary_decrypt,
        )

        self.assertIsNotNone(compensation_0)
        self.assertIsNotNone(compensation_1)

        print("\nSHARES:")
        print(compensation_0)
        print(compensation_1)

        # Check the share proofs
        self.assertTrue(
            compensation_0.proof.is_valid(
                first_selection.message,
                get_optional(self.guardians[0].recovery_public_key_for(
                    self.guardians[2].object_id)),
                compensation_0.share,
                self.context.crypto_extended_base_hash,
            ))

        self.assertTrue(
            compensation_1.proof.is_valid(
                first_selection.message,
                get_optional(self.guardians[1].recovery_public_key_for(
                    self.guardians[2].object_id)),
                compensation_1.share,
                self.context.crypto_extended_base_hash,
            ))

        share_pow_p = [
            pow_p(compensation_0.share, lagrange_0),
            pow_p(compensation_1.share, lagrange_1),
        ]

        print("\nSHARE_POW_P")
        print(share_pow_p)

        # reconstruct the missing share from the compensation shares
        reconstructed_share = mult_p(*[
            pow_p(compensation_0.share, lagrange_0),
            pow_p(compensation_1.share, lagrange_1),
        ])

        print("\nRECONSTRUCTED SHARE\n")
        print(reconstructed_share)

        share_2 = CiphertextDecryptionSelection(
            first_selection.object_id,
            self.guardians[2].object_id,
            first_selection.description_hash,
            reconstructed_share,
            {
                self.guardians[0].object_id: compensation_0,
                self.guardians[1].object_id: compensation_1,
            },
        )

        # Decrypt the result
        result = decrypt_selection_with_decryption_shares(
            first_selection,
            {
                self.guardians[0].object_id: (
                    self.guardians[0].share_election_public_key().key,
                    share_0,
                ),
                self.guardians[1].object_id: (
                    self.guardians[1].share_election_public_key().key,
                    share_1,
                ),
                self.guardians[2].object_id: (
                    self.guardians[2].share_election_public_key().key,
                    share_2,
                ),
            },
            self.context.crypto_extended_base_hash,
        )

        print(result)

        self.assertIsNotNone(result)
        self.assertEqual(
            result.plaintext,
            self.expected_plaintext_tally[first_selection.object_id])
コード例 #5
0
    def test_compute_compensated_selection(self):
        """
        demonstrates the complete workflow for computing a comepnsated decryption share
        For one selection. It is useful for verifying that the workflow is correct
        """
        # Arrange
        available_guardian_1 = self.guardians[0]
        available_guardian_2 = self.guardians[1]
        missing_guardian = self.guardians[2]
        available_guardian_1_key = available_guardian_1.share_election_public_key(
        )
        available_guardian_2_key = available_guardian_2.share_election_public_key(
        )
        missing_guardian_key = missing_guardian.share_election_public_key()

        first_selection = [
            selection for contest in self.ciphertext_tally.contests.values()
            for selection in contest.selections.values()
        ][0]

        # Compute lagrange coefficients for the guardians that are present
        lagrange_0 = compute_lagrange_coefficient(
            available_guardian_1.sequence_order,
            *[available_guardian_2.sequence_order],
        )
        lagrange_1 = compute_lagrange_coefficient(
            available_guardian_2.sequence_order,
            *[available_guardian_1.sequence_order],
        )

        print((
            f"lagrange: sequence_orders: ({available_guardian_1.sequence_order}, "
            f"{available_guardian_2.sequence_order}, {missing_guardian.sequence_order})\n"
        ))

        print(lagrange_0)
        print(lagrange_1)

        # compute their shares
        share_0 = compute_decryption_share_for_selection(
            available_guardian_1._election_keys, first_selection, self.context)

        share_1 = compute_decryption_share_for_selection(
            available_guardian_2._election_keys, first_selection, self.context)

        self.assertIsNotNone(share_0)
        self.assertIsNotNone(share_1)

        # compute compensations shares for the missing guardian
        compensation_0 = compute_compensated_decryption_share_for_selection(
            available_guardian_1.share_election_public_key(),
            available_guardian_1._auxiliary_keys,
            missing_guardian.share_election_public_key(),
            missing_guardian.share_election_partial_key_backup(
                available_guardian_1.id),
            first_selection,
            self.context,
            identity_auxiliary_decrypt,
        )

        compensation_1 = compute_compensated_decryption_share_for_selection(
            available_guardian_2.share_election_public_key(),
            available_guardian_2._auxiliary_keys,
            missing_guardian.share_election_public_key(),
            missing_guardian.share_election_partial_key_backup(
                available_guardian_2.id),
            first_selection,
            self.context,
            identity_auxiliary_decrypt,
        )

        self.assertIsNotNone(compensation_0)
        self.assertIsNotNone(compensation_1)

        print("\nSHARES:")
        print(compensation_0)
        print(compensation_1)

        # Check the share proofs
        self.assertTrue(
            compensation_0.proof.is_valid(
                first_selection.ciphertext,
                compute_recovery_public_key(available_guardian_1_key,
                                            missing_guardian_key),
                compensation_0.share,
                self.context.crypto_extended_base_hash,
            ))

        self.assertTrue(
            compensation_1.proof.is_valid(
                first_selection.ciphertext,
                compute_recovery_public_key(available_guardian_2_key,
                                            missing_guardian_key),
                compensation_1.share,
                self.context.crypto_extended_base_hash,
            ))

        share_pow_p = [
            pow_p(compensation_0.share, lagrange_0),
            pow_p(compensation_1.share, lagrange_1),
        ]

        print("\nSHARE_POW_P")
        print(share_pow_p)

        # reconstruct the missing share from the compensation shares
        reconstructed_share = mult_p(*[
            pow_p(compensation_0.share, lagrange_0),
            pow_p(compensation_1.share, lagrange_1),
        ])

        print("\nRECONSTRUCTED SHARE\n")
        print(reconstructed_share)

        share_2 = create_ciphertext_decryption_selection(
            first_selection.object_id,
            missing_guardian.id,
            reconstructed_share,
            {
                available_guardian_1.id: compensation_0,
                available_guardian_2.id: compensation_1,
            },
        )

        # Decrypt the result
        result = decrypt_selection_with_decryption_shares(
            first_selection,
            {
                available_guardian_1.id: (
                    available_guardian_1.share_election_public_key().key,
                    share_0,
                ),
                available_guardian_2.id: (
                    available_guardian_2.share_election_public_key().key,
                    share_1,
                ),
                missing_guardian.id: (
                    missing_guardian.share_election_public_key().key,
                    share_2,
                ),
            },
            self.context.crypto_extended_base_hash,
        )

        print(result)

        self.assertIsNotNone(result)
        self.assertEqual(
            result.tally,
            self.expected_plaintext_tally[first_selection.object_id])