Exemple #1
0
def _mock_ursula_reencrypts(ursula):
    delegating_privkey = SecretKey.random()
    capsule, _ciphertext = encrypt(delegating_privkey.public_key(), b'unused')
    signing_privkey = SecretKey.random()
    signing_pubkey = signing_privkey.public_key()
    signer = Signer(signing_privkey)
    priv_key_bob = SecretKey.random()
    pub_key_bob = priv_key_bob.public_key()
    kfrags = generate_kfrags(delegating_sk=delegating_privkey,
                             signer=signer,
                             receiving_pk=pub_key_bob,
                             threshold=2,
                             num_kfrags=4,
                             sign_delegating_key=False,
                             sign_receiving_key=False)

    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.sign(specification))

    cfrag = reencrypt(capsule, kfrags[0])
    cfrag_signature = ursula.stamp(bytes(cfrag))

    bob = Bob.from_public_keys(verifying_key=pub_key_bob)
    return WorkOrder.PRETask(capsule, task_signature, cfrag, cfrag_signature)
Exemple #2
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
def test_pre_task(mock_ursula_reencrypts, ursula, get_random_checksum_address):
    identity_evidence = ursula.decentralized_identity_evidence
    task = mock_ursula_reencrypts(ursula)
    cfrag = task.cfrag
    capsule = task.capsule
    capsule_bytes = bytes(capsule)

    signature = ursula.stamp(capsule_bytes)

    task = WorkOrder.PRETask(capsule=capsule, signature=signature)
    assert capsule == task.capsule
    assert signature == task.signature

    task_bytes = bytes(task)
    assert bytes(capsule) + bytes(signature) == task_bytes

    deserialized_task = WorkOrder.PRETask.from_bytes(task_bytes)
    assert capsule == deserialized_task.capsule
    assert signature == deserialized_task.signature

    # Attaching cfrags to the task
    cfrag_bytes = bytes(cfrag)
    cfrag_signature = ursula.stamp(cfrag_bytes)

    task.attach_work_result(cfrag, cfrag_signature)
    assert capsule == task.capsule
    assert signature == task.signature
    assert cfrag == task.cfrag
    assert cfrag_signature == task.cfrag_signature

    task_bytes = bytes(task)
    assert bytes(capsule) + bytes(signature) + cfrag_bytes + bytes(
        cfrag_signature) == task_bytes

    deserialized_task = WorkOrder.PRETask.from_bytes(task_bytes)
    assert capsule == deserialized_task.capsule
    assert signature == deserialized_task.signature
    assert bytes(cfrag) == bytes(
        deserialized_task.cfrag
    )  # We compare bytes as there's no CapsuleFrag.__eq__
    assert cfrag_signature == deserialized_task.cfrag_signature

    # Task specification
    alice_address = to_canonical_address(get_random_checksum_address())
    blockhash = os.urandom(ETH_HASH_BYTE_LENGTH)

    specification = task.get_specification(bytes(ursula.stamp), alice_address,
                                           blockhash, identity_evidence)

    expected_specification = bytes(capsule) + bytes(
        ursula.stamp) + identity_evidence + alice_address + blockhash
    assert expected_specification == specification

    with pytest.raises(
            ValueError,
            match=f"blockhash must be of length {ETH_HASH_BYTE_LENGTH}"):
        task.get_specification(bytes(ursula.stamp), alice_address,
                               os.urandom(42), identity_evidence)
Exemple #4
0
    def reencrypt_via_rest(id_as_hex):
        from nucypher.policy.collections import WorkOrder  # Avoid circular import
        arrangement_id = binascii.unhexlify(id_as_hex)
        try:
            with ThreadedSession(db_engine) as session:
                policy_arrangement = datastore.get_policy_arrangement(
                    arrangement_id=id_as_hex.encode(), session=session)
        except NotFound:
            return Response(response=arrangement_id, status=404)
        kfrag_bytes = policy_arrangement.kfrag  # Careful!  :-)
        verifying_key_bytes = policy_arrangement.alice_verifying_key.key_data

        # TODO: Push this to a lower level. Perhaps to Ursula character? #619
        kfrag = KFrag.from_bytes(kfrag_bytes)
        alices_verifying_key = UmbralPublicKey.from_bytes(verifying_key_bytes)
        alices_address = canonical_address_from_umbral_key(
            alices_verifying_key)

        work_order = WorkOrder.from_rest_payload(arrangement_id=arrangement_id,
                                                 rest_payload=request.data,
                                                 ursula=this_node,
                                                 alice_address=alices_address)

        log.info(
            f"Work Order from {work_order.bob}, signed {work_order.receipt_signature}"
        )

        cfrag_byte_stream = b""

        for task in work_order.tasks:
            # Ursula signs on top of Bob's signature of each task.
            # Now both are committed to the same task.  See #259.
            reencryption_metadata = bytes(
                this_node.stamp(bytes(task.signature)))

            capsule = task.capsule
            capsule.set_correctness_keys(verifying=alices_verifying_key)
            cfrag = pre.reencrypt(kfrag,
                                  capsule,
                                  metadata=reencryption_metadata)
            log.info(f"Re-encrypting for {capsule}, made {cfrag}.")

            # Finally, Ursula commits to her result
            reencryption_signature = this_node.stamp(bytes(cfrag))
            cfrag_byte_stream += VariableLengthBytestring(
                cfrag) + reencryption_signature

        # TODO: Put this in Ursula's datastore
        this_node._work_orders.append(work_order)

        headers = {'Content-Type': 'application/octet-stream'}

        return Response(response=cfrag_byte_stream, headers=headers)
Exemple #5
0
    def reencrypt_via_rest(id_as_hex):

        # Get Policy Arrangement
        try:
            arrangement_id = binascii.unhexlify(id_as_hex)
        except (binascii.Error, TypeError):
            return Response(response=b'Invalid arrangement ID', status=405)
        try:
            with ThreadedSession(db_engine) as session:
                arrangement = datastore.get_policy_arrangement(
                    arrangement_id=id_as_hex.encode(), session=session)
        except NotFound:
            return Response(response=arrangement_id, status=404)

        # Get KFrag
        # TODO: Yeah, well, what if this arrangement hasn't been enacted?  1702
        kfrag = KFrag.from_bytes(arrangement.kfrag)

        # Get Work Order
        from nucypher.policy.collections import WorkOrder  # Avoid circular import
        alice_verifying_key_bytes = arrangement.alice_verifying_key.key_data
        alice_verifying_key = UmbralPublicKey.from_bytes(
            alice_verifying_key_bytes)
        alice_address = canonical_address_from_umbral_key(alice_verifying_key)
        work_order_payload = request.data
        work_order = WorkOrder.from_rest_payload(
            arrangement_id=arrangement_id,
            rest_payload=work_order_payload,
            ursula=this_node,
            alice_address=alice_address)
        log.info(
            f"Work Order from {work_order.bob}, signed {work_order.receipt_signature}"
        )

        # Re-encrypt
        response = this_node._reencrypt(
            kfrag=kfrag,
            work_order=work_order,
            alice_verifying_key=alice_verifying_key)

        # Now, Ursula saves this workorder to her database...
        with ThreadedSession(db_engine):
            this_node.datastore.save_workorder(
                bob_verifying_key=bytes(work_order.bob.stamp),
                bob_signature=bytes(work_order.receipt_signature),
                arrangement_id=work_order.arrangement_id)

        headers = {'Content-Type': 'application/octet-stream'}
        return Response(headers=headers, response=response)
Exemple #6
0
    def reencrypt_via_rest(id_as_hex):

        # Get Policy Arrangement
        try:
            arrangement_id = binascii.unhexlify(id_as_hex)
        except (binascii.Error, TypeError):
            return Response(response=b'Invalid arrangement ID', status=405)
        try:
            # Get KFrag
            # TODO: Yeah, well, what if this arrangement hasn't been enacted?  1702
            with datastore.describe(PolicyArrangement,
                                    id_as_hex) as policy_arrangement:
                kfrag = policy_arrangement.kfrag
                alice_verifying_key = policy_arrangement.alice_verifying_key
        except RecordNotFound:
            return Response(response=arrangement_id, status=404)

        # Get Work Order
        from nucypher.policy.collections import WorkOrder  # Avoid circular import
        alice_address = canonical_address_from_umbral_key(alice_verifying_key)
        work_order_payload = request.data
        work_order = WorkOrder.from_rest_payload(
            arrangement_id=arrangement_id,
            rest_payload=work_order_payload,
            ursula=this_node,
            alice_address=alice_address)
        log.info(
            f"Work Order from {work_order.bob}, signed {work_order.receipt_signature}"
        )

        # Re-encrypt
        response = this_node._reencrypt(
            kfrag=kfrag,
            work_order=work_order,
            alice_verifying_key=alice_verifying_key)

        # Now, Ursula saves this workorder to her database...
        # Note: we give the work order a random ID to store it under.
        with datastore.describe(Workorder, str(uuid.uuid4()),
                                writeable=True) as new_workorder:
            new_workorder.arrangement_id = work_order.arrangement_id
            new_workorder.bob_verifying_key = work_order.bob.stamp.as_umbral_pubkey(
            )
            new_workorder.bob_signature = work_order.receipt_signature

        headers = {'Content-Type': 'application/octet-stream'}
        return Response(headers=headers, response=response)
def test_work_order_with_multiple_capsules(mock_ursula_reencrypts, ursula,
                                           get_random_checksum_address,
                                           federated_bob, federated_alice,
                                           number):

    tasks = [mock_ursula_reencrypts(ursula) for _ in range(number)]
    material = [(task.capsule, task.signature, task.cfrag,
                 task.cfrag_signature) for task in tasks]
    capsules, signatures, cfrags, cfrag_signatures = zip(*material)

    arrangement_id = os.urandom(Arrangement.ID_LENGTH)
    alice_address = canonical_address_from_umbral_key(federated_alice.stamp)
    blockhash = b'\0' * ETH_HASH_BYTE_LENGTH  # TODO: Prove freshness of work order - #259
    identity_evidence = ursula.decentralized_identity_evidence

    # Test construction of WorkOrders by Bob
    work_order = WorkOrder.construct_by_bob(
        arrangement_id=arrangement_id,
        bob=federated_bob,
        alice_verifying=federated_alice.stamp.as_umbral_pubkey(),
        ursula=ursula,
        capsules=capsules)

    receipt_input = WorkOrder.HEADER + bytes(ursula.stamp) + b''.join(
        map(bytes, capsules))
    bob_verifying_pubkey = federated_bob.stamp.as_umbral_pubkey()

    assert work_order.bob == federated_bob
    assert work_order.arrangement_id == arrangement_id
    assert work_order.alice_address == alice_address
    assert len(work_order.tasks) == len(work_order) == number
    for capsule in capsules:
        assert work_order.tasks[capsule].capsule == capsule
        task = WorkOrder.PRETask(capsule, signature=None)
        specification = task.get_specification(ursula.stamp, alice_address,
                                               blockhash, identity_evidence)
        assert work_order.tasks[capsule].signature.verify(
            bob_verifying_pubkey, specification)
    assert work_order.receipt_signature.verify(bob_verifying_pubkey,
                                               receipt_input)
    assert work_order.ursula == ursula
    assert work_order.blockhash == blockhash
    assert not work_order.completed

    # Test WorkOrders' payload serialization and deserialization
    tasks_bytes = b''.join(map(bytes, work_order.tasks.values()))
    expected_payload = bytes(work_order.receipt_signature) + bytes(
        federated_bob.stamp) + blockhash + tasks_bytes

    payload = work_order.payload()
    assert expected_payload == payload

    same_work_order = WorkOrder.from_rest_payload(
        arrangement_id=arrangement_id,
        rest_payload=payload,
        ursula=ursula,
        alice_address=alice_address)

    assert same_work_order.bob == federated_bob
    assert same_work_order.arrangement_id == arrangement_id
    assert same_work_order.alice_address == alice_address
    assert len(same_work_order.tasks) == len(same_work_order) == number
    for capsule in capsules:
        assert same_work_order.tasks[capsule].capsule == capsule
        assert same_work_order.tasks[capsule].signature == work_order.tasks[
            capsule].signature
    assert same_work_order.receipt_signature == work_order.receipt_signature
    assert same_work_order.ursula == ursula
    assert same_work_order.blockhash == blockhash
    assert not same_work_order.completed

    tampered_payload = bytearray(payload)
    somewhere_over_the_blockhash = 64 + 33 + 5
    tampered_payload[somewhere_over_the_blockhash] = 255 - payload[
        somewhere_over_the_blockhash]
    with pytest.raises(InvalidSignature):
        _ = WorkOrder.from_rest_payload(arrangement_id=arrangement_id,
                                        rest_payload=bytes(tampered_payload),
                                        ursula=ursula,
                                        alice_address=alice_address)

    # Testing WorkOrder.complete()

    # Let's use the original task signatures in our WorkOrder, instead
    for capsule, task_signature in zip(capsules, signatures):
        work_order.tasks[capsule].signature = task_signature

    # Now, complete() works as intended
    good_cfrags = work_order.complete(list(zip(cfrags, cfrag_signatures)))
    assert work_order.completed
    assert len(good_cfrags) == number

    # Testing some additional expected exceptions
    with pytest.raises(ValueError,
                       match="Ursula gave back the wrong number of cfrags"):
        work_order.complete(list(zip(cfrags, cfrag_signatures))[1:])

    bad_cfrag_signature = ursula.stamp(os.urandom(10))
    with pytest.raises(InvalidSignature,
                       match=f"{cfrags[0]} is not properly signed by Ursula."):
        work_order.complete(
            list(
                zip(cfrags,
                    [bad_cfrag_signature] + list(cfrag_signatures[1:]))))