Beispiel #1
0
def hkdf_expand_and_extract(
    secret: bytes,
    initiator_node_id: NodeID,
    recipient_node_id: NodeID,
    id_nonce: IDNonce,
) -> Tuple[bytes, bytes, bytes]:
    info = b"".join((
        HKDF_INFO,
        initiator_node_id.to_bytes(32, 'big'),
        recipient_node_id.to_bytes(32, 'big'),
    ))

    hkdf = HKDF(
        algorithm=SHA256(),
        length=3 * AES128_KEY_SIZE,
        salt=id_nonce,
        info=info,
        backend=cryptography_default_backend(),
    )
    expanded_key = hkdf.derive(secret)

    if len(expanded_key) != 3 * AES128_KEY_SIZE:
        raise Exception("Invariant: Secret is expanded to three AES128 keys")

    initiator_key = expanded_key[:AES128_KEY_SIZE]
    recipient_key = expanded_key[AES128_KEY_SIZE:2 * AES128_KEY_SIZE]
    auth_response_key = expanded_key[2 * AES128_KEY_SIZE:3 * AES128_KEY_SIZE]

    return initiator_key, recipient_key, auth_response_key
Beispiel #2
0
def recover_source_id_from_tag(tag: Tag,
                               destination_node_id: NodeID) -> NodeID:
    """Recover the node id of the source from the tag in a message packet."""
    destination_node_id_hash = hashlib.sha256(
        destination_node_id.to_bytes(32, 'big')).digest()
    source_node_id_bytes = _sxor(tag, destination_node_id_hash)
    return NodeID(int.from_bytes(source_node_id_bytes, 'big'))
Beispiel #3
0
def node_from_rpc(rpc_node: Tuple[str, str, int]) -> Node:
    node_id_as_hex, ip_address, port = rpc_node
    node_id = NodeID(to_int(hexstr=node_id_as_hex))
    node = Node(
        node_id,
        Endpoint(ipaddress.IPv4Address(ip_address), port),
    )
    return node
Beispiel #4
0
    def from_node_uri(cls, uri: str) -> 'Node':
        from alexandria.validation import validate_node_uri

        validate_node_uri(uri)  # Be no more permissive than the validation
        parsed = urlparse.urlparse(uri)
        if parsed.username is None:
            raise Exception("Unreachable code path")
        node_id = NodeID(to_int(hexstr=parsed.username))
        if parsed.port is None:
            raise Exception("Unreachable code path")
        endpoint = Endpoint(ipaddress.IPv4Address(parsed.hostname),
                            parsed.port)
        return cls(node_id, endpoint)
Beispiel #5
0
    async def _lookup_occasionally(self) -> None:
        async with trio.open_nursery() as nursery:
            async for _ in every(self.config.LOOKUP_INTERVAL):  # noqa: F841
                if self.routing_table.is_empty:
                    self.logger.debug(
                        'Aborting scheduled lookup due to empty routing table')
                    continue

                target_node_id = NodeID(secrets.randbits(256))
                found_nodes = await self.network.iterative_lookup(
                    target_node_id)
                self.logger.debug(
                    'Lookup for %s yielded %d nodes',
                    humanize_node_id(target_node_id),
                    len(found_nodes),
                )
                for node in found_nodes:
                    if node.node_id == self.client.local_node_id:
                        continue
                    nursery.start_soon(self.network.verify_and_add, node)
Beispiel #6
0
def compute_tag(source_node_id: NodeID, destination_node_id: NodeID) -> Tag:
    """Compute the tag used in message packets sent between two nodes."""
    destination_node_id_hash = hashlib.sha256(
        destination_node_id.to_bytes(32, 'big')).digest()
    tag = _sxor(destination_node_id_hash, source_node_id.to_bytes(32, 'big'))
    return Tag(tag)
Beispiel #7
0
    async def _initialize_network_graph(self) -> GraphAPI:
        async def do_get_introduction(
                node: Node,
                send_channel: trio.abc.SendChannel[Tuple[
                    SGNodeAPI, TraversalResults]],  # noqa: E501
        ) -> None:
            try:
                with trio.fail_after(INTRODUCTION_TIMEOUT):
                    candidates = await self.network.get_introduction(node)
            except trio.TooSlowError:
                self.logger.debug("Timeout getting introduction from %s", node)
                return

            self.logger.debug("Got %d introductions from %s", len(candidates),
                              node)

            async with send_channel:
                for candidate in candidates:
                    import time
                    start_at = time.monotonic()
                    try:
                        with trio.fail_after(TRAVERSAL_TIMEOUT):
                            result = await get_traversal_result(
                                self.network,
                                self.graph_db,
                                candidate,
                                max_traversal_distance=10,
                            )
                    except trio.TooSlowError:
                        self.logger.error(
                            "%s: Traversal timeout: %s",
                            self.client.local_node,
                            candidate,
                        )
                        return
                    else:
                        end_at = time.monotonic()
                        self.logger.info(
                            "%s: Traversal finished in %s seconds",
                            self.client.local_node,
                            end_at - start_at,
                        )
                    await send_channel.send(result)

        while self.manager.is_running:
            if self.config.can_initialize_network_skip_graph:
                # Use a probabalistic mechanism here so that multiple
                # bootnodes coming online at the same time are unlikely to
                # try and concurrently seed the network with content.
                if secrets.randbits(256) >= self.client.local_node_id:
                    for key in self.content_manager.iter_content_keys():
                        seed_node = SGNode(content_key_to_graph_key(key))
                        break
                    else:
                        continue
                    self.logger.info("%s: Seeding network graph",
                                     self.client.local_node)
                    self.graph_db.set(seed_node.key, seed_node)
                    return NetworkGraph(self.graph_db, seed_node, self.network)

            random_content_id = NodeID(secrets.randbits(256))
            candidates = await self.network.iterative_lookup(random_content_id)
            if not candidates:
                self.logger.debug("No candidates for introduction")
                await trio.sleep(5)
                continue

            send_channel, receive_channel = trio.open_memory_channel[Tuple[
                SGNodeAPI, TraversalResults]](256)  # noqa: E501

            async with trio.open_nursery() as nursery:
                async with send_channel:
                    for node in candidates:
                        nursery.start_soon(do_get_introduction, node,
                                           send_channel.clone())

                async with receive_channel:
                    introduction_results = tuple(
                        [result async for result in receive_channel])

            if not introduction_results:
                self.logger.debug("Received no introductions.  sleeping....")
                await trio.sleep(5)
                continue

            best_result = sorted(introduction_results,
                                 key=lambda result: result.score,
                                 reverse=True)[0]

            if best_result.score > 0.0:
                self.logger.info(
                    "%s: Network SkipGraph initialized: node=%s  score=%s",
                    self.client.local_node,
                    best_result.node,
                    best_result.score,
                )
                return NetworkGraph(self.graph_db, best_result.node,
                                    self.network)
            else:
                self.logger.info(
                    "Failed to initialize Skip Graph. All introductions were faulty: %s",
                    tuple(str(result) for result in introduction_results),
                )
                await trio.sleep(5)
Beispiel #8
0
def public_key_to_node_id(public_key: keys.PublicKey) -> NodeID:
    return NodeID(int.from_bytes(sha256(public_key.to_bytes()), 'big'))
Beispiel #9
0
def content_key_to_node_id(key: bytes) -> NodeID:
    return NodeID(int.from_bytes(sha256(key), 'big'))
Beispiel #10
0
def humanize_node_id(node_id: NodeID) -> str:
    node_id_bytes = node_id.to_bytes(32, 'big')
    return humanize_hash(Hash32(node_id_bytes))
Beispiel #11
0
def node_id_to_hex(node_id: NodeID) -> str:
    return encode_hex(node_id.to_bytes(32, 'big'))
Beispiel #12
0
def node_id_from_hex(node_id_as_hex: str) -> NodeID:
    return NodeID(to_int(hexstr=node_id_as_hex))
Beispiel #13
0
def compute_handshake_response_magic(destination_node_id: NodeID) -> Hash32:
    preimage = destination_node_id.to_bytes(32, 'big') + HANDSHAKE_RESPONSE_MAGIC_SUFFIX
    return Hash32(hashlib.sha256(preimage).digest())