diff --git a/electrum/ecc.py b/electrum/ecc.py index b05d165c8..4dabe5629 100644 --- a/electrum/ecc.py +++ b/electrum/ecc.py @@ -26,49 +26,36 @@ import base64 import hashlib import functools -import copy from typing import Union, Tuple, Optional +from ctypes import ( + byref, c_byte, c_int, c_uint, c_char_p, c_size_t, c_void_p, create_string_buffer, + CFUNCTYPE, POINTER, cast +) from .util import bfh, bh2u, assert_bytes, to_bytes, InvalidPassword, profiler, randrange from .crypto import (sha256d, aes_encrypt_with_iv, aes_decrypt_with_iv, hmac_oneshot) from . import constants from .logging import get_logger - -# TODO -->>> -import ctypes -from ctypes import ( - byref, c_byte, c_int, c_uint, c_char_p, c_size_t, c_void_p, create_string_buffer, - CFUNCTYPE, POINTER, cast -) from .ecc_fast import _libsecp256k1, SECP256K1_EC_UNCOMPRESSED -# TODO <<<-- _logger = get_logger(__name__) -def generator(): - return GENERATOR - - -def point_at_infinity(): - return ECPubkey(None) - - def string_to_number(b: bytes) -> int: return int.from_bytes(b, byteorder='big', signed=False) -def sig_string_from_der_sig(der_sig: bytes, order=None) -> bytes: +def sig_string_from_der_sig(der_sig: bytes) -> bytes: r, s = get_r_and_s_from_der_sig(der_sig) return sig_string_from_r_and_s(r, s) -def der_sig_from_sig_string(sig_string: bytes, order=None) -> bytes: +def der_sig_from_sig_string(sig_string: bytes) -> bytes: r, s = get_r_and_s_from_sig_string(sig_string) return der_sig_from_r_and_s(r, s) -def der_sig_from_r_and_s(r: int, s: int, order=None) -> bytes: +def der_sig_from_r_and_s(r: int, s: int) -> bytes: sig_string = (int.to_bytes(r, length=32, byteorder="big") + int.to_bytes(s, length=32, byteorder="big")) sig = create_string_buffer(64) @@ -85,7 +72,7 @@ def der_sig_from_r_and_s(r: int, s: int, order=None) -> bytes: return bytes(der_sig)[:der_sig_size] -def get_r_and_s_from_der_sig(der_sig: bytes, order=None) -> Tuple[int, int]: +def get_r_and_s_from_der_sig(der_sig: bytes) -> Tuple[int, int]: assert isinstance(der_sig, bytes) sig = create_string_buffer(64) ret = _libsecp256k1.secp256k1_ecdsa_signature_parse_der(_libsecp256k1.ctx, sig, der_sig, len(der_sig)) @@ -99,7 +86,7 @@ def get_r_and_s_from_der_sig(der_sig: bytes, order=None) -> Tuple[int, int]: return r, s -def get_r_and_s_from_sig_string(sig_string: bytes, order=None) -> Tuple[int, int]: +def get_r_and_s_from_sig_string(sig_string: bytes) -> Tuple[int, int]: if not (isinstance(sig_string, bytes) and len(sig_string) == 64): raise Exception("sig_string must be bytes, and 64 bytes exactly") sig = create_string_buffer(64) @@ -114,7 +101,7 @@ def get_r_and_s_from_sig_string(sig_string: bytes, order=None) -> Tuple[int, int return r, s -def sig_string_from_r_and_s(r: int, s: int, order=None) -> bytes: +def sig_string_from_r_and_s(r: int, s: int) -> bytes: sig_string = (int.to_bytes(r, length=32, byteorder="big") + int.to_bytes(s, length=32, byteorder="big")) sig = create_string_buffer(64) @@ -250,12 +237,12 @@ class ECPubkey(object): other %= CURVE_ORDER if self.is_at_infinity() or other == 0: - return point_at_infinity() + return POINT_AT_INFINITY pubkey = self._to_libsecp256k1_pubkey_ptr() ret = _libsecp256k1.secp256k1_ec_pubkey_tweak_mul(_libsecp256k1.ctx, pubkey, other.to_bytes(32, byteorder="big")) if not ret: - return point_at_infinity() + return POINT_AT_INFINITY return ECPubkey._from_libsecp256k1_pubkey_ptr(pubkey) def __rmul__(self, other: int): @@ -276,7 +263,7 @@ class ECPubkey(object): array_of_pubkey_ptrs = (c_char_p * 2)(pubkey1, pubkey2) ret = _libsecp256k1.secp256k1_ec_pubkey_combine(_libsecp256k1.ctx, pubkey_sum, array_of_pubkey_ptrs, 2) if not ret: - return point_at_infinity() + return POINT_AT_INFINITY return ECPubkey._from_libsecp256k1_pubkey_ptr(pubkey_sum) def __eq__(self, other) -> bool: @@ -345,7 +332,7 @@ class ECPubkey(object): return CURVE_ORDER def is_at_infinity(self): - return self == point_at_infinity() + return self == POINT_AT_INFINITY @classmethod def is_pubkey_bytes(cls, b: bytes): @@ -359,6 +346,7 @@ class ECPubkey(object): GENERATOR = ECPubkey(bytes.fromhex('0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798' '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8')) CURVE_ORDER = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_BAAEDCE6_AF48A03B_BFD25E8C_D0364141 +POINT_AT_INFINITY = ECPubkey(None) def msg_magic(message: bytes) -> bytes: @@ -414,7 +402,7 @@ class ECPrivkey(ECPubkey): raise InvalidECPointException('Invalid secret scalar (not within curve order)') self.secret_scalar = secret - pubkey = generator() * secret + pubkey = GENERATOR * secret super().__init__(pubkey.get_public_key_bytes(compressed=False)) @classmethod @@ -450,7 +438,7 @@ class ECPrivkey(ECPubkey): def get_secret_bytes(self) -> bytes: return int.to_bytes(self.secret_scalar, length=32, byteorder='big', signed=False) - def sign(self, msg_hash: bytes, sigencode=None, sigdecode=None) -> bytes: + def sign(self, msg_hash: bytes, sigencode=None) -> bytes: if not (isinstance(msg_hash, bytes) and len(msg_hash) == 32): raise Exception("msg_hash to be signed must be bytes, and 32 bytes exactly") if sigencode is None: @@ -481,13 +469,11 @@ class ECPrivkey(ECPubkey): sig_string = sig_string_from_r_and_s(r, s) self.verify_message_hash(sig_string, msg_hash) - sig = sigencode(r, s, CURVE_ORDER) + sig = sigencode(r, s) return sig def sign_transaction(self, hashed_preimage: bytes) -> bytes: - return self.sign(hashed_preimage, - sigencode=der_sig_from_r_and_s, - sigdecode=get_r_and_s_from_der_sig) + return self.sign(hashed_preimage, sigencode=der_sig_from_r_and_s) def sign_message(self, message: bytes, is_compressed: bool, algo=lambda x: sha256d(msg_magic(x))) -> bytes: def bruteforce_recid(sig_string): @@ -503,9 +489,7 @@ class ECPrivkey(ECPubkey): message = to_bytes(message, 'utf8') msg_hash = algo(message) - sig_string = self.sign(msg_hash, - sigencode=sig_string_from_r_and_s, - sigdecode=get_r_and_s_from_sig_string) + sig_string = self.sign(msg_hash, sigencode=sig_string_from_r_and_s) sig65, recid = bruteforce_recid(sig_string) return sig65 diff --git a/electrum/ecc_fast.py b/electrum/ecc_fast.py index d886494e1..4f6149375 100644 --- a/electrum/ecc_fast.py +++ b/electrum/ecc_fast.py @@ -109,10 +109,6 @@ def load_library(): return None -def is_using_fast_ecc(): - return True # TODO rm - - try: _libsecp256k1 = load_library() except BaseException as e: diff --git a/electrum/keystore.py b/electrum/keystore.py index 05305dd63..0f940333b 100644 --- a/electrum/keystore.py +++ b/electrum/keystore.py @@ -615,7 +615,7 @@ class Old_KeyStore(MasterPublicKeyMixin, Deterministic_KeyStore): def get_pubkey_from_mpk(cls, mpk, for_change, n) -> bytes: z = cls.get_sequence(mpk, for_change, n) master_public_key = ecc.ECPubkey(bfh('04'+mpk)) - public_key = master_public_key + z*ecc.generator() + public_key = master_public_key + z*ecc.GENERATOR return public_key.get_public_key_bytes(compressed=False) @lru_cache(maxsize=None) diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index e04c31a55..1a0855878 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -1039,7 +1039,7 @@ class Peer(Logger): timestamp=now.to_bytes(4, byteorder="big"), ) sighash = sha256d(chan_upd[2 + 64:]) - sig = ecc.ECPrivkey(self.privkey).sign(sighash, sig_string_from_r_and_s, get_r_and_s_from_sig_string) + sig = ecc.ECPrivkey(self.privkey).sign(sighash, sig_string_from_r_and_s) message_type, payload = decode_msg(chan_upd) payload['signature'] = sig chan_upd = encode_msg(message_type, **payload) @@ -1071,8 +1071,8 @@ class Peer(Logger): ) to_hash = chan_ann[256+2:] h = sha256d(to_hash) - bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string) - node_signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s, get_r_and_s_from_sig_string) + bitcoin_signature = ecc.ECPrivkey(chan.config[LOCAL].multisig_key.privkey).sign(h, sig_string_from_r_and_s) + node_signature = ecc.ECPrivkey(self.privkey).sign(h, sig_string_from_r_and_s) self.send_message("announcement_signatures", channel_id=chan.channel_id, short_channel_id=chan.short_channel_id, diff --git a/electrum/lnutil.py b/electrum/lnutil.py index 3390655ed..4ef9a747b 100644 --- a/electrum/lnutil.py +++ b/electrum/lnutil.py @@ -254,7 +254,7 @@ def privkey_to_pubkey(priv: bytes) -> bytes: return ecc.ECPrivkey(priv[:32]).get_public_key_bytes() def derive_pubkey(basepoint: bytes, per_commitment_point: bytes) -> bytes: - p = ecc.ECPubkey(basepoint) + ecc.generator() * ecc.string_to_number(sha256(per_commitment_point + basepoint)) + p = ecc.ECPubkey(basepoint) + ecc.GENERATOR * ecc.string_to_number(sha256(per_commitment_point + basepoint)) return p.get_public_key_bytes() def derive_privkey(secret: int, per_commitment_point: bytes) -> int: diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 81eb6d3b4..29093cc78 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -40,7 +40,6 @@ from .lntransport import LNTransport, LNResponderTransport from .lnpeer import Peer, LN_P2P_NETWORK_TIMEOUT from .lnaddr import lnencode, LnAddr, lndecode from .ecc import der_sig_from_sig_string -from .ecc_fast import is_using_fast_ecc from .lnchannel import Channel from .lnchannel import channel_states, peer_states from . import lnutil @@ -303,7 +302,6 @@ class LNGossip(LNWorker): self.localfeatures |= LnLocalFeatures.GOSSIP_QUERIES_OPT self.localfeatures |= LnLocalFeatures.GOSSIP_QUERIES_REQ self.unknown_ids = set() - assert is_using_fast_ecc(), "verifying LN gossip msgs without libsecp256k1 is hopeless" def start_network(self, network: 'Network'): assert network diff --git a/electrum/tests/test_bitcoin.py b/electrum/tests/test_bitcoin.py index c4a90795d..8f7eba196 100644 --- a/electrum/tests/test_bitcoin.py +++ b/electrum/tests/test_bitcoin.py @@ -100,7 +100,7 @@ class Test_bitcoin(ElectrumTestCase): self._do_test_crypto(message) def _do_test_crypto(self, message): - G = ecc.generator() + G = ecc.GENERATOR _r = G.order() pvk = randrange(_r) @@ -128,11 +128,11 @@ class Test_bitcoin(ElectrumTestCase): @needs_test_with_all_ecc_implementations def test_ecc_sanity(self): - G = ecc.generator() + G = ecc.GENERATOR n = G.order() self.assertEqual(ecc.CURVE_ORDER, n) inf = n * G - self.assertEqual(ecc.point_at_infinity(), inf) + self.assertEqual(ecc.POINT_AT_INFINITY, inf) self.assertTrue(inf.is_at_infinity()) self.assertFalse(G.is_at_infinity()) self.assertEqual(11 * G, 7 * G + 4 * G) diff --git a/electrum/wallet.py b/electrum/wallet.py index 3b740867e..963722f70 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -70,7 +70,6 @@ from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL, from .util import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED, PR_INFLIGHT from .contacts import Contacts from .interface import NetworkException -from .ecc_fast import is_using_fast_ecc from .mnemonic import Mnemonic from .logging import get_logger from .lnworker import LNWallet @@ -270,9 +269,6 @@ class Abstract_Wallet(AddressSynchronizer, ABC): def init_lightning(self): if self.db.get('lightning_privkey2'): return - if not is_using_fast_ecc(): - raise Exception('libsecp256k1 library not available. ' - 'Verifying Lightning channels is too computationally expensive without libsecp256k1, aborting.') # TODO derive this deterministically from wallet.keystore at keystore generation time # probably along a hardened path ( lnd-equivalent would be m/1017'/coinType'/ ) seed = os.urandom(32) @@ -2085,9 +2081,6 @@ class Deterministic_Wallet(Abstract_Wallet): @profiler def try_detecting_internal_addresses_corruption(self): - if not is_using_fast_ecc(): - self.logger.info("internal address corruption test skipped due to missing libsecp256k1") - return addresses_all = self.get_addresses() # sample 1: first few addresses_sample1 = addresses_all[:10]