Fix DoS vector on nodes through map storage

pull/2132/head
tuxxy 2020-09-30 17:01:39 +02:00
parent 94b489aa77
commit c965f72c2e
7 changed files with 21 additions and 21 deletions

View File

@ -565,7 +565,7 @@ class Bob(Character):
# Ugh stupid federated only mode....
if not self.federated_only:
map_identifier = _hrac.hex()
map_identifier = _hrac[:16].hex()
else:
map_identifier = map_id
treasure_map = self.get_treasure_map_from_known_ursulas(self.network_middleware,

View File

@ -209,10 +209,10 @@ class Amonia(Alice):
to the Policy for which I paid.
"""
# Here's the proper map associated with the policy for which I paid.
map = policy.treasure_map
the_map = policy.treasure_map
# I'll make a copy of it to modify for use in this attack.
like_a_map_but_awful = SignedTreasureMap.from_bytes(bytes(map))
like_a_map_but_awful = SignedTreasureMap.from_bytes(bytes(the_map))
# I'll split the film up into segments, because I know Ursula checks that the file size is under 50k.
for i in range(50):
@ -227,7 +227,7 @@ class Amonia(Alice):
#############################################################################################
# Now I'll mess with the hrac just a bit. I can't touch the last 16 bytes, because these #
# are checked against the blockchain. But the first half is up for grabs. #
bad_hrac = map._hrac[:28] + int(i).to_bytes(length=4, byteorder="big") #
bad_hrac = the_map._hrac[:28] + int(i).to_bytes(length=4, byteorder="big") #
# Note: if the hrac is reduced in length to 16 bytes, I'll be unable to perform this attack.#
#############################################################################################
@ -244,5 +244,4 @@ class Amonia(Alice):
like_a_map_but_awful.include_blockchain_signature(blockchain_signer=transacting_power.sign_message)
# Sucker.
self.network_middleware.put_treasure_map_on_node(sucker_ursula, map_id=like_a_map_but_awful.public_id(),
map_payload=bytes(like_a_map_but_awful))
self.network_middleware.put_treasure_map_on_node(sucker_ursula, map_payload=bytes(like_a_map_but_awful))

View File

@ -373,7 +373,8 @@ def _make_rest_app(datastore: Datastore, this_node, serving_domain: str, log: Lo
# Additionally, we determine the map identifier from the type of node.
# If the node is federated, we also set the expiration for a week.
if not this_node.federated_only:
map_identifier = received_treasure_map._hrac.hex()
# HOT LAVA - Do not forget to truncate the hrac to 16 bytes so it matches on-chain data.
map_identifier = received_treasure_map._hrac[:16].hex()
else:
map_identifier = received_treasure_map.public_id()
expiration_date = MayaDT.from_datetime(datetime.utcnow() + timedelta(days=7))
@ -392,8 +393,8 @@ def _make_rest_app(datastore: Datastore, this_node, serving_domain: str, log: Lo
# We also set the expiration from the data on the blockchain here.
if not this_node.federated_only:
policy_data, alice_checksum_address = this_node.policy_agent.fetch_policy(
received_treasure_map._hrac[:16],
with_owner=True)
received_treasure_map._hrac[:16],
with_owner=True)
# Check that this treasure map is from Alice per the Policy.
if not received_treasure_map.verify_blockchain_signature(checksum_address=alice_checksum_address):

View File

@ -116,8 +116,7 @@ class TreasureMap:
self.message_kit, _signature_for_bob = encrypt_and_sign(bob_encrypting_key,
plaintext=plaintext,
signer=alice_stamp,
)
signer=alice_stamp)
"""
Here's our "hashed resource access code".

View File

@ -89,7 +89,7 @@ def test_alice_sets_treasure_map_decentralized(enacted_blockchain_policy):
Same as test_alice_sets_treasure_map except with a blockchain policy.
"""
enacted_blockchain_policy.publish_treasure_map(network_middleware=MockRestMiddleware())
treasure_map_hrac = enacted_blockchain_policy.treasure_map._hrac.hex()
treasure_map_hrac = enacted_blockchain_policy.treasure_map._hrac[:16].hex()
found = 0
for node in enacted_blockchain_policy.bob.matching_nodes_among(enacted_blockchain_policy.alice.known_nodes):
with node.datastore.describe(DatastoreTreasureMap, treasure_map_hrac) as treasure_map_on_node:

View File

@ -20,7 +20,7 @@ import maya
import pytest
from nucypher.characters.unlawful import Amonia
from nucypher.datastore.models import PolicyArrangement
from nucypher.datastore.models import PolicyArrangement, TreasureMap as DatastoreTreasureMap
from nucypher.datastore.datastore import RecordNotFound
@ -145,14 +145,15 @@ def test_put_additional_treasure_map_on_network(blockchain_ursulas, blockchain_a
label = b"this_is_the_path_to_which_access_is_being_granted"
policy = amonia.grant(bob=blockchain_bob,
label=label,
m=2,
n=n,
rate=int(1e18), # one ether
expiration=policy_end_datetime)
label=label,
m=2,
n=n,
rate=int(1e18), # one ether
expiration=policy_end_datetime)
sucker = blockchain_ursulas[0]
amonia.use_ursula_as_an_involuntary_and_unbeknownst_cdn(policy, sucker_ursula=blockchain_ursulas[0])
# This assertion will fail if my attack succeeded.
assert len(sucker.treasure_maps) <= 1
with sucker.datastore.query_by(DatastoreTreasureMap) as all_treasure_maps:
assert len(all_treasure_maps) <= 1

View File

@ -57,8 +57,8 @@ def test_bob_already_knows_all_nodes_in_treasure_map(enacted_federated_policy, f
federated_bob.remember_node(ursula)
# Now, Bob can get the TreasureMap all by himself, and doesn't need a side channel.
map = federated_bob.get_treasure_map(federated_alice.stamp, enacted_federated_policy.label)
unknown, known = federated_bob.peek_at_treasure_map(treasure_map=map)
the_map = federated_bob.get_treasure_map(federated_alice.stamp, enacted_federated_policy.label)
unknown, known = federated_bob.peek_at_treasure_map(treasure_map=the_map)
# He finds that he didn't need to discover any new nodes...
assert len(unknown) == 0