Beispiel #1
0
    def handle_file_request(self, request_client_cid, file):
        """Checks availability of the requested file and creates a bridge if
        available.

        """
        if file not in self.files:
            return ("file not found in network files: " + file, 404) # 404 Not Found

        request_client_ip = self.peers[request_client_cid]

        # Find the CID and IP of the first client owning the requested file
        owning_client_cid = choice(self.files[file])
        owning_client_ip = self.peers[owning_client_cid]

        # Create the CID and FSID for this bridge
        bridge_cid = generate_bytes(cid_size).hex()
        fsid = self.fsid_counter
        self.fsid_counter += 1

        # Send a message to the node at the start of the bridge
        make_bridge_message = {
            "type": "make_bridge",
            "bridge_CID": bridge_cid,
            "to": request_client_ip,
            "FSID": fsid
        }
        # Send on the control channel of the node
        requests.post(get_url(owning_client_ip) + "/control", json=make_bridge_message)

        # Send a message to the node at the end of the bridge
        receive_bridge_message = {
            "type": "receive_bridge",
            "CID": request_client_cid,
            "bridge_CID": bridge_cid
        }
        # Send on the control channel of the node
        requests.post(get_url(request_client_ip) + "/control", json=receive_bridge_message)

        # Send a message to the client, asking he/she to send the file
        request_message = {
            "CID": owning_client_cid,
            "payload": json_to_bytes({
                "type": "request",
                "file": file,
                "FSID": fsid
            }).hex()
        }
        requests.post(get_url(owning_client_ip), json=request_message)
        return "ok"
Beispiel #2
0
    def forward_upstream(self, message, colour):
        """Sends message to next node, bridge or tracker.
        """
        up_cid = self.down_relay[message["CID"]]["UpCID"]
        up_ip = self.down_relay[message["CID"]]["UpIP"]
        sess_key = self.down_relay[message["CID"]]["SessKey"]

        # Decrypt the payload (peel one layer of the onion)
        payload = aes_decrypt(bytes.fromhex(message["payload"]), sess_key)

        # Try to decode the payload
        try:
            decoded_payload = bytes_to_json(payload)
            # No decoding exception, the payload was a valid JSON once
            # decoded.

            # Two possibilities here: the payload is for a bridge or
            # for the tracker
            if "FSID" in decoded_payload:
                return self.transmit_to_bridge(decoded_payload, colour)
            # If the message is a teardown message
            elif "type" in decoded_payload and decoded_payload[
                    "type"] == "teardown":
                self.teardown(message["CID"])

                # If we pass here, then we should just forward upstream

        except (UnicodeDecodeError, json.decoder.JSONDecodeError) as e:
            # A decoding exception occurred, just forward upstream
            pass

        self.cprint([message["CID"], "upstream", up_ip], "forward", colour)
        new_message = {"CID": up_cid, "payload": payload.hex()}
        requests.post(get_url(up_ip), json=new_message)
        return "ok"
Beispiel #3
0
    def receive_from_bridge(self, message, colour):
        """Receives messaged from bridge.
        """
        # Disabled check for now, it may cause problems
        if not self.bridgeCID_matches_existing_downCID(message["CID"]):
            return "Bridge CID does not matches with a down CID", 400  # 400 Bad Request

        down_cid = self.down_file_transfer[message["CID"]]

        down_ip = self.down_relay[down_cid]["DownIP"]
        sess_key = self.down_relay[down_cid]["SessKey"]

        self.cprint([message["CID"], down_ip], "receive_from_bridge", colour)

        payload = bytes.fromhex(message["payload"])
        signatures = [self.sign(payload).hex()]

        new_message = {
            "CID": down_cid,
            # Encrypt the message when sending downstream, we
            # received it as encoded plaintext
            "payload": aes_encrypt(payload, sess_key).hex(),
            "signatures": signatures
        }
        requests.post(get_url(down_ip), json=new_message)
        return "ok"
Beispiel #4
0
    def transmit_to_bridge(self, payload, colour):
        """Sends payload through bridge.
        """
        fsid = payload["FSID"]
        # Disabled for now, this test causes problems
        if fsid not in self.up_file_transfer:
            return "FSID not found for file sharing", 404  # 404 Not Found

        bridge_ip = self.up_file_transfer[fsid]["IP"]
        bridge_cid = self.up_file_transfer[fsid]["CID"]
        self.cprint([fsid, bridge_ip], "transmit_to_bridge", colour)

        new_message = {
            "CID":
            bridge_cid,
            # The payload is not encrypted, just encoded
            "payload":
            json_to_bytes({
                "type": "file",
                "file": payload["file"],
                "data": payload["data"]
            }).hex()
        }
        requests.post(get_url(bridge_ip), json=new_message)
        return "ok"
Beispiel #5
0
    def conn(self):
        """Connects to the torrent network, uploading the list of files this
        client has.

        """
        self.tunnel_nodes = self.select_nodes(node_pool)
        self.sesskeys = [
            generate_bytes(aes_common.key_size) for _ in self.tunnel_nodes
        ]
        self.cid = generate_bytes(cid_size).hex()

        tracker_payload = {
            "type": "ls",
            "files": list(self.owned_files.keys())
        }

        payloadZ = {
            "to": tracker,
            # The payload is plaintext between Z and the tracker, but encoded
            "relay": json_to_bytes(tracker_payload).hex()
        }

        payloadY = {
            "to":
            self.tunnel_nodes[2],
            # Encrypt the AES session key for Z with RSA, it will be copied by
            # the node
            "aes_key":
            rsa_encrypt(self.sesskeys[2],
                        public_keys[self.tunnel_nodes[2]]).hex(),
            # Encrypt the payload for Z with the AES session key
            "relay":
            aes_encrypt(json_to_bytes(payloadZ), self.sesskeys[2]).hex()
        }

        payloadX = {
            "to":
            self.tunnel_nodes[1],
            "aes_key":
            rsa_encrypt(self.sesskeys[1],
                        public_keys[self.tunnel_nodes[1]]).hex(),
            "relay":
            aes_encrypt(json_to_bytes(payloadY), self.sesskeys[1]).hex()
        }

        message = {
            "CID":
            self.cid,
            "aes_key":
            rsa_encrypt(self.sesskeys[0],
                        public_keys[self.tunnel_nodes[0]]).hex(),
            "payload":
            aes_encrypt(json_to_bytes(payloadX), self.sesskeys[0]).hex()
        }

        requests.post(get_url(self.tunnel_nodes[0]), json=message)
        self.connected = True
        return redirect("/")
Beispiel #6
0
    def create_tunnel(self, message, colour):
        """Creates tunnel using three nodes.
        Read more on /docs/index.md
        """
        # Decrypt the AES key
        sess_key = rsa_decrypt(bytes.fromhex(message["aes_key"]),
                               self.private_key)
        sess_key = sess_key[
            -aes_common.key_size:]  # Discard the right padding created by RSA

        # Decrypt the payload with the AES key
        # It should be a JSON string once decoded
        payload = aes_decrypt(bytes.fromhex(message["payload"]), sess_key)
        payload = bytes_to_json(payload)

        if "relay" not in payload or "to" not in payload:
            # All these fields should be present
            return "relay and to are needed in payload when creating the tunnel", 400  # 400 Bad Request

        # Generate a CID for the upstream link
        up_cid = generate_bytes(cid_size).hex()

        self.cprint([message["CID"]], "unknownCID", colour)
        # Add info to the relay tables
        self.down_relay[message["CID"]] = {
            "DownIP": request.remote_addr,
            "SessKey": sess_key,
            "UpCID": up_cid,
            "UpIP": payload["to"]
        }

        self.up_relay[up_cid] = {
            "DownCID": message["CID"],
            "DownIP": request.remote_addr,
            "SessKey": sess_key,
            "UpIP": payload["to"]
        }

        # Forward the payload to the next node upstream
        new_message = {
            "CID": up_cid,
            # Copy verbatim the encrypted key and payload for the next
            # node (not our business)
            "payload": payload["relay"]
        }
        if "aes_key" in payload:
            new_message["aes_key"] = payload["aes_key"]

        self.cprint([up_cid, payload["to"]], "add_to_relay", colour)
        requests.post(get_url(payload["to"]), json=new_message)
        return "ok"
Beispiel #7
0
    def send_payload(self, payload):
        """Encrypts three times a message and send it to the tunnel. The
        tunnel has to be established beforehand.

        """
        payload = json_to_bytes(payload)

        # Encrypt in the reverse order, the closest node (first in the
        # list) decrypts first
        for node, sesskey in reversed(
                list(zip(self.tunnel_nodes, self.sesskeys))):
            payload = aes_encrypt(payload, sesskey)

        message = {"CID": self.cid, "payload": payload.hex()}

        requests.post(get_url(self.tunnel_nodes[0]), json=message)
Beispiel #8
0
    def forward_downstream(self, message, colour):
        """Sends message to next node.
        """
        down_cid = self.up_relay[message["CID"]]["DownCID"]
        down_ip = self.up_relay[message["CID"]]["DownIP"]
        sess_key = self.up_relay[message["CID"]]["SessKey"]

        self.cprint([message["CID"], "downstream", down_ip], "forward", colour)
        signatures = []
        if "signatures" in message:
            signatures = message["signatures"]
        payload = bytes.fromhex(message["payload"])
        signature = self.sign(payload).hex()
        signatures.append(signature)

        new_message = {
            "CID": down_cid,
            # Encrypt the payload (add a layer to the onion)
            "payload": aes_encrypt(payload, sess_key).hex(),
            "signatures": signatures
        }
        print(new_message)
        requests.post(get_url(down_ip), json=new_message)
        return "ok"
Beispiel #9
0
    def handle_new_client(self, cid, ip, files):
        """Registers a new client by remembering the CID and IP of the exit
        node, and send back the list of available files.

        """
        self.peers[cid] = ip

        for file in files:
            # Create a list of owning clients for the file if there is none,
            # and put the cid of the client in it
            self.files.setdefault(file, []).append(cid)

        # Send a new list of files to all peers
        for peer_cid, peer_ip in self.peers.items():
            response = {
                "CID": peer_cid,
                "payload": json_to_bytes({
                    "type": "ls",
                    "files": list(self.files.keys())
                }).hex()
            }
            requests.post(get_url(peer_ip), json=response)

        return "ok"