You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
9.5 KiB
218 lines
9.5 KiB
# taken (with minor modifications) from pycoin
|
|
# https://github.com/richardkiss/pycoin/blob/01b1787ed902df23f99a55deb00d8cd076a906fe/pycoin/ecdsa/native/secp256k1.py
|
|
|
|
import os
|
|
import sys
|
|
import traceback
|
|
import ctypes
|
|
from ctypes.util import find_library
|
|
from ctypes import (
|
|
byref, c_byte, c_int, c_uint, c_char_p, c_size_t, c_void_p, create_string_buffer, CFUNCTYPE, POINTER
|
|
)
|
|
|
|
import ecdsa
|
|
|
|
from .util import print_stderr, print_error
|
|
|
|
|
|
SECP256K1_FLAGS_TYPE_MASK = ((1 << 8) - 1)
|
|
SECP256K1_FLAGS_TYPE_CONTEXT = (1 << 0)
|
|
SECP256K1_FLAGS_TYPE_COMPRESSION = (1 << 1)
|
|
# /** The higher bits contain the actual data. Do not use directly. */
|
|
SECP256K1_FLAGS_BIT_CONTEXT_VERIFY = (1 << 8)
|
|
SECP256K1_FLAGS_BIT_CONTEXT_SIGN = (1 << 9)
|
|
SECP256K1_FLAGS_BIT_COMPRESSION = (1 << 8)
|
|
|
|
# /** Flags to pass to secp256k1_context_create. */
|
|
SECP256K1_CONTEXT_VERIFY = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
|
|
SECP256K1_CONTEXT_SIGN = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
|
|
SECP256K1_CONTEXT_NONE = (SECP256K1_FLAGS_TYPE_CONTEXT)
|
|
|
|
SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
|
|
SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION)
|
|
|
|
|
|
def load_library():
|
|
if sys.platform == 'darwin':
|
|
library_path = 'libsecp256k1.0.dylib'
|
|
elif sys.platform in ('windows', 'win32'):
|
|
library_path = 'libsecp256k1.dll'
|
|
elif 'ANDROID_DATA' in os.environ:
|
|
library_path = 'libsecp256k1.so'
|
|
else:
|
|
library_path = 'libsecp256k1.so.0'
|
|
|
|
secp256k1 = ctypes.cdll.LoadLibrary(library_path)
|
|
if not secp256k1:
|
|
print_stderr('[ecc] warning: libsecp256k1 library failed to load')
|
|
return None
|
|
|
|
try:
|
|
secp256k1.secp256k1_context_create.argtypes = [c_uint]
|
|
secp256k1.secp256k1_context_create.restype = c_void_p
|
|
|
|
secp256k1.secp256k1_context_randomize.argtypes = [c_void_p, c_char_p]
|
|
secp256k1.secp256k1_context_randomize.restype = c_int
|
|
|
|
secp256k1.secp256k1_ec_pubkey_create.argtypes = [c_void_p, c_void_p, c_char_p]
|
|
secp256k1.secp256k1_ec_pubkey_create.restype = c_int
|
|
|
|
secp256k1.secp256k1_ecdsa_sign.argtypes = [c_void_p, c_char_p, c_char_p, c_char_p, c_void_p, c_void_p]
|
|
secp256k1.secp256k1_ecdsa_sign.restype = c_int
|
|
|
|
secp256k1.secp256k1_ecdsa_verify.argtypes = [c_void_p, c_char_p, c_char_p, c_char_p]
|
|
secp256k1.secp256k1_ecdsa_verify.restype = c_int
|
|
|
|
secp256k1.secp256k1_ec_pubkey_parse.argtypes = [c_void_p, c_char_p, c_char_p, c_size_t]
|
|
secp256k1.secp256k1_ec_pubkey_parse.restype = c_int
|
|
|
|
secp256k1.secp256k1_ec_pubkey_serialize.argtypes = [c_void_p, c_char_p, c_void_p, c_char_p, c_uint]
|
|
secp256k1.secp256k1_ec_pubkey_serialize.restype = c_int
|
|
|
|
secp256k1.secp256k1_ecdsa_signature_parse_compact.argtypes = [c_void_p, c_char_p, c_char_p]
|
|
secp256k1.secp256k1_ecdsa_signature_parse_compact.restype = c_int
|
|
|
|
secp256k1.secp256k1_ecdsa_signature_serialize_compact.argtypes = [c_void_p, c_char_p, c_char_p]
|
|
secp256k1.secp256k1_ecdsa_signature_serialize_compact.restype = c_int
|
|
|
|
secp256k1.secp256k1_ec_pubkey_tweak_mul.argtypes = [c_void_p, c_char_p, c_char_p]
|
|
secp256k1.secp256k1_ec_pubkey_tweak_mul.restype = c_int
|
|
|
|
secp256k1.ctx = secp256k1.secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)
|
|
r = secp256k1.secp256k1_context_randomize(secp256k1.ctx, os.urandom(32))
|
|
if r:
|
|
return secp256k1
|
|
else:
|
|
print_stderr('[ecc] warning: secp256k1_context_randomize failed')
|
|
return None
|
|
except (OSError, AttributeError):
|
|
#traceback.print_exc(file=sys.stderr)
|
|
print_stderr('[ecc] warning: libsecp256k1 library was found and loaded but there was an error when using it')
|
|
return None
|
|
|
|
|
|
class _patched_functions:
|
|
prepared_to_patch = False
|
|
monkey_patching_active = False
|
|
|
|
|
|
def _prepare_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1():
|
|
if not _libsecp256k1:
|
|
return
|
|
|
|
# save original functions so that we can undo patching (needed for tests)
|
|
_patched_functions.orig_sign = staticmethod(ecdsa.ecdsa.Private_key.sign)
|
|
_patched_functions.orig_verify = staticmethod(ecdsa.ecdsa.Public_key.verifies)
|
|
_patched_functions.orig_mul = staticmethod(ecdsa.ellipticcurve.Point.__mul__)
|
|
|
|
curve_secp256k1 = ecdsa.ecdsa.curve_secp256k1
|
|
curve_order = ecdsa.curves.SECP256k1.order
|
|
point_at_infinity = ecdsa.ellipticcurve.INFINITY
|
|
|
|
def mul(self: ecdsa.ellipticcurve.Point, other: int):
|
|
if self.curve() != curve_secp256k1:
|
|
# this operation is not on the secp256k1 curve; use original implementation
|
|
return _patched_functions.orig_mul(self, other)
|
|
other %= curve_order
|
|
if self == point_at_infinity or other == 0:
|
|
return point_at_infinity
|
|
pubkey = create_string_buffer(64)
|
|
public_pair_bytes = b'\4' + self.x().to_bytes(32, byteorder="big") + self.y().to_bytes(32, byteorder="big")
|
|
r = _libsecp256k1.secp256k1_ec_pubkey_parse(
|
|
_libsecp256k1.ctx, pubkey, public_pair_bytes, len(public_pair_bytes))
|
|
if not r:
|
|
return False
|
|
r = _libsecp256k1.secp256k1_ec_pubkey_tweak_mul(_libsecp256k1.ctx, pubkey, other.to_bytes(32, byteorder="big"))
|
|
if not r:
|
|
return point_at_infinity
|
|
|
|
pubkey_serialized = create_string_buffer(65)
|
|
pubkey_size = c_size_t(65)
|
|
_libsecp256k1.secp256k1_ec_pubkey_serialize(
|
|
_libsecp256k1.ctx, pubkey_serialized, byref(pubkey_size), pubkey, SECP256K1_EC_UNCOMPRESSED)
|
|
x = int.from_bytes(pubkey_serialized[1:33], byteorder="big")
|
|
y = int.from_bytes(pubkey_serialized[33:], byteorder="big")
|
|
return ecdsa.ellipticcurve.Point(curve_secp256k1, x, y, curve_order)
|
|
|
|
def sign(self: ecdsa.ecdsa.Private_key, hash: int, random_k: int):
|
|
# note: random_k is ignored
|
|
if self.public_key.curve != curve_secp256k1:
|
|
# this operation is not on the secp256k1 curve; use original implementation
|
|
return _patched_functions.orig_sign(self, hash, random_k)
|
|
secret_exponent = self.secret_multiplier
|
|
nonce_function = None
|
|
sig = create_string_buffer(64)
|
|
sig_hash_bytes = hash.to_bytes(32, byteorder="big")
|
|
_libsecp256k1.secp256k1_ecdsa_sign(
|
|
_libsecp256k1.ctx, sig, sig_hash_bytes, secret_exponent.to_bytes(32, byteorder="big"), nonce_function, None)
|
|
compact_signature = create_string_buffer(64)
|
|
_libsecp256k1.secp256k1_ecdsa_signature_serialize_compact(_libsecp256k1.ctx, compact_signature, sig)
|
|
r = int.from_bytes(compact_signature[:32], byteorder="big")
|
|
s = int.from_bytes(compact_signature[32:], byteorder="big")
|
|
return ecdsa.ecdsa.Signature(r, s)
|
|
|
|
def verify(self: ecdsa.ecdsa.Public_key, hash: int, signature: ecdsa.ecdsa.Signature):
|
|
if self.curve != curve_secp256k1:
|
|
# this operation is not on the secp256k1 curve; use original implementation
|
|
return _patched_functions.orig_verify(self, hash, signature)
|
|
sig = create_string_buffer(64)
|
|
input64 = signature.r.to_bytes(32, byteorder="big") + signature.s.to_bytes(32, byteorder="big")
|
|
r = _libsecp256k1.secp256k1_ecdsa_signature_parse_compact(_libsecp256k1.ctx, sig, input64)
|
|
if not r:
|
|
return False
|
|
r = _libsecp256k1.secp256k1_ecdsa_signature_normalize(_libsecp256k1.ctx, sig, sig)
|
|
|
|
public_pair_bytes = b'\4' + self.point.x().to_bytes(32, byteorder="big") + self.point.y().to_bytes(32, byteorder="big")
|
|
pubkey = create_string_buffer(64)
|
|
r = _libsecp256k1.secp256k1_ec_pubkey_parse(
|
|
_libsecp256k1.ctx, pubkey, public_pair_bytes, len(public_pair_bytes))
|
|
if not r:
|
|
return False
|
|
|
|
return 1 == _libsecp256k1.secp256k1_ecdsa_verify(_libsecp256k1.ctx, sig, hash.to_bytes(32, byteorder="big"), pubkey)
|
|
|
|
# save new functions so that we can (re-)do patching
|
|
_patched_functions.fast_sign = sign
|
|
_patched_functions.fast_verify = verify
|
|
_patched_functions.fast_mul = mul
|
|
|
|
_patched_functions.prepared_to_patch = True
|
|
|
|
|
|
def do_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1():
|
|
if not _libsecp256k1:
|
|
print_error('[ecc] warning: libsecp256k1 library not available, falling back to python-ecdsa')
|
|
return
|
|
if not _patched_functions.prepared_to_patch:
|
|
raise Exception("can't patch python-ecdsa without preparations")
|
|
ecdsa.ecdsa.Private_key.sign = _patched_functions.fast_sign
|
|
ecdsa.ecdsa.Public_key.verifies = _patched_functions.fast_verify
|
|
ecdsa.ellipticcurve.Point.__mul__ = _patched_functions.fast_mul
|
|
# ecdsa.ellipticcurve.Point.__add__ = ... # TODO??
|
|
|
|
_patched_functions.monkey_patching_active = True
|
|
|
|
|
|
def undo_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1():
|
|
if not _libsecp256k1:
|
|
return
|
|
if not _patched_functions.prepared_to_patch:
|
|
raise Exception("can't patch python-ecdsa without preparations")
|
|
ecdsa.ecdsa.Private_key.sign = _patched_functions.orig_sign
|
|
ecdsa.ecdsa.Public_key.verifies = _patched_functions.orig_verify
|
|
ecdsa.ellipticcurve.Point.__mul__ = _patched_functions.orig_mul
|
|
|
|
_patched_functions.monkey_patching_active = False
|
|
|
|
|
|
def is_using_fast_ecc():
|
|
return _patched_functions.monkey_patching_active
|
|
|
|
|
|
try:
|
|
_libsecp256k1 = load_library()
|
|
except:
|
|
_libsecp256k1 = None
|
|
#traceback.print_exc(file=sys.stderr)
|
|
|
|
_prepare_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1()
|
|
|