Ejemplo n.º 1
0
def test_load_private_key_der():
    # load_private_key_der expects a byte encoded data. Any other should fail and return None
    for wtype in WRONG_TYPES:
        assert Cryptographer.load_private_key_der(wtype) is None

    # On the other hand, any random formatter byte array would also fail (zeros for example)
    assert Cryptographer.load_private_key_der(bytes(32)) is None

    # A proper formatted key should load
    sk_der = generate_keypair()[0].to_der()
    assert Cryptographer.load_private_key_der(sk_der) is not None
Ejemplo n.º 2
0
def load_keys(user_sk_path):
    """
    Loads all the user private key and id.

    Args:
        user_sk_path (:obj:`str`): path to the user's private key file.

    Returns:
        :obj:`tuple`: A tuple containing a :obj:`PrivateKey` and a :obj:`str` representing the user sk and user id
        (compressed pk) respectively.

    Raises:
        :obj:`InvalidKey`: if any of the keys is invalid or cannot be loaded.
    """

    if not user_sk_path:
        raise InvalidKey(
            "Client's private key file not found. Please check your settings")

    try:
        user_sk_der = Cryptographer.load_key_file(user_sk_path)
        user_sk = Cryptographer.load_private_key_der(user_sk_der)

    except (InvalidParameter, InvalidKey):
        raise InvalidKey("Client private key is invalid or cannot be parsed")

    try:
        user_id = Cryptographer.get_compressed_pk(user_sk.public_key)

    except (InvalidParameter, InvalidKey):
        raise InvalidKey("Client public key cannot be loaded")

    return user_sk, user_id
Ejemplo n.º 3
0
def load_keys(data_dir):
    """
    Loads a the client key pair.

    Args:
        data_dir (:obj:`str`): path to data directory where the keys are stored.

    Returns:
        :obj:`tuple`: a tuple containing a ``PrivateKey`` and a ``str`` representing the client sk and compressed pk
        respectively.

    Raises:
        :obj:`InvalidKey <cli.exceptions.InvalidKey>`: if any of the keys is invalid or cannot be loaded.
    """

    if not isinstance(data_dir, str):
        raise ValueError("Invalid data_dir. Please check your settings")

    sk_file_path = os.path.join(data_dir, "sk.der")

    cli_sk_der = Cryptographer.load_key_file(sk_file_path)
    cli_sk = Cryptographer.load_private_key_der(cli_sk_der)

    if cli_sk is None:
        raise InvalidKey("Client private key is invalid or cannot be parsed")

    compressed_cli_pk = Cryptographer.get_compressed_pk(cli_sk.public_key)

    if compressed_cli_pk is None:
        raise InvalidKey("Client public key cannot be loaded")

    return cli_sk, compressed_cli_pk
Ejemplo n.º 4
0
 def __init__(self, db_manager, block_processor, responder, sk_der, max_appointments, expiry_delta):
     self.appointments = dict()
     self.locator_uuid_map = dict()
     self.block_queue = Queue()
     self.db_manager = db_manager
     self.block_processor = block_processor
     self.responder = responder
     self.max_appointments = max_appointments
     self.expiry_delta = expiry_delta
     self.signing_key = Cryptographer.load_private_key_der(sk_der)
Ejemplo n.º 5
0
def load_keys(teos_pk_path, cli_sk_path, cli_pk_path):
    """
    Loads all the keys required so sign, send, and verify the appointment.

    Args:
        teos_pk_path (:obj:`str`): path to the TEOS public key file.
        cli_sk_path (:obj:`str`): path to the client private key file.
        cli_pk_path (:obj:`str`): path to the client public key file.

    Returns:
        :obj:`tuple` or ``None``: a three item tuple containing a teos_pk object, cli_sk object and the cli_sk_der
        encoded key if all keys can be loaded. ``None`` otherwise.
    """

    if teos_pk_path is None:
        logger.error(
            "TEOS's public key file not found. Please check your settings")
        return None

    if cli_sk_path is None:
        logger.error(
            "Client's private key file not found. Please check your settings")
        return None

    if cli_pk_path is None:
        logger.error(
            "Client's public key file not found. Please check your settings")
        return None

    try:
        teos_pk_der = Cryptographer.load_key_file(teos_pk_path)
        teos_pk = PublicKey(teos_pk_der)

    except ValueError:
        logger.error("TEOS public key is invalid or cannot be parsed")
        return None

    cli_sk_der = Cryptographer.load_key_file(cli_sk_path)
    cli_sk = Cryptographer.load_private_key_der(cli_sk_der)

    if cli_sk is None:
        logger.error("Client private key is invalid or cannot be parsed")
        return None

    try:
        cli_pk_der = Cryptographer.load_key_file(cli_pk_path)
        PublicKey(cli_pk_der)

    except ValueError:
        logger.error("Client public key is invalid or cannot be parsed")
        return None

    return teos_pk, cli_sk, cli_pk_der
Ejemplo n.º 6
0
 def __init__(self, db_manager, gatekeeper, block_processor, responder,
              sk_der, max_appointments, blocks_in_cache):
     self.appointments = dict()
     self.locator_uuid_map = dict()
     self.block_queue = Queue()
     self.db_manager = db_manager
     self.gatekeeper = gatekeeper
     self.block_processor = block_processor
     self.responder = responder
     self.max_appointments = max_appointments
     self.signing_key = Cryptographer.load_private_key_der(sk_der)
     self.last_known_block = db_manager.load_last_block_hash_watcher()
     self.locator_cache = LocatorCache(blocks_in_cache)
Ejemplo n.º 7
0
def main(config):
    setup_data_folder(config.get("DATA_DIR"))

    logging_server_ready = multiprocessing.Event()
    stop_logging_server = multiprocessing.Event()
    logging_port = multiprocessing.Value("i")
    logging_process = multiprocessing.Process(
        target=serve_logging,
        daemon=True,
        args=(config.get("LOG_FILE"), logging_port, config.get("DAEMON"),
              logging_server_ready, stop_logging_server),
    )
    logging_process.start()
    logging_server_ready.wait()

    setup_logging(logging_port.value)
    logger = get_logger(component="Daemon")

    if not os.path.exists(
            config.get("TEOS_SECRET_KEY")) or config.get("OVERWRITE_KEY"):
        logger.info("Generating a new key pair")
        sk = Cryptographer.generate_key()
        Cryptographer.save_key_file(sk.to_der(), "teos_sk",
                                    config.get("DATA_DIR"))

    else:
        logger.info("Tower identity found. Loading keys")
        secret_key_der = Cryptographer.load_key_file(
            config.get("TEOS_SECRET_KEY"))

        if not secret_key_der:
            raise IOError("TEOS private key cannot be loaded")
        sk = Cryptographer.load_private_key_der(secret_key_der)

    # Windows cannot run gunicorn
    if os.name == "nt" and config.get("WSGI") == "gunicorn":
        logger.warning(
            "Windows cannot run gunicorn as WSGI. Changing to waitress")
        config["WSGI"] = "waitress"

    try:
        TeosDaemon(config, sk, logger, logging_port.value, stop_logging_server,
                   logging_process).start()
    except Exception as e:
        logger.error("An error occurred: {}. Shutting down".format(e))
        stop_logging_server.set()
        logging_process.join()
        exit(1)
Ejemplo n.º 8
0
def load_keys(teos_pk_path, user_sk_path):
    """
    Loads all the keys required so sign, send, and verify the appointment.

    Args:
        teos_pk_path (:obj:`str`): path to the tower's public key file.
        user_sk_path (:obj:`str`): path to the user's private key file.

    Returns:
        :obj:`tuple`: a three-item tuple containing a ``str``, a ``PrivateKey`` and a ``str``
        representing the tower id (compressed pk), user sk and user id (compressed pk) respectively.

    Raises:
        :obj:`InvalidKey <cli.exceptions.InvalidKey>`: if any of the keys is invalid or cannot be loaded.
    """

    if not teos_pk_path:
        raise InvalidKey(
            "TEOS's public key file not found. Please check your settings")

    if not user_sk_path:
        raise InvalidKey(
            "Client's private key file not found. Please check your settings")

    try:
        teos_pk_der = Cryptographer.load_key_file(teos_pk_path)
        teos_id = Cryptographer.get_compressed_pk(PublicKey(teos_pk_der))

    except (InvalidParameter, InvalidKey, ValueError):
        raise InvalidKey("TEOS public key cannot be loaded")

    try:
        user_sk_der = Cryptographer.load_key_file(user_sk_path)
        user_sk = Cryptographer.load_private_key_der(user_sk_der)

    except (InvalidParameter, InvalidKey):
        raise InvalidKey("Client private key is invalid or cannot be parsed")

    try:
        user_id = Cryptographer.get_compressed_pk(user_sk.public_key)

    except (InvalidParameter, InvalidKey):
        raise InvalidKey("Client public key cannot be loaded")

    return teos_id, user_sk, user_id
def test_load_private_key_der():
    # load_private_key_der expects a byte encoded data. Any other should fail and return None
    for wtype in WRONG_TYPES:
        with pytest.raises(InvalidKey, match="(wrong type)"):
            Cryptographer.load_private_key_der(wtype)

    # On the other hand, any random formatter byte array would also fail (zeros for example)
    with pytest.raises(InvalidKey, match="(wrong size or format)"):
        Cryptographer.load_private_key_der(bytes(32))

    # A proper formatted key should load
    sk_der = generate_keypair()[0].to_der()
    Cryptographer.load_private_key_der(sk_der)
Ejemplo n.º 10
0
def run_teosd():
    sk_file_path = os.path.join(config.get("DATA_DIR"), "teos_sk.der")
    if not os.path.exists(sk_file_path):
        # Generating teos sk so we can return the teos_id
        teos_sk = Cryptographer.generate_key()
        Cryptographer.save_key_file(teos_sk.to_der(), "teos_sk", config.get("DATA_DIR"))
    else:
        teos_sk = Cryptographer.load_private_key_der(Cryptographer.load_key_file(sk_file_path))

    teos_id = Cryptographer.get_compressed_pk(teos_sk.public_key)

    # Change the default WSGI for Windows
    if os.name == "nt":
        config["WSGI"] = "waitress"
    teosd_process = Process(target=main, kwargs={"config": config})
    teosd_process.start()

    # Give it some time to bootstrap
    # TODO: we should do better synchronization using an Event
    sleep(3)

    return teosd_process, teos_id
Ejemplo n.º 11
0
def main(command_line_conf):
    global db_manager, chain_monitor

    try:
        signal(SIGINT, handle_signals)
        signal(SIGTERM, handle_signals)
        signal(SIGQUIT, handle_signals)

        # Loads config and sets up the data folder and log file
        data_dir = command_line_conf.pop(
            "DATA_DIR") if "DATA_DIR" in command_line_conf else DATA_DIR
        config_loader = ConfigLoader(data_dir, CONF_FILE_NAME, DEFAULT_CONF,
                                     command_line_conf)
        config = config_loader.build_config()

        # Set default RPC port if not overwritten by the user.
        if "BTC_RPC_PORT" not in config_loader.overwritten_fields:
            config["BTC_RPC_PORT"] = get_default_rpc_port(
                config.get("BTC_NETWORK"))

        setup_data_folder(data_dir)
        setup_logging(config.get("LOG_FILE"), LOG_PREFIX)

        logger.info("Starting TEOS")

        bitcoind_connect_params = {
            k: v
            for k, v in config.items() if k.startswith("BTC")
        }
        bitcoind_feed_params = {
            k: v
            for k, v in config.items() if k.startswith("BTC_FEED")
        }

        if not can_connect_to_bitcoind(bitcoind_connect_params):
            logger.error("Cannot connect to bitcoind. Shutting down")

        elif not in_correct_network(bitcoind_connect_params,
                                    config.get("BTC_NETWORK")):
            logger.error(
                "bitcoind is running on a different network, check conf.py and bitcoin.conf. Shutting down"
            )

        else:
            secret_key_der = Cryptographer.load_key_file(
                config.get("TEOS_SECRET_KEY"))
            if not secret_key_der:
                raise IOError("TEOS private key cannot be loaded")

            logger.info("tower_id = {}".format(
                Cryptographer.get_compressed_pk(
                    Cryptographer.load_private_key_der(
                        secret_key_der).public_key)))
            block_processor = BlockProcessor(bitcoind_connect_params)
            carrier = Carrier(bitcoind_connect_params)

            gatekeeper = Gatekeeper(
                UsersDBM(config.get("USERS_DB_PATH")),
                block_processor,
                config.get("SUBSCRIPTION_SLOTS"),
                config.get("SUBSCRIPTION_DURATION"),
                config.get("EXPIRY_DELTA"),
            )
            db_manager = AppointmentsDBM(config.get("APPOINTMENTS_DB_PATH"))
            responder = Responder(db_manager, gatekeeper, carrier,
                                  block_processor)
            watcher = Watcher(
                db_manager,
                gatekeeper,
                block_processor,
                responder,
                secret_key_der,
                config.get("MAX_APPOINTMENTS"),
                config.get("LOCATOR_CACHE_SIZE"),
            )

            # Create the chain monitor and start monitoring the chain
            chain_monitor = ChainMonitor(watcher.block_queue,
                                         watcher.responder.block_queue,
                                         block_processor, bitcoind_feed_params)

            watcher_appointments_data = db_manager.load_watcher_appointments()
            responder_trackers_data = db_manager.load_responder_trackers()

            if len(watcher_appointments_data) == 0 and len(
                    responder_trackers_data) == 0:
                logger.info("Fresh bootstrap")

                watcher.awake()
                watcher.responder.awake()

            else:
                logger.info("Bootstrapping from backed up data")

                # Update the Watcher backed up data if found.
                if len(watcher_appointments_data) != 0:
                    watcher.appointments, watcher.locator_uuid_map = Builder.build_appointments(
                        watcher_appointments_data)

                # Update the Responder with backed up data if found.
                if len(responder_trackers_data) != 0:
                    watcher.responder.trackers, watcher.responder.tx_tracker_map = Builder.build_trackers(
                        responder_trackers_data)

                # Awaking components so the states can be updated.
                watcher.awake()
                watcher.responder.awake()

                last_block_watcher = db_manager.load_last_block_hash_watcher()
                last_block_responder = db_manager.load_last_block_hash_responder(
                )

                # Populate the block queues with data if they've missed some while offline. If the blocks of both match
                # we don't perform the search twice.

                # FIXME: 32-reorgs-offline dropped txs are not used at this point.
                last_common_ancestor_watcher, dropped_txs_watcher = block_processor.find_last_common_ancestor(
                    last_block_watcher)
                missed_blocks_watcher = block_processor.get_missed_blocks(
                    last_common_ancestor_watcher)

                if last_block_watcher == last_block_responder:
                    dropped_txs_responder = dropped_txs_watcher
                    missed_blocks_responder = missed_blocks_watcher

                else:
                    last_common_ancestor_responder, dropped_txs_responder = block_processor.find_last_common_ancestor(
                        last_block_responder)
                    missed_blocks_responder = block_processor.get_missed_blocks(
                        last_common_ancestor_responder)

                # If only one of the instances needs to be updated, it can be done separately.
                if len(missed_blocks_watcher
                       ) == 0 and len(missed_blocks_responder) != 0:
                    Builder.populate_block_queue(watcher.responder.block_queue,
                                                 missed_blocks_responder)
                    watcher.responder.block_queue.join()

                elif len(missed_blocks_responder
                         ) == 0 and len(missed_blocks_watcher) != 0:
                    Builder.populate_block_queue(watcher.block_queue,
                                                 missed_blocks_watcher)
                    watcher.block_queue.join()

                # Otherwise they need to be updated at the same time, block by block
                elif len(missed_blocks_responder) != 0 and len(
                        missed_blocks_watcher) != 0:
                    Builder.update_states(watcher, missed_blocks_watcher,
                                          missed_blocks_responder)

            # Fire the API and the ChainMonitor
            # FIXME: 92-block-data-during-bootstrap-db
            chain_monitor.monitor_chain()
            inspector = Inspector(block_processor,
                                  config.get("MIN_TO_SELF_DELAY"))
            API(config.get("API_BIND"), config.get("API_PORT"), inspector,
                watcher).start()
    except Exception as e:
        logger.error("An error occurred: {}. Shutting down".format(e))
        exit(1)