mirror of
https://github.com/aljazceru/python-teos.git
synced 2025-12-17 06:04:21 +01:00
Refactors Cryptographer to remove logs and avoid multi return types
Cryptographer now uses exceptions to report errors and does not use the log anymore
This commit is contained in:
@@ -6,25 +6,24 @@ import requests
|
||||
from sys import argv
|
||||
from uuid import uuid4
|
||||
from coincurve import PublicKey
|
||||
from requests import Timeout, ConnectionError
|
||||
from getopt import getopt, GetoptError
|
||||
from requests import Timeout, ConnectionError
|
||||
from requests.exceptions import MissingSchema, InvalidSchema, InvalidURL
|
||||
|
||||
from cli.exceptions import TowerResponseError
|
||||
from cli import DEFAULT_CONF, DATA_DIR, CONF_FILE_NAME, LOG_PREFIX
|
||||
from cli.exceptions import InvalidKey, InvalidParameter, TowerResponseError
|
||||
from cli.help import show_usage, help_add_appointment, help_get_appointment, help_register, help_get_all_appointments
|
||||
|
||||
import common.cryptographer
|
||||
from common import constants
|
||||
from common.logger import Logger
|
||||
from common.appointment import Appointment
|
||||
from common.config_loader import ConfigLoader
|
||||
from common.cryptographer import Cryptographer
|
||||
from common.tools import setup_logging, setup_data_folder
|
||||
from common.exceptions import InvalidKey, InvalidParameter, SignatureError
|
||||
from common.tools import is_256b_hex_str, is_locator, compute_locator, is_compressed_pk
|
||||
|
||||
logger = Logger(actor="Client", log_name_prefix=LOG_PREFIX)
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX)
|
||||
|
||||
|
||||
def register(compressed_pk, teos_url):
|
||||
@@ -107,10 +106,6 @@ def add_appointment(appointment_data, cli_sk, teos_pk, teos_url):
|
||||
appointment = Appointment.from_dict(appointment_data)
|
||||
signature = Cryptographer.sign(appointment.serialize(), cli_sk)
|
||||
|
||||
# FIXME: the cryptographer should return exception we can capture
|
||||
if not signature:
|
||||
raise ValueError("The provided appointment cannot be signed")
|
||||
|
||||
data = {"appointment": appointment.to_dict(), "signature": signature}
|
||||
|
||||
# Send appointment to the server.
|
||||
@@ -124,7 +119,7 @@ def add_appointment(appointment_data, cli_sk, teos_pk, teos_url):
|
||||
raise TowerResponseError("The response does not contain the signature of the appointment")
|
||||
|
||||
rpk = Cryptographer.recover_pk(appointment.serialize(), signature)
|
||||
if not Cryptographer.verify_rpk(teos_pk, rpk):
|
||||
if not teos_pk != Cryptographer.get_compressed_pk(rpk):
|
||||
raise TowerResponseError("The returned appointment's signature is invalid")
|
||||
|
||||
logger.info("Appointment accepted and signed by the Eye of Satoshi")
|
||||
@@ -467,7 +462,7 @@ def main(command, args, command_line_conf):
|
||||
|
||||
except (FileNotFoundError, IOError, ConnectionError, ValueError) as e:
|
||||
logger.error(str(e))
|
||||
except (InvalidKey, InvalidParameter, TowerResponseError) as e:
|
||||
except (InvalidKey, InvalidParameter, TowerResponseError, SignatureError) as e:
|
||||
logger.error(e.reason, **e.kwargs)
|
||||
except Exception as e:
|
||||
logger.error("Unknown error occurred", error=str(e))
|
||||
|
||||
@@ -7,13 +7,14 @@ from cryptography.exceptions import InvalidTag
|
||||
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
|
||||
|
||||
from common.tools import is_256b_hex_str
|
||||
from common.exceptions import InvalidKey, InvalidParameter, SignatureError, EncryptionError
|
||||
|
||||
LN_MESSAGE_PREFIX = b"Lightning Signed Message:"
|
||||
|
||||
|
||||
def sha256d(message):
|
||||
"""
|
||||
Computes the double sha256 of a given by message.
|
||||
Computes the double sha256 of a given message.
|
||||
|
||||
Args:
|
||||
message(:obj:`bytes`): the message to be used as input to the hash function.
|
||||
@@ -87,10 +88,6 @@ def sigrec_decode(sigrec):
|
||||
return rsig + rid
|
||||
|
||||
|
||||
# FIXME: Common has not log file, so it needs to log in the same log as the caller. This is a temporary fix.
|
||||
logger = None
|
||||
|
||||
|
||||
class Cryptographer:
|
||||
"""
|
||||
The :class:`Cryptographer` is in charge of all the cryptography in the tower.
|
||||
@@ -99,63 +96,49 @@ class Cryptographer:
|
||||
@staticmethod
|
||||
def check_data_key_format(data, secret):
|
||||
"""
|
||||
Checks that the data and secret that will be used to by ``encrypt`` / ``decrypt`` are properly
|
||||
formatted.
|
||||
Checks that the data and secret that will be used to by ``encrypt`` / ``decrypt`` are properly formatted.
|
||||
|
||||
Args:
|
||||
data(:obj:`str`): the data to be encrypted.
|
||||
secret(:obj:`str`): the secret used to derive the encryption key.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: Whether or not the ``key`` and ``data`` are properly formatted.
|
||||
|
||||
Raises:
|
||||
:obj:`ValueError`: if either the ``key`` or ``data`` is not properly formatted.
|
||||
:obj:`InvalidParameter`: if either the ``key`` and/or ``data`` are not properly formatted.
|
||||
"""
|
||||
|
||||
if len(data) % 2:
|
||||
error = "Incorrect (Odd-length) value"
|
||||
raise ValueError(error)
|
||||
raise InvalidParameter("Incorrect (Odd-length) data", data=data)
|
||||
|
||||
if not is_256b_hex_str(secret):
|
||||
error = "Secret must be a 32-byte hex value (64 hex chars)"
|
||||
raise ValueError(error)
|
||||
|
||||
return True
|
||||
raise InvalidParameter("Secret must be a 32-byte hex value (64 hex chars)", secret=secret)
|
||||
|
||||
@staticmethod
|
||||
def encrypt(blob, secret):
|
||||
def encrypt(message, secret):
|
||||
"""
|
||||
Encrypts a given :obj:`Blob <common.cli.blob.Blob>` data using ``CHACHA20POLY1305``.
|
||||
Encrypts a given message data using ``CHACHA20POLY1305``.
|
||||
|
||||
``SHA256(secret)`` is used as ``key``, and ``0 (12-byte)`` as ``iv``.
|
||||
|
||||
Args:
|
||||
blob (:obj:`Blob <common.cli.blob.Blob>`): a ``Blob`` object containing a raw penalty transaction.
|
||||
message (:obj:`str`): a message to be encrypted. Should be the hex-encoded commitment_tx.
|
||||
secret (:obj:`str`): a value to used to derive the encryption key. Should be the dispute txid.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The encrypted data (hex encoded).
|
||||
:obj:`str`: The encrypted data (hex-encoded).
|
||||
|
||||
Raises:
|
||||
:obj:`ValueError`: if either the ``secret`` or ``blob`` is not properly formatted.
|
||||
:obj:`InvalidParameter`: if either the ``key`` and/or ``data`` are not properly formatted.
|
||||
"""
|
||||
|
||||
Cryptographer.check_data_key_format(blob.data, secret)
|
||||
|
||||
# Transaction to be encrypted
|
||||
# FIXME: The blob data should contain more things that just the transaction. Leaving like this for now.
|
||||
tx = unhexlify(blob.data)
|
||||
Cryptographer.check_data_key_format(message, secret)
|
||||
|
||||
# sk is the H(txid) (32-byte) and nonce is set to 0 (12-byte)
|
||||
sk = sha256(unhexlify(secret)).digest()
|
||||
nonce = bytearray(12)
|
||||
|
||||
logger.debug("Encrypting blob", sk=hexlify(sk).decode(), nonce=hexlify(nonce).decode(), blob=blob.data)
|
||||
|
||||
# Encrypt the data
|
||||
cipher = ChaCha20Poly1305(sk)
|
||||
encrypted_blob = cipher.encrypt(nonce=nonce, data=tx, associated_data=None)
|
||||
encrypted_blob = cipher.encrypt(nonce=nonce, data=unhexlify(message), associated_data=None)
|
||||
encrypted_blob = hexlify(encrypted_blob).decode("utf8")
|
||||
|
||||
return encrypted_blob
|
||||
@@ -164,20 +147,20 @@ class Cryptographer:
|
||||
# ToDo: #20-test-tx-decrypting-edge-cases
|
||||
def decrypt(encrypted_blob, secret):
|
||||
"""
|
||||
Decrypts a given :obj:`EncryptedBlob <common.encrypted_blob.EncryptedBlob>` using ``CHACHA20POLY1305``.
|
||||
Decrypts a given encrypted_blob using ``CHACHA20POLY1305``.
|
||||
|
||||
``SHA256(secret)`` is used as ``key``, and ``0 (12-byte)`` as ``iv``.
|
||||
|
||||
Args:
|
||||
encrypted_blob(:obj:`EncryptedBlob <common.encrypted_blob.EncryptedBlob>`): an ``EncryptedBlob``
|
||||
potentially containing a penalty transaction.
|
||||
secret (:obj:`str`): a value to used to derive the decryption key. Should be the dispute txid.
|
||||
encrypted_blob(:obj:`str`): an encrypted blob of data potentially containing a penalty transaction.
|
||||
secret (:obj:`str`): a value used to derive the decryption key. Should be the dispute txid.
|
||||
|
||||
Returns:
|
||||
:obj:`str`: The decrypted data (hex encoded).
|
||||
:obj:`str`: The decrypted data (hex-encoded).
|
||||
|
||||
Raises:
|
||||
:obj:`ValueError`: if either the ``secret`` or ``encrypted_blob`` is not properly formatted.
|
||||
:obj:`InvalidParameter`: if either the ``key`` and/or ``data`` are not properly formatted.
|
||||
:obj:`EncryptionError`: if the data cannot be decrypted with the given key.
|
||||
"""
|
||||
|
||||
Cryptographer.check_data_key_format(encrypted_blob, secret)
|
||||
@@ -186,13 +169,6 @@ class Cryptographer:
|
||||
sk = sha256(unhexlify(secret)).digest()
|
||||
nonce = bytearray(12)
|
||||
|
||||
logger.info(
|
||||
"Decrypting blob",
|
||||
sk=hexlify(sk).decode(),
|
||||
nonce=hexlify(nonce).decode(),
|
||||
encrypted_blob=encrypted_blob.data,
|
||||
)
|
||||
|
||||
# Decrypt
|
||||
cipher = ChaCha20Poly1305(sk)
|
||||
data = unhexlify(encrypted_blob)
|
||||
@@ -202,8 +178,7 @@ class Cryptographer:
|
||||
blob = hexlify(blob).decode("utf8")
|
||||
|
||||
except InvalidTag:
|
||||
blob = None
|
||||
logger.error("Can't decrypt blob with the provided key")
|
||||
raise EncryptionError("Can't decrypt blob with the provided key", blob=encrypted_blob, key=secret)
|
||||
|
||||
return blob
|
||||
|
||||
@@ -216,12 +191,15 @@ class Cryptographer:
|
||||
file_path (:obj:`str`): the path to the key file to be loaded.
|
||||
|
||||
Returns:
|
||||
:obj:`bytes` or :obj:`None`: the key file data if the file can be found and read. ``None`` otherwise.
|
||||
:obj:`bytes`: the key file data if the file can be found and read.
|
||||
|
||||
Raises:
|
||||
:obj:`InvalidParameter`: if the file_path has wrong format or cannot be found.
|
||||
:obj:`InvalidKey`: if the key cannot be loaded from the file. It covers temporary I/O errors.
|
||||
"""
|
||||
|
||||
if not isinstance(file_path, str):
|
||||
logger.error("Key file path was expected, {} received".format(type(file_path)))
|
||||
return None
|
||||
raise InvalidParameter("Key file path was expected, {} received".format(type(file_path)))
|
||||
|
||||
try:
|
||||
with open(file_path, "rb") as key_file:
|
||||
@@ -229,12 +207,10 @@ class Cryptographer:
|
||||
return key
|
||||
|
||||
except FileNotFoundError:
|
||||
logger.error("Key file not found at {}. Please check your settings".format(file_path))
|
||||
return None
|
||||
raise InvalidParameter("Key file not found at {}. Please check your settings".format(file_path))
|
||||
|
||||
except IOError as e:
|
||||
logger.error("I/O error({}): {}".format(e.errno, e.strerror))
|
||||
return None
|
||||
raise InvalidKey("Key file cannot be loaded", exception=e)
|
||||
|
||||
@staticmethod
|
||||
def load_private_key_der(sk_der):
|
||||
@@ -245,50 +221,52 @@ class Cryptographer:
|
||||
sk_der(:obj:`str`): a private key encoded in ``DER`` format.
|
||||
|
||||
Returns:
|
||||
:obj:`PrivateKey` or :obj:`None`: A ``PrivateKey`` object. if the private key can be loaded. `None`
|
||||
otherwise.
|
||||
:obj:`PrivateKey`: A ``PrivateKey`` object if the private key can be loaded.
|
||||
|
||||
Raises:
|
||||
:obj:`InvalidKey`: if a ``PrivateKey`` cannot be loaded from the given data.
|
||||
"""
|
||||
|
||||
try:
|
||||
sk = PrivateKey.from_der(sk_der)
|
||||
return sk
|
||||
|
||||
except ValueError:
|
||||
logger.error("The provided data cannot be deserialized (wrong size or format)")
|
||||
raise InvalidKey("The provided key data cannot be deserialized (wrong size or format)")
|
||||
|
||||
except TypeError:
|
||||
logger.error("The provided data cannot be deserialized (wrong type)")
|
||||
|
||||
return None
|
||||
raise InvalidKey("The provided key data cannot be deserialized (wrong type)")
|
||||
|
||||
@staticmethod
|
||||
def sign(message, sk):
|
||||
"""
|
||||
Signs a given data using a given secret key using ECDSA over secp256k1.
|
||||
Signs a given message with a given secret key using ECDSA over secp256k1.
|
||||
|
||||
Args:
|
||||
message(:obj:`bytes`): the data to be signed.
|
||||
sk(:obj:`PrivateKey`): the ECDSA secret key used to signed the data.
|
||||
sk(:obj:`PrivateKey`): the ECDSA secret key to be used to sign the data.
|
||||
|
||||
Returns:
|
||||
:obj:`str` or :obj:`None`: The zbase32 signature of the given message is it can be signed. `None` otherwise.
|
||||
:obj:`str`: The zbase32 signature of the given message is it can be signed.
|
||||
|
||||
Raises:
|
||||
:obj:`InvalidParameter`: if the message and/or secret key have a wrong value.
|
||||
:obj:`SignatureError`: if there is an error during the signing process.
|
||||
"""
|
||||
|
||||
if not isinstance(message, bytes):
|
||||
logger.error("The message must be bytes. {} received".format(type(message)))
|
||||
return None
|
||||
raise InvalidParameter("Wrong value passed as message. Received {}, expected (bytes)".format(type(message)))
|
||||
|
||||
if not isinstance(sk, PrivateKey):
|
||||
logger.error("The value passed as sk is not a private key (PrivateKey)")
|
||||
return None
|
||||
raise InvalidParameter("Wrong value passed as sk. Received {}, expected (PrivateKey)".format(type(message)))
|
||||
|
||||
try:
|
||||
rsig_rid = sk.sign_recoverable(LN_MESSAGE_PREFIX + message, hasher=sha256d)
|
||||
sigrec = sigrec_encode(rsig_rid)
|
||||
zb32_sig = pyzbase32.encode_bytes(sigrec).decode()
|
||||
|
||||
except ValueError:
|
||||
logger.error("Couldn't sign the message")
|
||||
return None
|
||||
except ValueError as e:
|
||||
raise SignatureError("Couldn't sign the message. " + str(e))
|
||||
|
||||
return zb32_sig
|
||||
|
||||
@@ -302,16 +280,20 @@ class Cryptographer:
|
||||
zb32_sig(:obj:`str`): the zbase32 signature of the message.
|
||||
|
||||
Returns:
|
||||
:obj:`PublicKey` or :obj:`None`: The recovered public key if it can be recovered. `None` otherwise.
|
||||
:obj:`PublicKey`: The public key if it can be recovered.
|
||||
|
||||
Raises:
|
||||
:obj:`InvalidParameter`: if the message and/or signature have a wrong value.
|
||||
:obj:`SignatureError`: if a public key cannot be recovered from the given signature.
|
||||
"""
|
||||
|
||||
if not isinstance(message, bytes):
|
||||
logger.error("The message must be bytes. {} received".format(type(message)))
|
||||
return None
|
||||
raise InvalidParameter("Wrong value passed as message. Received {}, expected (bytes)".format(type(message)))
|
||||
|
||||
if not isinstance(zb32_sig, str):
|
||||
logger.error("The zbase32_sig must be str. {} received".format(type(zb32_sig)))
|
||||
return None
|
||||
raise InvalidParameter(
|
||||
"Wrong value passed as zbase32_sig. Received {}, expected (str)".format(type(zb32_sig))
|
||||
)
|
||||
|
||||
sigrec = pyzbase32.decode_bytes(zb32_sig)
|
||||
|
||||
@@ -323,31 +305,13 @@ class Cryptographer:
|
||||
except ValueError as e:
|
||||
# Several errors fit here: Signature length != 65, wrong recover id and failed to parse signature.
|
||||
# All of them return raise ValueError.
|
||||
logger.error(str(e))
|
||||
return None
|
||||
raise SignatureError("Cannot recover a public key from the given signature. " + str(e))
|
||||
|
||||
except Exception as e:
|
||||
if "failed to recover ECDSA public key" in str(e):
|
||||
logger.error("Cannot recover public key from signature")
|
||||
raise SignatureError("Cannot recover a public key from the given signature")
|
||||
else:
|
||||
logger.error("Unknown exception", error=str(e))
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def verify_rpk(pk, rpk):
|
||||
"""
|
||||
Verifies that that a recovered public key matches a given one.
|
||||
|
||||
Args:
|
||||
pk(:obj:`PublicKey`): a given public key (provided by the user).
|
||||
rpk(:obj:`PublicKey`): a public key recovered via ``recover_pk``.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: True if the public keys match, False otherwise.
|
||||
"""
|
||||
|
||||
return pk.point() == rpk.point()
|
||||
raise SignatureError("Unknown exception. " + str(e))
|
||||
|
||||
@staticmethod
|
||||
def get_compressed_pk(pk):
|
||||
@@ -358,18 +322,19 @@ class Cryptographer:
|
||||
pk(:obj:`PublicKey`): a given public key.
|
||||
|
||||
Returns:
|
||||
:obj:`str` or :obj:`None`: A compressed, hex-encoded, public key (33-byte long) if it can be compressed.
|
||||
`None` oterwise.
|
||||
:obj:`str`: A compressed, hex-encoded, public key (33-byte long) if it can be compressed.
|
||||
|
||||
Raises:
|
||||
:obj:`InvalidParameter`: if the value passed as public key is not a PublicKey object.
|
||||
:obj:`InvalidKey`: if the public key has not been properly created.
|
||||
"""
|
||||
|
||||
if not isinstance(pk, PublicKey):
|
||||
logger.error("The received data is not a PublicKey object")
|
||||
return None
|
||||
raise InvalidParameter("Wrong value passed as pk. Received {}, expected (PublicKey)".format(type(pk)))
|
||||
|
||||
try:
|
||||
compressed_pk = pk.format(compressed=True)
|
||||
return hexlify(compressed_pk).decode("utf-8")
|
||||
|
||||
except TypeError as e:
|
||||
logger.error("PublicKey has invalid initializer", error=str(e))
|
||||
return None
|
||||
raise InvalidKey("PublicKey has invalid initializer", error=str(e))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from common.tools import is_compressed_pk
|
||||
from common.cryptographer import Cryptographer
|
||||
from common.exceptions import InvalidParameter, InvalidKey, SignatureError
|
||||
|
||||
|
||||
class NotEnoughSlots(ValueError):
|
||||
@@ -71,7 +72,7 @@ class Gatekeeper:
|
||||
:obj:`IdentificationFailure`: if the user cannot be identified.
|
||||
"""
|
||||
|
||||
if isinstance(message, bytes) and isinstance(signature, str):
|
||||
try:
|
||||
rpk = Cryptographer.recover_pk(message, signature)
|
||||
compressed_pk = Cryptographer.get_compressed_pk(rpk)
|
||||
|
||||
@@ -80,7 +81,7 @@ class Gatekeeper:
|
||||
else:
|
||||
raise IdentificationFailure("User not found.")
|
||||
|
||||
else:
|
||||
except (InvalidParameter, InvalidKey, SignatureError):
|
||||
raise IdentificationFailure("Wrong message or signature.")
|
||||
|
||||
def fill_slots(self, user_pk, n):
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import re
|
||||
|
||||
import common.cryptographer
|
||||
from common.logger import Logger
|
||||
from common.tools import is_locator
|
||||
from common.constants import LOCATOR_LEN_HEX
|
||||
@@ -9,7 +8,6 @@ from common.appointment import Appointment
|
||||
from teos import errors, LOG_PREFIX
|
||||
|
||||
logger = Logger(actor="Inspector", log_name_prefix=LOG_PREFIX)
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX)
|
||||
|
||||
# FIXME: The inspector logs the wrong messages sent form the users. A possible attack surface would be to send a really
|
||||
# long field that, even if not accepted by TEOS, would be stored in the logs. This is a possible DoS surface
|
||||
|
||||
@@ -3,7 +3,6 @@ from sys import argv, exit
|
||||
from getopt import getopt, GetoptError
|
||||
from signal import signal, SIGINT, SIGQUIT, SIGTERM
|
||||
|
||||
import common.cryptographer
|
||||
from common.logger import Logger
|
||||
from common.config_loader import ConfigLoader
|
||||
from common.cryptographer import Cryptographer
|
||||
@@ -25,7 +24,6 @@ from teos.tools import can_connect_to_bitcoind, in_correct_network
|
||||
from teos import LOG_PREFIX, DATA_DIR, DEFAULT_CONF, CONF_FILE_NAME
|
||||
|
||||
logger = Logger(actor="Daemon", log_name_prefix=LOG_PREFIX)
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX)
|
||||
|
||||
|
||||
def handle_signals(signal_received, frame):
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
from queue import Queue
|
||||
from threading import Thread
|
||||
|
||||
import common.cryptographer
|
||||
from common.logger import Logger
|
||||
from common.tools import compute_locator
|
||||
from common.appointment import Appointment
|
||||
from common.exceptions import EncryptionError
|
||||
from common.cryptographer import Cryptographer, hash_160
|
||||
from common.exceptions import InvalidParameter, SignatureError
|
||||
|
||||
from teos import LOG_PREFIX
|
||||
from teos.cleaner import Cleaner
|
||||
|
||||
logger = Logger(actor="Watcher", log_name_prefix=LOG_PREFIX)
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX)
|
||||
|
||||
|
||||
class Watcher:
|
||||
@@ -57,7 +57,7 @@ class Watcher:
|
||||
last_known_block (:obj:`str`): the last block known by the ``Watcher``.
|
||||
|
||||
Raises:
|
||||
ValueError: if `teos_sk_file` is not found.
|
||||
:obj:`InvalidKey <common.exceptions.InvalidKey>`: if teos sk cannot be loaded.
|
||||
|
||||
"""
|
||||
|
||||
@@ -147,7 +147,14 @@ class Watcher:
|
||||
self.db_manager.create_append_locator_map(appointment.locator, uuid)
|
||||
|
||||
appointment_added = True
|
||||
signature = Cryptographer.sign(appointment.serialize(), self.signing_key)
|
||||
|
||||
try:
|
||||
signature = Cryptographer.sign(appointment.serialize(), self.signing_key)
|
||||
|
||||
except (InvalidParameter, SignatureError):
|
||||
# This should never happen since data is sanitized, just in case to avoid a crash
|
||||
logger.error("Data couldn't be signed", appointment=appointment.to_dict())
|
||||
signature = None
|
||||
|
||||
logger.info("New appointment accepted", locator=appointment.locator)
|
||||
|
||||
@@ -297,7 +304,7 @@ class Watcher:
|
||||
try:
|
||||
penalty_rawtx = Cryptographer.decrypt(appointment.encrypted_blob, dispute_txid)
|
||||
|
||||
except ValueError:
|
||||
except EncryptionError:
|
||||
penalty_rawtx = None
|
||||
|
||||
penalty_tx = self.block_processor.decode_raw_transaction(penalty_rawtx)
|
||||
|
||||
@@ -7,8 +7,6 @@ from binascii import hexlify
|
||||
from coincurve import PrivateKey
|
||||
from requests.exceptions import ConnectionError, Timeout
|
||||
|
||||
import common.cryptographer
|
||||
from common.logger import Logger
|
||||
from common.tools import compute_locator
|
||||
from common.appointment import Appointment
|
||||
from common.cryptographer import Cryptographer
|
||||
@@ -18,8 +16,6 @@ from cli.exceptions import InvalidParameter, InvalidKey, TowerResponseError
|
||||
|
||||
from test.cli.unit.conftest import get_random_value_hex, get_config
|
||||
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=teos_cli.LOG_PREFIX)
|
||||
|
||||
config = get_config()
|
||||
|
||||
# dummy keys for the tests
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import os
|
||||
from binascii import unhexlify
|
||||
from coincurve import PrivateKey, PublicKey
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
|
||||
from coincurve import PrivateKey, PublicKey
|
||||
import common.cryptographer
|
||||
from common.logger import Logger
|
||||
from common.cryptographer import Cryptographer
|
||||
from test.common.unit.conftest import get_random_value_hex
|
||||
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix="")
|
||||
|
||||
data = "6097cdf52309b1b2124efeed36bd34f46dc1c25ad23ac86f28380f746254f777"
|
||||
key = "b2e984a570f6f49bc38ace178e09147b0aa296cbb7c92eb01412f7e2d07b5659"
|
||||
encrypted_data = "8f31028097a8bf12a92e088caab5cf3fcddf0d35ed2b72c24b12269373efcdea04f9d2a820adafe830c20ff132d89810"
|
||||
@@ -185,7 +180,7 @@ def test_sign_ground_truth():
|
||||
sig = Cryptographer.sign(message, sk)
|
||||
rpk = Cryptographer.recover_pk(message, sig)
|
||||
|
||||
assert Cryptographer.verify_rpk(PublicKey(unhexlify(c_lightning_rpk)), rpk)
|
||||
assert c_lightning_rpk == Cryptographer.get_compressed_pk(rpk)
|
||||
|
||||
|
||||
def test_sign_wrong_sk():
|
||||
@@ -221,7 +216,7 @@ def test_recover_pk_ground_truth():
|
||||
|
||||
rpk = Cryptographer.recover_pk(message, zsig)
|
||||
|
||||
assert Cryptographer.verify_rpk(PublicKey(unhexlify(org_pk)), rpk)
|
||||
assert org_pk == Cryptographer.get_compressed_pk(rpk)
|
||||
|
||||
|
||||
def test_recover_pk_wrong_inputs():
|
||||
@@ -241,27 +236,6 @@ def test_recover_pk_wrong_inputs():
|
||||
assert Cryptographer.recover_pk(message, bytes(104)) is None
|
||||
|
||||
|
||||
def test_verify_pk():
|
||||
sk, _ = generate_keypair()
|
||||
message = b"Test message"
|
||||
|
||||
zbase32_sig = Cryptographer.sign(message, sk)
|
||||
rpk = Cryptographer.recover_pk(message, zbase32_sig)
|
||||
|
||||
assert Cryptographer.verify_rpk(sk.public_key, rpk)
|
||||
|
||||
|
||||
def test_verify_pk_wrong():
|
||||
sk, _ = generate_keypair()
|
||||
sk2, _ = generate_keypair()
|
||||
message = b"Test message"
|
||||
|
||||
zbase32_sig = Cryptographer.sign(message, sk)
|
||||
rpk = Cryptographer.recover_pk(message, zbase32_sig)
|
||||
|
||||
assert not Cryptographer.verify_rpk(sk2.public_key, rpk)
|
||||
|
||||
|
||||
def test_get_compressed_pk():
|
||||
sk, pk = generate_keypair()
|
||||
compressed_pk = Cryptographer.get_compressed_pk(pk)
|
||||
|
||||
@@ -8,8 +8,6 @@ from coincurve import PrivateKey
|
||||
from cli.exceptions import TowerResponseError
|
||||
from cli import teos_cli, DATA_DIR, DEFAULT_CONF, CONF_FILE_NAME
|
||||
|
||||
import common.cryptographer
|
||||
from common.logger import Logger
|
||||
from common.tools import compute_locator
|
||||
from common.appointment import Appointment
|
||||
from common.cryptographer import Cryptographer
|
||||
@@ -25,7 +23,6 @@ from test.teos.e2e.conftest import (
|
||||
)
|
||||
|
||||
cli_config = get_config(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF)
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix="")
|
||||
|
||||
teos_base_endpoint = "http://{}:{}".format(cli_config.get("API_CONNECT"), cli_config.get("API_PORT"))
|
||||
teos_add_appointment_endpoint = "{}/add_appointment".format(teos_base_endpoint)
|
||||
@@ -257,7 +254,7 @@ def test_appointment_wrong_decryption_key(bitcoin_cli):
|
||||
# Check that the server has accepted the appointment
|
||||
signature = response_json.get("signature")
|
||||
rpk = Cryptographer.recover_pk(appointment.serialize(), signature)
|
||||
assert Cryptographer.verify_rpk(teos_pk, rpk) is True
|
||||
assert teos_pk == Cryptographer.get_compressed_pk(rpk)
|
||||
assert response_json.get("locator") == appointment.locator
|
||||
|
||||
# Trigger the appointment
|
||||
|
||||
@@ -10,25 +10,21 @@ from bitcoind_mock.bitcoind import BitcoindMock
|
||||
from bitcoind_mock.conf import BTC_RPC_HOST, BTC_RPC_PORT
|
||||
from bitcoind_mock.transaction import create_dummy_transaction
|
||||
|
||||
from teos import DEFAULT_CONF
|
||||
from teos.carrier import Carrier
|
||||
from teos.tools import bitcoin_cli
|
||||
from teos.users_dbm import UsersDBM
|
||||
from teos.gatekeeper import Gatekeeper
|
||||
from teos import LOG_PREFIX, DEFAULT_CONF
|
||||
from teos.responder import TransactionTracker
|
||||
from teos.block_processor import BlockProcessor
|
||||
from teos.appointments_dbm import AppointmentsDBM
|
||||
|
||||
import common.cryptographer
|
||||
from common.logger import Logger
|
||||
from common.tools import compute_locator
|
||||
from common.appointment import Appointment
|
||||
from common.constants import LOCATOR_LEN_HEX
|
||||
from common.config_loader import ConfigLoader
|
||||
from common.cryptographer import Cryptographer
|
||||
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX)
|
||||
|
||||
# Set params to connect to regtest for testing
|
||||
DEFAULT_CONF["BTC_RPC_PORT"]["value"] = 18443
|
||||
DEFAULT_CONF["BTC_NETWORK"]["value"] = "regtest"
|
||||
|
||||
@@ -2,19 +2,14 @@ import pytest
|
||||
from binascii import unhexlify
|
||||
|
||||
import teos.errors as errors
|
||||
from teos import LOG_PREFIX
|
||||
from teos.block_processor import BlockProcessor
|
||||
from teos.inspector import Inspector, InspectionFailed
|
||||
|
||||
import common.cryptographer
|
||||
from common.logger import Logger
|
||||
from common.appointment import Appointment
|
||||
from common.constants import LOCATOR_LEN_BYTES, LOCATOR_LEN_HEX
|
||||
|
||||
from test.teos.unit.conftest import get_random_value_hex, bitcoind_connect_params, get_config
|
||||
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX)
|
||||
|
||||
NO_HEX_STRINGS = [
|
||||
"R" * LOCATOR_LEN_HEX,
|
||||
get_random_value_hex(LOCATOR_LEN_BYTES - 1) + "PP",
|
||||
|
||||
@@ -13,8 +13,6 @@ from teos.chain_monitor import ChainMonitor
|
||||
from teos.appointments_dbm import AppointmentsDBM
|
||||
from teos.block_processor import BlockProcessor
|
||||
|
||||
import common.cryptographer
|
||||
from common.logger import Logger
|
||||
from common.tools import compute_locator
|
||||
from common.cryptographer import Cryptographer
|
||||
|
||||
@@ -28,9 +26,6 @@ from test.teos.unit.conftest import (
|
||||
bitcoind_connect_params,
|
||||
)
|
||||
|
||||
common.cryptographer.logger = Logger(actor="Cryptographer", log_name_prefix=LOG_PREFIX)
|
||||
|
||||
|
||||
APPOINTMENTS = 5
|
||||
START_TIME_OFFSET = 1
|
||||
END_TIME_OFFSET = 1
|
||||
@@ -134,16 +129,16 @@ def test_add_appointment(watcher):
|
||||
added_appointment, sig = watcher.add_appointment(appointment, user_pk)
|
||||
|
||||
assert added_appointment is True
|
||||
assert Cryptographer.verify_rpk(
|
||||
watcher.signing_key.public_key, Cryptographer.recover_pk(appointment.serialize(), sig)
|
||||
assert Cryptographer.get_compressed_pk(watcher.signing_key.public_key) == Cryptographer.get_compressed_pk(
|
||||
Cryptographer.recover_pk(appointment.serialize(), sig)
|
||||
)
|
||||
|
||||
# Check that we can also add an already added appointment (same locator)
|
||||
added_appointment, sig = watcher.add_appointment(appointment, user_pk)
|
||||
|
||||
assert added_appointment is True
|
||||
assert Cryptographer.verify_rpk(
|
||||
watcher.signing_key.public_key, Cryptographer.recover_pk(appointment.serialize(), sig)
|
||||
assert Cryptographer.get_compressed_pk(watcher.signing_key.public_key) == Cryptographer.get_compressed_pk(
|
||||
Cryptographer.recover_pk(appointment.serialize(), sig)
|
||||
)
|
||||
|
||||
# If two appointments with the same locator from the same user are added, they are overwritten, but if they come
|
||||
@@ -153,8 +148,8 @@ def test_add_appointment(watcher):
|
||||
different_user_pk = get_random_value_hex(33)
|
||||
added_appointment, sig = watcher.add_appointment(appointment, different_user_pk)
|
||||
assert added_appointment is True
|
||||
assert Cryptographer.verify_rpk(
|
||||
watcher.signing_key.public_key, Cryptographer.recover_pk(appointment.serialize(), sig)
|
||||
assert Cryptographer.get_compressed_pk(watcher.signing_key.public_key) == Cryptographer.get_compressed_pk(
|
||||
Cryptographer.recover_pk(appointment.serialize(), sig)
|
||||
)
|
||||
assert len(watcher.locator_uuid_map[appointment.locator]) == 2
|
||||
|
||||
@@ -172,8 +167,8 @@ def test_add_too_many_appointments(watcher):
|
||||
added_appointment, sig = watcher.add_appointment(appointment, user_pk)
|
||||
|
||||
assert added_appointment is True
|
||||
assert Cryptographer.verify_rpk(
|
||||
watcher.signing_key.public_key, Cryptographer.recover_pk(appointment.serialize(), sig)
|
||||
assert Cryptographer.get_compressed_pk(watcher.signing_key.public_key) == Cryptographer.get_compressed_pk(
|
||||
Cryptographer.recover_pk(appointment.serialize(), sig)
|
||||
)
|
||||
|
||||
appointment, dispute_tx = generate_dummy_appointment(
|
||||
|
||||
Reference in New Issue
Block a user