Пример #1
0
def test_bad_capsule_fails_reencryption(kfrags):
    params = default_params()

    bollocks_capsule = Capsule(params,
                               point_e=Point.gen_rand(),
                               point_v=Point.gen_rand(),
                               bn_sig=CurveBN.gen_rand())

    for kfrag in kfrags:
        with pytest.raises(Capsule.NotValid):
            pre.reencrypt(kfrag, bollocks_capsule)
Пример #2
0
    def gen_key(
            cls,
            params: Optional[UmbralParameters] = None) -> 'UmbralPrivateKey':
        """
        Generates a private key and returns it.
        """
        if params is None:
            params = default_params()

        bn_key = CurveBN.gen_rand(params.curve)
        return cls(bn_key, params)
Пример #3
0
def test_capsule_equality():
    params = default_params()

    one_capsule = Capsule(params,
                          point_e=Point.gen_rand(),
                          point_v=Point.gen_rand(),
                          bn_sig=CurveBN.gen_rand())

    another_capsule = Capsule(params,
                              point_e=Point.gen_rand(),
                              point_v=Point.gen_rand(),
                              bn_sig=CurveBN.gen_rand())

    assert one_capsule != another_capsule

    activated_capsule = Capsule(params,
                                point_e_prime=Point.gen_rand(),
                                point_v_prime=Point.gen_rand(),
                                point_noninteractive=Point.gen_rand())

    assert activated_capsule != one_capsule
Пример #4
0
def _encapsulate(alice_pub_key: Point, key_length=32,
                 params: UmbralParameters=None) -> Tuple[bytes, Capsule]:
    """Generates a symmetric key and its associated KEM ciphertext"""
    params = params if params is not None else default_params()

    g = params.g

    priv_r = CurveBN.gen_rand(params.curve)
    pub_r = priv_r * g

    priv_u = CurveBN.gen_rand(params.curve)
    pub_u = priv_u * g

    h = CurveBN.hash_to_bn(pub_r, pub_u, params=params)
    s = priv_u + (priv_r * h)

    shared_key = (priv_r + priv_u) * alice_pub_key

    # 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)
Пример #5
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

    priv_u = CurveBN.gen_rand(params.curve)
    pub_u = priv_u * g

    h = CurveBN.hash(pub_r, pub_u, params=params)
    s = priv_u + (priv_r * h)

    shared_key = (priv_r + priv_u) * alice_pubkey.point_key

    # 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)
Пример #6
0
def test_cannot_create_capsule_from_bogus_material(alices_keys):
    params = alices_keys[0].params

    with pytest.raises(TypeError):
        _capsule_of_questionable_parentage = Capsule(params,
                                                     point_e=Point.gen_rand(),
                                                     point_v=42,
                                                     bn_sig=CurveBN.gen_rand())

    with pytest.raises(TypeError):
        _capsule_of_questionable_parentage = Capsule(params,
                                                     point_e=Point.gen_rand(),
                                                     point_v=Point.gen_rand(),
                                                     bn_sig=42)
Пример #7
0
def test_bad_capsule_fails_reencryption(alices_keys, bobs_keys):
    delegating_privkey, _signing_privkey = alices_keys
    signer_alice = Signer(_signing_privkey)

    _receiving_privkey, receiving_pubkey = bobs_keys

    kfrags = pre.split_rekey(delegating_privkey, signer_alice,
                             receiving_pubkey, 1, 2)

    bollocks_capsule = Capsule(point_e=Point.gen_rand(),
                               point_v=Point.gen_rand(),
                               bn_sig=CurveBN.gen_rand())

    with pytest.raises(Capsule.NotValid):
        pre.reencrypt(kfrags[0], bollocks_capsule)
Пример #8
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)
Пример #9
0
    def gen_rand(cls, curve: Optional[Curve] = None) -> 'Point':
        """
        Returns a Point object with a cryptographically secure EC_POINT based
        on the provided curve.
        """
        curve = curve if curve is not None else default_curve()

        rand_point = openssl._get_new_EC_POINT(curve)
        rand_bn = CurveBN.gen_rand(curve).bignum

        with backend._tmp_bn_ctx() as bn_ctx:
            res = backend._lib.EC_POINT_mul(curve.ec_group, rand_point,
                                            backend._ffi.NULL, curve.generator,
                                            rand_bn, bn_ctx)
            backend.openssl_assert(res == 1)

        return cls(rand_point, curve)
Пример #10
0
def test_capsule_creation(alices_keys):
    with pytest.raises(TypeError):
        rare_capsule = Capsule()  # Alice cannot make a capsule this way.

    # Some users may create capsules their own way.
    custom_capsule = Capsule(point_e=Point.gen_rand(),
                             point_v=Point.gen_rand(),
                             bn_sig=CurveBN.gen_rand())

    assert isinstance(custom_capsule, Capsule)

    # Typical Alice, constructing a typical capsule
    _, alices_public_key = alices_keys
    plaintext = b'peace at dawn'
    ciphertext, typical_capsule = pre.encrypt(alices_public_key, plaintext)

    assert isinstance(typical_capsule, Capsule)
Пример #11
0
def _mock_ursula_reencrypts(ursula, corrupt_cfrag: bool = False):
    delegating_privkey = UmbralPrivateKey.gen_key()
    _symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
    signing_privkey = UmbralPrivateKey.gen_key()
    signing_pubkey = signing_privkey.get_pubkey()
    signer = Signer(signing_privkey)
    priv_key_bob = UmbralPrivateKey.gen_key()
    pub_key_bob = priv_key_bob.get_pubkey()
    kfrags = pre.generate_kfrags(delegating_privkey=delegating_privkey,
                                 signer=signer,
                                 receiving_pubkey=pub_key_bob,
                                 threshold=2,
                                 N=4,
                                 sign_delegating_key=False,
                                 sign_receiving_key=False)
    capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob,
                                 signing_pubkey)

    ursula_pubkey = ursula.stamp.as_umbral_pubkey()

    alice_address = canonical_address_from_umbral_key(signing_pubkey)
    blockhash = bytes(32)

    specification = b''.join((bytes(capsule), bytes(ursula_pubkey),
                              bytes(ursula.decentralized_identity_evidence),
                              alice_address, blockhash))

    bobs_signer = Signer(priv_key_bob)
    task_signature = bytes(bobs_signer(specification))

    metadata = bytes(ursula.stamp(task_signature))

    cfrag = pre.reencrypt(kfrags[0], capsule, metadata=metadata)

    if corrupt_cfrag:
        cfrag.proof.bn_sig = CurveBN.gen_rand(capsule.params.curve)

    cfrag_signature = bytes(ursula.stamp(bytes(cfrag)))

    bob = Bob.from_public_keys(verifying_key=pub_key_bob)
    task = WorkOrder.Task(capsule, task_signature, cfrag, cfrag_signature)
    work_order = WorkOrder(bob, None, alice_address, [task], None, ursula,
                           blockhash)

    evidence = IndisputableEvidence(task, work_order)
    return evidence
Пример #12
0
def prove_cfrag_correctness(
        cfrag: "CapsuleFrag",
        kfrag: "KFrag",
        capsule: "Capsule",
        metadata: bytes = None,
        params: UmbralParameters = None) -> "CorrectnessProof":
    params = params if params is not None else default_params()

    rk = kfrag._bn_key
    t = CurveBN.gen_rand(params.curve)
    ####
    ## Here are the formulaic constituents shared with `assess_cfrag_correctness`.
    ####
    e = capsule._point_e
    v = capsule._point_v

    e1 = cfrag._point_e1
    v1 = cfrag._point_v1

    u = params.u
    u1 = kfrag._point_commitment

    e2 = t * e
    v2 = t * v
    u2 = t * u

    hash_input = (e, e1, e2, v, v1, v2, u, u1, u2)
    if metadata is not None:
        hash_input += (metadata, )
    h = CurveBN.hash(*hash_input, params=params)
    ########

    z3 = t + h * rk

    cfrag.attach_proof(e2,
                       v2,
                       u1,
                       u2,
                       metadata=metadata,
                       z3=z3,
                       kfrag_signature=kfrag.signature)

    # Check correctness of original ciphertext (check nº 2) at the end
    # to avoid timing oracles
    if not capsule.verify():
        raise capsule.NotValid("Capsule verification failed.")
Пример #13
0
def test_cannot_attach_cfrag_without_keys():
    """
    We need the proper keys to verify the correctness of CFrags
    in order to attach them to a Capsule.
    """
    capsule = Capsule(point_e=Point.gen_rand(),
                      point_v=Point.gen_rand(),
                      bn_sig=CurveBN.gen_rand())

    cfrag = CapsuleFrag(
        point_e1=Point.gen_rand(),
        point_v1=Point.gen_rand(),
        kfrag_id=os.urandom(10),
        point_noninteractive=Point.gen_rand(),
        point_xcoord=Point.gen_rand(),
    )
    with pytest.raises(TypeError):
        capsule.attach_cfrag(cfrag)
Пример #14
0
def _prove_correctness(cfrag: CapsuleFrag, kfrag: KFrag, capsule: Capsule, 
                       metadata: bytes=None, params: UmbralParameters=None
                      ) -> CorrectnessProof:
    params = params if params is not None else default_params()

    e1 = cfrag._point_e1
    v1 = cfrag._point_v1

    e = capsule._point_e
    v = capsule._point_v

    u = params.u
    u1 = kfrag._point_commitment

    rk = kfrag._bn_key

    t = CurveBN.gen_rand(params.curve)
    e2 = t * e
    v2 = t * v
    u2 = t * u

    hash_input = [e, e1, e2, v, v1, v2, u, u1, u2]

    if metadata is not None:
        hash_input.append(metadata)

    h = CurveBN.hash_to_bn(*hash_input, params=params)

    z3 = t + h * rk

    cfrag.proof = CorrectnessProof(point_e2=e2, 
                                   point_v2=v2, 
                                   point_kfrag_commitment=u1,
                                   point_kfrag_pok=u2,
                                   bn_kfrag_sig1=kfrag._bn_sig1,
                                   bn_kfrag_sig2=kfrag._bn_sig2,
                                   bn_sig=z3,
                                   metadata=metadata)

    # Check correctness of original ciphertext (check nº 2) at the end
    # to avoid timing oracles
    if not capsule.verify(params):
        raise capsule.NotValid("Capsule verification failed.")
Пример #15
0
def test_cannot_create_capsule_from_bogus_material(alices_keys):
    with pytest.raises(TypeError):
        capsule_of_questionable_parentage = pre.Capsule(point_e=Point.gen_rand(),
                                                        point_v=42,
                                                        bn_sig=CurveBN.gen_rand())

    with pytest.raises(TypeError):
        capsule_of_questionable_parentage = pre.Capsule(point_e=Point.gen_rand(),
                                                        point_v=Point.gen_rand(),
                                                        bn_sig=42)

    with pytest.raises(TypeError):
        capsule_of_questionable_parentage = pre.Capsule(point_e_prime=Point.gen_rand(),
                                                        point_v_prime=42,
                                                        point_noninteractive=Point.gen_rand())

    with pytest.raises(TypeError):
        capsule_of_questionable_parentage = pre.Capsule(point_e_prime=Point.gen_rand(),
                                                        point_v_prime=Point.gen_rand(),
                                                        point_noninteractive=42)
Пример #16
0
    def gen_rand(cls, curve: ec.EllipticCurve=None):
        """
        Returns a Point object with a cryptographically secure EC_POINT based
        on the provided curve.
        """
        curve = curve if curve is not None else default_curve()
        curve_nid = backend._elliptic_curve_to_nid(curve)

        group = openssl._get_ec_group_by_curve_nid(curve_nid)
        generator = openssl._get_ec_generator_by_curve_nid(curve_nid)

        rand_point = openssl._get_new_EC_POINT(ec_group=group)
        rand_bn = CurveBN.gen_rand(curve).bignum

        with backend._tmp_bn_ctx() as bn_ctx:
            res = backend._lib.EC_POINT_mul(
                group, rand_point, backend._ffi.NULL, generator, rand_bn, bn_ctx
            )
            backend.openssl_assert(res == 1)

        return Point(rand_point, curve_nid, group)
Пример #17
0
def prove_cfrag_correctness(cfrag: 'CapsuleFrag',
                            kfrag: 'KFrag',
                            capsule: 'Capsule',
                            metadata: Optional[bytes] = None
                            ) -> 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 `assess_cfrag_correctness`.
    ####
    e = capsule._point_e
    v = capsule._point_v

    e1 = cfrag._point_e1
    v1 = cfrag._point_v1

    u = params.u
    u1 = kfrag._point_commitment

    e2 = t * e
    v2 = t * v
    u2 = t * u

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

    z3 = t + h * rk

    cfrag.attach_proof(e2, v2, u1, u2, metadata=metadata, z3=z3, kfrag_signature=kfrag.signature_for_bob)
Пример #18
0
def test_cannot_set_different_keys():
    """
    Once a key is set on a Capsule, it can't be changed to a different key.
    """
    capsule = Capsule(point_e=Point.gen_rand(),
                      point_v=Point.gen_rand(),
                      bn_sig=CurveBN.gen_rand())

    capsule.set_correctness_keys(
        delegating=UmbralPrivateKey.gen_key().get_pubkey(),
        receiving=UmbralPrivateKey.gen_key().get_pubkey(),
        verifying=UmbralPrivateKey.gen_key().get_pubkey())

    with pytest.raises(ValueError):
        capsule.set_correctness_keys(
            delegating=UmbralPrivateKey.gen_key().get_pubkey())

    with pytest.raises(ValueError):
        capsule.set_correctness_keys(
            receiving=UmbralPrivateKey.gen_key().get_pubkey())

    with pytest.raises(ValueError):
        capsule.set_correctness_keys(
            verifying=UmbralPrivateKey.gen_key().get_pubkey())
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')

    worker_stake = 1000
    worker_penalty_history = 0
    investigator_balance = 0
    number_of_evaluations = 0

    # Prepare one miner
    tx = escrow.functions.setMinerInfo(miner, worker_stake).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)

    assert worker_stake == escrow.functions.minerInfo(miner).call()[0]

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

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

    # Test: Don'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)

    # Test: Ursula produces incorrect proof:
    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()
    assert not cfrag.verify_correctness(capsule)

    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)
    number_of_evaluations += 1

    # Hash of the data is saved and miner was slashed
    assert adjudicator_contract.functions.evaluatedCFrags(data_hash).call()

    penalty, reward = compute_penalty_and_reward(worker_stake,
                                                 worker_penalty_history)
    worker_stake -= penalty
    investigator_balance += reward
    worker_penalty_history += 1

    assert worker_stake == escrow.functions.minerInfo(miner).call()[0]
    assert investigator_balance == escrow.functions.rewardInfo(
        investigator).call()

    events = evaluation_log.get_all_entries()
    assert number_of_evaluations == 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']

    ###############################
    # Test: Bob produces wrong precomputed data
    ###############################

    metadata = os.urandom(34)
    capsule, cfrag = fragments(metadata)
    capsule_bytes = capsule.to_bytes()
    cfrag_bytes = cfrag.to_bytes()
    assert cfrag.verify_correctness(capsule)

    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()

    # Bob produces a random point and gets the bytes of coords x and y
    random_point_bytes = Point.gen_rand().to_bytes(is_compressed=False)[1:]
    # He uses this garbage instead of correct precomputation of z*E
    evidence_data = bytearray(evidence_data)
    evidence_data[32:32 + 64] = random_point_bytes
    evidence_data = bytes(evidence_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)

    assert not adjudicator_contract.functions.evaluatedCFrags(data_hash).call()

    # Evaluation must fail since Bob precomputed wrong values
    with pytest.raises((TransactionFailed, ValueError)):
        tx = adjudicator_contract.functions.evaluateCFrag(*args).transact(
            {'from': investigator})
        testerchain.wait_for_receipt(tx)

    ###############################
    # Test: Signatures and Metadata mismatch
    ###############################

    # TODO

    ###############################
    # Test: Second violation. Penalty is increased
    ###############################

    # 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()

    worker_stake = escrow.functions.minerInfo(miner).call()[0]
    investigator_balance = 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)
    number_of_evaluations += 1

    assert adjudicator_contract.functions.evaluatedCFrags(data_hash).call()

    previous_penalty = penalty
    penalty, reward = compute_penalty_and_reward(worker_stake,
                                                 worker_penalty_history)
    # Penalty was increased because it's the second violation
    assert penalty == previous_penalty + PENALTY_HISTORY_COEFFICIENT
    worker_stake -= penalty
    investigator_balance += reward
    worker_penalty_history += 1

    assert worker_stake == escrow.functions.minerInfo(miner).call()[0]
    assert investigator_balance == escrow.functions.rewardInfo(
        investigator).call()

    events = evaluation_log.get_all_entries()
    assert number_of_evaluations == 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']

    ###############################
    # Test: Third violation. Penalty reaches the maximum allowed
    ###############################

    # 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
    ]

    worker_stake = escrow.functions.minerInfo(miner).call()[0]
    investigator_balance = 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)
    number_of_evaluations += 1

    assert adjudicator_contract.functions.evaluatedCFrags(data_hash).call()

    penalty, reward = compute_penalty_and_reward(worker_stake,
                                                 worker_penalty_history)
    # Penalty has reached maximum available percentage of value
    assert penalty == worker_stake // PERCENTAGE_PENALTY_COEFFICIENT
    worker_stake -= penalty
    investigator_balance += reward
    worker_penalty_history += 1

    assert worker_stake == escrow.functions.minerInfo(miner).call()[0]
    assert investigator_balance == escrow.functions.rewardInfo(
        investigator).call()

    events = evaluation_log.get_all_entries()
    assert number_of_evaluations == 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']

    #################
    # Test: Invalid evaluations
    ##############

    # 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)
Пример #20
0
def generate_args_for_slashing(testerchain, miner, corrupt: bool = True):
    def sign_data(data, umbral_privkey):
        umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes(
            is_compressed=False)

        # Prepare hash of the data
        hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
        hash_ctx.update(data)
        data_hash = hash_ctx.finalize()

        # Sign data and calculate recoverable signature
        cryptography_priv_key = umbral_privkey.to_cryptography_privkey()
        signature_der_bytes = cryptography_priv_key.sign(
            data, ec.ECDSA(hashes.SHA256()))
        signature = Signature.from_bytes(signature_der_bytes, der_encoded=True)
        recoverable_signature = bytes(signature) + bytes([0])
        pubkey_bytes = coincurve.PublicKey.from_signature_and_message(recoverable_signature, data_hash, hasher=None) \
            .format(compressed=False)
        if pubkey_bytes != umbral_pubkey_bytes:
            recoverable_signature = bytes(signature) + bytes([1])
        return recoverable_signature

    delegating_privkey = UmbralPrivateKey.gen_key()
    _symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
    signing_privkey = UmbralPrivateKey.gen_key()
    signer = Signer(signing_privkey)
    priv_key_bob = UmbralPrivateKey.gen_key()
    pub_key_bob = priv_key_bob.get_pubkey()
    kfrags = pre.generate_kfrags(delegating_privkey=delegating_privkey,
                                 signer=signer,
                                 receiving_pubkey=pub_key_bob,
                                 threshold=2,
                                 N=4,
                                 sign_delegating_key=False,
                                 sign_receiving_key=False)
    capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob,
                                 signing_privkey.get_pubkey())
    cfrag = pre.reencrypt(kfrags[0], capsule, metadata=os.urandom(34))
    capsule_bytes = capsule.to_bytes()
    if corrupt:
        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)
    requester_umbral_private_key = UmbralPrivateKey.gen_key()
    requester_umbral_public_key_bytes = requester_umbral_private_key.get_pubkey(
    ).to_bytes(is_compressed=False)
    capsule_signature_by_requester = sign_data(capsule_bytes,
                                               requester_umbral_private_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()
    address = to_canonical_address(miner)
    sig_key = testerchain.interface.provider.ethereum_tester.backend._key_lookup[
        address]
    signed_miner_umbral_public_key = bytes(
        sig_key.sign_msg_hash(miner_umbral_public_key_hash))

    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()
    return (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)
Пример #21
0
def test_ec_point_operations(testerchain, reencryption_validator):
    valid_point = Point.gen_rand()
    x, y = valid_point.to_affine()

    # Test isOnCurve
    assert reencryption_validator.functions.isOnCurve(x, y).call()

    bad_y = y - 1
    assert not reencryption_validator.functions.isOnCurve(x, bad_y).call()

    # Test checkCompressedPoint
    sign = 2 + (y % 2)
    assert reencryption_validator.functions.checkCompressedPoint(sign, x,
                                                                 y).call()

    bad_sign = 3 - (y % 2)
    assert not reencryption_validator.functions.checkCompressedPoint(
        bad_sign, x, y).call()

    # Test checkSerializedCoordinates
    coords = valid_point.to_bytes(is_compressed=False)[1:]
    assert reencryption_validator.functions.checkSerializedCoordinates(
        coords).call()

    coords = coords[:-1] + ((coords[-1] + 42) % 256).to_bytes(1, 'big')
    assert not reencryption_validator.functions.checkSerializedCoordinates(
        coords).call()

    # Test ecmulVerify
    P = valid_point
    scalar = CurveBN.gen_rand()
    Q = scalar * P
    qx, qy = Q.to_affine()

    assert reencryption_validator.functions.ecmulVerify(
        x, y, int(scalar), qx, qy).call()
    assert not reencryption_validator.functions.ecmulVerify(
        x, y, int(scalar), x, y).call()

    # Test eqAffineJacobian
    Q_affine = [qx, qy]
    Q_jacobian = [qx, qy, 1]
    assert reencryption_validator.functions.eqAffineJacobian(
        Q_affine, Q_jacobian).call()

    P_jacobian = [x, y, 1]
    assert not reencryption_validator.functions.eqAffineJacobian(
        Q_affine, P_jacobian).call()

    point_at_infinity = [x, y, 0]
    random_point = Point.gen_rand()
    assert not reencryption_validator.functions.eqAffineJacobian(
        random_point.to_affine(), point_at_infinity).call()

    # Test doubleJacobian
    doubleP = reencryption_validator.functions.doubleJacobian(
        P_jacobian).call()
    assert reencryption_validator.functions.eqAffineJacobian(
        (P + P).to_affine(), doubleP).call()

    # Test addAffineJacobian
    scalar1 = CurveBN.gen_rand()
    scalar2 = CurveBN.gen_rand()
    R1 = scalar1 * P
    R2 = scalar2 * P

    assert R1 + R2 == (scalar1 + scalar2) * P
    R = reencryption_validator.functions.addAffineJacobian(
        R1.to_affine(), R2.to_affine()).call()
    assert reencryption_validator.functions.eqAffineJacobian(
        (R1 + R2).to_affine(), R).call()

    P_plus_P = reencryption_validator.functions.addAffineJacobian(
        P.to_affine(), P.to_affine()).call()
    assert reencryption_validator.functions.eqAffineJacobian(
        (P + P).to_affine(), P_plus_P).call()
Пример #22
0
def split_rekey(delegating_privkey: UmbralPrivateKey, signer: Signer,
                receiving_pubkey: UmbralPublicKey, threshold: int,
                N: int) -> List[KFrag]:
    """
    Creates a re-encryption key from Alice to Bob and splits it in KFrags,
    using Shamir's Secret Sharing. Requires a threshold number of KFrags
    out of N to guarantee correctness of re-encryption.

    Returns a list of 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

    pubkey_a_point = delegating_privkey.get_pubkey().point_key
    privkey_a_bn = delegating_privkey.bn_key

    pubkey_b_point = receiving_pubkey.point_key

    # 'ni' stands for 'Non Interactive'.
    # This point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'd' allows to make Umbral non-interactive
    priv_ni = CurveBN.gen_rand(params.curve)
    ni = priv_ni * g
    d = CurveBN.hash(ni,
                     pubkey_b_point,
                     pubkey_b_point * priv_ni,
                     params=params)

    coeffs = [privkey_a_bn * (~d)]
    coeffs += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)]

    u = params.u

    # 'xcoord' stands for 'X coordinate'.
    # This point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'dh_xcoord' contributes to prevent
    # reconstruction of the re-encryption key without Bob's intervention
    priv_xcoord = CurveBN.gen_rand(params.curve)
    xcoord = priv_xcoord * g

    dh_xcoord = priv_xcoord * pubkey_b_point

    blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend)
    blake2b.update(xcoord.to_bytes())
    blake2b.update(pubkey_b_point.to_bytes())
    blake2b.update(dh_xcoord.to_bytes())
    hashed_dh_tuple = blake2b.finalize()

    bn_size = CurveBN.expected_bytes_length(params.curve)

    kfrags = []
    for _ in range(N):
        id = os.urandom(bn_size)

        share_x = CurveBN.hash(id, hashed_dh_tuple, params=params)

        rk = poly_eval(coeffs, share_x)

        u1 = rk * u

        kfrag_validity_message = bytes().join(
            bytes(material) for material in (id, pubkey_a_point,
                                             pubkey_b_point, u1, ni, xcoord))
        signature = signer(kfrag_validity_message)

        kfrag = KFrag(id=id,
                      bn_key=rk,
                      point_noninteractive=ni,
                      point_commitment=u1,
                      point_xcoord=xcoord,
                      signature=signature)

        kfrags.append(kfrag)

    return kfrags
Пример #23
0
                             verifying=verifying_key)

cfrag = pre.reencrypt(kfrags[0], capsule)
points = [capsule.point_e, cfrag.point_e1, cfrag.proof.point_e2,
          capsule.point_v, cfrag.point_v1, cfrag.proof.point_v2,
          capsule.params.u, cfrag.proof.point_kfrag_commitment, cfrag.proof.point_kfrag_pok]

z = cfrag.proof.bn_sig


#######################
# CurveBN arithmetics #
#######################

# Let's generate two random CurveBNs
bn1 = CurveBN.gen_rand(curve)
bn2 = CurveBN.gen_rand(curve)

# Expected results for some binary operations
expected = [('Addition', bn1 + bn2),
            ('Subtraction', bn1 - bn2),
            ('Multiplication', bn1 * bn2),
            ('Division', bn1 / bn2),
            ('Pow', bn1 ** bn2),
            ('Mod', bn1 % bn2),
            ('Inverse', ~bn1),
            ('Neg', -bn1),
            ]

expected = [{'operation': op, 'result': hexlify(result)} for (op, result) in expected]
def share_secret(secret, threshold, n):
    coeff = [secret] + [CurveBN.gen_rand() for _ in range(threshold - 1)]
    xs = [CurveBN.gen_rand() for _ in range(n)]
    points = [(x, utils.poly_eval(coeff, x)) for x in xs]
    return points
Пример #25
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']
Пример #26
0
def random_ec_curvebn2():
    yield CurveBN.gen_rand()
Пример #27
0
def split_rekey(privkey_a_bn: Union[UmbralPrivateKey, CurveBN],
                signer_a: Signer,
                pubkey_b_point: Union[UmbralPublicKey, Point],
                threshold: int,
                N: int,
                params: UmbralParameters = None) -> List[KFrag]:
    """
    Creates a re-encryption key from Alice to Bob and splits it in KFrags,
    using Shamir's Secret Sharing. Requires a threshold number of KFrags
    out of N to guarantee correctness of re-encryption.

    Returns a list of KFrags.
    """
    params = params if params is not None else default_params()

    g = params.g

    if isinstance(privkey_a_bn, UmbralPrivateKey):
        pubkey_a_point = privkey_a_bn.get_pubkey().point_key
        privkey_a_bn = privkey_a_bn.bn_key
    else:
        pubkey_a_point = privkey_a_bn * g

    if isinstance(pubkey_b_point, UmbralPublicKey):
        pubkey_b_point = pubkey_b_point.point_key

    # 'ni' stands for 'Non Interactive'.
    # This point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'd' allows to make Umbral non-interactive
    priv_ni = CurveBN.gen_rand(params.curve)
    ni = priv_ni * g
    d = CurveBN.hash(ni,
                     pubkey_b_point,
                     pubkey_b_point * priv_ni,
                     params=params)

    coeffs = [privkey_a_bn * (~d)]
    coeffs += [CurveBN.gen_rand(params.curve) for _ in range(threshold - 1)]

    u = params.u

    # 'xcoord' stands for 'X coordinate'.
    # This point is used as an ephemeral public key in a DH key exchange,
    # and the resulting shared secret 'dh_xcoord' contributes to prevent
    # reconstruction of the re-encryption key without Bob's intervention
    priv_xcoord = CurveBN.gen_rand(params.curve)
    xcoord = priv_xcoord * g

    dh_xcoord = priv_xcoord * pubkey_b_point

    blake2b = hashes.Hash(hashes.BLAKE2b(64), backend=backend)
    blake2b.update(xcoord.to_bytes())
    blake2b.update(pubkey_b_point.to_bytes())
    blake2b.update(dh_xcoord.to_bytes())
    hashed_dh_tuple = blake2b.finalize()

    bn_size = CurveBN.expected_bytes_length(params.curve)

    kfrags = []
    for _ in range(N):
        id = os.urandom(bn_size)

        share_x = CurveBN.hash(id, hashed_dh_tuple, params=params)

        rk = poly_eval(coeffs, share_x)

        u1 = rk * u

        kfrag_validity_message = bytes().join(
            bytes(material) for material in (id, pubkey_a_point,
                                             pubkey_b_point, u1, ni, xcoord))
        signature = signer_a(kfrag_validity_message)

        kfrag = KFrag(id=id,
                      bn_key=rk,
                      point_noninteractive=ni,
                      point_commitment=u1,
                      point_xcoord=xcoord,
                      signature=signature)
        kfrags.append(kfrag)

    return kfrags
Пример #28
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