def follow_treasure_map(self, hrac, using_dht=False): treasure_map = self.treasure_maps[hrac] number_of_known_treasure_ursulas = 0 if not using_dht: for ursula_interface_id in treasure_map: pubkey = UmbralPublicKey.from_bytes(ursula_interface_id) if pubkey in self.known_nodes: number_of_known_treasure_ursulas += 1 newly_discovered_nodes = {} nodes_to_check = iter(self.known_nodes.values()) while number_of_known_treasure_ursulas < treasure_map.m: try: node_to_check = next(nodes_to_check) except StopIteration: raise self.NotEnoughUrsulas( "Unable to follow the TreasureMap; we just don't know enough nodes to ask about this. Maybe try using the DHT instead." ) new_nodes = self.learn_about_nodes(node_to_check.ip_address, node_to_check.rest_port) for new_node_pubkey in new_nodes.keys(): if new_node_pubkey in treasure_map: number_of_known_treasure_ursulas += 1 newly_discovered_nodes.update(new_nodes) self.known_nodes.update(newly_discovered_nodes) return newly_discovered_nodes, number_of_known_treasure_ursulas else: for ursula_interface_id in self.treasure_maps[hrac]: pubkey = UmbralPublicKey.from_bytes(ursula_interface_id) if ursula_interface_id in self.known_nodes: # If we already know about this Ursula, # we needn't learn about it again. continue if using_dht: # TODO: perform this part concurrently. value = self.server.get_now(ursula_interface_id) # TODO: Make this much prettier header, signature, ursula_pubkey_sig, _hrac, ( port, interface, ttl) = dht_value_splitter(value, msgpack_remainder=True) if header != constants.BYTESTRING_IS_URSULA_IFACE_INFO: raise TypeError( "Unknown DHT value. How did this get on the network?" ) # TODO: If we're going to implement TTL, it will be here. self.known_nodes[ursula_interface_id] = \ Ursula.as_discovered_on_network( dht_port=port, dht_interface=interface, powers_and_keys=({SigningPower: ursula_pubkey_sig}) )
def follow_treasure_map(self, hrac): for ursula_interface_id in self.treasure_maps[hrac]: if ursula_interface_id in self.known_nodes: # If we already know about this Ursula, # we needn't learn about it again. continue # TODO: perform this part concurrently. value = self.server.get_now(ursula_interface_id) # TODO: Make this much prettier header, signature, ursula_pubkey_sig, _hrac, ( port, interface, ttl) = dht_value_splitter(value, msgpack_remainder=True) if header != constants.BYTESTRING_IS_URSULA_IFACE_INFO: raise TypeError( "Unknown DHT value. How did this get on the network?") # TODO: If we're going to implement TTL, it will be here. self.known_nodes[ursula_interface_id] = \ Ursula.as_discovered_on_network( dht_port=port, dht_interface=interface, powers_and_keys=({SigningPower: ursula_pubkey_sig}) )
def get_treasure_map(self, policy_group): dht_key = policy_group.treasure_map_dht_key() ursula_coro = self.server.get(dht_key) event_loop = asyncio.get_event_loop() packed_encrypted_treasure_map = event_loop.run_until_complete( ursula_coro) # TODO: Make this prettier header, _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map =\ dht_value_splitter(packed_encrypted_treasure_map, return_remainder=True) tmap_messaage_kit = MessageKit.from_bytes(encrypted_treasure_map) if header != BYTESTRING_IS_TREASURE_MAP: raise TypeError( "Unknown DHT value. How did this get on the network?") verified, packed_node_list = self.verify_from( self.alice, tmap_messaage_kit, signature_is_on_cleartext=True, decrypt=True) if not verified: return NOT_FROM_ALICE else: from nkms.policy.models import TreasureMap self.treasure_maps[policy_group.hrac] = TreasureMap( msgpack.loads(packed_node_list)) return self.treasure_maps[policy_group.hrac]
def follow_treasure_map(self, hrac): for ursula_interface_id in self.treasure_maps[hrac]: # TODO: perform this part concurrently. getter = self.server.get(ursula_interface_id) loop = asyncio.get_event_loop() value = loop.run_until_complete(getter) signature, ursula_pubkey_sig, hrac, (port, interface, ttl) = dht_value_splitter(value.lstrip(b"uaddr"), msgpack_remainder=True) # TODO: If we're going to implement TTL, it will be here. self._ursulas[ursula_interface_id] = Ursula.as_discovered_on_network(port=port, interface=interface, pubkey_sig_bytes=ursula_pubkey_sig)
def test_alice_finds_ursula_via_dht(alice, ursulas): """ With the help of any Ursula, Alice can find a specific Ursula. """ ursula_index = 1 all_ursulas = blockchain_client._ursulas_on_blockchain value = alice.server.get_now(all_ursulas[ursula_index]) header, _signature, _ursula_pubkey_sig, interface_info = dht_value_splitter( value, return_remainder=True) assert header == constants.BYTESTRING_IS_URSULA_IFACE_INFO port = msgpack.loads(interface_info)[1] assert port == URSULA_PORT + ursula_index
def test_alice_finds_ursula(alice, ursulas): """ With the help of any Ursula, Alice can find a specific Ursula. """ ursula_index = 1 all_ursulas = blockchain_client._ursulas_on_blockchain getter = alice.server.get(all_ursulas[ursula_index]) loop = asyncio.get_event_loop() value = loop.run_until_complete(getter) _signature, _ursula_pubkey_sig, _hrac, interface_info = dht_value_splitter(value.lstrip(b"uaddr-"), return_remainder=True) port = msgpack.loads(interface_info)[0] assert port == URSULA_PORT + ursula_index
def test_treaure_map_is_legit(enacted_policy): """ Sure, the TreasureMap can get to Bob, but we also need to know that each Ursula in the TreasureMap is on the network. """ alice = enacted_policy.alice for ursula_interface_id in enacted_policy.treasure_map: value = alice.server.get_now(ursula_interface_id) header, signature, ursula_pubkey_sig, hrac, interface_info = dht_value_splitter(value, return_remainder=True) assert header == BYTESTRING_IS_URSULA_IFACE_INFO port = msgpack.loads(interface_info)[0] legal_ports = range(NUMBER_OF_URSULAS_IN_NETWORK, NUMBER_OF_URSULAS_IN_NETWORK + URSULA_PORT) assert port in legal_ports
def test_treaure_map_is_legit(enacted_policy): """ Sure, the TreasureMap can get to Bob, but we also need to know that each Ursula in the TreasureMap is on the network. """ alice = enacted_policy.alice for ursula_interface_id in enacted_policy.treasure_map: getter = alice.server.get(ursula_interface_id) loop = asyncio.get_event_loop() value = loop.run_until_complete(getter) signature, ursula_pubkey_sig, hrac, interface_info = dht_value_splitter(value.lstrip(b"uaddr-"), return_remainder=True) port = msgpack.loads(interface_info)[0] legal_ports = range(NUMBER_OF_URSULAS_IN_NETWORK, NUMBER_OF_URSULAS_IN_NETWORK + URSULA_PORT) assert port in legal_ports
def get_treasure_map_from_known_ursulas(self, networky_stuff, map_id): """ Iterate through swarm, asking for the TreasureMap. Return the first one who has it. TODO: What if a node gives a bunk TreasureMap? """ from nkms.network.protocols import dht_value_splitter for node in self.known_nodes.values(): response = networky_stuff.get_treasure_map_from_node(node, map_id) if response.status_code == 200 and response.content: # TODO: Make this prettier header, _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = \ dht_value_splitter(response.content, return_remainder=True) tmap_messaage_kit = MessageKit.from_bytes(encrypted_treasure_map) return tmap_messaage_kit else: assert False
def get_treasure_map(self, policy_group): dht_key = policy_group.treasure_map_dht_key() ursula_coro = self.server.get(dht_key) event_loop = asyncio.get_event_loop() packed_encrypted_treasure_map = event_loop.run_until_complete(ursula_coro) _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter( packed_encrypted_treasure_map[5::], msgpack_remainder=True) verified, cleartext = self.verify_from(self.alice, encrypted_treasure_map, signature_is_on_cleartext=True, decrypt=True) alices_signature, packed_node_list = BytestringSplitter(Signature)(cleartext, return_remainder=True) if not verified: return NOT_FROM_ALICE else: from nkms.policy.models import TreasureMap self.treasure_maps[policy_group.hrac] = TreasureMap(msgpack.loads(packed_node_list)) return self.treasure_maps[policy_group.hrac]
def test_treasure_map_stored_by_ursula_is_the_correct_one_for_bob(alice, bob, ursulas, enacted_policy): """ The TreasureMap given by Alice to Ursula is the correct one for Bob; he can decrypt and read it. """ treasure_map_as_set_on_network = ursulas[0].server.storage[ digest(enacted_policy.treasure_map_dht_key())] _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter( treasure_map_as_set_on_network[5::], msgpack_remainder=True) # 5:: to account for prepended "trmap" verified, cleartext = treasure_map_as_decrypted_by_bob = bob.verify_from(alice, encrypted_treasure_map, decrypt=True, signature_is_on_cleartext=True, ) _alices_signature, treasure_map_as_decrypted_by_bob = BytestringSplitter(Signature)(cleartext, return_remainder=True) assert treasure_map_as_decrypted_by_bob == enacted_policy.treasure_map.packed_payload() assert verified is True
def test_treasure_map_stored_by_ursula_is_the_correct_one_for_bob(alice, bob, ursulas, enacted_policy): """ The TreasureMap given by Alice to Ursula is the correct one for Bob; he can decrypt and read it. """ treasure_map_as_set_on_network = ursulas[0].server.storage[ digest(enacted_policy.treasure_map_dht_key())] header, _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter( treasure_map_as_set_on_network, return_remainder=True) assert header == BYTESTRING_IS_TREASURE_MAP tmap_message_kit = MessageKit.from_bytes(encrypted_treasure_map) verified, treasure_map_as_decrypted_by_bob = bob.verify_from(alice, tmap_message_kit, decrypt=True, signature_is_on_cleartext=True, ) assert treasure_map_as_decrypted_by_bob == enacted_policy.treasure_map.packed_payload() assert verified is True
def receive_treasure_map(self, treasure_map_id_as_hex, request: http.Request): # TODO: This function is the epitome of #172. treasure_map_id = binascii.unhexlify(treasure_map_id_as_hex) header, signature_for_ursula, pubkey_sig_alice, hrac, tmap_message_kit = \ dht_value_splitter(request.body, return_remainder=True) # TODO: This next line is possibly the worst in the entire codebase at the moment. #172. # Also TODO: TTL? do_store = self.server.protocol.determine_legality_of_dht_key( signature_for_ursula, pubkey_sig_alice, tmap_message_kit, hrac, digest(treasure_map_id), request.body) if do_store: # TODO: Stop storing things in the protocol storage. Do this better. # TODO: Propagate to other nodes. self.server.protocol.storage[digest( treasure_map_id)] = request.body return # TODO: Proper response here. else: # TODO: Make this a proper 500 or whatever. assert False
def follow_treasure_map(self, hrac): for ursula_interface_id in self.treasure_maps[hrac]: # TODO: perform this part concurrently. value = self.server.get_now(ursula_interface_id) # TODO: Make this much prettier header, signature, ursula_pubkey_sig, _hrac, ( port, interface, ttl) = dht_value_splitter(value, msgpack_remainder=True) if header != BYTESTRING_IS_URSULA_IFACE_INFO: raise TypeError( "Unknown DHT value. How did this get on the network?") # TODO: If we're going to implement TTL, it will be here. self._ursulas[ursula_interface_id] =\ Ursula.as_discovered_on_network( dht_port=port, dht_interface=interface, pubkey_sig_bytes=ursula_pubkey_sig )