Skip to content

Commit 8bd9269

Browse files
committed
Merge remote-tracking branch 'sanket/update_license' into HEAD
This merge contains code originally contributed by me, Dmitry Petukhov, into python-bitcointx (https://github.com/Simplexum/python-bitcointx) and python-elementstx (https://github.com/Simplexum/python-elementstx) projects. The code was originally submitted to these projects under GNU Lesser General Public License. As the author of the code in question, I thereby give my permission to use, modify and distribute this code under MIT software license.
2 parents 88067dd + 6cca652 commit 8bd9269

File tree

2 files changed

+454
-0
lines changed

2 files changed

+454
-0
lines changed
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
# Copyright (C) 2018 The python-bitcointx developers
2+
#
3+
# This file is from python-bitcointx.
4+
#
5+
# This file was originally under the GNU LESSER GENERAL PUBLIC LICENSE
6+
# from python-bitcointx.(https://github.com/Simplexum/python-bitcointx/blob/master/LICENSE)
7+
# and was later allowed by the permission of the authors to relicensed
8+
# for the elements project under the MIT LICENSE.
9+
# Author: Dmitry Petukhov <[email protected]>
10+
#
11+
# Copyright (c) 2014-2018 The Bitcoin Core developers
12+
# Distributed under the MIT software license, see the accompanying
13+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
14+
#
15+
16+
# pylama:ignore=E501,E221
17+
18+
# NOTE: for simplicity, when we need to pass an array of structs to secp256k1
19+
# function, we will build an array of bytes out of elements, and then pass
20+
# this array. we are dealing with 32 or 64-byte aligned data,
21+
# so this should be safe. You can use build_aligned_data_array() for this.
22+
23+
# NOTE: special care should be taken with functions that may write to parts
24+
# of their arguments, like secp256k1_pedersen_blind_generator_blind_sum,
25+
# which will overwrite the element pointed to by blinding_factor.
26+
# python's byte instance is supposed to be immutable, and for mutable byte
27+
# buffers you should use ctypes.create_string_buffer().
28+
29+
import os
30+
import ctypes
31+
import ctypes.util
32+
from types import FunctionType
33+
from typing import Dict, Union, Any, Optional, cast
34+
35+
import bitcointx.util
36+
37+
38+
PUBLIC_KEY_SIZE = 65
39+
COMPRESSED_PUBLIC_KEY_SIZE = 33
40+
SIGNATURE_SIZE = 72
41+
COMPACT_SIGNATURE_SIZE = 65
42+
43+
44+
class Libsecp256k1Exception(EnvironmentError):
45+
pass
46+
47+
48+
SECP256K1_FLAGS_TYPE_CONTEXT = (1 << 0)
49+
SECP256K1_FLAGS_BIT_CONTEXT_SIGN = (1 << 9)
50+
SECP256K1_FLAGS_BIT_CONTEXT_VERIFY = (1 << 8)
51+
52+
SECP256K1_CONTEXT_SIGN = \
53+
(SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
54+
SECP256K1_CONTEXT_VERIFY = \
55+
(SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
56+
57+
SECP256K1_FLAGS_TYPE_COMPRESSION = (1 << 1)
58+
SECP256K1_FLAGS_BIT_COMPRESSION = (1 << 8)
59+
60+
SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
61+
SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION)
62+
63+
64+
class Secp256k1LastErrorContextVar(bitcointx.util.ContextVarsCompat):
65+
last_error: Optional[Dict[str, Union[int, str]]]
66+
67+
68+
_secp256k1_error_storage = Secp256k1LastErrorContextVar(last_error=None)
69+
70+
_ctypes_functype = getattr(ctypes, 'WINFUNCTYPE', getattr(ctypes, 'CFUNCTYPE'))
71+
72+
73+
@_ctypes_functype(ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p)
74+
def _secp256k1_illegal_callback_fn(error_str, _data): # type: ignore
75+
_secp256k1_error_storage.last_error = {'code': -2, 'type': 'illegal_argument', 'message': str(error_str)}
76+
77+
78+
def secp256k1_get_last_error() -> Dict[str, Union[int, str]]:
79+
return cast(Dict[str, Union[int, str]],
80+
getattr(_secp256k1_error_storage, 'last_error', None))
81+
82+
83+
def _check_ressecp256k1_void_p(val: int, _func: FunctionType,
84+
_args: Any) -> ctypes.c_void_p:
85+
if val == 0:
86+
err = getattr(_secp256k1_error_storage, 'last_error', None)
87+
if err is None:
88+
raise Libsecp256k1Exception(
89+
-3, ('error handling callback function was not called, '
90+
'error is not known'))
91+
raise Libsecp256k1Exception(err['code'], err['message'])
92+
return ctypes.c_void_p(val)
93+
94+
95+
secp256k1_has_pubkey_recovery = False
96+
secp256k1_has_privkey_negate = False
97+
secp256k1_has_pubkey_negate = False
98+
secp256k1_has_ecdh = False
99+
100+
101+
def _add_function_definitions(_secp256k1: ctypes.CDLL) -> None:
102+
global secp256k1_has_pubkey_recovery
103+
global secp256k1_has_privkey_negate
104+
global secp256k1_has_pubkey_negate
105+
global secp256k1_has_ecdh
106+
107+
if getattr(_secp256k1, 'secp256k1_ecdsa_sign_recoverable', None):
108+
secp256k1_has_pubkey_recovery = True
109+
_secp256k1.secp256k1_ecdsa_sign_recoverable.restype = ctypes.c_int
110+
_secp256k1.secp256k1_ecdsa_sign_recoverable.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_void_p]
111+
112+
_secp256k1.secp256k1_ecdsa_recoverable_signature_serialize_compact.restype = ctypes.c_int
113+
_secp256k1.secp256k1_ecdsa_recoverable_signature_serialize_compact.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_int), ctypes.c_char_p]
114+
115+
_secp256k1.secp256k1_ecdsa_recover.restype = ctypes.c_int
116+
_secp256k1.secp256k1_ecdsa_recover.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
117+
118+
_secp256k1.secp256k1_ecdsa_recoverable_signature_parse_compact.restype = ctypes.c_int
119+
_secp256k1.secp256k1_ecdsa_recoverable_signature_parse_compact.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int]
120+
121+
_secp256k1.secp256k1_context_create.restype = ctypes.c_void_p
122+
_secp256k1.secp256k1_context_create.errcheck = _check_ressecp256k1_void_p # type: ignore
123+
_secp256k1.secp256k1_context_create.argtypes = [ctypes.c_uint]
124+
125+
_secp256k1.secp256k1_context_randomize.restype = ctypes.c_int
126+
_secp256k1.secp256k1_context_randomize.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
127+
128+
_secp256k1.secp256k1_context_set_illegal_callback.restype = None
129+
_secp256k1.secp256k1_context_set_illegal_callback.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
130+
131+
_secp256k1.secp256k1_ecdsa_sign.restype = ctypes.c_int
132+
_secp256k1.secp256k1_ecdsa_sign.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_void_p]
133+
134+
_secp256k1.secp256k1_ecdsa_signature_serialize_der.restype = ctypes.c_int
135+
_secp256k1.secp256k1_ecdsa_signature_serialize_der.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_size_t), ctypes.c_char_p]
136+
137+
_secp256k1.secp256k1_ec_pubkey_create.restype = ctypes.c_int
138+
_secp256k1.secp256k1_ec_pubkey_create.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
139+
140+
_secp256k1.secp256k1_ec_seckey_verify.restype = ctypes.c_int
141+
_secp256k1.secp256k1_ec_seckey_verify.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
142+
143+
_secp256k1.secp256k1_ecdsa_signature_parse_der.restype = ctypes.c_int
144+
_secp256k1.secp256k1_ecdsa_signature_parse_der.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t]
145+
146+
_secp256k1.secp256k1_ecdsa_signature_normalize.restype = ctypes.c_int
147+
_secp256k1.secp256k1_ecdsa_signature_normalize.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
148+
149+
_secp256k1.secp256k1_ecdsa_verify.restype = ctypes.c_int
150+
_secp256k1.secp256k1_ecdsa_verify.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
151+
152+
_secp256k1.secp256k1_ec_pubkey_parse.restype = ctypes.c_int
153+
_secp256k1.secp256k1_ec_pubkey_parse.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_size_t]
154+
155+
_secp256k1.secp256k1_ec_pubkey_tweak_add.restype = ctypes.c_int
156+
_secp256k1.secp256k1_ec_pubkey_tweak_add.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
157+
158+
_secp256k1.secp256k1_ec_privkey_tweak_add.restype = ctypes.c_int
159+
_secp256k1.secp256k1_ec_privkey_tweak_add.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
160+
161+
if getattr(_secp256k1, 'secp256k1_ec_pubkey_negate', None):
162+
secp256k1_has_pubkey_negate = True
163+
_secp256k1.secp256k1_ec_pubkey_negate.restype = ctypes.c_int
164+
_secp256k1.secp256k1_ec_pubkey_negate.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
165+
166+
if getattr(_secp256k1, 'secp256k1_ec_privkey_negate', None):
167+
secp256k1_has_privkey_negate = True
168+
_secp256k1.secp256k1_ec_privkey_negate.restype = ctypes.c_int
169+
_secp256k1.secp256k1_ec_privkey_negate.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
170+
171+
_secp256k1.secp256k1_ec_pubkey_combine.restype = ctypes.c_int
172+
_secp256k1.secp256k1_ec_pubkey_combine.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_char_p), ctypes.c_int]
173+
174+
if getattr(_secp256k1, 'secp256k1_ecdh', None):
175+
secp256k1_has_ecdh = True
176+
_secp256k1.secp256k1_ecdh.restype = ctypes.c_int
177+
_secp256k1.secp256k1_ecdh.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p, ctypes.c_void_p]
178+
179+
180+
class _secp256k1_context:
181+
"""dummy type for typecheck purposes"""
182+
183+
184+
def secp256k1_create_and_init_context(_secp256k1: ctypes.CDLL, flags: int
185+
) -> _secp256k1_context:
186+
if flags not in (SECP256K1_CONTEXT_SIGN, SECP256K1_CONTEXT_VERIFY,
187+
(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)):
188+
raise ValueError(
189+
'Value for flags is unexpected. '
190+
'Must be either SECP256K1_CONTEXT_SIGN, SECP256K1_CONTEXT_VERIFY, '
191+
'or a combination of these two')
192+
193+
ctx = _secp256k1.secp256k1_context_create(flags)
194+
if ctx is None:
195+
raise RuntimeError('secp256k1_context_create() returned None')
196+
197+
_secp256k1.secp256k1_context_set_illegal_callback(ctx, _secp256k1_illegal_callback_fn, 0)
198+
199+
seed = os.urandom(32)
200+
# secp256k1 commit 6198375218b8132f016b701ef049fb295ca28c95 comment
201+
# says that "non-signing contexts may use randomization in the future"
202+
# so we always call randomize, but check for success only for
203+
# signing context, because older lib versions return 0 for non-signing ctx.
204+
res = _secp256k1.secp256k1_context_randomize(ctx, seed)
205+
if res != 1:
206+
assert res == 0
207+
if (flags & SECP256K1_CONTEXT_SIGN) == SECP256K1_CONTEXT_SIGN:
208+
raise RuntimeError("secp256k1 context randomization failed")
209+
elif flags != SECP256K1_CONTEXT_VERIFY:
210+
raise AssertionError('unexpected value for flags')
211+
212+
return cast(_secp256k1_context, ctx)
213+
214+
215+
def load_secp256k1_library(path: Optional[str] = None) -> ctypes.CDLL:
216+
"""load libsecp256k1 via ctypes, add default function definitions
217+
to the library handle, and return this handle.
218+
219+
Callers of this function must assume responsibility for correct usage
220+
of the underlying C library.
221+
ctypes is a low-level foreign function interface, and using the underlying
222+
library though it should be done with the same care as if you would be
223+
programming in C directly.
224+
225+
Note that default function definitions are only those that relevant
226+
to the code that uses them in python code within this library.
227+
You probably should to add your own definitions for the functions that
228+
you want to call directly, even if they are defined here by default.
229+
Although removing the default function definition should be considered
230+
mild API breakage and should be communicated via release notes.
231+
"""
232+
233+
if path is None:
234+
path = ctypes.util.find_library('secp256k1')
235+
if path is None:
236+
raise ImportError('secp256k1 library not found')
237+
238+
try:
239+
handle = ctypes.cdll.LoadLibrary(path)
240+
except Exception as e:
241+
raise ImportError('Cannot load secp256k1 library: {}'.format(e))
242+
243+
_add_function_definitions(handle)
244+
245+
return handle
246+
247+
248+
# the handle is not exported purposefully: ctypes interface is low-level,
249+
# you are basically calling the C functions directly.
250+
# Anyone that uses it directly should know that they are doing.
251+
_secp256k1 = load_secp256k1_library(bitcointx.util._secp256k1_library_path)
252+
253+
secp256k1_context_sign = secp256k1_create_and_init_context(_secp256k1, SECP256K1_CONTEXT_SIGN)
254+
secp256k1_context_verify = secp256k1_create_and_init_context(_secp256k1, SECP256K1_CONTEXT_VERIFY)
255+
256+
257+
__all__ = (
258+
'load_secp256k1_library',
259+
'secp256k1_context_sign',
260+
'secp256k1_context_verify',
261+
'SIGNATURE_SIZE',
262+
'COMPACT_SIGNATURE_SIZE',
263+
'PUBLIC_KEY_SIZE',
264+
'COMPRESSED_PUBLIC_KEY_SIZE',
265+
'SECP256K1_EC_COMPRESSED',
266+
'SECP256K1_EC_UNCOMPRESSED',
267+
'SECP256K1_CONTEXT_SIGN',
268+
'SECP256K1_CONTEXT_VERIFY',
269+
'secp256k1_has_pubkey_recovery',
270+
)

0 commit comments

Comments
 (0)