def test_bob_can_follow_treasure_map_even_if_he_only_knows_of_one_node( enacted_federated_policy, federated_ursulas, certificates_tempdir): """ Similar to above, but this time, we'll show that if Bob can connect to a single node, he can learn enough to follow the TreasureMap. Also, we'll get the TreasureMap from the hrac alone (ie, not via a side channel). """ from nucypher.characters.lawful import Bob bob = Bob(network_middleware=MockRestMiddleware(), domain=TEMPORARY_DOMAIN, start_learning_now=False, abort_on_learning_error=True, federated_only=True) # Again, let's assume that he received the TreasureMap via a side channel. hrac, treasure_map = enacted_federated_policy.hrac( ), enacted_federated_policy.treasure_map map_id = treasure_map.public_id() bob.treasure_maps[map_id] = treasure_map # Now, let's create a scenario in which Bob knows of only one node. assert len(bob.known_nodes) == 0 first_ursula = list(federated_ursulas).pop(0) bob.remember_node(first_ursula) assert len(bob.known_nodes) == 1 # This time, when he follows the TreasureMap... unknown_nodes, known_nodes = bob.peek_at_treasure_map(map_id=map_id) # Bob already knew about one node; the rest are unknown. assert len(unknown_nodes) == len(treasure_map) - 1 # He needs to actually follow the treasure map to get the rest. bob.follow_treasure_map(map_id=map_id) # The nodes in the learning loop are now his top target, but he's not learning yet. assert not bob._learning_task.running # ...so he hasn't learned anything (ie, Bob still knows of just one node). assert len(bob.known_nodes) == 1 # Now, we'll start his learning loop. bob.start_learning_loop() # ...and block until the unknown_nodes have all been found. d = threads.deferToThread(bob.block_until_specific_nodes_are_known, unknown_nodes) yield d # ...and he now has no more unknown_nodes. assert len(bob.known_nodes) == len(treasure_map) bob.disenchant()
for counter, plaintext in enumerate(finnegans_wake): # Enrico knows the policy's public key from a side-channel. # In this demo a new enrico is being constructed for each line of text. # This demonstrates how many individual encryptors may encrypt for a single policy. # In other words -- Many data sources (Enricos) can encrypt for the policy's public key. enrico = Enrico(policy_encrypting_key=policy_public_key) # In this case, the plaintext is a single passage from James Joyce's Finnegan's Wake. # The matter of whether encryption makes the passage more or less readable # is left to the reader to determine. message_kit = enrico.encrypt_message(plaintext) enrico_public_key = bytes(enrico.stamp) del enrico ############### # Back to Bob # ############### # Now Bob can retrieve the original message by requesting re-encryption from nodes. cleartexts = bob.retrieve_and_decrypt( [message_kit], alice_verifying_key=alice_verifying_key, encrypted_treasure_map=policy.treasure_map) # We show that indeed this is the passage originally encrypted by Enrico. assert plaintext == cleartexts[0] print(cleartexts) bob.disenchant()
def test_bob_joins_policy_and_retrieves(federated_alice, federated_ursulas, certificates_tempdir, ): # Let's partition Ursulas in two parts a_couple_of_ursulas = list(federated_ursulas)[:2] rest_of_ursulas = list(federated_ursulas)[2:] # Bob becomes bob = Bob(federated_only=True, domain=TEMPORARY_DOMAIN, start_learning_now=True, network_middleware=MockRestMiddleware(), abort_on_learning_error=True, known_nodes=a_couple_of_ursulas, ) # Bob has only connected to - at most - 2 nodes. assert sum(node.verified_node for node in bob.known_nodes) <= 2 # Alice creates a policy granting access to Bob # Just for fun, let's assume she distributes KFrags among Ursulas unknown to Bob n = NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK - 2 label = b'label://' + os.urandom(32) contract_end_datetime = maya.now() + datetime.timedelta(days=5) policy = federated_alice.grant(bob=bob, label=label, m=3, n=n, expiration=contract_end_datetime, handpicked_ursulas=set(rest_of_ursulas), ) assert bob == policy.bob assert label == policy.label try: # Now, Bob joins the policy bob.join_policy(label=label, alice_verifying_key=federated_alice.stamp, block=True) except policy.treasure_map.NowhereToBeFound: maps = [] for ursula in federated_ursulas: for map in ursula.treasure_maps.values(): maps.append(map) if policy.treasure_map in maps: # This is a nice place to put a breakpoint to examine Bob's failure to join a policy. bob.join_policy(label=label, alice_verifying_key=federated_alice.stamp, block=True) pytest.fail(f"Bob didn't find map {policy.treasure_map} even though it was available. Come on, Bob.") else: pytest.fail(f"It seems that Alice didn't publish {policy.treasure_map}. Come on, Alice.") # In the end, Bob should know all the Ursulas assert len(bob.known_nodes) == len(federated_ursulas) # Enrico becomes enrico = Enrico(policy_encrypting_key=policy.public_key) plaintext = b"What's your approach? Mississippis or what?" message_kit, _signature = enrico.encrypt_message(plaintext) alices_verifying_key = federated_alice.stamp.as_umbral_pubkey() # Bob takes the message_kit and retrieves the message within delivered_cleartexts = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, retain_cfrags=True) assert plaintext == delivered_cleartexts[0] # Bob tries to retrieve again, but without using the cached CFrags, it fails. with pytest.raises(TypeError): delivered_cleartexts = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label) cleartexts_delivered_a_second_time = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, use_attached_cfrags=True) # Indeed, they're the same cleartexts. assert delivered_cleartexts == cleartexts_delivered_a_second_time # Let's try retrieve again, but Alice revoked the policy. failed_revocations = federated_alice.revoke(policy) assert len(failed_revocations) == 0 # One thing to note here is that Bob *can* still retrieve with the cached CFrags, even though this Policy has been revoked. #892 _cleartexts = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, use_precedent_work_orders=True, ) assert _cleartexts == delivered_cleartexts # TODO: 892 # OK, but we imagine that the message_kit is fresh here. message_kit.capsule.clear_cfrags() with pytest.raises(Ursula.NotEnoughUrsulas): _cleartexts = bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=alices_verifying_key, label=policy.label, ) bob.disenchant()
# In this case, the plaintext is a # single passage from James Joyce's Finnegan's Wake. # The matter of whether encryption makes the passage more or less readable # is left to the reader to determine. single_passage_ciphertext, _signature = enrico.encrypt_message(plaintext) data_source_public_key = bytes(enrico.stamp) del enrico ############### # Back to Bob # ############### enrico_as_understood_by_bob = Enrico.from_public_keys( verifying_key=data_source_public_key, policy_encrypting_key=policy_pubkey) # Now Bob can retrieve the original message. alice_pubkey_restored_from_ancient_scroll = UmbralPublicKey.from_bytes( alices_pubkey_bytes_saved_for_posterity) delivered_cleartexts = BOB.retrieve( single_passage_ciphertext, enrico=enrico_as_understood_by_bob, alice_verifying_key=alice_pubkey_restored_from_ancient_scroll, label=label) # We show that indeed this is the passage originally encrypted by Enrico. assert plaintext == delivered_cleartexts[0] print("Retrieved: {}".format(delivered_cleartexts[0])) BOB.disenchant()
def test_bob_retrieves(federated_alice, federated_ursulas, certificates_tempdir): # Let's partition Ursulas in two parts a_couple_of_ursulas = list(federated_ursulas)[:2] rest_of_ursulas = list(federated_ursulas)[2:] # Bob becomes bob = Bob( federated_only=True, domain=TEMPORARY_DOMAIN, start_learning_now=True, network_middleware=MockRestMiddleware(), abort_on_learning_error=True, known_nodes=a_couple_of_ursulas, ) # Bob has only connected to - at most - 2 nodes. assert sum(node.verified_node for node in bob.known_nodes) <= 2 # Alice creates a policy granting access to Bob # Just for fun, let's assume she distributes KFrags among Ursulas unknown to Bob shares = NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK - 2 label = b'label://' + os.urandom(32) contract_end_datetime = maya.now() + datetime.timedelta(days=5) policy = federated_alice.grant( bob=bob, label=label, threshold=3, shares=shares, expiration=contract_end_datetime, ursulas=set(rest_of_ursulas), ) assert label == policy.label # Enrico becomes enrico = Enrico(policy_encrypting_key=policy.public_key) plaintext = b"What's your approach? Mississippis or what?" message_kit = enrico.encrypt_message(plaintext) alices_verifying_key = federated_alice.stamp.as_umbral_pubkey() # Bob takes the message_kit and retrieves the message within delivered_cleartexts = bob.retrieve_and_decrypt( [message_kit], alice_verifying_key=alices_verifying_key, encrypted_treasure_map=policy.treasure_map) assert plaintext == delivered_cleartexts[0] cleartexts_delivered_a_second_time = bob.retrieve_and_decrypt( [message_kit], alice_verifying_key=alices_verifying_key, encrypted_treasure_map=policy.treasure_map) # Indeed, they're the same cleartexts. assert delivered_cleartexts == cleartexts_delivered_a_second_time # Let's try retrieve again, but Alice revoked the policy. receipt, failed_revocations = federated_alice.revoke(policy) assert len(failed_revocations) == 0 # One thing to note here is that Bob *can* still retrieve with the cached CFrags, # even though this Policy has been revoked. #892 _cleartexts = bob.retrieve_and_decrypt( [message_kit], alice_verifying_key=alices_verifying_key, encrypted_treasure_map=policy.treasure_map) assert _cleartexts == delivered_cleartexts # TODO: 892 bob.disenchant()