2017-09-28 00:07:36 +00:00
|
|
|
import asyncio
|
2017-09-30 17:32:39 +00:00
|
|
|
|
2017-10-28 17:53:56 +00:00
|
|
|
import msgpack
|
2017-10-04 04:13:46 +00:00
|
|
|
import pytest
|
2017-11-05 04:59:35 +00:00
|
|
|
|
2017-11-07 04:42:21 +00:00
|
|
|
from kademlia.utils import digest
|
2017-11-22 04:19:31 +00:00
|
|
|
from nkms.characters import Ursula, Character
|
2017-11-22 06:03:31 +00:00
|
|
|
from nkms.crypto.signature import Signature
|
|
|
|
from nkms.crypto.utils import BytestringSplitter
|
2017-11-22 04:22:16 +00:00
|
|
|
from nkms.network import blockchain_client
|
2017-11-12 01:45:33 +00:00
|
|
|
from nkms.network.protocols import dht_value_splitter
|
2017-11-22 04:22:16 +00:00
|
|
|
from nkms.policy.models import Policy
|
|
|
|
from tests.utilities import MockNetworkyStuff, EVENT_LOOP, URSULA_PORT, NUMBER_OF_URSULAS_IN_NETWORK
|
2017-10-27 01:39:13 +00:00
|
|
|
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_all_ursulas_know_about_all_other_ursulas(fake_ursulas):
|
2017-11-09 21:48:40 +00:00
|
|
|
"""
|
|
|
|
Once launched, all Ursulas know about - and can help locate - all other Ursulas in the network.
|
|
|
|
"""
|
2017-11-06 21:35:54 +00:00
|
|
|
ignorance = []
|
2017-11-22 04:22:16 +00:00
|
|
|
for acounter, announcing_ursula in enumerate(blockchain_client._ursulas_on_blockchain):
|
|
|
|
for counter, propagating_ursula in enumerate(fake_ursulas):
|
2017-11-06 21:35:54 +00:00
|
|
|
if not digest(announcing_ursula) in propagating_ursula.server.storage:
|
|
|
|
ignorance.append((counter, acounter))
|
|
|
|
if ignorance:
|
|
|
|
pytest.fail(str(["{} didn't know about {}".format(counter, acounter) for counter, acounter in ignorance]))
|
|
|
|
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_vladimir_illegal_interface_key_does_not_propagate(fake_ursulas):
|
2017-11-09 21:48:40 +00:00
|
|
|
"""
|
|
|
|
Although Ursulas propagate each other's interface information, as demonstrated above, they do not propagate
|
|
|
|
interface information for Vladimir, an Evil Ursula.
|
|
|
|
"""
|
2017-11-22 04:22:16 +00:00
|
|
|
vladimir = fake_ursulas[0]
|
|
|
|
ursula = fake_ursulas[1]
|
2017-11-06 03:28:13 +00:00
|
|
|
|
|
|
|
# Ursula hasn't seen any illegal keys.
|
|
|
|
assert ursula.server.protocol.illegal_keys_seen == []
|
|
|
|
|
|
|
|
# Vladimir does almost everything right....
|
2017-11-12 01:45:33 +00:00
|
|
|
value = vladimir.interface_dht_value()
|
2017-11-06 03:28:13 +00:00
|
|
|
|
|
|
|
# Except he sets an illegal key for his interface.
|
|
|
|
illegal_key = "Not allowed to set arbitrary key for this."
|
|
|
|
setter = vladimir.server.set(key=illegal_key, value=value)
|
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
loop.run_until_complete(setter)
|
|
|
|
|
|
|
|
# Now Ursula has seen an illegal key.
|
|
|
|
assert digest(illegal_key) in ursula.server.protocol.illegal_keys_seen
|
|
|
|
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_alice_cannot_offer_policy_without_first_finding_ursula(alice, bob, fake_ursulas):
|
2017-11-09 22:28:11 +00:00
|
|
|
"""
|
|
|
|
Alice can't just offer a Policy if she doesn't know whether any Ursulas are available (she gets Ursula.NotFound).
|
|
|
|
"""
|
2017-11-22 04:22:16 +00:00
|
|
|
networky_stuff = MockNetworkyStuff(fake_ursulas)
|
|
|
|
policy = Policy(alice, bob)
|
2017-11-09 22:28:11 +00:00
|
|
|
|
|
|
|
with pytest.raises(Ursula.NotFound):
|
|
|
|
policy_offer = policy.encrypt_payload_for_ursula()
|
|
|
|
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_trying_to_find_unknown_actor_raises_not_found(alice):
|
2017-11-09 22:28:11 +00:00
|
|
|
"""
|
|
|
|
Tony the test character can't make reference to a character he doesn't know about yet.
|
|
|
|
"""
|
|
|
|
tony_clifton = Character()
|
|
|
|
|
|
|
|
message = b"some_message"
|
2017-11-22 04:22:16 +00:00
|
|
|
signature = alice.seal(message)
|
2017-11-09 22:28:11 +00:00
|
|
|
|
|
|
|
# Tony can't reference Alice...
|
|
|
|
with pytest.raises(Character.NotFound):
|
2017-11-22 04:22:16 +00:00
|
|
|
verification = tony_clifton.verify_from(alice, signature, message)
|
2017-11-09 22:28:11 +00:00
|
|
|
|
|
|
|
# ...before learning about Alice.
|
2017-11-22 04:22:16 +00:00
|
|
|
tony_clifton.learn_about_actor(alice)
|
2017-11-22 06:03:31 +00:00
|
|
|
verification, NO_DECRYPTION_PERFORMED = tony_clifton.verify_from(alice, message, signature)
|
2017-11-09 22:28:11 +00:00
|
|
|
|
|
|
|
assert verification is True
|
|
|
|
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_alice_finds_ursula(alice, fake_ursulas):
|
2017-11-09 21:48:40 +00:00
|
|
|
"""
|
|
|
|
With the help of any Ursula, Alice can find a specific Ursula.
|
|
|
|
"""
|
|
|
|
ursula_index = 1
|
2017-11-22 04:22:16 +00:00
|
|
|
all_ursulas = blockchain_client._ursulas_on_blockchain
|
|
|
|
getter = alice.server.get(all_ursulas[ursula_index])
|
2017-11-09 21:48:40 +00:00
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
value = loop.run_until_complete(getter)
|
2017-11-18 21:19:07 +00:00
|
|
|
_signature, _ursula_pubkey_sig, _hrac, interface_info = dht_value_splitter(value.lstrip(b"uaddr-"),
|
|
|
|
return_remainder=True)
|
2017-11-12 01:45:33 +00:00
|
|
|
port = msgpack.loads(interface_info)[0]
|
2017-11-09 21:48:40 +00:00
|
|
|
assert port == URSULA_PORT + ursula_index
|
2017-10-18 08:52:01 +00:00
|
|
|
|
2017-10-28 02:26:23 +00:00
|
|
|
|
2017-11-22 00:27:21 +00:00
|
|
|
def test_alice_creates_policy_group_with_correct_hrac(alices_policy_group):
|
2017-11-09 22:28:11 +00:00
|
|
|
"""
|
2017-11-21 03:50:38 +00:00
|
|
|
Alice creates a PolicyGroup. It has the proper HRAC, unique per her, Bob, and the uri (resource_id).
|
2017-11-09 22:28:11 +00:00
|
|
|
"""
|
2017-11-22 04:22:16 +00:00
|
|
|
alice = alices_policy_group.alice
|
|
|
|
bob = alices_policy_group.bob
|
2017-11-09 22:28:11 +00:00
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
assert alices_policy_group.hrac() == alices_policy_group.hash(
|
|
|
|
bytes(alice.seal) + bytes(bob.seal) + alice.__resource_id)
|
2017-11-21 03:24:03 +00:00
|
|
|
|
2017-11-21 03:50:38 +00:00
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_alice_enacts_policies_in_policy_group_via_rest(enacted_policy_group):
|
2017-11-21 03:50:38 +00:00
|
|
|
"""
|
|
|
|
Now that Alice has made a PolicyGroup, she can enact its policies, using Ursula's Public Key to encrypt each offer
|
|
|
|
and transmitting them via REST.
|
|
|
|
"""
|
2017-11-22 04:22:16 +00:00
|
|
|
ursula = enacted_policy_group.policies[0].ursula
|
|
|
|
kfrag_that_was_set = ursula.keystore.get_kfrag(enacted_policy_group.hrac())
|
|
|
|
assert bool(kfrag_that_was_set) # TODO: This can be a more poignant assertion.
|
2017-11-21 03:24:03 +00:00
|
|
|
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_alice_sets_treasure_map_on_network(enacted_policy_group, fake_ursulas):
|
2017-10-04 04:13:46 +00:00
|
|
|
"""
|
2017-11-21 03:50:38 +00:00
|
|
|
Having enacted all the policies of a PolicyGroup, Alice creates a TreasureMap and sends it to Ursula via the DHT.
|
2017-10-04 04:13:46 +00:00
|
|
|
"""
|
2017-11-22 04:22:16 +00:00
|
|
|
alice = enacted_policy_group.alice
|
|
|
|
setter, encrypted_treasure_map, packed_encrypted_treasure_map, signature_for_bob, signature_for_ursula = alice.publish_treasure_map(
|
|
|
|
enacted_policy_group)
|
2017-11-07 04:42:21 +00:00
|
|
|
_set_event = EVENT_LOOP.run_until_complete(setter)
|
2017-10-03 15:43:12 +00:00
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
treasure_map_as_set_on_network = fake_ursulas[0].server.storage[
|
|
|
|
digest(enacted_policy_group.treasure_map_dht_key())]
|
2017-11-10 10:04:01 +00:00
|
|
|
assert treasure_map_as_set_on_network == b"trmap" + packed_encrypted_treasure_map
|
2017-10-24 00:28:10 +00:00
|
|
|
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_treasure_map_with_bad_id_does_not_propagate(alices_policy_group, fake_ursulas):
|
2017-11-10 00:07:25 +00:00
|
|
|
"""
|
|
|
|
In order to prevent spam attacks, Ursula refuses to propagate a TreasureMap whose PolicyGroup ID does not comport to convention.
|
|
|
|
"""
|
|
|
|
illegal_policygroup_id = "This is not a conventional policygroup id"
|
2017-11-22 04:22:16 +00:00
|
|
|
alice = alices_policy_group.alice
|
|
|
|
bob = alices_policy_group.bob
|
|
|
|
treasure_map = alices_policy_group.treasure_map
|
2017-11-10 00:07:25 +00:00
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
encrypted_treasure_map, signature = alice.encrypt_for(bob, treasure_map.packed_payload())
|
2017-11-18 21:19:07 +00:00
|
|
|
packed_encrypted_treasure_map = msgpack.dumps(encrypted_treasure_map) # TODO: #114? Do we even need to pack here?
|
2017-11-10 00:07:25 +00:00
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
setter = alice.server.set(illegal_policygroup_id, packed_encrypted_treasure_map)
|
2017-11-10 00:07:25 +00:00
|
|
|
_set_event = EVENT_LOOP.run_until_complete(setter)
|
|
|
|
|
|
|
|
with pytest.raises(KeyError):
|
2017-11-22 04:22:16 +00:00
|
|
|
fake_ursulas[0].server.storage[digest(illegal_policygroup_id)]
|
2017-11-10 00:07:25 +00:00
|
|
|
|
|
|
|
|
2017-11-22 06:03:31 +00:00
|
|
|
def test_treasure_map_stored_by_ursula_is_the_correct_one_for_bob(alice, bob, fake_ursulas, enacted_policy_group):
|
2017-11-09 22:28:11 +00:00
|
|
|
"""
|
|
|
|
The TreasureMap given by Alice to Ursula is the correct one for Bob; he can decrypt and read it.
|
|
|
|
"""
|
2017-11-22 04:22:16 +00:00
|
|
|
treasure_map_as_set_on_network = fake_ursulas[0].server.storage[
|
|
|
|
digest(enacted_policy_group.treasure_map_dht_key())]
|
2017-11-12 01:45:33 +00:00
|
|
|
|
2017-11-12 02:00:32 +00:00
|
|
|
_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"
|
|
|
|
|
2017-11-22 06:03:31 +00:00
|
|
|
verified, cleartext = treasure_map_as_decrypted_by_bob = bob.verify_from(alice,
|
2017-10-28 17:53:56 +00:00
|
|
|
encrypted_treasure_map,
|
2017-10-13 19:56:10 +00:00
|
|
|
decrypt=True,
|
|
|
|
signature_is_on_cleartext=True,
|
2017-10-13 21:06:33 +00:00
|
|
|
)
|
2017-11-22 06:03:31 +00:00
|
|
|
_alices_signature, treasure_map_as_decrypted_by_bob = BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
|
|
|
|
|
|
|
assert treasure_map_as_decrypted_by_bob == enacted_policy_group.treasure_map.packed_payload()
|
2017-10-13 02:11:07 +00:00
|
|
|
assert verified is True
|
2017-10-03 15:43:12 +00:00
|
|
|
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_bob_can_retreive_the_treasure_map_and_decrypt_it(enacted_policy_group, fake_ursulas):
|
2017-10-24 00:28:10 +00:00
|
|
|
"""
|
2017-11-09 22:28:11 +00:00
|
|
|
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.
|
2017-10-24 00:28:10 +00:00
|
|
|
"""
|
2017-11-22 04:22:16 +00:00
|
|
|
bob = enacted_policy_group.bob
|
|
|
|
networky_stuff = MockNetworkyStuff(fake_ursulas)
|
2017-10-28 02:26:23 +00:00
|
|
|
|
2017-11-10 10:04:01 +00:00
|
|
|
# Of course, in the real world, Bob has sufficient information to reconstitute a PolicyGroup, gleaned, we presume,
|
|
|
|
# through a side-channel with Alice.
|
2017-11-22 06:03:31 +00:00
|
|
|
treasure_map_from_wire = bob.get_treasure_map(enacted_policy_group)
|
2017-10-27 02:46:34 +00:00
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
assert enacted_policy_group.treasure_map == treasure_map_from_wire
|
2017-10-24 00:28:10 +00:00
|
|
|
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
def test_treaure_map_is_legit(enacted_policy_group):
|
2017-11-09 22:28:11 +00:00
|
|
|
"""
|
|
|
|
Sure, the TreasureMap can get to Bob, but we also need to know that each Ursula in the TreasureMap is on the network.
|
|
|
|
"""
|
2017-11-22 04:22:16 +00:00
|
|
|
alice = enacted_policy_group.alice
|
|
|
|
for ursula_interface_id in enacted_policy_group.treasure_map:
|
|
|
|
getter = alice.server.get(ursula_interface_id)
|
2017-11-07 02:11:03 +00:00
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
value = loop.run_until_complete(getter)
|
2017-11-18 21:19:07 +00:00
|
|
|
signature, ursula_pubkey_sig, hrac, interface_info = dht_value_splitter(value.lstrip(b"uaddr-"),
|
|
|
|
return_remainder=True)
|
2017-11-12 01:45:33 +00:00
|
|
|
port = msgpack.loads(interface_info)[0]
|
2017-11-22 04:22:16 +00:00
|
|
|
legal_ports = range(NUMBER_OF_URSULAS_IN_NETWORK, NUMBER_OF_URSULAS_IN_NETWORK + URSULA_PORT)
|
|
|
|
assert port in legal_ports
|