Exemplo n.º 1
0
def _decapsulate_reencrypted(receiving_privkey: UmbralPrivateKey,
                             capsule: Capsule,
                             key_length: int = DEM_KEYSIZE) -> bytes:
    """Derive the same symmetric encapsulated_key"""

    params = capsule.params

    pub_key = receiving_privkey.get_pubkey().point_key
    priv_key = receiving_privkey.bn_key

    precursor = capsule._attached_cfrags[0].point_precursor
    dh_point = priv_key * precursor

    # Combination of CFrags via Shamir's Secret Sharing reconstruction
    xs = list()
    for cfrag in capsule._attached_cfrags:
        x = hash_to_curvebn(precursor,
                            pub_key,
                            dh_point,
                            bytes(constants.X_COORDINATE),
                            cfrag.kfrag_id,
                            params=params)
        xs.append(x)

    e_summands, v_summands = list(), list()
    for cfrag, x in zip(capsule._attached_cfrags, xs):
        if precursor != cfrag.point_precursor:
            raise ValueError("Attached CFrags are not pairwise consistent")
        lambda_i = lambda_coeff(x, xs)
        e_summands.append(lambda_i * cfrag.point_e1)
        v_summands.append(lambda_i * cfrag.point_v1)

    e_prime = sum(e_summands[1:], e_summands[0])
    v_prime = sum(v_summands[1:], v_summands[0])

    # Secret value 'd' allows to make Umbral non-interactive
    d = hash_to_curvebn(precursor,
                        pub_key,
                        dh_point,
                        bytes(constants.NON_INTERACTIVE),
                        params=params)

    e, v, s = capsule.components()
    h = hash_to_curvebn(e, v, params=params)

    orig_pub_key = capsule.get_correctness_keys(
    )['delegating'].point_key  # type: ignore

    if not (s / d) * orig_pub_key == (h * e_prime) + v_prime:
        raise GenericUmbralError()

    shared_key = d * (e_prime + v_prime)
    encapsulated_key = kdf(shared_key, key_length)
    return encapsulated_key
Exemplo n.º 2
0
def test_extended_keccak_to_bn(testerchain, reencryption_validator):
    test_data = os.urandom(40)
    h = hash_to_curvebn(test_data,
                        params=default_params(),
                        hash_class=ExtendedKeccak)
    assert int(h) == reencryption_validator.functions.extendedKeccakToBN(
        test_data).call()
Exemplo n.º 3
0
    def verify_correctness(self, capsule) -> bool:
        if self.proof is None:
            raise CapsuleFrag.NoProofProvided

        correctness_keys = capsule.get_correctness_keys()

        delegating_pubkey = correctness_keys['delegating']
        signing_pubkey = correctness_keys['verifying']
        receiving_pubkey = correctness_keys['receiving']

        params = capsule.params

        ####
        # Here are the formulaic constituents shared with `prove_correctness`.
        ####
        e = capsule.point_e
        v = capsule.point_v

        e1 = self.point_e1
        v1 = self.point_v1

        u = params.u
        u1 = self.proof.point_kfrag_commitment

        e2 = self.proof.point_e2
        v2 = self.proof.point_v2
        u2 = self.proof.point_kfrag_pok

        hash_input = [e, e1, e2, v, v1, v2, u, u1, u2]
        if self.proof.metadata is not None:
            hash_input.append(self.proof.metadata)

        h = hash_to_curvebn(*hash_input,
                            params=params,
                            hash_class=ExtendedKeccak)
        ########

        precursor = self.point_precursor
        kfrag_id = self.kfrag_id

        validity_input = (kfrag_id, delegating_pubkey, receiving_pubkey, u1,
                          precursor)

        kfrag_validity_message = bytes().join(
            bytes(item) for item in validity_input)
        # valid_kfrag_signature = self.proof.kfrag_signature.verify(kfrag_validity_message, signing_pubkey)

        valid_kfrag_signature = True

        z3 = self.proof.bn_sig
        correct_reencryption_of_e = z3 * e == e2 + (h * e1)

        correct_reencryption_of_v = z3 * v == v2 + (h * v1)

        correct_rk_commitment = z3 * u == u2 + (h * u1)

        return valid_kfrag_signature \
               & correct_reencryption_of_e \
               & correct_reencryption_of_v \
               & correct_rk_commitment
Exemplo n.º 4
0
    def verify(self) -> bool:

        g = self.params.g
        e, v, s = self.components()
        h = hash_to_curvebn(e, v, params=self.params)

        result = s * g == v + (h * e)  # type: bool
        return result
def test_curvebn_hash():

    vector_file = os.path.join('vectors', 'vectors_curvebn_hash.json')
    try:
        with open(vector_file) as f:
            vector_suite = json.load(f)
    except OSError:
        raise

    params = default_params()

    for vector in vector_suite['vectors']:
        hash_input = [bytes.fromhex(item['bytes']) for item in vector['input']]
        expected = CurveBN.from_bytes(bytes.fromhex(vector['output']))
        assert hash_to_curvebn(*hash_input, params=params) == expected
Exemplo n.º 6
0
    def prove_correctness(self,
                          capsule,
                          kfrag,
                          metadata: Optional[bytes] = None):

        params = capsule.params

        # Check correctness of original ciphertext
        if not capsule.verify():
            raise capsule.NotValid("Capsule verification failed.")

        rk = kfrag.bn_key
        t = CurveBN.gen_rand(params.curve)
        ####
        # Here are the formulaic constituents shared with `verify_correctness`.
        ####
        e = capsule.point_e
        v = capsule.point_v

        e1 = self.point_e1
        v1 = self.point_v1

        u = params.u
        u1 = kfrag.point_commitment

        e2 = t * e  # type: Any
        v2 = t * v  # type: Any
        u2 = t * u  # type: Any

        hash_input = [e, e1, e2, v, v1, v2, u, u1, u2]
        if metadata is not None:
            hash_input.append(metadata)

        h = hash_to_curvebn(*hash_input,
                            params=params,
                            hash_class=ExtendedKeccak)
        ########

        z3 = t + h * rk

        self.attach_proof(e2,
                          v2,
                          u1,
                          u2,
                          metadata=metadata,
                          z3=z3,
                          kfrag_signature=kfrag.signature_for_bob)
Exemplo n.º 7
0
    def derive_privkey_by_label(
            self,
            label: bytes,
            salt: Optional[bytes] = None,
            params: Optional[UmbralParameters] = None) -> UmbralPrivateKey:
        """
        Derives an UmbralPrivateKey using a KDF from this instance of
        UmbralKeyingMaterial, a label, and an optional salt.
        """
        params = params if params is not None else default_params()

        key_material = Hkdf(
            salt,
            self.__keying_material,
            hash=blake2b,
        ).expand(info=b"NuCypher/KeyDerivation/" + label, length=64)

        bn_key = hash_to_curvebn(key_material, params=params)
        return UmbralPrivateKey(bn_key, params)
    def derive_privkey_by_label(self,
                                label: bytes,
                                salt: Optional[bytes] = None,
                                params: Optional[UmbralParameters] = None) -> UmbralPrivateKey:
        """
        Derives an UmbralPrivateKey using a KDF from this instance of 
        UmbralKeyingMaterial, a label, and an optional salt.
        """
        params = params if params is not None else default_params()

        key_material = HKDF(
            algorithm=hashes.BLAKE2b(64),
            length=64,
            salt=salt,
            info=b"NuCypher/KeyDerivation/"+label,
            backend=default_backend()
        ).derive(self.__keying_material)

        bn_key = hash_to_curvebn(key_material, params=params)
        return UmbralPrivateKey(bn_key, params)
Exemplo n.º 9
0
    def get_proof_challenge_scalar(self) -> CurveBN:
        umbral_params = default_params()
        e, v, _ = self.capsule.components()

        e1 = self.cfrag.point_e1
        v1 = self.cfrag.point_v1
        e2 = self.cfrag.proof.point_e2
        v2 = self.cfrag.proof.point_v2
        u = umbral_params.u
        u1 = self.cfrag.proof.point_kfrag_commitment
        u2 = self.cfrag.proof.point_kfrag_pok
        metadata = self.cfrag.proof.metadata

        from umbral.random_oracles import hash_to_curvebn, ExtendedKeccak

        hash_input = (e, e1, e2, v, v1, v2, u, u1, u2, metadata)

        h = hash_to_curvebn(*hash_input,
                            params=umbral_params,
                            hash_class=ExtendedKeccak)
        return h
Exemplo n.º 10
0
def _encapsulate(alice_pubkey: UmbralPublicKey,
                 key_length: int = DEM_KEYSIZE) -> Tuple[bytes, Capsule]:
    """Generates a symmetric key and its associated KEM ciphertext"""

    params = alice_pubkey.params
    g = params.g

    priv_r = CurveBN.gen_rand(params.curve)
    pub_r = priv_r * g  # type: Any

    priv_u = CurveBN.gen_rand(params.curve)
    pub_u = priv_u * g  # type: Any

    h = hash_to_curvebn(pub_r, pub_u, params=params)
    s = priv_u + (priv_r * h)

    shared_key = (priv_r + priv_u) * alice_pubkey.point_key  # type: Any

    # Key to be used for symmetric encryption
    key = kdf(shared_key, key_length)

    return key, Capsule(point_e=pub_r, point_v=pub_u, bn_sig=s, params=params)
Exemplo n.º 11
0
def test_cant_hash_arbitrary_object_into_bignum():
    whatever = object()
    with pytest.raises(TypeError):
        hash_to_curvebn(whatever)
Exemplo n.º 12
0
###################
# hash_to_curvebn #
###################

# Test vectors for different kinds of inputs (bytes, Points, CurveBNs, etc.)
inputs = ([b''],
          [b'abc'],
          [capsule.point_e],
          [z],
          [capsule.point_e, z],
          points,
          )

vectors = list()
for input_to_hash in inputs:
    bn_output = hash_to_curvebn(*input_to_hash, params=params)
    json_input = [{'class': data.__class__.__name__,
                   'bytes': hexlify(data),
                   } for data in input_to_hash]

    json_input = {'input': json_input, 'output': hexlify(bn_output) }

    vectors.append(json_input)

vector_suite = {
    'name' : 'Test vectors for umbral.curvebn.CurveBN.hash()',
    'params' : 'default',
    'vectors' : vectors
}

create_test_vector_file(vector_suite, 'vectors_curvebn_hash.json', generate_again=generate_again)
Exemplo n.º 13
0
def generate_kfrags(
    delegating_privkey: UmbralPrivateKey,
    receiving_pubkey: UmbralPublicKey,
    threshold: int,
    N: int,
    signer: Signer,
    sign_delegating_key: Optional[bool] = True,
    sign_receiving_key: Optional[bool] = True,
) -> List[KFrag]:
    """
    Creates a re-encryption key from Alice's delegating public key to Bob's
    receiving public key, and splits it in KFrags, using Shamir's Secret Sharing.
    Requires a threshold number of KFrags out of N.

    Returns a list of N KFrags
    """

    if threshold <= 0 or threshold > N:
        raise ValueError(
            'Arguments threshold and N must satisfy 0 < threshold <= N')

    if delegating_privkey.params != receiving_pubkey.params:
        raise ValueError("Keys must have the same parameter set.")

    params = delegating_privkey.params

    g = params.g

    delegating_pubkey = delegating_privkey.get_pubkey()

    bob_pubkey_point = receiving_pubkey.point_key

    # The precursor point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'dh_point' is used to derive other secret values
    private_precursor = CurveBN.gen_rand(params.curve)
    precursor = private_precursor * g  # type: Any

    dh_point = private_precursor * bob_pubkey_point

    # Secret value 'd' allows to make Umbral non-interactive
    d = hash_to_curvebn(precursor,
                        bob_pubkey_point,
                        dh_point,
                        bytes(constants.NON_INTERACTIVE),
                        params=params)

    # Coefficients of the generating polynomial
    coefficients = [delegating_privkey.bn_key * (~d)]
    coefficients += [
        CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)
    ]

    bn_size = CurveBN.expected_bytes_length(params.curve)

    kfrags = list()
    for _ in range(N):
        kfrag_id = os.urandom(bn_size)

        # The index of the re-encryption key share (which in Shamir's Secret
        # Sharing corresponds to x in the tuple (x, f(x)), with f being the
        # generating polynomial), is used to prevent reconstruction of the
        # re-encryption key without Bob's intervention
        share_index = hash_to_curvebn(precursor,
                                      bob_pubkey_point,
                                      dh_point,
                                      bytes(constants.X_COORDINATE),
                                      kfrag_id,
                                      params=params)

        # The re-encryption key share is the result of evaluating the generating
        # polynomial for the index value
        rk = poly_eval(coefficients, share_index)

        commitment = rk * params.u  # type: Any

        validity_message_for_bob = (
            kfrag_id,
            delegating_pubkey,
            receiving_pubkey,
            commitment,
            precursor,
        )  # type: Any
        validity_message_for_bob = bytes().join(
            bytes(item) for item in validity_message_for_bob)
        signature_for_bob = signer(validity_message_for_bob)

        if sign_delegating_key and sign_receiving_key:
            mode = DELEGATING_AND_RECEIVING
        elif sign_delegating_key:
            mode = DELEGATING_ONLY
        elif sign_receiving_key:
            mode = RECEIVING_ONLY
        else:
            mode = NO_KEY

        validity_message_for_proxy = [kfrag_id, commitment, precursor,
                                      mode]  # type: Any

        if sign_delegating_key:
            validity_message_for_proxy.append(delegating_pubkey)
        if sign_receiving_key:
            validity_message_for_proxy.append(receiving_pubkey)

        validity_message_for_proxy = bytes().join(
            bytes(item) for item in validity_message_for_proxy)
        signature_for_proxy = signer(validity_message_for_proxy)

        kfrag = KFrag(
            identifier=kfrag_id,
            bn_key=rk,
            point_commitment=commitment,
            point_precursor=precursor,
            signature_for_proxy=signature_for_proxy,
            signature_for_bob=signature_for_bob,
            keys_in_signature=mode,
        )

        kfrags.append(kfrag)

    return kfrags
Exemplo n.º 14
0
def test_evaluate_cfrag(testerchain, escrow, adjudicator_contract):
    creator, miner, wrong_miner, investigator, *everyone_else = testerchain.interface.w3.eth.accounts
    evaluation_log = adjudicator_contract.events.CFragEvaluated.createFilter(fromBlock='latest')

    # TODO: Move this to an integration test?
    umbral_params = default_params()
    u_xcoord, u_ycoord = umbral_params.u.to_affine()
    u_sign = 2 + (u_ycoord % 2)
    assert u_sign == adjudicator_contract.functions.UMBRAL_PARAMETER_U_SIGN().call()
    assert u_xcoord == adjudicator_contract.functions.UMBRAL_PARAMETER_U_XCOORD().call()
    assert u_ycoord == adjudicator_contract.functions.UMBRAL_PARAMETER_U_YCOORD().call()

    # TODO: Move this to an integration test?
    test_data = os.urandom(40)
    h = hash_to_curvebn(test_data,
                        params=umbral_params,
                        hash_class=ExtendedKeccak)
    assert int(h) == adjudicator_contract.functions.extendedKeccakToBN(test_data).call()

    # Prepare one miner
    tx = escrow.functions.setMinerInfo(miner, 1000).transact()
    testerchain.wait_for_receipt(tx)

    # Generate miner's Umbral key
    miner_umbral_private_key = UmbralPrivateKey.gen_key()
    miner_umbral_public_key_bytes = miner_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)

    # Sign Umbral public key using eth-key
    hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
    hash_ctx.update(miner_umbral_public_key_bytes)
    miner_umbral_public_key_hash = hash_ctx.finalize()
    provider = testerchain.interface.provider
    address = to_canonical_address(miner)
    sig_key = provider.ethereum_tester.backend._key_lookup[address]
    signed_miner_umbral_public_key = bytes(sig_key.sign_msg_hash(miner_umbral_public_key_hash))

    # Prepare hash of the data
    metadata = os.urandom(33)
    capsule, cfrag = fragments(metadata)

    assert cfrag.verify_correctness(capsule)

    capsule_bytes = capsule.to_bytes()
    cfrag_bytes = cfrag.to_bytes()

    # Bob prepares supporting Evidence
    evidence = IndisputableEvidence(capsule, cfrag, ursula=None)

    evidence_data = evidence.precompute_values()
    assert len(evidence_data) == 20 * 32 + 32 + 20 + 1

    # This check is a workaround in order to test the signature validation is correct.
    # In reality, this check should be part of the isCapsuleFragCorrect logic,
    # but we're facing with "Stack too deep" errors at the moment.
    address = adjudicator_contract.functions.aliceAddress(cfrag_bytes, evidence_data).call()
    assert address == to_checksum_address(evidence_data[-21:-1].hex())

    proof_challenge_scalar = int(evidence.get_proof_challenge_scalar())
    computeProofChallengeScalar = adjudicator_contract.functions.computeProofChallengeScalar
    assert proof_challenge_scalar == computeProofChallengeScalar(capsule_bytes, cfrag_bytes).call()

    hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
    hash_ctx.update(capsule_bytes + cfrag_bytes)
    data_hash = hash_ctx.finalize()
    # This capsule and cFrag are not yet evaluated
    assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()

    # Generate requester's Umbral key
    requester_umbral_private_key = UmbralPrivateKey.gen_key()
    requester_umbral_public_key_bytes = requester_umbral_private_key.get_pubkey().to_bytes(is_compressed=False)

    # Sign capsule and cFrag
    capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
    capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
    cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)

    # Challenge using good data
    args = (capsule_bytes,
            capsule_signature_by_requester,
            capsule_signature_by_requester_and_miner,
            cfrag_bytes,
            cfrag_signature_by_miner,
            requester_umbral_public_key_bytes,
            miner_umbral_public_key_bytes,
            signed_miner_umbral_public_key,
            evidence_data)

    value = escrow.functions.minerInfo(miner).call()[0]
    tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
    testerchain.wait_for_receipt(tx)
    # Hash of the data is saved and miner was not slashed
    assert adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
    assert value == escrow.functions.minerInfo(miner).call()[0]
    assert 0 == escrow.functions.rewardInfo(investigator).call()

    events = evaluation_log.get_all_entries()
    assert 1 == len(events)
    event_args = events[0]['args']
    assert data_hash == event_args['evaluationHash']
    assert miner == event_args['miner']
    assert investigator == event_args['investigator']
    assert event_args['correctness']

    # Can't evaluate miner with data that already was checked
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*args).transact()
        testerchain.wait_for_receipt(tx)

    # Challenge using bad data
    metadata = os.urandom(34)
    capsule, cfrag = fragments(metadata)
    capsule_bytes = capsule.to_bytes()
    # Corrupt proof
    cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
    cfrag_bytes = cfrag.to_bytes()
    hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
    hash_ctx.update(capsule_bytes + cfrag_bytes)
    data_hash = hash_ctx.finalize()
    capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
    capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
    cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
    evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
    evidence_data = evidence.precompute_values()
    args = (capsule_bytes,
            capsule_signature_by_requester,
            capsule_signature_by_requester_and_miner,
            cfrag_bytes,
            cfrag_signature_by_miner,
            requester_umbral_public_key_bytes,
            miner_umbral_public_key_bytes,
            signed_miner_umbral_public_key,
            evidence_data)

    assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
    tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
    testerchain.wait_for_receipt(tx)
    # Hash of the data is saved and miner was slashed
    assert adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
    assert value - BASE_PENALTY == escrow.functions.minerInfo(miner).call()[0]
    assert BASE_PENALTY / REWARD_COEFFICIENT == escrow.functions.rewardInfo(investigator).call()

    events = evaluation_log.get_all_entries()
    assert 2 == len(events)
    event_args = events[1]['args']
    assert data_hash == event_args['evaluationHash']
    assert miner == event_args['miner']
    assert investigator == event_args['investigator']
    assert not event_args['correctness']

    # Prepare hash of the data
    metadata = os.urandom(34)
    capsule, cfrag = fragments(metadata)
    capsule_bytes = capsule.to_bytes()
    # Corrupt proof
    cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
    cfrag_bytes = cfrag.to_bytes()
    hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
    hash_ctx.update(capsule_bytes + cfrag_bytes)
    data_hash = hash_ctx.finalize()
    capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
    capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
    cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
    evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
    evidence_data = evidence.precompute_values()
    args = [capsule_bytes,
            capsule_signature_by_requester,
            capsule_signature_by_requester_and_miner,
            cfrag_bytes,
            cfrag_signature_by_miner,
            requester_umbral_public_key_bytes,
            miner_umbral_public_key_bytes,
            signed_miner_umbral_public_key,
            evidence_data]
    assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()

    # Can't evaluate miner using broken signatures
    wrong_args = args[:]
    wrong_args[1] = capsule_signature_by_requester[1:]
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)
    wrong_args = args[:]
    wrong_args[2] = capsule_signature_by_requester_and_miner[1:]
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)
    wrong_args = args[:]
    wrong_args[4] = cfrag_signature_by_miner[1:]
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)
    wrong_args = args[:]
    wrong_args[7] = signed_miner_umbral_public_key[1:]
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    # Can't evaluate miner using wrong keys
    wrong_args = args[:]
    wrong_args[5] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(is_compressed=False)
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)
    wrong_args = args[:]
    wrong_args[6] = UmbralPrivateKey.gen_key().get_pubkey().to_bytes(is_compressed=False)
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    # Can't use signature for another data
    wrong_args = args[:]
    wrong_args[0] = bytes(args[0][0] + 1) + args[0][1:]
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)
    wrong_args = args[:]
    wrong_args[3] = bytes(args[3][0] + 1) + args[3][1:]
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    # Can't evaluate nonexistent miner
    address = to_canonical_address(wrong_miner)
    sig_key = provider.ethereum_tester.backend._key_lookup[address]
    signed_wrong_miner_umbral_public_key = bytes(sig_key.sign_msg_hash(miner_umbral_public_key_hash))
    wrong_args = args[:]
    wrong_args[7] = signed_wrong_miner_umbral_public_key
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*wrong_args).transact()
        testerchain.wait_for_receipt(tx)

    # Initial arguments were correct
    value = escrow.functions.minerInfo(miner).call()[0]
    reward = escrow.functions.rewardInfo(investigator).call()
    assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
    tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
    testerchain.wait_for_receipt(tx)
    assert adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
    # Penalty was increased because it's the second violation
    assert value - (BASE_PENALTY + PENALTY_HISTORY_COEFFICIENT) == escrow.functions.minerInfo(miner).call()[0]
    assert reward + (BASE_PENALTY + PENALTY_HISTORY_COEFFICIENT) / REWARD_COEFFICIENT == \
           escrow.functions.rewardInfo(investigator).call()

    events = evaluation_log.get_all_entries()
    assert 3 == len(events)
    event_args = events[2]['args']
    assert data_hash == event_args['evaluationHash']
    assert miner == event_args['miner']
    assert investigator == event_args['investigator']
    assert not event_args['correctness']

    # Prepare corrupted data again
    capsule, cfrag = fragments(metadata)
    capsule_bytes = capsule.to_bytes()
    # Corrupt proof
    cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)
    cfrag_bytes = cfrag.to_bytes()
    hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
    hash_ctx.update(capsule_bytes + cfrag_bytes)
    data_hash = hash_ctx.finalize()
    capsule_signature_by_requester = sign_data(capsule_bytes, requester_umbral_private_key)
    capsule_signature_by_requester_and_miner = sign_data(capsule_signature_by_requester, miner_umbral_private_key)
    cfrag_signature_by_miner = sign_data(cfrag_bytes, miner_umbral_private_key)
    evidence = IndisputableEvidence(capsule, cfrag, ursula=None)
    evidence_data = evidence.precompute_values()
    args = (capsule_bytes,
            capsule_signature_by_requester,
            capsule_signature_by_requester_and_miner,
            cfrag_bytes,
            cfrag_signature_by_miner,
            requester_umbral_public_key_bytes,
            miner_umbral_public_key_bytes,
            signed_miner_umbral_public_key,
            evidence_data)

    value = escrow.functions.minerInfo(miner).call()[0]
    reward = escrow.functions.rewardInfo(investigator).call()
    assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
    tx = adjudicator_contract.functions.evaluateCFrag(*args).transact({'from': investigator})
    testerchain.wait_for_receipt(tx)
    assert adjudicator_contract.functions.evaluatedCFrags(data_hash).call()
    # Penalty was decreased because it's more than maximum available percentage of value
    assert value - value // PERCENTAGE_PENALTY_COEFFICIENT == escrow.functions.minerInfo(miner).call()[0]
    assert reward + value // PERCENTAGE_PENALTY_COEFFICIENT / REWARD_COEFFICIENT == \
           escrow.functions.rewardInfo(investigator).call()

    events = evaluation_log.get_all_entries()
    assert 4 == len(events)
    event_args = events[3]['args']
    assert data_hash == event_args['evaluationHash']
    assert miner == event_args['miner']
    assert investigator == event_args['investigator']
    assert not event_args['correctness']