Пример #1
0
def test_bob_rpc_character_control_retrieve_with_tmap(
        enacted_blockchain_policy, blockchain_bob, blockchain_alice,
        bob_rpc_controller, retrieve_control_request):

    # So that this test can run even independently.
    if not blockchain_bob.done_seeding:
        blockchain_bob.learn_from_teacher_node()

    tmap_64 = b64encode(bytes(enacted_blockchain_policy.treasure_map)).decode()
    method_name, params = retrieve_control_request
    params['treasure_map'] = tmap_64
    request_data = {'method': method_name, 'params': params}
    response = bob_rpc_controller.send(request_data)
    assert response.data['result']['cleartexts'][
        0] == 'Welcome to flippering number 1.'

    # Make a wrong (empty) treasure map

    wrong_tmap = SignedTreasureMap(m=0)
    wrong_tmap.prepare_for_publication(
        blockchain_bob.public_keys(DecryptingPower),
        blockchain_bob.public_keys(SigningPower), blockchain_alice.stamp,
        b'Wrong!')
    wrong_tmap._blockchain_signature = b"this is not a signature, but we don't need one for this test....."  # ...because it only matters when Ursula looks at it.
    tmap_bytes = bytes(wrong_tmap)
    tmap_64 = b64encode(tmap_bytes).decode()
    request_data['params']['treasure_map'] = tmap_64
    with pytest.raises(SignedTreasureMap.IsDisorienting):
        bob_rpc_controller.send(request_data)
def test_alice_web_character_control_grant(alice_web_controller_test_client,
                                           grant_control_request):
    method_name, params = grant_control_request
    endpoint = f'/{method_name}'

    response = alice_web_controller_test_client.put(endpoint,
                                                    data=json.dumps(params))
    assert response.status_code == 200

    response_data = json.loads(response.data)
    assert 'treasure_map' in response_data['result']
    assert 'policy_encrypting_key' in response_data['result']
    assert 'alice_verifying_key' in response_data['result']

    map_bytes = b64decode(response_data['result']['treasure_map'])
    encrypted_map = SignedTreasureMap.from_bytes(map_bytes)
    assert encrypted_map._hrac is not None

    # Send bad data to assert error returns
    response = alice_web_controller_test_client.put(endpoint,
                                                    data=json.dumps(
                                                        {'bad': 'input'}))
    assert response.status_code == 400

    bad_params = params.copy()
    # Malform the request
    del (bad_params['bob_encrypting_key'])

    response = alice_web_controller_test_client.put(
        endpoint, data=json.dumps(bad_params))
    assert response.status_code == 400
Пример #3
0
    def use_ursula_as_an_involuntary_and_unbeknownst_cdn(
            self, policy, sucker_ursula):
        """
        Ursula is a sucker.

        After I distract her, by paying for one Policy, maybe she'll store my copy of the Nicholas Cage remake of
        The Wicker Man (I have neither the respect nor the inclination to trick her into storing the original 1973
        version, which after all is a very decent film).

        I'll make this work by fudging the HRAC a bit to create a new map ID which still appears to be connected
        to the Policy for which I paid.
        """
        # Here's the proper map associated with the policy for which I paid.
        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(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):
            # I'll include a small portion of this awful film in a new message kit.  We don't care about the signature for bob.
            not_the_bees = b"Not the bees!" + int(i).to_bytes(length=4,
                                                              byteorder="big")
            like_a_map_but_awful.message_kit, _signature_for_bob_which_is_never_Used = encrypt_and_sign(
                policy.bob.public_keys(DecryptingPower),
                plaintext=not_the_bees,
                signer=self.stamp,
            )

            #############################################################################################
            # 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 = the_map._hrac[:15] + int(i).to_bytes(length=1,
                                                            byteorder="big")  #
            # Note: if the hrac is reduced in length to 16 bytes, I'll be unable to perform this attack.#
            #############################################################################################
            # Also note that we only touch the last byte to demonstrate that this attack isn't possible

            # I know Ursula checks the public signature because she thinks I'm Alice.  So I'll sign my bad hrac.
            like_a_map_but_awful._public_signature = self.stamp(
                bytes(self.stamp) + bad_hrac)
            like_a_map_but_awful._hrac = bad_hrac

            # With the bad hrac and the segment of the film, I'm ready to make a phony payload and map ID.
            like_a_map_but_awful._set_payload()
            like_a_map_but_awful._set_id()

            # I'll sign it again, so that it appears to match the policy for which I already paid.
            transacting_power = self._crypto_power.power_ups(TransactingPower)
            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_payload=bytes(like_a_map_but_awful))
Пример #4
0
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[: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:
            assert DecentralizedTreasureMap.from_bytes(treasure_map_on_node.treasure_map) == enacted_blockchain_policy.treasure_map
        found += 1
    assert found
Пример #5
0
    def receive_treasure_map(treasure_map_id):
        # TODO: Any of the codepaths that trigger 4xx Responses here are also SuspiciousActivity.
        if not this_node.federated_only:
            from nucypher.policy.collections import SignedTreasureMap as _MapClass
        else:
            from nucypher.policy.collections import TreasureMap as _MapClass

        try:
            treasure_map = _MapClass.from_bytes(
                bytes_representation=request.data, verify=True)
        except _MapClass.InvalidSignature:
            log.info("Bad TreasureMap HRAC Signature; not storing {}".format(
                treasure_map_id))
            return Response("This TreasureMap's HRAC is not properly signed.",
                            status=401)

        treasure_map_index = bytes.fromhex(treasure_map_id)

        # First let's see if we already have this map.

        try:
            previously_saved_map = this_node.treasure_maps[treasure_map_index]
        except KeyError:
            pass  # We don't have the map.  We'll validate and perhaps save it.
        else:
            if previously_saved_map == treasure_map:
                return Response("Already have this map.", status=303)
                # Otherwise, if it's a different map with the same ID, we move on to validation.

        if treasure_map.public_id() == treasure_map_id:
            do_store = True
        else:
            return Response("Can't save a TreasureMap with this ID from you.",
                            status=409)

        if do_store and not this_node.federated_only:
            alice_checksum_address = this_node.policy_agent.contract.functions.getPolicyOwner(
                treasure_map._hrac[:16]).call()
            do_store = treasure_map.verify_blockchain_signature(
                checksum_address=alice_checksum_address)

        if do_store:
            log.info("{} storing TreasureMap {}".format(
                this_node, treasure_map_id))
            this_node.treasure_maps[treasure_map_index] = treasure_map
            return Response(bytes(treasure_map), status=202)
        else:
            log.info(
                "Bad TreasureMap ID; not storing {}".format(treasure_map_id))
            return Response("This TreasureMap doesn't match a paid Policy.",
                            status=402)
Пример #6
0
    def from_json(cls, data: str):
        """Deserializes the PolicyCredential from JSON."""
        cred_json = json.loads(data)
        alice_verifying_key = UmbralPublicKey.from_bytes(
            cred_json['alice_verifying_key'], decoder=bytes.fromhex)
        label = bytes.fromhex(cred_json['label'])
        expiration = maya.MayaDT.from_iso8601(cred_json['expiration'])
        policy_pubkey = UmbralPublicKey.from_bytes(cred_json['policy_pubkey'],
                                                   decoder=bytes.fromhex)
        treasure_map = None
        if 'treasure_map' in cred_json:
            # TODO: Support unsigned treasuremaps?
            treasure_map = SignedTreasureMap.from_bytes(
                bytes.fromhex(cred_json['treasure_map']))

        return cls(alice_verifying_key, label, expiration, policy_pubkey,
                   treasure_map)
Пример #7
0
def test_alice_sets_treasure_map_decentralized(enacted_blockchain_policy,
                                               blockchain_alice,
                                               blockchain_bob):
    """
    Same as test_alice_sets_treasure_map except with a blockchain policy.
    """
    treasure_map_hrac = enacted_blockchain_policy.treasure_map._hrac[:16].hex()
    found = 0
    for node in blockchain_bob.matching_nodes_among(
            blockchain_alice.known_nodes):
        with node.datastore.describe(
                DatastoreTreasureMap,
                treasure_map_hrac) as treasure_map_on_node:
            assert DecentralizedTreasureMap.from_bytes(
                treasure_map_on_node.treasure_map
            ) == enacted_blockchain_policy.treasure_map
        found += 1
    assert found
Пример #8
0
    def receive_treasure_map():
        """
        Okay, so we've received a TreasureMap to store. We begin verifying
        the treasure map by first validating the request and the received
        treasure map itself.

        We set the datastore identifier as the HRAC iff the node is running
        as a decentralized node. Otherwise, we use the map_id in
        federated mode.
        """
        if not this_node.federated_only:
            from nucypher.policy.collections import SignedTreasureMap as _MapClass
        else:
            from nucypher.policy.collections import TreasureMap as _MapClass

        # Step 1: First, we verify the signature of the received treasure map.
        # This step also deserializes the treasure map iff it's signed correctly.
        try:
            received_treasure_map = _MapClass.from_bytes(
                bytes_representation=request.data, verify=True)
        except _MapClass.InvalidSignature:
            log.info(
                f"Bad TreasureMap HRAC Signature; not storing for HRAC {received_treasure_map._hrac.hex()}"
            )
            return Response("This TreasureMap's HRAC is not properly signed.",
                            status=401)

        # 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()
        else:
            map_identifier = received_treasure_map.public_id()
            expiration_date = MayaDT.from_datetime(datetime.utcnow() +
                                                   timedelta(days=7))

        # Step 2: Check if we already have the treasure map.
        try:
            with datastore.describe(TreasureMap,
                                    map_identifier) as stored_treasure_map:
                if _MapClass.from_bytes(stored_treasure_map.treasure_map
                                        ) == received_treasure_map:
                    return Response("Already have this map.", status=303)
        except RecordNotFound:
            # This appears to be a new treasure map that we don't have!
            pass

        # Step 3: If the node is decentralized, we check that the received
        # treasure map is valid pursuant to an active policy.
        # 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, with_owner=True)
            # If the Policy doesn't exist, the policy_data is all zeros.
            if not policy_data[5]:
                log.info(
                    f"TreasureMap is for non-existent Policy; not storing {map_identifier}"
                )
                return Response(
                    "The Policy for this TreasureMap doesn't exist.",
                    status=409)

            # Check that this treasure map is from Alice per the Policy.
            if not received_treasure_map.verify_blockchain_signature(
                    checksum_address=alice_checksum_address):
                log.info(f"Bad TreasureMap ID; not storing {map_identifier}")
                return Response(
                    "This TreasureMap doesn't match a paid Policy.",
                    status=402)

            # Check that this treasure map is valid for the Policy datetime and that it's not disabled.
            if policy_data[0] or datetime.utcnow(
            ) >= datetime.utcfromtimestamp(policy_data[5]):
                log.info(
                    f"Received TreasureMap for an expired/disabled policy; not storing {map_identifier}"
                )
                return Response(
                    "This TreasureMap is for an expired/disabled policy.",
                    status=402)
            expiration_date = MayaDT.from_datetime(
                datetime.utcfromtimestamp(policy_data[5]))

        # Step 4: Finally, we store our treasure map under its identifier!
        log.info(f"{this_node} storing TreasureMap {map_identifier}")
        with datastore.describe(TreasureMap, map_identifier,
                                writeable=True) as new_treasure_map:
            new_treasure_map.treasure_map = bytes(received_treasure_map)
            new_treasure_map.expiration = expiration_date
        return Response("Treasure map stored!", status=201)