Browse Source

ecc: small API clean-up

hard-fail-on-bad-server-string
SomberNight 5 years ago
parent
commit
0a5ad9fda4
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 56
      electrum/ecc.py
  2. 4
      electrum/ecc_fast.py
  3. 2
      electrum/keystore.py
  4. 6
      electrum/lnpeer.py
  5. 2
      electrum/lnutil.py
  6. 2
      electrum/lnworker.py
  7. 6
      electrum/tests/test_bitcoin.py
  8. 7
      electrum/wallet.py

56
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

4
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:

2
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)

6
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,

2
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:

2
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

6
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)

7
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]

Loading…
Cancel
Save