def test_use_vladimir_without_development_installation(import_mocker, mocker): # Expected error message (Related object) from tests.utils.middleware import EvilMiddleWare import_path = f'{EvilMiddleWare.__module__}.{EvilMiddleWare.__name__}' message = DevelopmentInstallationRequired.MESSAGE.format( importable_name=import_path) del EvilMiddleWare with import_mocker: from nucypher.characters.unlawful import Vladimir # Import OK with pytest.raises(DevelopmentInstallationRequired, match=message): # Expect lazy failure Vladimir.from_target_ursula(target_ursula=mocker.Mock())
def test_treasure_map_cannot_be_duplicated(blockchain_ursulas, blockchain_alice, blockchain_bob, agency): # Setup the policy details n = 3 policy_end_datetime = maya.now() + datetime.timedelta(days=5) label = b"this_is_the_path_to_which_access_is_being_granted" # Create the Policy, Granting access to Bob policy = blockchain_alice.grant( bob=blockchain_bob, label=label, m=2, n=n, rate=int(1e18), # one ether expiration=policy_end_datetime) u = blockchain_bob.matching_nodes_among(blockchain_alice.known_nodes)[0] saved_map = u.treasure_maps[bytes.fromhex(policy.treasure_map.public_id())] assert saved_map == policy.treasure_map # This Ursula was actually a Vladimir. # Thus, he has access to the (encrypted) TreasureMap and can use its details to # try to store his own fake details. vladimir = Vladimir.from_target_ursula(u) node_on_which_to_store_bad_map = blockchain_ursulas[1] with pytest.raises(vladimir.network_middleware.UnexpectedResponse) as e: vladimir.publish_fraudulent_treasure_map( legit_treasure_map=saved_map, target_node=node_on_which_to_store_bad_map) assert e.value.status == 402
def test_alice_refuses_to_make_arrangement_unless_ursula_is_valid( blockchain_alice, idle_blockchain_policy, blockchain_ursulas): target = list(blockchain_ursulas)[2] # First, let's imagine that Alice has sampled a Vladimir while making this policy. vladimir = Vladimir.from_target_ursula(target) message = vladimir._signable_interface_info_message() signature = vladimir._crypto_power.power_ups(SigningPower).sign(message) vladimir.substantiate_stamp() vladimir._Teacher__interface_signature = signature class FakeArrangement: federated = False ursula = vladimir def __bytes__(self): return b"" vladimir.node_storage.store_node_certificate( certificate=target.certificate) with pytest.raises(vladimir.InvalidNode): idle_blockchain_policy.propose_arrangement( network_middleware=blockchain_alice.network_middleware, arrangement=FakeArrangement())
def test_vladimir_cannot_verify_interface_with_ursulas_signing_key( blockchain_ursulas): his_target = list(blockchain_ursulas)[4] # Vladimir has his own ether address; he hopes to publish it along with Ursula's details # so that Alice (or whomever) pays him instead of Ursula, even though Ursula is providing the service. # He finds a target and verifies that its interface is valid. assert his_target.validate_interface() # Now Vladimir imitates Ursula - copying her public keys and interface info, but inserting his ether address. vladimir = Vladimir.from_target_ursula(his_target, claim_signing_key=True) # Vladimir can substantiate the stamp using his own ether address... vladimir._Ursula__substantiate_stamp() vladimir.validate_worker = lambda: True vladimir.validate_worker() # lol # Now, even though his public signing key matches Ursulas... assert vladimir.stamp == his_target.stamp # ...he is unable to pretend that his interface is valid # because the interface validity check contains the canonical public address as part of its message. with pytest.raises(vladimir.InvalidNode): vladimir.validate_interface() # Consequently, the metadata as a whole is also invalid. with pytest.raises(vladimir.InvalidNode): vladimir.validate_metadata()
def test_vladimir_uses_his_own_signing_key(blockchain_alice, blockchain_ursulas): """ Similar to the attack above, but this time Vladimir makes his own interface signature using his own signing key, which he claims is Ursula's. """ his_target = list(blockchain_ursulas)[4] vladimir = Vladimir.from_target_ursula(target_ursula=his_target) message = vladimir._signable_interface_info_message() signature = vladimir._crypto_power.power_ups(SigningPower).sign( vladimir.timestamp_bytes() + message) vladimir._Teacher__interface_signature = signature vladimir._Ursula__substantiate_stamp() vladimir._worker_is_bonded_to_staker = lambda: True vladimir._staker_is_really_staking = lambda: True vladimir.validate_worker() # lol # With this slightly more sophisticated attack, his metadata does appear valid. vladimir.validate_metadata() # However, the actual handshake proves him wrong. with pytest.raises(vladimir.InvalidNode): vladimir.verify_node(blockchain_alice.network_middleware.client, certificate_filepath="doesn't matter")
def test_vladimir_uses_his_own_signing_key(blockchain_alice, blockchain_ursulas): """ Similar to the attack above, but this time Vladimir makes his own interface signature using his own signing key, which he claims is Ursula's. """ his_target = list(blockchain_ursulas)[4] fraduluent_keys = CryptoPower( power_ups=Ursula._default_crypto_powerups) # TODO: Why is this unused? vladimir = Vladimir.from_target_ursula(target_ursula=his_target) message = vladimir._signable_interface_info_message() signature = vladimir._crypto_power.power_ups(SigningPower).sign( vladimir.timestamp_bytes() + message) vladimir._interface_signature_object = signature vladimir.substantiate_stamp(password=INSECURE_DEVELOPMENT_PASSWORD) # With this slightly more sophisticated attack, his metadata does appear valid. vladimir.validate_metadata() # However, the actual handshake proves him wrong. with pytest.raises(vladimir.InvalidNode): vladimir.verify_node(blockchain_alice.network_middleware, certificate_filepath="doesn't matter")
def test_vladimir_illegal_interface_key_does_not_propagate(blockchain_ursulas): """ Although Ursulas propagate each other's interface information, as demonstrated above, they do not propagate interface information for Vladimir. Specifically, if Vladimir tries to perform the most obvious imitation attack - propagating his own wallet address along with Ursula's information - the validity check will catch it and Ursula will refuse to propagate it and also record Vladimir's details. """ ursulas = list(blockchain_ursulas) ursula_whom_vladimir_will_imitate, other_ursula = ursulas[0], ursulas[1] # Vladimir sees Ursula on the network and tries to use her public information. vladimir = Vladimir.from_target_ursula(ursula_whom_vladimir_will_imitate) # This Ursula is totally legit... ursula_whom_vladimir_will_imitate.verify_node(MockRestMiddleware()) vladimir.network_middleware.propagate_shitty_interface_id(other_ursula, bytes(vladimir)) # So far, Ursula hasn't noticed any Vladimirs. assert other_ursula.suspicious_activities_witnessed['vladimirs'] == [] # ...but now, Ursula will now try to learn about Vladimir on a different thread. other_ursula.block_until_specific_nodes_are_known([vladimir.checksum_address]) vladimir_as_learned = other_ursula.known_nodes[vladimir.checksum_address] # OK, so cool, let's see what happens when Ursula tries to learn with Vlad as the teacher. other_ursula._current_teacher_node = vladimir_as_learned result = other_ursula.learn_from_teacher_node()
def test_alice_refuses_to_make_arrangement_unless_ursula_is_valid( blockchain_alice, idle_blockchain_policy, blockchain_ursulas): target = list(blockchain_ursulas)[2] # First, let's imagine that Alice has sampled a Vladimir while making this policy. vladimir = Vladimir.from_target_ursula(target) message = vladimir._signable_interface_info_message() signature = vladimir._crypto_power.power_ups(SigningPower).sign(message) vladimir._Ursula__substantiate_stamp() vladimir._Teacher__interface_signature = signature vladimir.node_storage.store_node_certificate( certificate=target.certificate) # Ideally, a fishy node shouldn't be present in `known_nodes`, # but I guess we're testing the case when it became fishy somewhere between we learned about it # and the proposal arrangement. blockchain_alice.known_nodes.record_node(vladimir) blockchain_alice.known_nodes.record_fleet_state() with pytest.raises(vladimir.InvalidNode): idle_blockchain_policy._propose_arrangement( address=vladimir.checksum_address, network_middleware=blockchain_alice.network_middleware)
def test_vladimir_invalidity_without_stake(testerchain, blockchain_ursulas, blockchain_alice): his_target = list(blockchain_ursulas)[4] vladimir = Vladimir.from_target_ursula(target_ursula=his_target) message = vladimir._signable_interface_info_message() signature = vladimir._crypto_power.power_ups(SigningPower).sign(vladimir.timestamp_bytes() + message) vladimir._Teacher__interface_signature = signature vladimir.substantiate_stamp() # However, the actual handshake proves him wrong. with pytest.raises(vladimir.InvalidNode): vladimir.verify_node(blockchain_alice.network_middleware.client, certificate_filepath="doesn't matter")
def test_vladimir_illegal_interface_key_does_not_propagate(blockchain_ursulas): """ Although Ursulas propagate each other's interface information, as demonstrated above, they do not propagate interface information for Vladimir. Specifically, if Vladimir tries to perform the most obvious imitation attack - propagating his own wallet address along with Ursula's information - the validity check will catch it and Ursula will refuse to propagate it and also record Vladimir's details. """ warnings = [] def warning_trapper(event): if event['log_level'] == LogLevel.warn: warnings.append(event) ursulas = list(blockchain_ursulas) ursula_whom_vladimir_will_imitate, other_ursula = ursulas[0], ursulas[1] # Vladimir sees Ursula on the network and tries to use her public information. vladimir = Vladimir.from_target_ursula(ursula_whom_vladimir_will_imitate) # This Ursula is totally legit... ursula_whom_vladimir_will_imitate.verify_node(MockRestMiddleware()) globalLogPublisher.addObserver(warning_trapper) vladimir.network_middleware.propagate_shitty_interface_id( other_ursula, vladimir.metadata()) globalLogPublisher.removeObserver(warning_trapper) # So far, Ursula hasn't noticed any Vladimirs. assert len(warnings) == 0 # ...but now, Ursula will now try to learn about Vladimir on a different thread. other_ursula.block_until_specific_nodes_are_known( [vladimir.checksum_address]) vladimir_as_learned = other_ursula.known_nodes[vladimir.checksum_address] # OK, so cool, let's see what happens when Ursula tries to learn with Vlad as the teacher. other_ursula._current_teacher_node = vladimir_as_learned globalLogPublisher.addObserver(warning_trapper) result = other_ursula.learn_from_teacher_node() globalLogPublisher.removeObserver(warning_trapper) # Indeed, Ursula noticed that something was up. assert len(warnings) == 1 warning = warnings[0]['log_format'] assert "Teacher " + str(vladimir_as_learned) + " is invalid" in warning assert "Metadata signature is invalid" in warning # TODO: Cleanup logging templates
def test_vladimir_illegal_interface_key_does_not_propagate(blockchain_ursulas): """ Although Ursulas propagate each other's interface information, as demonstrated above, they do not propagate interface information for Vladimir. Specifically, if Vladimir tries to perform the most obvious imitation attack - propagating his own wallet address along with Ursula's information - the validity check will catch it and Ursula will refuse to propagate it and also record Vladimir's details. """ ursulas = list(blockchain_ursulas) ursula_whom_vladimir_will_imitate, other_ursula = ursulas[0], ursulas[1] # Vladimir sees Ursula on the network and tries to use her public information. vladimir = Vladimir.from_target_ursula(ursula_whom_vladimir_will_imitate) # This Ursula is totally legit... ursula_whom_vladimir_will_imitate.verify_node(MockRestMiddleware(), accept_federated_only=True) learning_callers = [] crosstown_traffic.decorator = crosstownTaskListDecoratorFactory( learning_callers) vladimir.network_middleware.propagate_shitty_interface_id( other_ursula, bytes(vladimir)) # So far, Ursula hasn't noticed any Vladimirs. assert other_ursula.suspicious_activities_witnessed['vladimirs'] == [] # ...but now, Ursula will now try to learn about Vladimir on a different thread. # We only passed one node (Vladimir)... learn_about_vladimir = learning_callers.pop() # ...so there was only one learning caller in the queue (now none since we popped it just now). assert len(learning_callers) == 0 # OK, so cool, let's see what happens when Ursula tries to learn about Vlad. learn_about_vladimir() # And indeed, Ursula noticed the situation. # She didn't record Vladimir's address. assert vladimir not in other_ursula.known_nodes # But she *did* record the actual Ursula's address. assert ursula_whom_vladimir_will_imitate in other_ursula.known_nodes # Furthermore, she properly marked Vladimir as suspicious. assert vladimir in other_ursula.suspicious_activities_witnessed[ 'vladimirs']
def test_treasure_map_cannot_be_duplicated(blockchain_ursulas, blockchain_alice, blockchain_bob, agency): # Setup the policy details n = 3 policy_end_datetime = maya.now() + datetime.timedelta(days=5) label = b"this_is_the_path_to_which_access_is_being_granted" # Create the Policy, Granting access to Bob policy = blockchain_alice.grant( bob=blockchain_bob, label=label, m=2, n=n, rate=int(1e18), # one ether expiration=policy_end_datetime) matching_ursulas = blockchain_bob.matching_nodes_among(blockchain_ursulas) completed_ursulas = policy.treasure_map_publisher.block_until_success_is_reasonably_likely( ) # Ursulas in `treasure_map_publisher` are not real Ursulas, but just some metadata of remote ones. # We need a real one to access its datastore. first_completed_ursula = [ ursula for ursula in matching_ursulas if ursula in completed_ursulas ][0] with first_completed_ursula.datastore.describe( TreasureMap, policy.treasure_map._hrac.hex()) as saved_map_record: assert saved_map_record.treasure_map == bytes(policy.treasure_map) # This Ursula was actually a Vladimir. # Thus, he has access to the (encrypted) TreasureMap and can use its details to # try to store his own fake details. vladimir = Vladimir.from_target_ursula(first_completed_ursula) ursulas_who_probably_do_not_have_the_map = [ u for u in blockchain_ursulas if not u in matching_ursulas ] node_on_which_to_store_bad_map = ursulas_who_probably_do_not_have_the_map[ 0] with pytest.raises(vladimir.network_middleware.UnexpectedResponse) as e: vladimir.publish_fraudulent_treasure_map( legit_treasure_map=policy.treasure_map, target_node=node_on_which_to_store_bad_map) assert e.value.status == 402
def test_alice_refuses_to_make_arrangement_unless_ursula_is_valid(blockchain_alice, idle_blockchain_policy, blockchain_ursulas): target = list(blockchain_ursulas)[2] # First, let's imagine that Alice has sampled a Vladimir while making this policy. vladimir = Vladimir.from_target_ursula(target) message = vladimir._signable_interface_info_message() signature = vladimir._crypto_power.power_ups(SigningPower).sign(message) vladimir.substantiate_stamp() vladimir._interface_signature_object = signature class FakeArrangement: federated = False with pytest.raises(vladimir.InvalidNode): idle_blockchain_policy.consider_arrangement(network_middleware=blockchain_alice.network_middleware, arrangement=FakeArrangement(), ursula=vladimir)
def test_vladimir_uses_his_own_signing_key(blockchain_alice, blockchain_ursulas): """ Similar to the attack above, but this time Vladimir makes his own interface signature using his own signing key, which he claims is Ursula's. """ his_target = list(blockchain_ursulas)[4] vladimir = Vladimir.from_target_ursula(target_ursula=his_target) message = vladimir._signable_interface_info_message() signature = vladimir._crypto_power.power_ups(SigningPower).sign(vladimir.timestamp_bytes() + message) vladimir._Teacher__interface_signature = signature vladimir.substantiate_stamp(client_password=INSECURE_DEVELOPMENT_PASSWORD) # With this slightly more sophisticated attack, his metadata does appear valid. vladimir._is_valid_worker = lambda: True # bypass staking verification TODO: Split into two tests vladimir.validate_metadata() # However, the actual handshake proves him wrong. with pytest.raises(vladimir.InvalidNode): vladimir.verify_node(blockchain_alice.network_middleware, certificate_filepath="doesn't matter")
def test_alice_refuses_to_select_node_unless_ursula_is_valid(blockchain_alice, idle_blockchain_policy, blockchain_ursulas): target = list(blockchain_ursulas)[2] # First, let's imagine that Alice has sampled a Vladimir while making this policy. vladimir = Vladimir.from_target_ursula(target, substitute_verifying_key=True, sign_metadata=True) vladimir.node_storage.store_node_certificate(certificate=target.certificate, port=vladimir.rest_interface.port) # Ideally, a fishy node will be present in `known_nodes`, # This tests the case when it became fishy after discovering it # but before being selected for a policy. blockchain_alice.known_nodes.record_node(vladimir) blockchain_alice.known_nodes.record_fleet_state() with pytest.raises(vladimir.InvalidNode): idle_blockchain_policy._ping_node(address=vladimir.checksum_address, network_middleware=blockchain_alice.network_middleware)
def remote_vladimir(**kwds): vladimir = Vladimir.from_target_ursula(**kwds) remote_vladimir = Ursula.from_metadata_bytes(bytes( vladimir.metadata())).mature() return remote_vladimir