def test_bob_can_issue_a_work_order_to_a_specific_ursula( enacted_policy, alice, bob, ursulas): """ Now that Bob has his list of Ursulas, he can issue a WorkOrder to one. Upon receiving the WorkOrder, Ursula saves it and responds by re-encrypting and giving Bob a cFrag. This is a multipart test; it shows proper relations between the Characters Ursula and Bob and also proper interchange between a KFrag, PFrag, and CFrag object in the context of REST-driven proxy re-encryption. """ # We pick up our story with Bob already having followed the treasure map above, ie: hrac, treasure_map = enacted_policy.hrac(), enacted_policy.treasure_map bob.treasure_maps[hrac] = treasure_map bob.follow_treasure_map(hrac) assert len(bob._ursulas) == len(ursulas) the_pfrag = enacted_policy.pfrag the_hrac = enacted_policy.hrac() # Bob has no saved work orders yet, ever. assert len(bob._saved_work_orders) == 0 # We'll test against just a single Ursula - here, we make a WorkOrder for just one. work_orders = bob.generate_work_orders(the_hrac, the_pfrag, num_ursulas=1) assert len(work_orders) == 1 # Bob has saved the WorkOrder, but since he hasn't used it for reencryption yet, it's empty. assert len(bob._saved_work_orders) == 1 assert len(list(bob._saved_work_orders.items())[0][1]) == 0 networky_stuff = MockNetworkyStuff(ursulas) ursula_dht_key, work_order = list(work_orders.items())[0] # **** RE-ENCRYPTION HAPPENS HERE! **** cfrags = bob.get_reencrypted_c_frags(networky_stuff, work_order) the_cfrag = cfrags[0] # We only gave one pFrag, so we only got one cFrag. # Having received the cFrag, Bob also saved the WorkOrder as complete. assert len(list(bob._saved_work_orders.items())[0][1]) == 1 # OK, so cool - Bob has his cFrag! Let's make sure everything went properly. First, we'll show that it is in fact # the correct cFrag (ie, that Ursula performed reencryption properly). ursula = networky_stuff.get_ursula_by_id(work_order.ursula_id) the_kfrag = ursula.keystore.get_kfrag(work_order.kfrag_hrac) the_correct_cfrag = api.ecies_reencrypt(the_kfrag, the_pfrag.encrypted_key) assert the_cfrag == the_correct_cfrag # It's the correct cfrag! # Now we'll show that Ursula saved the correct WorkOrder. work_orders_from_bob = ursula.work_orders(bob=bob) assert len(work_orders_from_bob) == 1 assert work_orders_from_bob[0] == work_order
def test_bob_can_retreive_the_treasure_map_and_decrypt_it( enacted_policy, ursulas): """ Above, we showed that the TreasureMap saved on the network is the correct one for Bob. Here, we show that Bob can retrieve it with only the information about which he is privy pursuant to the PolicyGroup. """ bob = enacted_policy.bob networky_stuff = MockNetworkyStuff(ursulas) # Of course, in the real world, Bob has sufficient information to reconstitute a PolicyGroup, gleaned, we presume, # through a side-channel with Alice. # If Bob doesn't know about any Ursulas, he can't find the TreasureMap via the REST swarm: with pytest.raises(bob.NotEnoughUrsulas): treasure_map_from_wire = bob.get_treasure_map( enacted_policy.alice.stamp, enacted_policy.hrac()) # Let's imagine he has learned about some - say, from the blockchain. bob.known_nodes = {u.interface_info_with_metadata(): u for u in ursulas} # Now try. treasure_map_from_wire = bob.get_treasure_map(enacted_policy.alice.stamp, enacted_policy.hrac()) assert enacted_policy.treasure_map == treasure_map_from_wire
def alice(ursulas, mock_policy_agent, nucypher_test_config): ALICE = Alice(network_middleware=MockNetworkyStuff(ursulas), policy_agent=mock_policy_agent, config=nucypher_test_config) ALICE.server.listen(8471) ALICE.__resource_id = b"some_resource_id" EVENT_LOOP.run_until_complete(ALICE.server.bootstrap([("127.0.0.1", u.dht_port) for u in ursulas])) ALICE.network_bootstrap([("127.0.0.1", u.rest_port) for u in ursulas]) return ALICE
def test_grant(alice, bob, ursulas): networky_stuff = MockNetworkyStuff(ursulas) policy_end_datetime = datetime.datetime.now() + datetime.timedelta(days=5) n = 5 uri = b"this_is_the_path_to_which_access_is_being_granted" policy = alice.grant(bob, uri, networky_stuff, m=3, n=n, expiration=policy_end_datetime) # The number of policies is equal to the number of Ursulas we're using (n) assert len(policy._accepted_contracts) == n # Let's look at the first Ursula. ursula = list(policy._accepted_contracts.values())[0].ursula # Get the Policy from Ursula's datastore, looking up by hrac. proper_hrac = keccak_digest(bytes(alice.stamp) + bytes(bob.stamp) + uri) retrieved_policy = ursula.keystore.get_policy_contract( proper_hrac.hex().encode()) # TODO: Make this a legit KFrag, not bytes. retrieved_k_frag = KFrag.from_bytes(retrieved_policy.k_frag) # TODO: Implement KFrag.__eq__ found = False for k_frag in policy.kfrags: if bytes(k_frag) == bytes(retrieved_k_frag): found = True assert found
def test_bob_gathers_and_combines(enacted_policy, alice, bob, ursulas, alicebob_side_channel): # Bob has saved two WorkOrders so far. assert len(bob._saved_work_orders) == 2 # ...but the policy requires us to collect more cfrags. assert len(bob._saved_work_orders) < enacted_policy.m # Bob can't decrypt yet with just two CFrags. He needs to gather at least m. with pytest.raises(pre.GenericUmbralError): bob.decrypt(alicebob_side_channel) number_left_to_collect = enacted_policy.m - len(bob._saved_work_orders) new_work_orders = bob.generate_work_orders( enacted_policy.hrac(), alicebob_side_channel.capsule, num_ursulas=number_left_to_collect) _id_of_yet_another_ursula, new_work_order = list( new_work_orders.items())[0] networky_stuff = MockNetworkyStuff(ursulas) # In the real world, we'll have a full Ursula node here. But in this case, we need to fake it. new_work_order.ursula = ursulas[2] cfrags = bob.get_reencrypted_c_frags(networky_stuff, new_work_order) alicebob_side_channel.capsule.attach_cfrag(cfrags[0]) # Now. # At long last. assert bob.decrypt(alicebob_side_channel) == b'Welcome to the flippering.'
def test_alice_cannot_offer_policy_without_first_finding_ursula(alice, ursulas): """ Alice can't just make a deal out of thin air if she doesn't know whether any Ursulas are available (she gets Ursula.NotFound). """ networky_stuff = MockNetworkyStuff(ursulas) contract = Contract(alice, "some_hrac,", datetime.datetime.now() + datetime.timedelta(days=5), ursula=ursulas[0]) with pytest.raises(Ursula.NotFound): policy_offer = contract.encrypt_payload_for_ursula()
def test_alice_sets_treasure_map_on_network(enacted_policy, ursulas): """ Having enacted all the policies of a PolicyGroup, Alice creates a TreasureMap and sends it to Ursula via the DHT. """ networky_stuff = MockNetworkyStuff(ursulas) _, packed_encrypted_treasure_map, _, _ = enacted_policy.publish_treasure_map(networky_stuff=networky_stuff, use_dht=True) treasure_map_as_set_on_network = ursulas[0].server.storage[ digest(enacted_policy.treasure_map_dht_key())] assert treasure_map_as_set_on_network == constants.BYTESTRING_IS_TREASURE_MAP + packed_encrypted_treasure_map
def test_alice_finds_ursula_via_rest(alice, ursulas): networky_stuff = MockNetworkyStuff(ursulas) # Imagine alice knows of nobody. alice.known_nodes = {} new_nodes = alice.learn_about_nodes(address="https://localhost", port=ursulas[0].rest_port) assert len(new_nodes) == len(ursulas) for ursula in ursulas: assert ursula.stamp.as_umbral_pubkey() in new_nodes
def enacted_policy(idle_policy, ursulas): # Alice has a policy in mind and knows of enough qualifies Ursulas; she crafts an offer for them. deposit = constants.NON_PAYMENT(b"0000000") contract_end_datetime = maya.now() + datetime.timedelta(days=5) networky_stuff = MockNetworkyStuff(ursulas) found_ursulas = idle_policy.find_ursulas(networky_stuff, deposit, expiration=contract_end_datetime) idle_policy.match_kfrags_to_found_ursulas(found_ursulas) idle_policy.enact(networky_stuff) # REST call happens here, as does population of TreasureMap. return idle_policy
def test_bob_can_retreive_the_treasure_map_and_decrypt_it(enacted_policy, ursulas): """ Above, we showed that the TreasureMap saved on the network is the correct one for Bob. Here, we show that Bob can retrieve it with only the information about which he is privy pursuant to the PolicyGroup. """ bob = enacted_policy.bob networky_stuff = MockNetworkyStuff(ursulas) # Of course, in the real world, Bob has sufficient information to reconstitute a PolicyGroup, gleaned, we presume, # through a side-channel with Alice. treasure_map_from_wire = bob.get_treasure_map(enacted_policy) assert enacted_policy.treasure_map == treasure_map_from_wire
def test_bob_remembers_that_he_has_cfrags_for_a_particular_capsule( enacted_policy, alice, bob, ursulas, capsule_side_channel): # In our last episode, Bob made a WorkOrder for the capsule... assert len( bob._saved_work_orders.by_capsule( capsule_side_channel[0].capsule)) == 1 # ...and he used it to obtain a CFrag from Ursula. assert len(capsule_side_channel[0].capsule._attached_cfrags) == 1 # He can also get a dict of {Ursula:WorkOrder} by looking them up from the capsule. workorders_by_capsule = bob._saved_work_orders.by_capsule( capsule_side_channel[0].capsule) # Bob has just one WorkOrder from that one Ursula. assert len(workorders_by_capsule) == 1 saved_work_order = list(workorders_by_capsule.values())[0] # The rest of this test will show that if Bob generates another WorkOrder, it's for a *different* Ursula. generated_work_orders = bob.generate_work_orders( enacted_policy.hrac(), capsule_side_channel[0].capsule, num_ursulas=1) id_of_this_new_ursula, new_work_order = list( generated_work_orders.items())[0] # This new Ursula isn't the same one to whom we've already issued a WorkOrder. id_of_ursula_from_whom_we_already_have_a_cfrag = list( workorders_by_capsule.keys())[0] assert id_of_ursula_from_whom_we_already_have_a_cfrag != id_of_this_new_ursula # ...and, although this WorkOrder has the same capsules as the saved one... assert new_work_order.capsules == saved_work_order.capsules # ...it's not the same WorkOrder. assert new_work_order != saved_work_order # We can get a new CFrag, just like last time. networky_stuff = MockNetworkyStuff(ursulas) # In the real world, we'll have a full Ursula node here. But in this case, we need to fake it. new_work_order.ursula = ursulas[1] cfrags = bob.get_reencrypted_c_frags(networky_stuff, new_work_order) # Again: one Capsule, one cFrag. assert len(cfrags) == 1 new_cfrag = cfrags[0] # Attach the CFrag to the Capsule. capsule_side_channel[0].capsule.attach_cfrag(new_cfrag)
def test_grant(alice, bob, ursulas): networky_stuff = MockNetworkyStuff(ursulas) policy_end_datetime = datetime.datetime.now() + datetime.timedelta(days=5) n = 5 uri = b"this_is_the_path_to_which_access_is_being_granted" policy = alice.grant(bob, uri, networky_stuff, m=3, n=n, expiration=policy_end_datetime) # The number of policies is equal to the number of Ursulas we're using (n) assert len(policy._accepted_contracts) == n # Let's look at the first Ursula. ursula = list(policy._accepted_contracts.values())[0].ursula # Get the kfrag, based in the hrac. proper_hrac = keccak_digest(bytes(alice.seal) + bytes(bob.seal) + uri) kfrag_that_was_set = ursula.keystore.get_kfrag(proper_hrac) assert kfrag_that_was_set
def test_bob_gathers_and_combines(enacted_policy, bob, ursulas, capsule_side_channel): # The side channel is represented as a single MessageKit, which is all that Bob really needs. the_message_kit, the_data_source = capsule_side_channel # Bob has saved two WorkOrders so far. assert len(bob._saved_work_orders) == 2 # ...but the policy requires us to collect more cfrags. assert len(bob._saved_work_orders) < enacted_policy.m # Bob can't decrypt yet with just two CFrags. He needs to gather at least m. with pytest.raises(pre.GenericUmbralError): bob.decrypt(the_message_kit) number_left_to_collect = enacted_policy.m - len(bob._saved_work_orders) new_work_orders = bob.generate_work_orders( enacted_policy.hrac(), the_message_kit.capsule, num_ursulas=number_left_to_collect) _id_of_yet_another_ursula, new_work_order = list( new_work_orders.items())[0] networky_stuff = MockNetworkyStuff(ursulas) # In the real world, we'll have a full Ursula node here. But in this case, we need to fake it. new_work_order.ursula = ursulas[2] cfrags = bob.get_reencrypted_c_frags(networky_stuff, new_work_order) the_message_kit.capsule.attach_cfrag(cfrags[0]) # Now. # At long last. is_valid, cleartext = bob.verify_from(the_data_source, the_message_kit, decrypt=True) assert cleartext == b'Welcome to the flippering.' assert is_valid
def test_bob_can_issue_a_work_order_to_a_specific_ursula( enacted_policy, alice, bob, ursulas, capsule_side_channel): """ Now that Bob has his list of Ursulas, he can issue a WorkOrder to one. Upon receiving the WorkOrder, Ursula saves it and responds by re-encrypting and giving Bob a cFrag. This is a multipart test; it shows proper relations between the Characters Ursula and Bob and also proper interchange between a KFrag, Capsule, and CFrag object in the context of REST-driven proxy re-encryption. """ # We pick up our story with Bob already having followed the treasure map above, ie: hrac, treasure_map = enacted_policy.hrac(), enacted_policy.treasure_map bob.treasure_maps[hrac] = treasure_map bob.follow_treasure_map(hrac) assert len(bob.known_nodes) == len(ursulas) the_hrac = enacted_policy.hrac() # Bob has no saved work orders yet, ever. assert len(bob._saved_work_orders) == 0 # We'll test against just a single Ursula - here, we make a WorkOrder for just one. # We can pass any number of capsules as args; here we pass just one. work_orders = bob.generate_work_orders(the_hrac, capsule_side_channel[0].capsule, num_ursulas=1) # Again: one Ursula, one work_order. assert len(work_orders) == 1 # Bob saved the WorkOrder. assert len(bob._saved_work_orders) == 1 # And the Ursula. assert len(bob._saved_work_orders.ursulas) == 1 networky_stuff = MockNetworkyStuff(ursulas) ursula_dht_key, work_order = list(work_orders.items())[0] # In the real world, we'll have a full Ursula node here. But in this case, we need to fake it. work_order.ursula = ursulas[0] # **** RE-ENCRYPTION HAPPENS HERE! **** cfrags = bob.get_reencrypted_c_frags(networky_stuff, work_order) # We only gave one Capsule, so we only got one cFrag. assert len(cfrags) == 1 the_cfrag = cfrags[0] # Attach the CFrag to the Capsule. capsule_side_channel[0].capsule.attach_cfrag(the_cfrag) # Having received the cFrag, Bob also saved the WorkOrder as complete. assert len(bob._saved_work_orders) == 1 # OK, so cool - Bob has his cFrag! Let's make sure everything went properly. First, we'll show that it is in fact # the correct cFrag (ie, that Ursula performed reencryption properly). ursula = work_order.ursula kfrag_bytes = ursula.datastore.get_policy_arrangement( work_order.kfrag_hrac.hex().encode()).k_frag the_kfrag = KFrag.from_bytes(kfrag_bytes) the_correct_cfrag = pre.reencrypt(the_kfrag, capsule_side_channel[0].capsule) assert bytes(the_cfrag) == bytes( the_correct_cfrag) # It's the correct cfrag! # Now we'll show that Ursula saved the correct WorkOrder. work_orders_from_bob = ursula.work_orders(bob=bob) assert len(work_orders_from_bob) == 1 assert work_orders_from_bob[0] == work_order
def treasure_map_is_set_on_dht(enacted_policy, ursulas): networky_stuff = MockNetworkyStuff(ursulas) enacted_policy.publish_treasure_map(networky_stuff, use_dht=True)
def bob(ursulas): BOB = Bob(network_middleware=MockNetworkyStuff(ursulas)) return BOB