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)
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)
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:]))))