From 2dfea673835e524ffaeb11dc1c2aecaf23188195 Mon Sep 17 00:00:00 2001 From: Claire's Monster Date: Sat, 2 Sep 2023 20:14:32 -0500 Subject: [PATCH] Updated to use secp256k1 tags. Using: v0.3.2 (#126) * Updated to use secp256k1 tags. Using: v0.3.2 * Ruff compliance * Reinstate use of revision number. Remove prelim Source in build.py * Updated _windows_libsecp256k1.py * Updated secp256k1.h * Mistakenly used v0.3.3-dev. Added `nake check` that highlights the version number during the build * New syntax for 'tox -e docs-ci -- build' ? * 'bench' fails - commenting to test the workflow - updated deprecated actions * Update macos runner. Missing dlll ? * Odd ill-numbered dll * Remove UPSTREAM_TAG comment Co-authored-by: Ofek Lev * Remove TARBALL_TAG comment Co-authored-by: Ofek Lev * Remove superfluous bool() casting Co-authored-by: Ofek Lev --------- Co-authored-by: Ofek Lev --- .github/scripts/build-windows-wheels.sh | 8 ++-- .github/workflows/build.yml | 35 +++++++++-------- .github/workflows/docs.yml | 6 +-- _cffi_build/secp256k1.h | 20 +++++++++- coincurve/_windows_libsecp256k1.py | 39 +++++++++++++------ coincurve/context.py | 7 ++-- coincurve/keys.py | 4 +- coincurve/utils.py | 2 +- setup.py | 52 ++++++++++++------------- setup_support.py | 29 ++++---------- tox.ini | 2 +- 11 files changed, 114 insertions(+), 90 deletions(-) diff --git a/.github/scripts/build-windows-wheels.sh b/.github/scripts/build-windows-wheels.sh index b6ccce069..0dbf52273 100755 --- a/.github/scripts/build-windows-wheels.sh +++ b/.github/scripts/build-windows-wheels.sh @@ -3,7 +3,7 @@ set -ex build_dll() { ./autogen.sh - ./configure --host=$1 --enable-module-recovery --enable-experimental --enable-module-ecdh --enable-module-extrakeys --enable-module-schnorrsig --enable-benchmark=no --enable-tests=no --enable-openssl-tests=no --enable-exhaustive-tests=no --enable-static --disable-dependency-tracking --with-pic + ./configure --host=$1 --enable-module-recovery --enable-experimental --enable-module-ecdh --enable-module-extrakeys --enable-module-schnorrsig --enable-benchmark=no --enable-tests=no --enable-exhaustive-tests=no --enable-static --disable-dependency-tracking --with-pic make } @@ -26,14 +26,16 @@ cp 64bit 32bit -R cd 64bit build_dll x86_64-w64-mingw32 -mv .libs/libsecp256k1-0.dll ../clean/coincurve/libsecp256k1.dll +# Not sure why it ended-up being a -2.dll instead of -0.dll: Researching +mv .libs/libsecp256k1-?.dll ../clean/coincurve/libsecp256k1.dll cd ../clean python setup.py bdist_wheel --plat-name=win_amd64 rm coincurve/libsecp256k1.dll cd ../32bit build_dll i686-w64-mingw32 -mv .libs/libsecp256k1-0.dll ../clean/coincurve/libsecp256k1.dll +# Not sure why it ended-up being a -2.dll instead of -0.dll: Researching +mv .libs/libsecp256k1-?.dll ../clean/coincurve/libsecp256k1.dll cd ../clean python setup.py bdist_wheel --plat-name=win32 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0495a3503..194b9f5a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ concurrency: cancel-in-progress: true env: - COINCURVE_UPSTREAM_REF: ddf2b2910eb19032f8dd657c66735115ae24bfba + COINCURVE_UPSTREAM_REF: acf5c55ae6a94e5ca847e07def40427547876101 COINCURVE_IGNORE_SYSTEM_LIB: '1' CIBW_BEFORE_ALL_MACOS: ./.github/scripts/install-macos-build-deps.sh CIBW_ENVIRONMENT_PASS_LINUX: > @@ -28,8 +28,9 @@ env: b=PrivateKey(); assert a.ecdh(b.public_key.format())==b.ecdh(a.public_key.format()) " + CIBW_TEST_SKIP: "*-macosx_arm64" CIBW_SKIP: > - pp* + pp* jobs: test: @@ -40,10 +41,10 @@ jobs: PYTHON_VERSION: '3.10' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ env.PYTHON_VERSION }} @@ -77,12 +78,12 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Build wheels uses: pypa/cibuildwheel@v2.11.2 - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: artifacts path: wheelhouse/*.whl @@ -92,17 +93,17 @@ jobs: name: Build macOS wheels needs: - test - runs-on: macos-10.15 + runs-on: macos-12 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Build wheels uses: pypa/cibuildwheel@v2.11.2 env: CIBW_ARCHS_MACOS: x86_64 - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: artifacts path: wheelhouse/*.whl @@ -112,10 +113,10 @@ jobs: name: Build macOS wheels for ARM needs: - test - runs-on: macos-10.15 + runs-on: macos-12 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Build wheels uses: pypa/cibuildwheel@v2.11.2 @@ -124,7 +125,7 @@ jobs: COINCURVE_CROSS_HOST: aarch64-apple-darwin CFLAGS: -target arm64-apple-macos11 - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: artifacts path: wheelhouse/*.whl @@ -137,7 +138,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install build dependencies run: python -m pip install --upgrade cffi @@ -148,7 +149,7 @@ jobs: - name: Build Windows wheels run: ./.github/scripts/build-windows-wheels.sh - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: artifacts path: dist/* @@ -165,7 +166,7 @@ jobs: (github.ref == 'refs/heads/master' || startsWith(github.event.ref, 'refs/tags')) steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up QEMU uses: docker/setup-qemu-action@v1 @@ -177,7 +178,7 @@ jobs: env: CIBW_ARCHS_LINUX: aarch64 - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: artifacts path: wheelhouse/*.whl @@ -196,7 +197,7 @@ jobs: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: artifacts path: dist diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index eda2b74f0..26712e204 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,13 +13,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: # Fetch all history for applying timestamps to every page fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: '3.9' @@ -30,7 +30,7 @@ jobs: run: python -m pip install --upgrade tox - name: Build documentation - run: tox -e docs-ci build + run: tox -e docs-ci -- build - uses: actions/upload-artifact@v2 with: diff --git a/_cffi_build/secp256k1.h b/_cffi_build/secp256k1.h index 0f9d7e1d9..d71c3e508 100644 --- a/_cffi_build/secp256k1.h +++ b/_cffi_build/secp256k1.h @@ -1,4 +1,5 @@ typedef struct secp256k1_context_struct secp256k1_context; +typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space; typedef struct { unsigned char data[64]; @@ -22,15 +23,27 @@ typedef int (*secp256k1_nonce_function)( #define SECP256K1_FLAGS_TYPE_COMPRESSION ... #define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY ... #define SECP256K1_FLAGS_BIT_CONTEXT_SIGN ... +#define SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY ... #define SECP256K1_FLAGS_BIT_COMPRESSION ... +#define SECP256K1_CONTEXT_NONE ... + #define SECP256K1_CONTEXT_VERIFY ... #define SECP256K1_CONTEXT_SIGN ... -#define SECP256K1_CONTEXT_NONE ... + +#define SECP256K1_CONTEXT_DECLASSIFY ... #define SECP256K1_EC_COMPRESSED ... #define SECP256K1_EC_UNCOMPRESSED ... +#define SECP256K1_TAG_PUBKEY_EVEN ... +#define SECP256K1_TAG_PUBKEY_ODD ... +#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED ... +#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN ... +#define SECP256K1_TAG_PUBKEY_HYBRID_ODD ... + +void secp256k1_selftest(void); + secp256k1_context* secp256k1_context_create( unsigned int flags ); @@ -55,6 +68,11 @@ void secp256k1_context_set_error_callback( const void* data ); +secp256k1_scratch_space* secp256k1_scratch_space_create( + const secp256k1_context* ctx, + size_t size +); + int secp256k1_ec_pubkey_parse( const secp256k1_context* ctx, secp256k1_pubkey* pubkey, diff --git a/coincurve/_windows_libsecp256k1.py b/coincurve/_windows_libsecp256k1.py index 34115a7fb..a99a02721 100644 --- a/coincurve/_windows_libsecp256k1.py +++ b/coincurve/_windows_libsecp256k1.py @@ -4,6 +4,7 @@ BASE_DEFINITIONS = """ typedef struct secp256k1_context_struct secp256k1_context; +typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space; typedef struct { unsigned char data[64]; @@ -22,20 +23,31 @@ unsigned int attempt ); -#define SECP256K1_FLAGS_TYPE_MASK 255 -#define SECP256K1_FLAGS_TYPE_CONTEXT 1 -#define SECP256K1_FLAGS_TYPE_COMPRESSION 2 +#define SECP256K1_FLAGS_TYPE_MASK ... +#define SECP256K1_FLAGS_TYPE_CONTEXT ... +#define SECP256K1_FLAGS_TYPE_COMPRESSION ... +#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY ... +#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN ... +#define SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY ... +#define SECP256K1_FLAGS_BIT_COMPRESSION ... -#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY 256 -#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN 512 -#define SECP256K1_FLAGS_BIT_COMPRESSION 256 +#define SECP256K1_CONTEXT_NONE ... -#define SECP256K1_CONTEXT_VERIFY 257 -#define SECP256K1_CONTEXT_SIGN 513 -#define SECP256K1_CONTEXT_NONE 1 +#define SECP256K1_CONTEXT_VERIFY ... +#define SECP256K1_CONTEXT_SIGN ... -#define SECP256K1_EC_COMPRESSED 258 -#define SECP256K1_EC_UNCOMPRESSED 2 +#define SECP256K1_CONTEXT_DECLASSIFY ... + +#define SECP256K1_EC_COMPRESSED ... +#define SECP256K1_EC_UNCOMPRESSED ... + +#define SECP256K1_TAG_PUBKEY_EVEN ... +#define SECP256K1_TAG_PUBKEY_ODD ... +#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED ... +#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN ... +#define SECP256K1_TAG_PUBKEY_HYBRID_ODD ... + +void secp256k1_selftest(void); secp256k1_context* secp256k1_context_create( unsigned int flags @@ -61,6 +73,11 @@ const void* data ); +secp256k1_scratch_space* secp256k1_scratch_space_create( + const secp256k1_context* ctx, + size_t size +); + int secp256k1_ec_pubkey_parse( const secp256k1_context* ctx, secp256k1_pubkey* pubkey, diff --git a/coincurve/context.py b/coincurve/context.py index f298f9418..03e0923a7 100644 --- a/coincurve/context.py +++ b/coincurve/context.py @@ -1,5 +1,6 @@ from os import urandom from threading import Lock +from typing import Optional from coincurve.flags import CONTEXT_ALL, CONTEXT_FLAGS @@ -7,9 +8,9 @@ class Context: - def __init__(self, seed: bytes = None, flag=CONTEXT_ALL, name: str = ''): + def __init__(self, seed: Optional[bytes] = None, flag=CONTEXT_ALL, name: str = ''): if flag not in CONTEXT_FLAGS: - raise ValueError('{} is an invalid context flag.'.format(flag)) + raise ValueError(f'{flag} is an invalid context flag.') self._lock = Lock() self.ctx = ffi.gc(lib.secp256k1_context_create(flag), lib.secp256k1_context_destroy) @@ -17,7 +18,7 @@ def __init__(self, seed: bytes = None, flag=CONTEXT_ALL, name: str = ''): self.name = name - def reseed(self, seed: bytes = None): + def reseed(self, seed: Optional[bytes] = None): """ Protects against certain possible future side-channel timing attacks. """ diff --git a/coincurve/keys.py b/coincurve/keys.py index 59159a1aa..1f260d15e 100644 --- a/coincurve/keys.py +++ b/coincurve/keys.py @@ -1,5 +1,5 @@ import os -from typing import Tuple +from typing import Optional, Tuple from asn1crypto.keys import ECDomainParameters, ECPointBitString, ECPrivateKey, PrivateKeyAlgorithm, PrivateKeyInfo @@ -24,7 +24,7 @@ class PrivateKey: - def __init__(self, secret: bytes = None, context: Context = GLOBAL_CONTEXT): + def __init__(self, secret: Optional[bytes] = None, context: Context = GLOBAL_CONTEXT): """ :param secret: The secret used to initialize the private key. If not provided or `None`, a new key will be generated. diff --git a/coincurve/utils.py b/coincurve/utils.py index b9d23e91e..1e0bf8f4e 100644 --- a/coincurve/utils.py +++ b/coincurve/utils.py @@ -88,7 +88,7 @@ def pad_scalar(scalar: bytes) -> bytes: def validate_secret(secret: bytes) -> bytes: if not 0 < bytes_to_int(secret) < GROUP_ORDER_INT: - raise ValueError('Secret scalar must be greater than 0 and less than {}.'.format(GROUP_ORDER_INT)) + raise ValueError(f'Secret scalar must be greater than 0 and less than {GROUP_ORDER_INT}.') return pad_scalar(secret) diff --git a/setup.py b/setup.py index c121d333d..12df421c0 100644 --- a/setup.py +++ b/setup.py @@ -35,11 +35,10 @@ # IMPORTANT: keep in sync with .github/workflows/build.yml # # Version of libsecp256k1 to download if none exists in the `libsecp256k1` directory -UPSTREAM_REF = os.getenv('COINCURVE_UPSTREAM_REF') or 'ddf2b2910eb19032f8dd657c66735115ae24bfba' +UPSTREAM_REF = os.getenv('COINCURVE_UPSTREAM_TAG') or 'acf5c55ae6a94e5ca847e07def40427547876101' LIB_TARBALL_URL = f'https://github.com/bitcoin-core/secp256k1/archive/{UPSTREAM_REF}.tar.gz' - # We require setuptools >= 3.3 if [int(i) for i in setuptools_version.split('.', 2)[:2]] < [3, 3]: raise SystemExit( @@ -59,22 +58,23 @@ def download_library(command): command.announce('downloading libsecp256k1 source code', level=log.INFO) try: import requests - - r = requests.get(LIB_TARBALL_URL, stream=True) - status_code = r.status_code - if status_code == 200: - content = BytesIO(r.raw.read()) - content.seek(0) - with tarfile.open(fileobj=content) as tf: - dirname = tf.getnames()[0].partition('/')[0] - tf.extractall() - shutil.move(dirname, libdir) - else: - raise SystemExit('Unable to download secp256k1 library: HTTP-Status: %d', status_code) - except requests.exceptions.RequestException as e: + try: + r = requests.get(LIB_TARBALL_URL, stream=True, timeout=10) + status_code = r.status_code + if status_code == 200: + content = BytesIO(r.raw.read()) + content.seek(0) + with tarfile.open(fileobj=content) as tf: + dirname = tf.getnames()[0].partition('/')[0] + tf.extractall() + shutil.move(dirname, libdir) + else: + raise SystemExit('Unable to download secp256k1 library: HTTP-Status: %d', status_code) + except requests.exceptions.RequestException as e: + raise SystemExit('Unable to download secp256k1 library: %s', str(e)) + except ImportError as e: raise SystemExit('Unable to download secp256k1 library: %s', str(e)) - class egg_info(_egg_info): def run(self): # Ensure library has been downloaded (sdist might have been skipped) @@ -150,8 +150,8 @@ def run(self): if not os.path.exists(absolute('libsecp256k1/configure')): # configure script hasn't been generated yet autogen = absolute('libsecp256k1/autogen.sh') - os.chmod(absolute(autogen), 0o755) - subprocess.check_call([autogen], cwd=absolute('libsecp256k1')) + os.chmod(absolute(autogen), 0o700) + subprocess.check_call([autogen], cwd=absolute('libsecp256k1')) # noqa S603 for filename in [ 'libsecp256k1/configure', @@ -164,7 +164,7 @@ def run(self): 'libsecp256k1/build-aux/test-driver', ]: try: - os.chmod(absolute(filename), 0o755) + os.chmod(absolute(filename), 0o700) except OSError as e: # some of these files might not exist depending on autoconf version if e.errno != errno.ENOENT: @@ -187,24 +187,22 @@ def run(self): '--enable-module-ecdh', '--enable-benchmark=no', '--enable-tests=no', - '--enable-openssl-tests=no', '--enable-exhaustive-tests=no', ] if 'COINCURVE_CROSS_HOST' in os.environ: - cmd.append('--host={}'.format(os.environ['COINCURVE_CROSS_HOST'])) + cmd.append(f"--host={os.environ['COINCURVE_CROSS_HOST']}") - log.debug('Running configure: {}'.format(' '.join(cmd))) - subprocess.check_call(cmd, cwd=build_temp) + log.debug(f"Running configure: {' '.join(cmd)}") + subprocess.check_call(cmd, cwd=build_temp) # noqa S603 - subprocess.check_call([MAKE], cwd=build_temp) - subprocess.check_call([MAKE, 'install'], cwd=build_temp) + subprocess.check_call([MAKE], cwd=build_temp) # noqa S603 + subprocess.check_call([MAKE, 'check'], cwd=build_temp) # noqa S603 + subprocess.check_call([MAKE, 'install'], cwd=build_temp) # noqa S603 self.build_flags['include_dirs'].extend(build_flags('libsecp256k1', 'I', build_temp)) self.build_flags['library_dirs'].extend(build_flags('libsecp256k1', 'L', build_temp)) if not has_system_lib(): self.build_flags['define'].append(('CFFI_ENABLE_RECOVERY', None)) - else: - pass class build_ext(_build_ext): diff --git a/setup_support.py b/setup_support.py index fcd1d9dd8..035c459a0 100644 --- a/setup_support.py +++ b/setup_support.py @@ -2,7 +2,7 @@ import os import shutil import subprocess -from contextlib import contextmanager +from contextlib import contextmanager, suppress from tempfile import mkdtemp @@ -47,16 +47,12 @@ def build_flags(library, type_, path): pkg_config_path.append(os.environ['LIB_DIR']) pkg_config_path.append(os.path.join(os.environ['LIB_DIR'], 'pkgconfig')) - options = ['--static', {'I': '--cflags-only-I', 'L': '--libs-only-L', 'l': '--libs-only-l'}[type_]] + options = {'I': '--cflags-only-I', 'L': '--libs-only-L', 'l': '--libs-only-l'} + env = dict(os.environ, PKG_CONFIG_PATH=':'.join(pkg_config_path)) + flags = subprocess.check_output(['pkg-config', '--static', options[type_], library], env=env) # noqa S603 + flags = list(flags.decode('UTF-8').split()) - return [ - flag.strip('-{}'.format(type_)) - for flag in subprocess.check_output( - ['pkg-config'] + options + [library], env=dict(os.environ, PKG_CONFIG_PATH=':'.join(pkg_config_path)) - ) - .decode('UTF-8') - .split() - ] + return [flag.strip(f'-{type_}') for flag in flags] def _find_lib(): @@ -68,24 +64,15 @@ def _find_lib(): ffi = FFI() try: ffi.dlopen('secp256k1') - if os.path.exists('/usr/include/secp256k1_ecdh.h'): - return True - else: - # The system library lacks the ecdh module - return False + return os.path.exists('/usr/include/secp256k1_ecdh.h') except OSError: if 'LIB_DIR' in os.environ: for path in glob.glob(os.path.join(os.environ['LIB_DIR'], '*secp256k1*')): - try: + with suppress(OSError): FFI().dlopen(path) return True - except OSError: - pass # We couldn't locate libsecp256k1 so we'll use the bundled one return False - else: - # If we got this far then the system library should be good enough - return True _has_system_lib = None diff --git a/tox.ini b/tox.ini index 31da40d36..6348ec33a 100644 --- a/tox.ini +++ b/tox.ini @@ -29,7 +29,7 @@ skip_install = true envdir = {toxworkdir}/{env:PYTHON_VERSION:bench} commands = python -c "import shutil; shutil.move('coincurve', '_coincurve')" - pytest -v --benchmark-only --benchmark-sort=name --benchmark-cprofile=tottime + - output=pytest -v --benchmark-only --benchmark-sort=name --benchmark-cprofile=tottime python -c "import shutil; shutil.move('_coincurve', 'coincurve')" [testenv:lint]