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