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