2020-05-21 16:15:07 +00:00
|
|
|
"""
|
|
|
|
This file is part of nucypher.
|
|
|
|
|
|
|
|
nucypher is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
nucypher is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
|
|
|
2021-03-30 04:46:54 +00:00
|
|
|
|
2019-03-28 21:39:18 +00:00
|
|
|
import datetime
|
2019-02-15 02:27:41 +00:00
|
|
|
import maya
|
2020-05-13 03:23:33 +00:00
|
|
|
import os
|
2018-11-30 20:18:13 +00:00
|
|
|
import pytest
|
2020-02-27 05:00:53 +00:00
|
|
|
import time
|
2019-02-10 19:56:43 +00:00
|
|
|
from constant_sorrow.constants import NO_DECRYPTION_PERFORMED
|
2020-02-26 05:49:27 +00:00
|
|
|
from twisted.internet.task import Clock
|
|
|
|
|
2020-05-13 03:23:33 +00:00
|
|
|
from nucypher.characters.lawful import Bob, Enrico, Ursula
|
2020-05-13 18:24:36 +00:00
|
|
|
from nucypher.config.constants import TEMPORARY_DOMAIN
|
2021-03-30 04:46:54 +00:00
|
|
|
from nucypher.policy.maps import TreasureMap
|
|
|
|
from tests.constants import (MOCK_POLICY_DEFAULT_M, NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK)
|
2020-05-13 03:21:15 +00:00
|
|
|
from tests.utils.middleware import MockRestMiddleware
|
2018-10-29 00:51:29 +00:00
|
|
|
|
2018-10-29 11:40:34 +00:00
|
|
|
|
2019-02-15 04:36:09 +00:00
|
|
|
def test_federated_bob_full_retrieve_flow(federated_ursulas,
|
2019-04-27 19:33:03 +00:00
|
|
|
federated_bob,
|
|
|
|
federated_alice,
|
|
|
|
capsule_side_channel,
|
2021-08-15 05:00:50 +00:00
|
|
|
federated_treasure_map,
|
2019-04-27 19:33:03 +00:00
|
|
|
enacted_federated_policy):
|
2018-11-02 20:03:36 +00:00
|
|
|
# Assume for the moment that Bob has already received a TreasureMap.
|
2021-08-15 05:00:50 +00:00
|
|
|
federated_bob.treasure_maps[federated_treasure_map.hrac] = federated_treasure_map
|
2018-10-29 00:51:29 +00:00
|
|
|
|
2018-10-29 11:40:34 +00:00
|
|
|
for ursula in federated_ursulas:
|
|
|
|
federated_bob.remember_node(ursula)
|
2018-10-29 00:51:29 +00:00
|
|
|
|
2018-11-02 20:03:36 +00:00
|
|
|
# The side channel delivers all that Bob needs at this point:
|
|
|
|
# - A single MessageKit, containing a Capsule
|
|
|
|
# - A representation of the data source
|
2019-09-17 06:08:44 +00:00
|
|
|
the_message_kit = capsule_side_channel()
|
2018-10-29 00:51:29 +00:00
|
|
|
alices_verifying_key = federated_alice.stamp.as_umbral_pubkey()
|
|
|
|
|
2019-09-17 06:08:44 +00:00
|
|
|
delivered_cleartexts = federated_bob.retrieve(the_message_kit,
|
|
|
|
enrico=capsule_side_channel.enrico,
|
2019-02-15 02:27:41 +00:00
|
|
|
alice_verifying_key=alices_verifying_key,
|
|
|
|
label=enacted_federated_policy.label)
|
2018-10-29 00:51:29 +00:00
|
|
|
|
2019-02-13 20:14:25 +00:00
|
|
|
# We show that indeed this is the passage originally encrypted by the Enrico.
|
2019-07-03 20:51:58 +00:00
|
|
|
assert b"Welcome to flippering number 1." == delivered_cleartexts[0]
|
2018-10-29 00:51:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
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,
|
2020-09-08 09:16:17 +00:00
|
|
|
domain=TEMPORARY_DOMAIN,
|
2018-10-29 00:51:29 +00:00
|
|
|
start_learning_now=True,
|
|
|
|
network_middleware=MockRestMiddleware(),
|
|
|
|
abort_on_learning_error=True,
|
|
|
|
known_nodes=a_couple_of_ursulas,
|
|
|
|
)
|
|
|
|
|
2020-08-18 18:59:15 +00:00
|
|
|
# Bob has only connected to - at most - 2 nodes.
|
|
|
|
assert sum(node.verified_node for node in bob.known_nodes) <= 2
|
2018-10-29 00:51:29 +00:00
|
|
|
|
|
|
|
# Alice creates a policy granting access to Bob
|
2018-11-02 20:03:36 +00:00
|
|
|
# Just for fun, let's assume she distributes KFrags among Ursulas unknown to Bob
|
2018-11-22 18:31:04 +00:00
|
|
|
n = NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK - 2
|
2018-10-29 00:51:29 +00:00
|
|
|
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 label == policy.label
|
|
|
|
|
2020-04-19 23:05:43 +00:00
|
|
|
try:
|
|
|
|
# Now, Bob joins the policy
|
|
|
|
bob.join_policy(label=label,
|
2021-08-11 22:32:48 +00:00
|
|
|
publisher_verifying_key=federated_alice.stamp.as_umbral_pubkey(),
|
2020-04-19 23:05:43 +00:00
|
|
|
block=True)
|
2021-08-15 05:00:50 +00:00
|
|
|
except TreasureMap.NowhereToBeFound:
|
|
|
|
if policy.treasure_map.hrac in ursula.treasure_maps:
|
2020-04-19 23:05:43 +00:00
|
|
|
# This is a nice place to put a breakpoint to examine Bob's failure to join a policy.
|
2020-05-16 04:43:45 +00:00
|
|
|
bob.join_policy(label=label,
|
2021-08-11 22:32:48 +00:00
|
|
|
publisher_verifying_key=federated_alice.stamp.as_umbral_pubkey(),
|
2020-05-16 04:43:45 +00:00
|
|
|
block=True)
|
2020-04-19 23:05:43 +00:00
|
|
|
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.")
|
2018-10-29 00:51:29 +00:00
|
|
|
|
|
|
|
# In the end, Bob should know all the Ursulas
|
|
|
|
assert len(bob.known_nodes) == len(federated_ursulas)
|
|
|
|
|
2019-02-13 20:14:25 +00:00
|
|
|
# Enrico becomes
|
2019-02-15 02:27:41 +00:00
|
|
|
enrico = Enrico(policy_encrypting_key=policy.public_key)
|
2018-10-29 00:51:29 +00:00
|
|
|
|
|
|
|
plaintext = b"What's your approach? Mississippis or what?"
|
2019-02-13 20:14:25 +00:00
|
|
|
message_kit, _signature = enrico.encrypt_message(plaintext)
|
2018-10-29 00:51:29 +00:00
|
|
|
|
|
|
|
alices_verifying_key = federated_alice.stamp.as_umbral_pubkey()
|
|
|
|
|
2018-11-02 20:03:36 +00:00
|
|
|
# Bob takes the message_kit and retrieves the message within
|
2019-09-17 06:08:44 +00:00
|
|
|
delivered_cleartexts = bob.retrieve(message_kit,
|
2019-08-11 23:23:20 +00:00
|
|
|
enrico=enrico,
|
2019-02-15 02:27:41 +00:00
|
|
|
alice_verifying_key=alices_verifying_key,
|
2019-09-17 06:08:44 +00:00
|
|
|
label=policy.label,
|
|
|
|
retain_cfrags=True)
|
2018-10-29 00:51:29 +00:00
|
|
|
|
|
|
|
assert plaintext == delivered_cleartexts[0]
|
2018-11-30 20:18:13 +00:00
|
|
|
|
2019-03-28 21:39:18 +00:00
|
|
|
# Bob tries to retrieve again, but without using the cached CFrags, it fails.
|
|
|
|
with pytest.raises(TypeError):
|
2019-09-17 06:08:44 +00:00
|
|
|
delivered_cleartexts = bob.retrieve(message_kit,
|
2019-08-11 23:23:20 +00:00
|
|
|
enrico=enrico,
|
2019-03-28 21:39:18 +00:00
|
|
|
alice_verifying_key=alices_verifying_key,
|
|
|
|
label=policy.label)
|
|
|
|
|
2019-09-17 06:08:44 +00:00
|
|
|
cleartexts_delivered_a_second_time = bob.retrieve(message_kit,
|
2019-08-11 23:23:20 +00:00
|
|
|
enrico=enrico,
|
2019-03-28 21:39:18 +00:00
|
|
|
alice_verifying_key=alices_verifying_key,
|
|
|
|
label=policy.label,
|
2019-09-17 06:08:44 +00:00
|
|
|
use_attached_cfrags=True)
|
2019-03-28 21:39:18 +00:00
|
|
|
|
|
|
|
# Indeed, they're the same cleartexts.
|
|
|
|
assert delivered_cleartexts == cleartexts_delivered_a_second_time
|
2019-03-04 22:10:12 +00:00
|
|
|
|
2019-03-28 21:39:18 +00:00
|
|
|
# Let's try retrieve again, but Alice revoked the policy.
|
2021-03-30 04:46:54 +00:00
|
|
|
receipt, failed_revocations = federated_alice.revoke(policy)
|
2019-03-04 22:54:27 +00:00
|
|
|
assert len(failed_revocations) == 0
|
|
|
|
|
2021-03-30 04:46:54 +00:00
|
|
|
# One thing to note here is that Bob *can* still retrieve with the cached CFrags,
|
|
|
|
# even though this Policy has been revoked. #892
|
2019-09-17 06:08:44 +00:00
|
|
|
_cleartexts = bob.retrieve(message_kit,
|
2019-08-11 23:23:20 +00:00
|
|
|
enrico=enrico,
|
2019-03-28 21:42:24 +00:00
|
|
|
alice_verifying_key=alices_verifying_key,
|
|
|
|
label=policy.label,
|
2021-03-30 04:46:54 +00:00
|
|
|
use_precedent_work_orders=True)
|
2019-03-28 21:42:24 +00:00
|
|
|
assert _cleartexts == delivered_cleartexts # TODO: 892
|
|
|
|
|
|
|
|
# OK, but we imagine that the message_kit is fresh here.
|
2021-06-08 03:05:35 +00:00
|
|
|
message_kit.clear_cfrags()
|
2019-03-28 21:42:24 +00:00
|
|
|
|
2021-03-30 04:46:54 +00:00
|
|
|
# with pytest.raises(Ursula.NotEnoughUrsulas): # FIXME
|
|
|
|
_cleartexts = bob.retrieve(message_kit,
|
|
|
|
enrico=enrico,
|
|
|
|
alice_verifying_key=alices_verifying_key,
|
|
|
|
label=policy.label)
|
2019-02-10 18:30:04 +00:00
|
|
|
|
2020-07-20 20:36:33 +00:00
|
|
|
bob.disenchant()
|
|
|
|
|
2019-02-10 18:30:04 +00:00
|
|
|
|
2019-10-03 08:11:26 +00:00
|
|
|
def test_bob_retrieves_with_treasure_map(
|
|
|
|
federated_bob, federated_ursulas,
|
|
|
|
enacted_federated_policy, capsule_side_channel):
|
2019-11-06 21:14:25 +00:00
|
|
|
enrico = capsule_side_channel.enrico
|
|
|
|
message_kit = capsule_side_channel()
|
2019-10-03 08:11:26 +00:00
|
|
|
treasure_map = enacted_federated_policy.treasure_map
|
2021-08-11 22:32:48 +00:00
|
|
|
alice_verifying_key = enacted_federated_policy.publisher_verifying_key
|
2019-10-03 08:11:26 +00:00
|
|
|
|
|
|
|
# Teach Bob about the network
|
|
|
|
federated_bob.remember_node(list(federated_ursulas)[0])
|
|
|
|
federated_bob.learn_from_teacher_node(eager=True)
|
|
|
|
|
2019-10-03 21:59:23 +00:00
|
|
|
# Deserialized treasure map
|
2019-10-24 10:19:21 +00:00
|
|
|
text1 = federated_bob.retrieve(
|
2019-11-06 21:14:25 +00:00
|
|
|
message_kit,
|
|
|
|
enrico=enrico,
|
|
|
|
alice_verifying_key=alice_verifying_key,
|
|
|
|
label=enacted_federated_policy.label,
|
|
|
|
treasure_map=treasure_map)
|
2019-10-03 21:59:23 +00:00
|
|
|
|
2021-06-08 03:05:35 +00:00
|
|
|
message_kit.clear_cfrags() # Return back to a non re-encrypted state
|
2019-10-24 10:19:21 +00:00
|
|
|
|
2021-06-26 18:53:17 +00:00
|
|
|
assert text1 == [b'Welcome to flippering number 2.']
|
2020-02-26 05:49:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_bob_retrieves_too_late(federated_bob, federated_ursulas,
|
|
|
|
enacted_federated_policy, capsule_side_channel):
|
|
|
|
|
|
|
|
clock = Clock()
|
2020-02-27 05:00:53 +00:00
|
|
|
clock.advance(time.time())
|
|
|
|
for urs in federated_ursulas:
|
2020-09-30 14:15:21 +00:00
|
|
|
if urs._datastore_pruning_task.running:
|
|
|
|
urs._datastore_pruning_task.stop()
|
|
|
|
urs._datastore_pruning_task.clock = clock
|
|
|
|
urs._datastore_pruning_task.start(interval=Ursula._pruning_interval)
|
2020-02-27 05:00:53 +00:00
|
|
|
|
2020-10-19 22:56:14 +00:00
|
|
|
clock.advance(86400 * 8) # 1 week # TODO: this is supposed to be seven days, not eight
|
2020-02-26 05:49:27 +00:00
|
|
|
|
|
|
|
enrico = capsule_side_channel.enrico
|
|
|
|
message_kit = capsule_side_channel()
|
|
|
|
treasure_map = enacted_federated_policy.treasure_map
|
2021-08-11 22:32:48 +00:00
|
|
|
alice_verifying_key = enacted_federated_policy.publisher_verifying_key
|
2020-02-26 05:49:27 +00:00
|
|
|
|
2021-03-30 04:46:54 +00:00
|
|
|
# with pytest.raises(Ursula.NotEnoughUrsulas):
|
|
|
|
federated_bob.retrieve(
|
|
|
|
message_kit,
|
|
|
|
enrico=enrico,
|
|
|
|
alice_verifying_key=alice_verifying_key,
|
|
|
|
label=enacted_federated_policy.label,
|
|
|
|
treasure_map=treasure_map,
|
|
|
|
use_attached_cfrags=False)
|
2020-09-30 14:15:21 +00:00
|
|
|
|
|
|
|
# Check that Bob can't get the treasure map after the policy is expired
|
|
|
|
with pytest.raises(TreasureMap.NowhereToBeFound):
|
|
|
|
federated_bob.get_treasure_map(alice_verifying_key, label=enacted_federated_policy.label)
|