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)
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)
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.PRETask(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_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:]))))