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