|
19 | 19 | import ctypes.util
|
20 | 20 | import hashlib
|
21 | 21 | import sys
|
| 22 | +from os import urandom |
22 | 23 | import bitcoin
|
23 | 24 | import bitcoin.signature
|
24 | 25 |
|
|
32 | 33 |
|
33 | 34 | _ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library('ssl') or 'libeay32')
|
34 | 35 |
|
| 36 | +_libsecp256k1_path = ctypes.util.find_library('secp256k1') |
| 37 | +_libsecp256k1_enable_signing = False |
| 38 | +_libsecp256k1_context = None |
| 39 | +_libsecp256k1 = None |
| 40 | + |
| 41 | + |
35 | 42 | class OpenSSLException(EnvironmentError):
|
36 | 43 | pass
|
37 | 44 |
|
@@ -185,12 +192,56 @@ def _check_res_void_p(val, func, args): # pylint: disable=unused-argument
|
185 | 192 | _ssl.o2i_ECPublicKey.restype = ctypes.c_void_p
|
186 | 193 | _ssl.o2i_ECPublicKey.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_long]
|
187 | 194 |
|
| 195 | +_ssl.BN_num_bits.restype = ctypes.c_int |
| 196 | +_ssl.BN_num_bits.argtypes = [ctypes.c_void_p] |
| 197 | +_ssl.EC_KEY_get0_private_key.restype = ctypes.c_void_p |
| 198 | + |
188 | 199 | # this specifies the curve used with ECDSA.
|
189 | 200 | _NID_secp256k1 = 714 # from openssl/obj_mac.h
|
190 | 201 |
|
191 | 202 | # test that OpenSSL supports secp256k1
|
192 | 203 | _ssl.EC_KEY_new_by_curve_name(_NID_secp256k1)
|
193 | 204 |
|
| 205 | +SECP256K1_FLAGS_TYPE_CONTEXT = (1 << 0) |
| 206 | +SECP256K1_FLAGS_BIT_CONTEXT_SIGN = (1 << 9) |
| 207 | +SECP256K1_CONTEXT_SIGN = \ |
| 208 | + (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN) |
| 209 | + |
| 210 | + |
| 211 | +def is_libsec256k1_available(): |
| 212 | + return _libsecp256k1_path is not None |
| 213 | + |
| 214 | + |
| 215 | +def use_libsecp256k1_for_signing(do_use): |
| 216 | + global _libsecp256k1 |
| 217 | + global _libsecp256k1_context |
| 218 | + global _libsecp256k1_enable_signing |
| 219 | + |
| 220 | + if not do_use: |
| 221 | + _libsecp256k1_enable_signing = False |
| 222 | + return |
| 223 | + |
| 224 | + if not is_libsec256k1_available(): |
| 225 | + raise ImportError("unable to locate libsecp256k1") |
| 226 | + |
| 227 | + if _libsecp256k1_context is None: |
| 228 | + _libsecp256k1 = ctypes.cdll.LoadLibrary(_libsecp256k1_path) |
| 229 | + _libsecp256k1.secp256k1_context_create.restype = ctypes.c_void_p |
| 230 | + _libsecp256k1.secp256k1_context_create.errcheck = _check_res_void_p |
| 231 | + _libsecp256k1.secp256k1_context_randomize.restype = ctypes.c_int |
| 232 | + _libsecp256k1.secp256k1_context_randomize.argtypes = [ctypes.c_void_p, ctypes.c_char_p] |
| 233 | + _libsecp256k1_context = _libsecp256k1.secp256k1_context_create(SECP256K1_CONTEXT_SIGN) |
| 234 | + assert(_libsecp256k1_context is not None) |
| 235 | + seed = urandom(32) |
| 236 | + result = _libsecp256k1.secp256k1_context_randomize(_libsecp256k1_context, seed) |
| 237 | + assert 1 == result |
| 238 | + |
| 239 | + |
| 240 | + |
| 241 | + _libsecp256k1_enable_signing = True |
| 242 | + |
| 243 | + |
| 244 | + |
194 | 245 | # From openssl/ecdsa.h
|
195 | 246 | class ECDSA_SIG_st(ctypes.Structure):
|
196 | 247 | _fields_ = [("r", ctypes.c_void_p),
|
@@ -258,12 +309,39 @@ def get_ecdh_key(self, other_pubkey, kdf=lambda k: hashlib.sha256(k).digest()):
|
258 | 309 | r = self.get_raw_ecdh_key(other_pubkey)
|
259 | 310 | return kdf(r)
|
260 | 311 |
|
| 312 | + def get_raw_privkey(self): |
| 313 | + bn = _ssl.EC_KEY_get0_private_key(self.k) |
| 314 | + bn = ctypes.c_void_p(bn) |
| 315 | + size = (_ssl.BN_num_bits(bn) + 7) / 8 |
| 316 | + mb = ctypes.create_string_buffer(int(size)) |
| 317 | + _ssl.BN_bn2bin(bn, mb) |
| 318 | + return mb.raw.rjust(32, b'\x00') |
| 319 | + |
| 320 | + def _sign_with_libsecp256k1(self, hash): |
| 321 | + raw_sig = ctypes.create_string_buffer(64) |
| 322 | + result = _libsecp256k1.secp256k1_ecdsa_sign( |
| 323 | + _libsecp256k1_context, raw_sig, hash, self.get_raw_privkey(), None, None) |
| 324 | + assert 1 == result |
| 325 | + sig_size0 = ctypes.c_size_t() |
| 326 | + sig_size0.value = 75 |
| 327 | + mb_sig = ctypes.create_string_buffer(sig_size0.value) |
| 328 | + result = _libsecp256k1.secp256k1_ecdsa_signature_serialize_der( |
| 329 | + _libsecp256k1_context, mb_sig, ctypes.byref(sig_size0), raw_sig) |
| 330 | + assert 1 == result |
| 331 | + # libsecp256k1 creates signatures already in lower-S form, no further |
| 332 | + # conversion needed. |
| 333 | + return mb_sig.raw[:sig_size0.value] |
| 334 | + |
| 335 | + |
261 | 336 | def sign(self, hash): # pylint: disable=redefined-builtin
|
262 | 337 | if not isinstance(hash, bytes):
|
263 | 338 | raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
|
264 | 339 | if len(hash) != 32:
|
265 | 340 | raise ValueError('Hash must be exactly 32 bytes long')
|
266 | 341 |
|
| 342 | + if _libsecp256k1_enable_signing: |
| 343 | + return self._sign_with_libsecp256k1(hash) |
| 344 | + |
267 | 345 | sig_size0 = ctypes.c_uint32()
|
268 | 346 | sig_size0.value = _ssl.ECDSA_size(self.k)
|
269 | 347 | mb_sig = ctypes.create_string_buffer(sig_size0.value)
|
|
0 commit comments