示例#1
0
def test_init_blob():
    data = get_random_value_hex(64)
    blob = Blob(data)
    assert isinstance(blob, Blob)

    # Wrong data
    try:
        Blob(unhexlify(get_random_value_hex(64)))
        assert False, "Able to create blob with wrong data"

    except ValueError:
        assert True
示例#2
0
def test_encrypt_wrong_key_size():
    blob = Blob(get_random_value_hex(64))
    key = get_random_value_hex(31)

    try:
        Cryptographer.encrypt(blob, key)
        assert False

    except ValueError:
        assert True
示例#3
0
def test_encrypt_odd_length_data():
    blob = Blob(get_random_value_hex(64)[-1])
    key = get_random_value_hex(32)

    try:
        Cryptographer.encrypt(blob, key)
        assert False

    except ValueError:
        assert True
示例#4
0
def test_appointment_wrong_key(bitcoin_cli, create_txs):
    # This tests an appointment encrypted with a key that has not been derived from the same source as the locator.
    # Therefore the tower won't be able to decrypt the blob once the appointment is triggered.
    commitment_tx, penalty_tx = create_txs

    # The appointment data is built using a random 32-byte value.
    appointment_data = build_appointment_data(bitcoin_cli, get_random_value_hex(32), penalty_tx)

    # We can't use teos_cli.add_appointment here since it computes the locator internally, so let's do it manually.
    # We will encrypt the blob using the random value and derive the locator from the commitment tx.
    appointment_data["locator"] = compute_locator(bitcoin_cli.decoderawtransaction(commitment_tx).get("txid"))
    appointment_data["encrypted_blob"] = Cryptographer.encrypt(Blob(penalty_tx), get_random_value_hex(32))
    appointment = Appointment.from_dict(appointment_data)

    teos_pk, cli_sk, cli_pk_der = teos_cli.load_keys(
        cli_config.get("TEOS_PUBLIC_KEY"), cli_config.get("CLI_PRIVATE_KEY"), cli_config.get("CLI_PUBLIC_KEY")
    )
    hex_pk_der = binascii.hexlify(cli_pk_der)

    signature = Cryptographer.sign(appointment.serialize(), cli_sk)
    data = {"appointment": appointment.to_dict(), "signature": signature, "public_key": hex_pk_der.decode("utf-8")}

    # Send appointment to the server.
    response = teos_cli.post_appointment(data, teos_add_appointment_endpoint)
    response_json = teos_cli.process_post_appointment_response(response)

    # Check that the server has accepted the appointment
    signature = response_json.get("signature")
    assert signature is not None
    rpk = Cryptographer.recover_pk(appointment.serialize(), signature)
    assert Cryptographer.verify_rpk(teos_pk, rpk) is True
    assert response_json.get("locator") == appointment.locator

    # Trigger the appointment
    new_addr = bitcoin_cli.getnewaddress()
    broadcast_transaction_and_mine_block(bitcoin_cli, commitment_tx, new_addr)

    # The appointment should have been removed since the decryption failed.
    sleep(1)
    appointment_info = get_appointment_info(appointment.locator)

    assert appointment_info is not None
    assert len(appointment_info) == 1
    assert appointment_info[0].get("status") == "not_found"
示例#5
0
def generate_dummy_appointment_data(real_height=True, start_time_offset=5, end_time_offset=30):
    if real_height:
        current_height = bitcoin_cli(bitcoind_connect_params).getblockcount()

    else:
        current_height = 10

    dispute_tx = create_dummy_transaction()
    dispute_txid = dispute_tx.tx_id.hex()
    penalty_tx = create_dummy_transaction(dispute_txid)

    dummy_appointment_data = {
        "tx": penalty_tx.hex(),
        "tx_id": dispute_txid,
        "start_time": current_height + start_time_offset,
        "end_time": current_height + end_time_offset,
        "to_self_delay": 20,
    }

    # dummy keys for this test
    client_sk, client_pk = generate_keypair()
    client_pk_hex = client_pk.format().hex()

    locator = compute_locator(dispute_txid)
    blob = Blob(dummy_appointment_data.get("tx"))

    encrypted_blob = Cryptographer.encrypt(blob, dummy_appointment_data.get("tx_id"))

    appointment_data = {
        "locator": locator,
        "start_time": dummy_appointment_data.get("start_time"),
        "end_time": dummy_appointment_data.get("end_time"),
        "to_self_delay": dummy_appointment_data.get("to_self_delay"),
        "encrypted_blob": encrypted_blob,
    }

    signature = Cryptographer.sign(Appointment.from_dict(appointment_data).serialize(), client_sk)

    data = {"appointment": appointment_data, "signature": signature, "public_key": client_pk_hex}

    return data, dispute_tx.hex()
示例#6
0
def add_appointment(args, teos_url, config):
    """
    Manages the add_appointment command, from argument parsing, trough sending the appointment to the tower, until
    saving the appointment receipt.

    The life cycle of the function is as follows:
        - Load the add_appointment arguments
        - Check that the given commitment_txid is correct (proper format and not missing)
        - Check that the transaction is correct (not missing)
        - Create the appointment locator and encrypted blob from the commitment_txid and the penalty_tx
        - Load the client private key and sign the appointment
        - Send the appointment to the tower
        - Wait for the response
        - Check the tower's response and signature
        - Store the receipt (appointment + signature) on disk

    If any of the above-mentioned steps fails, the method returns false, otherwise it returns true.

    Args:
        args (:obj:`list`): a list of arguments to pass to ``parse_add_appointment_args``. Must contain a json encoded
            appointment, or the file option and the path to a file containing a json encoded appointment.
        teos_url (:obj:`str`): the teos base url.
        config (:obj:`dict`): a config dictionary following the format of :func:`create_config_dict <common.config_loader.ConfigLoader.create_config_dict>`.

    Returns:
        :obj:`bool`: True if the appointment is accepted by the tower and the receipt is properly stored, false if any
        error occurs during the process.
    """

    # Currently the base_url is the same as the add_appointment_endpoint
    add_appointment_endpoint = teos_url

    teos_pk, cli_sk, cli_pk_der = load_keys(config.get("TEOS_PUBLIC_KEY"),
                                            config.get("CLI_PRIVATE_KEY"),
                                            config.get("CLI_PUBLIC_KEY"))

    try:
        hex_pk_der = binascii.hexlify(cli_pk_der)

    except binascii.Error as e:
        logger.error("Could not successfully encode public key as hex",
                     error=str(e))
        return False

    if teos_pk is None:
        return False

    # Get appointment data from user.
    appointment_data = parse_add_appointment_args(args)

    if appointment_data is None:
        logger.error("The provided appointment JSON is empty")
        return False

    valid_txid = check_sha256_hex_format(appointment_data.get("tx_id"))

    if not valid_txid:
        logger.error("The provided txid is not valid")
        return False

    tx_id = appointment_data.get("tx_id")
    tx = appointment_data.get("tx")

    if None not in [tx_id, tx]:
        appointment_data["locator"] = compute_locator(tx_id)
        appointment_data["encrypted_blob"] = Cryptographer.encrypt(
            Blob(tx), tx_id)

    else:
        logger.error("Appointment data is missing some fields")
        return False

    appointment = Appointment.from_dict(appointment_data)
    signature = Cryptographer.sign(appointment.serialize(), cli_sk)

    if not (appointment and signature):
        return False

    data = {
        "appointment": appointment.to_dict(),
        "signature": signature,
        "public_key": hex_pk_der.decode("utf-8")
    }

    # Send appointment to the server.
    server_response = post_appointment(data, add_appointment_endpoint)
    if server_response is None:
        return False

    response_json = process_post_appointment_response(server_response)

    if response_json is None:
        return False

    signature = response_json.get("signature")
    # Check that the server signed the appointment as it should.
    if signature is None:
        logger.error(
            "The response does not contain the signature of the appointment")
        return False

    rpk = Cryptographer.recover_pk(appointment.serialize(), signature)
    if not Cryptographer.verify_rpk(teos_pk, rpk):
        logger.error("The returned appointment's signature is invalid")
        return False

    logger.info("Appointment accepted and signed by the Eye of Satoshi")

    # All good, store appointment and signature
    return save_appointment_receipt(appointment.to_dict(), signature, config)
示例#7
0
dummy_appointment_request = {
    "tx": get_random_value_hex(192),
    "tx_id": get_random_value_hex(32),
    "start_time": 1500,
    "end_time": 50000,
    "to_self_delay": 200,
}

# This is the format appointment turns into once it hits "add_appointment"
dummy_appointment_full = {
    "locator": compute_locator(dummy_appointment_request.get("tx_id")),
    "start_time": dummy_appointment_request.get("start_time"),
    "end_time": dummy_appointment_request.get("end_time"),
    "to_self_delay": dummy_appointment_request.get("to_self_delay"),
    "encrypted_blob": Cryptographer.encrypt(
        Blob(dummy_appointment_request.get("tx")), dummy_appointment_request.get("tx_id")
    ),
}

dummy_appointment = Appointment.from_dict(dummy_appointment_full)


def load_dummy_keys(*args):
    return dummy_pk, dummy_sk, dummy_pk.format(compressed=True)


def get_dummy_signature(*args):
    return Cryptographer.sign(dummy_appointment.serialize(), dummy_sk)


def get_bad_signature(*args):
示例#8
0
def test_encrypt():
    blob = Blob(data)

    assert Cryptographer.encrypt(blob, key) == encrypted_data