diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3b218cc..f25221df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,6 +104,10 @@ jobs: os: ubuntu-latest python-version: 3.9 tox-env: codechecks + - name: mutation testing + os: ubuntu-latest + python-version: '3.11' + mutation: 'true' steps: - uses: actions/checkout@v2 if: ${{ !matrix.container }} @@ -149,6 +153,11 @@ jobs: run: | apt-get update apt-get install -y git make python-is-python3 python3 curl wget python3-distutils python3-pip + + - name: Dependencies for mutation testing + if: ${{ matrix.mutation == 'true' }} + run: | + sudo apt-get install -y sqlite3 - name: workaround git failures with py3.10 run: | git config --global --add safe.directory /__w/python-ecdsa/python-ecdsa @@ -256,11 +265,15 @@ jobs: else pip install -r build-requirements.txt; fi + - name: Install mutation testing dependencies + if: ${{ matrix.mutation == 'true' }} + run: | + pip install cosmic-ray - name: Display installed python package versions run: pip list - name: Test native speed # tox uses pip to install dependenceis, so it breaks on py2.6 - if: ${{ !contains(matrix.tox-env, 'gmpy') && matrix.python-version != '2.6'}} + if: ${{ !contains(matrix.tox-env, 'gmpy') && matrix.python-version != '2.6' && ! matrix.mutation && !contains(matrix.tox-env, 'codechecks') }} run: tox -e speed - name: Test speed with gmpy if: ${{ contains(matrix.tox-env, 'gmpyp') }} @@ -277,6 +290,25 @@ jobs: - name: Run unit tests if: ${{ matrix.tox-env }} run: tox -e ${{ matrix.tox-env }} + - name: Init for mutation testing in PR + if: ${{ matrix.mutation == 'true' && github.event.pull_request }} + run: | + cosmic-ray init cosmic-ray.toml session-vs-master.sqlite + git branch master origin/master + cr-filter-git --config cosmic-ray.toml session-vs-master.sqlite + cr-report session-vs-master.sqlite | tail -n 5 + - name: Exec mutation testing for PR + if: ${{ matrix.mutation == 'true' && github.event.pull_request }} + run: | + cosmic-ray exec cosmic-ray.toml session-vs-master.sqlite + - name: Check test coverage for PR + if: ${{ matrix.mutation == 'true' && github.event.pull_request }} + run: | + # remove not-executed results + sqlite3 session-vs-master.sqlite "DELETE from work_results WHERE work_results.worker_outcome = 'SKIPPED'" + cr-report session-vs-master.sqlite | tail -n 5 + # check if executed have at most 5% survival rate + cr-rate --fail-over 5 session-vs-master.sqlite - name: instrumental test coverage on PR if: ${{ contains(matrix.opt-deps, 'instrumental') && github.event.pull_request }} env: @@ -337,3 +369,272 @@ jobs: COVERALLS_SERVICE_NAME: github run: | coveralls --finish + + mutation-prepare: + name: Prepare job files for the mutation runners + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + if: ${{ !matrix.container }} + with: + fetch-depth: 50 + - name: save session objects + uses: actions/cache@v3 + with: + path: | + sessions/ + key: sessions-${{ github.sha }} + - name: Install cosmic-ray + run: | + pip3 install cosmic-ray + - name: Install dependencies + run: | + sudo apt-get install -y sqlite3 + - name: Display Python version + run: python -c "import sys; print(sys.version)" + - name: Create list of mutations + run: | + cosmic-ray init cosmic-ray.toml session.sqlite + - name: Log number of jobs created + run: | + cr-report session.sqlite | tail -n 3 + - name: Split up mutations to workers + run: | + cp session.sqlite session-to_del.sqlite + sqlite3 session-to_del.sqlite "$(cat sql/create_to_del.sql)" + mkdir sessions + for i in $(seq 0 19); do + sed "s/%SHARD%/$i/" < sql/shard-db.sql > shard.sql + cp session-to_del.sqlite session-$i.sqlite + sqlite3 session-$i.sqlite "$(cat shard.sql)" + mv session-$i.sqlite sessions/ + done + mutation-execute: + name: Execute mutation testing + needs: mutation-prepare + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: 0 + - name: 1 + - name: 2 + - name: 3 + - name: 4 + - name: 5 + - name: 6 + - name: 7 + - name: 8 + - name: 9 + - name: 10 + - name: 11 + - name: 12 + - name: 13 + - name: 14 + - name: 15 + - name: 16 + - name: 17 + - name: 18 + - name: 19 + steps: + - uses: actions/checkout@v2 + if: ${{ !matrix.container }} + with: + fetch-depth: 1 + - name: Session objects + uses: actions/cache@v3 + with: + path: | + sessions/ + key: sessions-${{ github.sha }} + - name: Session done objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-${{ matrix.name }}-done.sqlite + key: sessions-${{ github.sha }}-${{ matrix.name }}-done + - name: Install gmpy2 dependencies + run: sudo apt-get install -y libmpfr-dev libmpc-dev + - name: Install gmpy2 + run: pip install gmpy2 + - name: Install build dependencies + run: | + pip install -r build-requirements.txt + pip install cosmic-ray + - name: Run mutation testing + run: | + cp sessions/session-${{ matrix.name }}.sqlite session.sqlite + systemd-run --user --scope -p MemoryMax=2G -p MemoryHigh=2G cosmic-ray exec cosmic-ray.toml session.sqlite & + cosmic_pid=$! + for i in $(seq 1 10); do + echo $i + sleep 60 + done + kill $cosmic_pid + mkdir sessions-done/ + cp session.sqlite sessions-done/session-${{ matrix.name }}-done.sqlite + - name: Report executed + run: | + cr-report session.sqlite | tail -n 3 + mutation-combine: + name: Combine mutation testing results + needs: mutation-execute + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + if: ${{ !matrix.container }} + with: + fetch-depth: 1 + - name: Session done 0 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-0-done.sqlite + key: sessions-${{ github.sha }}-0-done + - name: Session done 1 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-1-done.sqlite + key: sessions-${{ github.sha }}-1-done + - name: Session done 2 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-2-done.sqlite + key: sessions-${{ github.sha }}-2-done + - name: Session done 3 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-3-done.sqlite + key: sessions-${{ github.sha }}-3-done + - name: Session done 4 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-4-done.sqlite + key: sessions-${{ github.sha }}-4-done + - name: Session done 5 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-5-done.sqlite + key: sessions-${{ github.sha }}-5-done + - name: Session done 6 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-6-done.sqlite + key: sessions-${{ github.sha }}-6-done + - name: Session done 7 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-7-done.sqlite + key: sessions-${{ github.sha }}-7-done + - name: Session done 8 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-8-done.sqlite + key: sessions-${{ github.sha }}-8-done + - name: Session done 9 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-9-done.sqlite + key: sessions-${{ github.sha }}-9-done + - name: Session done 10 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-10-done.sqlite + key: sessions-${{ github.sha }}-10-done + - name: Session done 11 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-11-done.sqlite + key: sessions-${{ github.sha }}-11-done + - name: Session done 12 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-12-done.sqlite + key: sessions-${{ github.sha }}-12-done + - name: Session done 13 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-13-done.sqlite + key: sessions-${{ github.sha }}-13-done + - name: Session done 14 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-14-done.sqlite + key: sessions-${{ github.sha }}-14-done + - name: Session done 15 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-15-done.sqlite + key: sessions-${{ github.sha }}-15-done + - name: Session done 16 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-16-done.sqlite + key: sessions-${{ github.sha }}-16-done + - name: Session done 17 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-17-done.sqlite + key: sessions-${{ github.sha }}-17-done + - name: Session done 18 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-18-done.sqlite + key: sessions-${{ github.sha }}-18-done + - name: Session done 19 objects + uses: actions/cache@v3 + with: + path: | + sessions-done/session-19-done.sqlite + key: sessions-${{ github.sha }}-19-done + - name: Install cosmic-ray + run: | + pip3 install cosmic-ray + - name: Install dependencies + run: | + sudo apt-get install -y sqlite3 + - name: Combine worker results + run: | + cp sessions-done/session-0-done.sqlite session.sqlite + for i in $(seq 1 19); do + cp sessions-done/session-$i-done.sqlite session-to_merge.sqlite && sqlite3 session.sqlite "$(cat sql/combine.sql)" || true + done + - name: Report executed + run: | + cr-report session.sqlite | tail -n 3 + - name: Log survival estimate + run: cr-rate --estimate --fail-over 32 --confidence 99.9 session.sqlite || true + - name: Get mutation score + run: | + echo "print(100-$(cr-rate session.sqlite))" > print-score.py + echo "MUT_SCORE=$(python print-score.py)" >> $GITHUB_ENV + - name: Create mutation score badge + uses: schneegans/dynamic-badges-action@v1.4.0 + with: + auth: ${{ secrets.GIST_SECRET }} + gistID: 9b6ca1f3410207fbeca785a178781651 + filename: python-ecdsa-mutation-score.json + label: mutation score + message: ${{ env.MUT_SCORE }}% + valColorRange: ${{ env.MUT_SCORE }} + maxColorRange: 100 + minColorRange: 0 diff --git a/.travis.yml b/.travis.yml index f31a7197..8729ca80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,9 +12,13 @@ addons: - libmpc-dev before_cache: - rm -f $HOME/.cache/pip/log/debug.log -# place the slowest (instrumental and py2.6) first +# place the slowest (instrumental, mutation and py2.6) first matrix: include: + - python: 3.9 + dist: bionic + sudo: true + env: MUTATION=yes - python: 2.7 env: INSTRUMENTAL=yes dist: bionic @@ -125,15 +129,16 @@ install: else travis_retry pip install -r build-requirements.txt; fi - - if [[ $TOX_ENV =~ gmpy2 ]] || [[ $INSTRUMENTAL ]]; then travis_retry pip install gmpy2; fi + - if [[ $TOX_ENV =~ gmpy2 ]] || [[ $INSTRUMENTAL ]] || [[ $MUTATION ]]; then travis_retry pip install gmpy2; fi - if [[ $TOX_ENV =~ gmpyp ]]; then travis_retry pip install gmpy; fi - if [[ $INSTRUMENTAL ]]; then travis_retry pip install instrumental; fi + - if [[ $MUTATION ]]; then travis_retry pip install cosmic-ray; fi - pip list script: - if [[ $TOX_ENV ]]; then tox -e $TOX_ENV; fi - - if [[ $TOX_ENV =~ gmpy2 ]]; then tox -e speedgmpy2; fi - - if [[ $TOX_ENV =~ gmpyp ]]; then tox -e speedgmpy; fi - - if ! [[ $TOX_ENV =~ gmpy ]]; then tox -e speed; fi + - if [[ $TOX_ENV =~ gmpy2 ]] && [[ -z $MUTATION ]]; then tox -e speedgmpy2; fi + - if [[ $TOX_ENV =~ gmpyp ]] && [[ -z $MUTATION ]]; then tox -e speedgmpy; fi + - if ! [[ $TOX_ENV =~ gmpy ]] && [[ -z $MUTATION ]]; then tox -e speed; fi - | if [[ $INSTRUMENTAL && $TRAVIS_PULL_REQUEST != "false" ]]; then git checkout $PR_FIRST^ @@ -155,6 +160,26 @@ script: if [[ $INSTRUMENTAL && $TRAVIS_PULL_REQUEST != "false" ]]; then instrumental -f .instrumental.cov -s | python diff-instrumental.py --read .diff-instrumental --fail-under 70 --max-difference -0.1 fi + # cosmic-ray (mutation testing) runs + - if [[ $MUTATION ]]; then cosmic-ray init cosmic-ray.toml session.sqlite; fi + - if [[ $MUTATION ]]; then cosmic-ray baseline --report session.sqlite; fi + - if [[ $MUTATION ]]; then cr-report --show-output session.baseline.sqlite; fi + - | + if [[ $MUTATION ]]; then + cosmic-ray exec session.sqlite & + COSMIC_PID=$! + # make sure that we output something every 5 minutes (otherwise travis will kill us) + while kill -s 0 $COSMIC_PID; do + sleep 300 + cr-report session.sqlite | tail -n 3; + done & + REPORT_PID=$! + # kill exec after 40 minutes + (sleep $((60*40)); kill $COSMIC_PID) & + fi + - if [[ $MUTATION ]]; then wait $COSMIC_PID ; kill $REPORT_PID ; true; fi + - if [[ $MUTATION ]]; then cr-report --show-output session.sqlite | tail -n 40; fi + - if [[ $MUTATION ]]; then cr-rate --estimate --fail-over 29 --confidence 99.9 session.sqlite; fi after_success: - - if [[ -z $INSTRUMENTAL ]]; then coveralls; fi + - if [[ -z $INSTRUMENTAL && -z $MUTATION ]]; then coveralls; fi diff --git a/README.md b/README.md index fbb41a63..6c43f85d 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Documentation Status](https://readthedocs.org/projects/ecdsa/badge/?version=latest)](https://ecdsa.readthedocs.io/en/latest/?badge=latest) [![Coverage Status](https://coveralls.io/repos/github/tlsfuzzer/python-ecdsa/badge.svg?branch=master)](https://coveralls.io/github/tlsfuzzer/python-ecdsa?branch=master) ![condition coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/tomato42/9b6ca1f3410207fbeca785a178781651/raw/python-ecdsa-condition-coverage.json) +![mutation score](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/tomato42/9b6ca1f3410207fbeca785a178781651/raw/python-ecdsa-mutation-score.json) [![CodeQL](https://github.com/tlsfuzzer/python-ecdsa/actions/workflows/codeql.yml/badge.svg)](https://github.com/tlsfuzzer/python-ecdsa/actions/workflows/codeql.yml) [![Latest Version](https://img.shields.io/pypi/v/ecdsa.svg?style=flat)](https://pypi.python.org/pypi/ecdsa/) ![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat) diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..6c163acf --- /dev/null +++ b/conftest.py @@ -0,0 +1,11 @@ +def pytest_addoption(parser): + parser.addoption( + "--fast", action="store_true", default=False, help="run tests fast" + ) + + +def pytest_configure(config): + config.addinivalue_line( + "markers", + "slow: mark test as slow to run (deselect with '-m \"not slow\"')", + ) diff --git a/cosmic-ray-12way.sh b/cosmic-ray-12way.sh new file mode 100644 index 00000000..7b16fb2d --- /dev/null +++ b/cosmic-ray-12way.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +cosmic-ray init cosmic-ray-12way.toml session.sqlite +cosmic-ray baseline --session-file session.baseline.sqlite cosmic-ray-12way.toml +cr-report --show-output session.baseline.sqlite +# some mutations cause huge memory use, so put it in a cgroup +# systemd-run --user --scope -p MemoryMax=8G -p MemoryHigh=8G cr-http-workers cosmic-ray-12way.toml . +cr-http-workers cosmic-ray-12way.toml . +cosmic-ray exec cosmic-ray-12way.toml session.sqlite +cr-report session.sqlite +cr-html session.sqlite > session.html +cr-rate --estimate --fail-over 29 --confidence 99.9 session.sqlite diff --git a/cosmic-ray-12way.toml b/cosmic-ray-12way.toml new file mode 100644 index 00000000..78ee99c3 --- /dev/null +++ b/cosmic-ray-12way.toml @@ -0,0 +1,28 @@ +[cosmic-ray] +module-path = "src" +timeout = 20.0 +excluded-modules = ['src/ecdsa/_sha3.py', 'src/ecdsa/_version.py', 'src/ecdsa/test*'] +test-command = "pytest -x --fast -m 'not slow' src/" + +[cosmic-ray.distributor] +name = "http" + +[cosmic-ray.distributor.http] +worker-urls = [ + "http://localhost:9870", + "http://localhost:9871", + "http://localhost:9872", + "http://localhost:9873", + "http://localhost:9874", + "http://localhost:9875", + "http://localhost:9876", + "http://localhost:9877", + "http://localhost:9878", + "http://localhost:9879", + "http://localhost:9880", + "http://localhost:9881", + "http://localhost:9882" +] + +[cosmic-ray.filters.git-filter] +branch = "master" diff --git a/cosmic-ray.sh b/cosmic-ray.sh new file mode 100644 index 00000000..51181a3a --- /dev/null +++ b/cosmic-ray.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +cosmic-ray init cosmic-ray.toml session.sqlite +cosmic-ray baseline --session-file session.baseline.sqlite cosmic-ray.toml +cr-report --show-output session.baseline.sqlite +# some mutations cause huge memory use, so put it in a cgroup +# systemd-run --user --scope -p MemoryMax=2G -p MemoryHigh=2G cosmic-ray exec cosmic-ray.toml session.sqlite +cosmic-ray exec cosmic-ray.toml session.sqlite +cr-report session.sqlite +cr-html session.sqlite > session.html +cr-rate --estimate --fail-over 29 --confidence 99.9 session.sqlite diff --git a/cosmic-ray.toml b/cosmic-ray.toml new file mode 100644 index 00000000..6f1c54fa --- /dev/null +++ b/cosmic-ray.toml @@ -0,0 +1,11 @@ +[cosmic-ray] +module-path = "src" +timeout = 20.0 +excluded-modules = ['src/ecdsa/_sha3.py', 'src/ecdsa/_version.py', 'src/ecdsa/test*'] +test-command = "pytest -x --fast -m 'not slow' src/" + +[cosmic-ray.distributor] +name = "local" + +[cosmic-ray.filters.git-filter] +branch = "master" diff --git a/sql/combine.sql b/sql/combine.sql new file mode 100644 index 00000000..d2a4490d --- /dev/null +++ b/sql/combine.sql @@ -0,0 +1,7 @@ +attach 'session-to_merge.sqlite' as toMerge; +BEGIN; + insert into work_items select * from toMerge.work_items; + insert into mutation_specs select * from toMerge.mutation_specs; + insert into work_results select * from toMerge.work_results; +COMMIT; +detach toMerge; diff --git a/sql/create_to_del.sql b/sql/create_to_del.sql new file mode 100644 index 00000000..956d1a28 --- /dev/null +++ b/sql/create_to_del.sql @@ -0,0 +1,2 @@ +create table to_del (job_id VARCHAR NOT NULL, id INTEGER PRIMARY KEY); +insert into to_del select *, ROWID from work_items; diff --git a/sql/shard-db.sql b/sql/shard-db.sql new file mode 100644 index 00000000..7460bd31 --- /dev/null +++ b/sql/shard-db.sql @@ -0,0 +1,3 @@ +delete from mutation_specs where job_id in (select job_id from to_del where to_del.ID % 20 != %SHARD%); +delete from work_items where job_id in (select job_id from to_del where to_del.ID % 20 != %SHARD%); +drop table to_del; diff --git a/src/ecdsa/ecdsa.py b/src/ecdsa/ecdsa.py index 5a891ec9..7ac76c2f 100644 --- a/src/ecdsa/ecdsa.py +++ b/src/ecdsa/ecdsa.py @@ -64,6 +64,7 @@ modified as part of the python-ecdsa package. """ +import warnings from six import int2byte, b from . import ellipticcurve from . import numbertheory @@ -269,8 +270,14 @@ def sign(self, hash, random_k): return Signature(r, s) -def int_to_string(x): +def int_to_string(x): # pragma: no cover """Convert integer x into a string of bytes, as per X9.62.""" + # deprecated in 0.19 + warnings.warn( + "Function is unused in library code. If you use this code, " + "change to util.string_to_number.", + DeprecationWarning, + ) assert x >= 0 if x == 0: return b("\0") @@ -284,8 +291,14 @@ def int_to_string(x): return b("").join(result) -def string_to_int(s): +def string_to_int(s): # pragma: no cover """Convert a string of bytes into an integer, as per X9.62.""" + # deprecated in 0.19 + warnings.warn( + "Function is unused in library code. If you use this code, " + "change to util.number_to_string.", + DeprecationWarning, + ) result = 0 for c in s: if not isinstance(c, int): @@ -294,9 +307,16 @@ def string_to_int(s): return result -def digest_integer(m): +def digest_integer(m): # pragma: no cover """Convert an integer into a string of bytes, compute its SHA-1 hash, and convert the result to an integer.""" + # deprecated in 0.19 + warnings.warn( + "Function is unused in library code. If you use this code, " + "change to a one-liner with util.number_to_string and " + "util.string_to_number methods.", + DeprecationWarning, + ) # # I don't expect this function to be used much. I wrote # it in order to be able to duplicate the examples diff --git a/src/ecdsa/ellipticcurve.py b/src/ecdsa/ellipticcurve.py index af2aaeab..067a149c 100644 --- a/src/ecdsa/ellipticcurve.py +++ b/src/ecdsa/ellipticcurve.py @@ -136,11 +136,17 @@ def contains_point(self, x, y): return (y * y - ((x * x + self.__a) * x + self.__b)) % self.__p == 0 def __str__(self): - return "CurveFp(p=%d, a=%d, b=%d, h=%d)" % ( + if self.__h is not None: + return "CurveFp(p={0}, a={1}, b={2}, h={3})".format( + self.__p, + self.__a, + self.__b, + self.__h, + ) + return "CurveFp(p={0}, a={1}, b={2})".format( self.__p, self.__a, self.__b, - self.__h, ) @@ -219,11 +225,17 @@ def cofactor(self): return self.__h def __str__(self): - return "CurveEdTw(p={0}, a={1}, d={2}, h={3})".format( + if self.__h is not None: + return "CurveEdTw(p={0}, a={1}, d={2}, h={3})".format( + self.__p, + self.__a, + self.__d, + self.__h, + ) + return "CurveEdTw(p={0}, a={1}, d={2})".format( self.__p, self.__a, self.__d, - self.__h, ) diff --git a/src/ecdsa/test_der.py b/src/ecdsa/test_der.py index 833d12d8..f08ea573 100644 --- a/src/ecdsa/test_der.py +++ b/src/ecdsa/test_der.py @@ -7,9 +7,10 @@ import unittest2 as unittest except ImportError: import unittest +import sys from six import b import hypothesis.strategies as st -from hypothesis import given +from hypothesis import given, settings import pytest from ._compat import str_idx_as_int from .curves import NIST256p, NIST224p @@ -462,6 +463,14 @@ def st_oid(draw, max_value=2**512, max_size=50): return (first, second) + tuple(rest) +HYP_SETTINGS = {} + + +if "--fast" in sys.argv: # pragma: no cover + HYP_SETTINGS["max_examples"] = 2 + + +@settings(**HYP_SETTINGS) @given(st_oid()) def test_oids(ids): encoded_oid = encode_oid(*ids) diff --git a/src/ecdsa/test_ecdh.py b/src/ecdsa/test_ecdh.py index 872d4d14..3395a212 100644 --- a/src/ecdsa/test_ecdh.py +++ b/src/ecdsa/test_ecdh.py @@ -366,6 +366,7 @@ def run_openssl(cmd): ) +@pytest.mark.slow @pytest.mark.parametrize( "vcurve", curves, diff --git a/src/ecdsa/test_ecdsa.py b/src/ecdsa/test_ecdsa.py index 2af527b6..c1e25829 100644 --- a/src/ecdsa/test_ecdsa.py +++ b/src/ecdsa/test_ecdsa.py @@ -27,6 +27,7 @@ generator_112r2, int_to_string, ) +from .ellipticcurve import Point HYP_SETTINGS = {} @@ -76,6 +77,19 @@ def test_verification(self): def test_rejection(self): assert not self.pubk.verifies(self.msg - 1, self.sig) + def test_verification_with_regular_point(self): + pubk = Public_key( + Point( + generator_192.curve(), + generator_192.x(), + generator_192.y(), + generator_192.order(), + ), + self.pubk.point, + ) + + assert pubk.verifies(self.msg, self.sig) + class TestPublicKey(unittest.TestCase): def test_equality_public_keys(self): @@ -584,7 +598,13 @@ def test_signature_validity(gen, msg, qx, qy, r, s, expected): elliptic curve of `gen`, `r` and `s` are the signature, and `expected` is True iff the signature is expected to be valid.""" pubk = Public_key(gen, ellipticcurve.Point(gen.curve(), qx, qy)) - assert expected == pubk.verifies(digest_integer(msg), Signature(r, s)) + with pytest.warns(DeprecationWarning) as warns: + msg_dgst = digest_integer(msg) + assert len(warns) == 3 + assert "unused" in warns[0].message.args[0] + assert "unused" in warns[1].message.args[0] + assert "unused" in warns[2].message.args[0] + assert expected == pubk.verifies(msg_dgst, Signature(r, s)) @pytest.mark.parametrize( @@ -593,7 +613,13 @@ def test_signature_validity(gen, msg, qx, qy, r, s, expected): def test_pk_recovery(gen, msg, r, s, qx, qy, expected): del expected sign = Signature(r, s) - pks = sign.recover_public_keys(digest_integer(msg), gen) + with pytest.warns(DeprecationWarning) as warns: + msg_dgst = digest_integer(msg) + assert len(warns) == 3 + assert "unused" in warns[0].message.args[0] + assert "unused" in warns[1].message.args[0] + assert "unused" in warns[2].message.args[0] + pks = sign.recover_public_keys(msg_dgst, gen) assert pks @@ -634,7 +660,10 @@ def st_random_gen_key_msg_nonce(draw): SIG_VER_SETTINGS = dict(HYP_SETTINGS) -SIG_VER_SETTINGS["max_examples"] = 10 +if "--fast" in sys.argv: # pragma: no cover + SIG_VER_SETTINGS["max_examples"] = 1 +else: + SIG_VER_SETTINGS["max_examples"] = 10 @settings(**SIG_VER_SETTINGS) @@ -658,4 +687,8 @@ def test_sig_verify(args): def test_int_to_string_with_zero(): - assert int_to_string(0) == b"\x00" + with pytest.warns(DeprecationWarning) as warns: + assert int_to_string(0) == b"\x00" + + assert len(warns) == 1 + assert "unused" in warns[0].message.args[0] diff --git a/src/ecdsa/test_eddsa.py b/src/ecdsa/test_eddsa.py index a125b94e..1a35fb32 100644 --- a/src/ecdsa/test_eddsa.py +++ b/src/ecdsa/test_eddsa.py @@ -1,3 +1,4 @@ +import sys import pickle import hashlib import pytest @@ -259,6 +260,22 @@ def test_add_wrong_point_type(self): self.assertIn("different curve", str(e.exception)) +def test_generate_with_point(): + x1 = int( + "427838232691226969392843410947554224151809796397784248136826" + "78720006717057747" + ) + y1 = int( + "463168356949264781694283940034751631413079938662562256157830" + "33603165251855960" + ) + p = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) + + pk = PublicKey(generator_ed25519, b"0" * 32, public_point=p) + + assert pk.public_point() == p + + def test_ed25519_mul_to_order_min_1(): x1 = int( "427838232691226969392843410947554224151809796397784248136826" @@ -647,7 +664,10 @@ def test_invalid_r_value(self): HYP_SETTINGS = dict() -HYP_SETTINGS["max_examples"] = 10 +if "--fast" in sys.argv: + HYP_SETTINGS["max_examples"] = 2 +else: + HYP_SETTINGS["max_examples"] = 10 @settings(**HYP_SETTINGS) diff --git a/src/ecdsa/test_ellipticcurve.py b/src/ecdsa/test_ellipticcurve.py index f46fd9ea..9fac7eed 100644 --- a/src/ecdsa/test_ellipticcurve.py +++ b/src/ecdsa/test_ellipticcurve.py @@ -14,7 +14,7 @@ except ImportError: # pragma: no cover HC_PRESENT = False from .numbertheory import inverse_mod -from .ellipticcurve import CurveFp, INFINITY, Point +from .ellipticcurve import CurveFp, INFINITY, Point, CurveEdTw HYP_SETTINGS = {} @@ -40,7 +40,7 @@ HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) -HYP_SLOW_SETTINGS["max_examples"] = 10 +HYP_SLOW_SETTINGS["max_examples"] = 2 @settings(**HYP_SLOW_SETTINGS) @@ -97,6 +97,32 @@ def test_conflation_curves(self): self.assertDictEqual({c_23: None}, {eq1: None}) self.assertIn(eq2, {eq3: None}) + def test___str__(self): + self.assertEqual(str(self.c_23), "CurveFp(p=23, a=1, b=1)") + + def test___str___with_cofactor(self): + c = CurveFp(23, 1, 1, 4) + self.assertEqual(str(c), "CurveFp(p=23, a=1, b=1, h=4)") + + +class TestCurveEdTw(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.c_23 = CurveEdTw(23, 1, 1) + + def test___str__(self): + self.assertEqual(str(self.c_23), "CurveEdTw(p=23, a=1, d=1)") + + def test___str___with_cofactor(self): + c = CurveEdTw(23, 1, 1, 4) + self.assertEqual(str(c), "CurveEdTw(p=23, a=1, d=1, h=4)") + + def test_usability_in_a_hashed_collection_curves(self): + {self.c_23: None} + + def test_hashability_curves(self): + hash(self.c_23) + class TestPoint(unittest.TestCase): @classmethod diff --git a/src/ecdsa/test_jacobi.py b/src/ecdsa/test_jacobi.py index 322b568c..2100cc16 100644 --- a/src/ecdsa/test_jacobi.py +++ b/src/ecdsa/test_jacobi.py @@ -1,4 +1,5 @@ import pickle +import sys try: import unittest2 as unittest @@ -22,6 +23,7 @@ generator_brainpoolp160r1, curve_brainpoolp160r1, generator_112r2, + curve_112r2, ) from .numbertheory import inverse_mod from .util import randrange @@ -32,6 +34,13 @@ NO_OLD_SETTINGS["deadline"] = 5000 +SLOW_SETTINGS = {} +if "--fast" in sys.argv: # pragma: no cover + SLOW_SETTINGS["max_examples"] = 2 +else: + SLOW_SETTINGS["max_examples"] = 10 + + class TestJacobi(unittest.TestCase): def test___init__(self): curve = object() @@ -199,7 +208,7 @@ def test_compare_double_with_multiply(self): self.assertEqual(dbl, mlpl) - @settings(max_examples=10) + @settings(**SLOW_SETTINGS) @given( st.integers( min_value=0, max_value=int(generator_brainpoolp160r1.order() - 1) @@ -214,7 +223,7 @@ def test_multiplications(self, mul): self.assertEqual((pj.x(), pj.y()), (pw.x(), pw.y())) self.assertEqual(pj, pw) - @settings(max_examples=10) + @settings(**SLOW_SETTINGS) @given( st.integers( min_value=0, max_value=int(generator_brainpoolp160r1.order() - 1) @@ -232,7 +241,7 @@ def test_precompute(self, mul): self.assertEqual(a, b) - @settings(max_examples=10) + @settings(**SLOW_SETTINGS) @given( st.integers( min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) @@ -251,7 +260,7 @@ def test_add_scaled_points(self, a_mul, b_mul): self.assertEqual(c, j_g * (a_mul + b_mul)) - @settings(max_examples=10) + @settings(**SLOW_SETTINGS) @given( st.integers( min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) @@ -283,7 +292,8 @@ def test_add_one_scaled_point(self, a_mul, b_mul, new_z): self.assertEqual(c, j_g * (a_mul + b_mul)) - @settings(max_examples=10) + @pytest.mark.slow + @settings(**SLOW_SETTINGS) @given( st.integers( min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) @@ -348,7 +358,8 @@ def test_add_same_scale_points_static(self): self.assertEqual(c, x + y) - @settings(max_examples=14) + @pytest.mark.slow + @settings(**SLOW_SETTINGS) @given( st.integers( min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) @@ -423,6 +434,78 @@ def test_add_different_scale_points_static(self): self.assertEqual(c, x + y) + def test_add_different_points_same_scale_static(self): + j_g = generator_brainpoolp160r1 + p = curve_brainpoolp160r1.p() + a = j_g * 11 + a.scale() + b = j_g * 12 + z = 13 + x = PointJacobi( + curve_brainpoolp160r1, + a.x() * z**2 % p, + a.y() * z**3 % p, + z, + ) + y = PointJacobi( + curve_brainpoolp160r1, + b.x() * z**2 % p, + b.y() * z**3 % p, + z, + ) + + c = a + b + + self.assertEqual(c, x + y) + + def test_add_same_point_different_scale_second_z_1_static(self): + j_g = generator_112r2 + p = curve_112r2.p() + z = 11 + a = j_g * z + a.scale() + + x = PointJacobi( + curve_112r2, + a.x() * z**2 % p, + a.y() * z**3 % p, + z, + ) + y = PointJacobi( + curve_112r2, + a.x(), + a.y(), + 1, + ) + + c = a + a + + self.assertEqual(c, x + y) + + def test_add_to_infinity_static(self): + j_g = generator_112r2 + + z = 11 + a = j_g * z + a.scale() + + b = -a + + x = PointJacobi( + curve_112r2, + a.x(), + a.y(), + 1, + ) + y = PointJacobi( + curve_112r2, + b.x(), + b.y(), + 1, + ) + + self.assertEqual(INFINITY, x + y) + def test_add_point_3_times(self): j_g = PointJacobi.from_affine(generator_256) @@ -557,6 +640,7 @@ def test_pickle(self): pj = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) self.assertEqual(pickle.loads(pickle.dumps(pj)), pj) + @pytest.mark.slow @settings(**NO_OLD_SETTINGS) @pytest.mark.skipif( platform.python_implementation() == "PyPy", @@ -595,6 +679,7 @@ def runner(generator): generator_112r2._PointJacobi__precompute, ) + @pytest.mark.slow @pytest.mark.skipif( platform.system() == "Windows" or platform.python_implementation() == "PyPy", diff --git a/src/ecdsa/test_keys.py b/src/ecdsa/test_keys.py index 4d9e976e..a41a3dbc 100644 --- a/src/ecdsa/test_keys.py +++ b/src/ecdsa/test_keys.py @@ -249,13 +249,13 @@ def test_array_array_of_bytes_memoryview(self): self.assertEqual(self.vk.to_string(), vk.to_string()) def test_equality_on_verifying_keys(self): - self.assertEqual(self.vk, self.sk.get_verifying_key()) + self.assertTrue(self.vk == self.sk.get_verifying_key()) def test_inequality_on_verifying_keys(self): - self.assertNotEqual(self.vk, self.vk2) + self.assertFalse(self.vk == self.vk2) def test_inequality_on_verifying_keys_not_implemented(self): - self.assertNotEqual(self.vk, None) + self.assertFalse(self.vk == None) def test_VerifyingKey_inequality_on_same_curve(self): self.assertNotEqual(self.vk, self.sk2.verifying_key) @@ -264,7 +264,7 @@ def test_SigningKey_inequality_on_same_curve(self): self.assertNotEqual(self.sk, self.sk2) def test_inequality_on_wrong_types(self): - self.assertNotEqual(self.vk, self.sk) + self.assertFalse(self.vk == self.sk) def test_from_public_point_old(self): pj = self.vk.pubkey.point @@ -272,7 +272,7 @@ def test_from_public_point_old(self): vk = VerifyingKey.from_public_point(point, self.vk.curve) - self.assertEqual(vk, self.vk) + self.assertTrue(vk == self.vk) def test_ed25519_VerifyingKey_repr__(self): sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) @@ -970,14 +970,14 @@ def test_VerifyingKey_inequality_with_different_curves(): sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) sk2 = SigningKey.from_secret_exponent(2, NIST256p) - assert sk1.verifying_key != sk2.verifying_key + assert not (sk1.verifying_key == sk2.verifying_key) def test_VerifyingKey_inequality_with_different_secret_points(): sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) sk2 = SigningKey.from_secret_exponent(3, BRAINPOOLP160r1) - assert sk1.verifying_key != sk2.verifying_key + assert not (sk1.verifying_key == sk2.verifying_key) def test_SigningKey_from_pem_pkcs8v2_EdDSA(): diff --git a/src/ecdsa/test_malformed_sigs.py b/src/ecdsa/test_malformed_sigs.py index 20912376..d7050d2f 100644 --- a/src/ecdsa/test_malformed_sigs.py +++ b/src/ecdsa/test_malformed_sigs.py @@ -154,12 +154,17 @@ def st_fuzzed_sig(draw, keys_and_sigs): HealthCheck.filter_too_much, HealthCheck.too_slow, ] +if "--fast" in sys.argv: # pragma: no cover + params["max_examples"] = 20 slow_params = dict(params) -slow_params["max_examples"] = 10 +if "--fast" in sys.argv: # pragma: no cover + slow_params["max_examples"] = 1 +else: + slow_params["max_examples"] = 10 -@settings(**params) +@settings(**slow_params) @given(st_fuzzed_sig(keys_and_sigs)) def test_fuzzed_der_signatures(args): verifying_key, sig = args @@ -303,7 +308,7 @@ def st_der(): ) -@settings(**params) +@settings(**slow_params) @given(st.sampled_from(keys_and_sigs), st_der()) def test_random_der_as_signature(params, der): """Check if random DER structures are rejected as signature""" @@ -313,7 +318,7 @@ def test_random_der_as_signature(params, der): verifying_key.verify(der, example_data, sigdecode=sigdecode_der) -@settings(**params) +@settings(**slow_params) @given(st.sampled_from(keys_and_sigs), st.binary(max_size=1024**2)) @example( keys_and_sigs[0], encode_sequence(encode_integer(0), encode_integer(0)) @@ -360,7 +365,7 @@ def test_random_bytes_as_signature(params, der): ] -@settings(**params) +@settings(**slow_params) @given(st_fuzzed_sig(keys_and_string_sigs)) def test_fuzzed_string_signatures(params): verifying_key, sig = params diff --git a/src/ecdsa/test_numbertheory.py b/src/ecdsa/test_numbertheory.py index 239eaa8c..983039e5 100644 --- a/src/ecdsa/test_numbertheory.py +++ b/src/ecdsa/test_numbertheory.py @@ -1,5 +1,6 @@ import operator from functools import reduce +import sys try: import unittest2 as unittest @@ -66,6 +67,7 @@ def test_next_prime_with_nums_less_2(val): assert next_prime(val) == 2 +@pytest.mark.slow @pytest.mark.parametrize("prime", smallprimes) def test_square_root_mod_prime_for_small_primes(prime): squares = set() @@ -251,9 +253,15 @@ def st_comp_no_com_fac(draw): # the factorization() sometimes takes a long time to finish HYP_SETTINGS["deadline"] = 5000 +if "--fast" in sys.argv: # pragma: no cover + HYP_SETTINGS["max_examples"] = 20 + HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) -HYP_SLOW_SETTINGS["max_examples"] = 10 +if "--fast" in sys.argv: # pragma: no cover + HYP_SLOW_SETTINGS["max_examples"] = 1 +else: + HYP_SLOW_SETTINGS["max_examples"] = 20 class TestIsPrime(unittest.TestCase): @@ -320,6 +328,7 @@ def test_gcd_with_uncom_factor(self, numbers): n = gcd(numbers) assert n == 1 + @settings(**HYP_SLOW_SETTINGS) @given( st.lists( st.integers(min_value=1, max_value=2**8192), @@ -338,6 +347,7 @@ def test_lcm(self): assert lcm([3, 5 * 3, 7 * 3]) == 3 * 5 * 7 assert lcm(3) == 3 + @settings(**HYP_SLOW_SETTINGS) @given( st.lists( st.integers(min_value=1, max_value=2**8192), @@ -356,7 +366,7 @@ def test_lcm_with_random_numbers(self, numbers): "meet requirements (like `is_prime()`), the test " "case times-out on it", ) - @settings(**HYP_SETTINGS) + @settings(**HYP_SLOW_SETTINGS) @given(st_num_square_prime()) def test_square_root_mod_prime(self, vals): square, prime = vals @@ -364,7 +374,8 @@ def test_square_root_mod_prime(self, vals): calc = square_root_mod_prime(square, prime) assert calc * calc % prime == square - @settings(**HYP_SETTINGS) + @pytest.mark.slow + @settings(**HYP_SLOW_SETTINGS) @given(st.integers(min_value=1, max_value=10**12)) @example(265399 * 1526929) @example(373297**2 * 553991) @@ -401,7 +412,7 @@ def test_jacobi_with_zero(self): def test_jacobi_with_one(self): assert jacobi(1, 3) == 1 - @settings(**HYP_SETTINGS) + @settings(**HYP_SLOW_SETTINGS) @given(st.integers(min_value=3, max_value=1000).filter(lambda x: x % 2)) def test_jacobi(self, mod): if is_prime(mod): @@ -420,6 +431,7 @@ def test_jacobi(self, mod): c *= jacobi(a, i[0]) ** i[1] assert c == jacobi(a, mod) + @settings(**HYP_SLOW_SETTINGS) @given(st_two_nums_rel_prime()) def test_inverse_mod(self, nums): num, mod = nums diff --git a/src/ecdsa/test_pyecdsa.py b/src/ecdsa/test_pyecdsa.py index 3ce3e04f..069f3e0c 100644 --- a/src/ecdsa/test_pyecdsa.py +++ b/src/ecdsa/test_pyecdsa.py @@ -9,11 +9,12 @@ import shutil import subprocess import pytest +import sys from binascii import hexlify, unhexlify import hashlib from functools import partial -from hypothesis import given +from hypothesis import given, settings import hypothesis.strategies as st from six import b, print_, binary_type @@ -75,6 +76,13 @@ class SubprocessError(Exception): pass +HYP_SETTINGS = {} + + +if "--fast" in sys.argv: # pragma: no cover + HYP_SETTINGS["max_examples"] = 2 + + def run_openssl(cmd): OPENSSL = "openssl" p = subprocess.Popen( @@ -138,41 +146,13 @@ def test_bad_usage(self): self.assertRaises(TypeError, SigningKey) self.assertRaises(TypeError, VerifyingKey) - def test_lengths(self): + def test_lengths_default(self): default = NIST192p priv = SigningKey.generate() pub = priv.get_verifying_key() self.assertEqual(len(pub.to_string()), default.verifying_key_length) - sig = priv.sign(b("data")) + sig = priv.sign(b"data") self.assertEqual(len(sig), default.signature_length) - for curve in ( - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - BRAINPOOLP160t1, - BRAINPOOLP192t1, - BRAINPOOLP224t1, - BRAINPOOLP256t1, - BRAINPOOLP320t1, - BRAINPOOLP384t1, - BRAINPOOLP512t1, - ): - priv = SigningKey.generate(curve=curve) - pub1 = priv.get_verifying_key() - pub2 = VerifyingKey.from_string(pub1.to_string(), curve) - self.assertEqual(pub1.to_string(), pub2.to_string()) - self.assertEqual(len(pub1.to_string()), curve.verifying_key_length) - sig = priv.sign(b("data")) - self.assertEqual(len(sig), curve.signature_length) def test_serialize(self): seed = b("secret") @@ -1005,6 +985,24 @@ def test_VerifyingKey_encode_decode(curve, encoding): assert vk.pubkey.point == from_enc.pubkey.point +if "--fast" in sys.argv: # pragma: no cover + params = [NIST192p, BRAINPOOLP160r1] +else: + params = curves + + +@pytest.mark.parametrize("curve", params) +def test_lengths(curve): + priv = SigningKey.generate(curve=curve) + pub1 = priv.get_verifying_key() + pub2 = VerifyingKey.from_string(pub1.to_string(), curve) + assert pub1.to_string() == pub2.to_string() + assert len(pub1.to_string()) == curve.verifying_key_length + sig = priv.sign(b"data") + assert len(sig) == curve.signature_length + + +@pytest.mark.slow class OpenSSL(unittest.TestCase): # test interoperability with OpenSSL tools. Note that openssl's ECDSA # sign/verify arguments changed between 0.9.8 and 1.0.0: the early @@ -1041,6 +1039,7 @@ def get_openssl_messagedigest_arg(self, hash_name): # vk: 3:OpenSSL->python 4:python->OpenSSL # sig: 5:OpenSSL->python 6:python->OpenSSL + @pytest.mark.slow @pytest.mark.skipif( "secp112r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp112r1", @@ -1048,6 +1047,7 @@ def get_openssl_messagedigest_arg(self, hash_name): def test_from_openssl_secp112r1(self): return self.do_test_from_openssl(SECP112r1) + @pytest.mark.slow @pytest.mark.skipif( "secp112r2" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp112r2", @@ -1055,6 +1055,7 @@ def test_from_openssl_secp112r1(self): def test_from_openssl_secp112r2(self): return self.do_test_from_openssl(SECP112r2) + @pytest.mark.slow @pytest.mark.skipif( "secp128r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp128r1", @@ -1062,6 +1063,7 @@ def test_from_openssl_secp112r2(self): def test_from_openssl_secp128r1(self): return self.do_test_from_openssl(SECP128r1) + @pytest.mark.slow @pytest.mark.skipif( "secp160r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp160r1", @@ -1069,6 +1071,7 @@ def test_from_openssl_secp128r1(self): def test_from_openssl_secp160r1(self): return self.do_test_from_openssl(SECP160r1) + @pytest.mark.slow @pytest.mark.skipif( "prime192v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime192v1", @@ -1076,6 +1079,7 @@ def test_from_openssl_secp160r1(self): def test_from_openssl_nist192p(self): return self.do_test_from_openssl(NIST192p) + @pytest.mark.slow @pytest.mark.skipif( "prime192v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime192v1", @@ -1083,6 +1087,7 @@ def test_from_openssl_nist192p(self): def test_from_openssl_nist192p_sha256(self): return self.do_test_from_openssl(NIST192p, "SHA256") + @pytest.mark.slow @pytest.mark.skipif( "secp224r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp224r1", @@ -1090,6 +1095,7 @@ def test_from_openssl_nist192p_sha256(self): def test_from_openssl_nist224p(self): return self.do_test_from_openssl(NIST224p) + @pytest.mark.slow @pytest.mark.skipif( "prime256v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime256v1", @@ -1097,6 +1103,7 @@ def test_from_openssl_nist224p(self): def test_from_openssl_nist256p(self): return self.do_test_from_openssl(NIST256p) + @pytest.mark.slow @pytest.mark.skipif( "prime256v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime256v1", @@ -1104,6 +1111,7 @@ def test_from_openssl_nist256p(self): def test_from_openssl_nist256p_sha384(self): return self.do_test_from_openssl(NIST256p, "SHA384") + @pytest.mark.slow @pytest.mark.skipif( "prime256v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime256v1", @@ -1111,6 +1119,7 @@ def test_from_openssl_nist256p_sha384(self): def test_from_openssl_nist256p_sha512(self): return self.do_test_from_openssl(NIST256p, "SHA512") + @pytest.mark.slow @pytest.mark.skipif( "secp384r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp384r1", @@ -1118,6 +1127,7 @@ def test_from_openssl_nist256p_sha512(self): def test_from_openssl_nist384p(self): return self.do_test_from_openssl(NIST384p) + @pytest.mark.slow @pytest.mark.skipif( "secp521r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp521r1", @@ -1125,6 +1135,7 @@ def test_from_openssl_nist384p(self): def test_from_openssl_nist521p(self): return self.do_test_from_openssl(NIST521p) + @pytest.mark.slow @pytest.mark.skipif( "secp256k1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp256k1", @@ -1132,6 +1143,7 @@ def test_from_openssl_nist521p(self): def test_from_openssl_secp256k1(self): return self.do_test_from_openssl(SECP256k1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP160r1", @@ -1139,6 +1151,7 @@ def test_from_openssl_secp256k1(self): def test_from_openssl_brainpoolp160r1(self): return self.do_test_from_openssl(BRAINPOOLP160r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP192r1", @@ -1146,6 +1159,7 @@ def test_from_openssl_brainpoolp160r1(self): def test_from_openssl_brainpoolp192r1(self): return self.do_test_from_openssl(BRAINPOOLP192r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP224r1", @@ -1153,6 +1167,7 @@ def test_from_openssl_brainpoolp192r1(self): def test_from_openssl_brainpoolp224r1(self): return self.do_test_from_openssl(BRAINPOOLP224r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP256r1", @@ -1160,6 +1175,7 @@ def test_from_openssl_brainpoolp224r1(self): def test_from_openssl_brainpoolp256r1(self): return self.do_test_from_openssl(BRAINPOOLP256r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP320r1", @@ -1167,6 +1183,7 @@ def test_from_openssl_brainpoolp256r1(self): def test_from_openssl_brainpoolp320r1(self): return self.do_test_from_openssl(BRAINPOOLP320r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP384r1", @@ -1174,6 +1191,7 @@ def test_from_openssl_brainpoolp320r1(self): def test_from_openssl_brainpoolp384r1(self): return self.do_test_from_openssl(BRAINPOOLP384r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP512r1", @@ -1181,6 +1199,7 @@ def test_from_openssl_brainpoolp384r1(self): def test_from_openssl_brainpoolp512r1(self): return self.do_test_from_openssl(BRAINPOOLP512r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP160t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP160t1", @@ -1188,6 +1207,7 @@ def test_from_openssl_brainpoolp512r1(self): def test_from_openssl_brainpoolp160t1(self): return self.do_test_from_openssl(BRAINPOOLP160t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP192t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP192t1", @@ -1195,6 +1215,7 @@ def test_from_openssl_brainpoolp160t1(self): def test_from_openssl_brainpoolp192t1(self): return self.do_test_from_openssl(BRAINPOOLP192t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP224t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP224t1", @@ -1202,6 +1223,7 @@ def test_from_openssl_brainpoolp192t1(self): def test_from_openssl_brainpoolp224t1(self): return self.do_test_from_openssl(BRAINPOOLP224t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP256t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP256t1", @@ -1209,6 +1231,7 @@ def test_from_openssl_brainpoolp224t1(self): def test_from_openssl_brainpoolp256t1(self): return self.do_test_from_openssl(BRAINPOOLP256t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP320t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP320t1", @@ -1216,6 +1239,7 @@ def test_from_openssl_brainpoolp256t1(self): def test_from_openssl_brainpoolp320t1(self): return self.do_test_from_openssl(BRAINPOOLP320t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP384t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP384t1", @@ -1223,6 +1247,7 @@ def test_from_openssl_brainpoolp320t1(self): def test_from_openssl_brainpoolp384t1(self): return self.do_test_from_openssl(BRAINPOOLP384t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP512t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP512t1", @@ -1282,6 +1307,7 @@ def do_test_from_openssl(self, curve, hash_name="SHA1"): sk_from_p8 = SigningKey.from_pem(privkey_p8_pem) self.assertEqual(sk, sk_from_p8) + @pytest.mark.slow @pytest.mark.skipif( "secp112r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp112r1", @@ -1289,6 +1315,7 @@ def do_test_from_openssl(self, curve, hash_name="SHA1"): def test_to_openssl_secp112r1(self): self.do_test_to_openssl(SECP112r1) + @pytest.mark.slow @pytest.mark.skipif( "secp112r2" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp112r2", @@ -1296,6 +1323,7 @@ def test_to_openssl_secp112r1(self): def test_to_openssl_secp112r2(self): self.do_test_to_openssl(SECP112r2) + @pytest.mark.slow @pytest.mark.skipif( "secp128r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp128r1", @@ -1303,6 +1331,7 @@ def test_to_openssl_secp112r2(self): def test_to_openssl_secp128r1(self): self.do_test_to_openssl(SECP128r1) + @pytest.mark.slow @pytest.mark.skipif( "secp160r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp160r1", @@ -1310,6 +1339,7 @@ def test_to_openssl_secp128r1(self): def test_to_openssl_secp160r1(self): self.do_test_to_openssl(SECP160r1) + @pytest.mark.slow @pytest.mark.skipif( "prime192v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime192v1", @@ -1317,6 +1347,7 @@ def test_to_openssl_secp160r1(self): def test_to_openssl_nist192p(self): self.do_test_to_openssl(NIST192p) + @pytest.mark.slow @pytest.mark.skipif( "prime192v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime192v1", @@ -1324,6 +1355,7 @@ def test_to_openssl_nist192p(self): def test_to_openssl_nist192p_sha256(self): self.do_test_to_openssl(NIST192p, "SHA256") + @pytest.mark.slow @pytest.mark.skipif( "secp224r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp224r1", @@ -1331,6 +1363,7 @@ def test_to_openssl_nist192p_sha256(self): def test_to_openssl_nist224p(self): self.do_test_to_openssl(NIST224p) + @pytest.mark.slow @pytest.mark.skipif( "prime256v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime256v1", @@ -1338,6 +1371,7 @@ def test_to_openssl_nist224p(self): def test_to_openssl_nist256p(self): self.do_test_to_openssl(NIST256p) + @pytest.mark.slow @pytest.mark.skipif( "prime256v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime256v1", @@ -1345,6 +1379,7 @@ def test_to_openssl_nist256p(self): def test_to_openssl_nist256p_sha384(self): self.do_test_to_openssl(NIST256p, "SHA384") + @pytest.mark.slow @pytest.mark.skipif( "prime256v1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support prime256v1", @@ -1352,6 +1387,7 @@ def test_to_openssl_nist256p_sha384(self): def test_to_openssl_nist256p_sha512(self): self.do_test_to_openssl(NIST256p, "SHA512") + @pytest.mark.slow @pytest.mark.skipif( "secp384r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp384r1", @@ -1359,6 +1395,7 @@ def test_to_openssl_nist256p_sha512(self): def test_to_openssl_nist384p(self): self.do_test_to_openssl(NIST384p) + @pytest.mark.slow @pytest.mark.skipif( "secp521r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp521r1", @@ -1366,6 +1403,7 @@ def test_to_openssl_nist384p(self): def test_to_openssl_nist521p(self): self.do_test_to_openssl(NIST521p) + @pytest.mark.slow @pytest.mark.skipif( "secp256k1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support secp256k1", @@ -1373,6 +1411,7 @@ def test_to_openssl_nist521p(self): def test_to_openssl_secp256k1(self): self.do_test_to_openssl(SECP256k1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP160r1", @@ -1380,6 +1419,7 @@ def test_to_openssl_secp256k1(self): def test_to_openssl_brainpoolp160r1(self): self.do_test_to_openssl(BRAINPOOLP160r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP192r1", @@ -1387,6 +1427,7 @@ def test_to_openssl_brainpoolp160r1(self): def test_to_openssl_brainpoolp192r1(self): self.do_test_to_openssl(BRAINPOOLP192r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP224r1", @@ -1394,6 +1435,7 @@ def test_to_openssl_brainpoolp192r1(self): def test_to_openssl_brainpoolp224r1(self): self.do_test_to_openssl(BRAINPOOLP224r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP256r1", @@ -1401,6 +1443,7 @@ def test_to_openssl_brainpoolp224r1(self): def test_to_openssl_brainpoolp256r1(self): self.do_test_to_openssl(BRAINPOOLP256r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP320r1", @@ -1408,6 +1451,7 @@ def test_to_openssl_brainpoolp256r1(self): def test_to_openssl_brainpoolp320r1(self): self.do_test_to_openssl(BRAINPOOLP320r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP384r1", @@ -1415,6 +1459,7 @@ def test_to_openssl_brainpoolp320r1(self): def test_to_openssl_brainpoolp384r1(self): self.do_test_to_openssl(BRAINPOOLP384r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP512r1", @@ -1422,6 +1467,7 @@ def test_to_openssl_brainpoolp384r1(self): def test_to_openssl_brainpoolp512r1(self): self.do_test_to_openssl(BRAINPOOLP512r1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP160t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP160t1", @@ -1429,6 +1475,7 @@ def test_to_openssl_brainpoolp512r1(self): def test_to_openssl_brainpoolp160t1(self): self.do_test_to_openssl(BRAINPOOLP160t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP192t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP192t1", @@ -1436,6 +1483,7 @@ def test_to_openssl_brainpoolp160t1(self): def test_to_openssl_brainpoolp192t1(self): self.do_test_to_openssl(BRAINPOOLP192t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP224t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP224t1", @@ -1443,6 +1491,7 @@ def test_to_openssl_brainpoolp192t1(self): def test_to_openssl_brainpoolp224t1(self): self.do_test_to_openssl(BRAINPOOLP224t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP256t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP256t1", @@ -1450,6 +1499,7 @@ def test_to_openssl_brainpoolp224t1(self): def test_to_openssl_brainpoolp256t1(self): self.do_test_to_openssl(BRAINPOOLP256t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP320t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP320t1", @@ -1457,6 +1507,7 @@ def test_to_openssl_brainpoolp256t1(self): def test_to_openssl_brainpoolp320t1(self): self.do_test_to_openssl(BRAINPOOLP320t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP384t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP384t1", @@ -1464,6 +1515,7 @@ def test_to_openssl_brainpoolp320t1(self): def test_to_openssl_brainpoolp384t1(self): self.do_test_to_openssl(BRAINPOOLP384t1) + @pytest.mark.slow @pytest.mark.skipif( "brainpoolP512t1" not in OPENSSL_SUPPORTED_CURVES, reason="system openssl does not support brainpoolP512t1", @@ -1604,6 +1656,7 @@ def do_eddsa_test_to_openssl(self, curve): # in practice at least OpenSSL 3.0.0 is needed to make EdDSA signatures # earlier versions support EdDSA only in X.509 certificates + @pytest.mark.slow @pytest.mark.skipif( "ed25519" not in OPENSSL_SUPPORTED_TYPES, reason="system openssl does not support signing with Ed25519", @@ -1611,6 +1664,7 @@ def do_eddsa_test_to_openssl(self, curve): def test_to_openssl_ed25519(self): return self.do_eddsa_test_to_openssl(Ed25519) + @pytest.mark.slow @pytest.mark.skipif( "ed448" not in OPENSSL_SUPPORTED_TYPES, reason="system openssl does not support signing with Ed448", @@ -1654,6 +1708,7 @@ def do_eddsa_test_from_openssl(self, curve): shutil.rmtree("t") + @pytest.mark.slow @pytest.mark.skipif( "ed25519" not in OPENSSL_SUPPORTED_TYPES, reason="system openssl does not support signing with Ed25519", @@ -1661,6 +1716,7 @@ def do_eddsa_test_from_openssl(self, curve): def test_from_openssl_ed25519(self): return self.do_eddsa_test_from_openssl(Ed25519) + @pytest.mark.slow @pytest.mark.skipif( "ed448" not in OPENSSL_SUPPORTED_TYPES, reason="system openssl does not support signing with Ed448", @@ -1779,6 +1835,7 @@ def test_constructed(self): class Util(unittest.TestCase): + @pytest.mark.slow def test_trytryagain(self): tta = util.randrange_from_seed__trytryagain for i in range(1000): @@ -1808,9 +1865,10 @@ def test_trytryagain_single(self): # known issue: https://github.com/warner/python-ecdsa/issues/221 if sys.version_info < (3, 0): # pragma: no branch self.assertEqual(n, 228) - else: + else: # pragma: no branch self.assertEqual(n, 18) + @settings(**HYP_SETTINGS) @given(st.integers(min_value=0, max_value=10**200)) def test_randrange(self, i): # util.randrange does not provide long-term stability: we might @@ -2143,6 +2201,7 @@ def test_brainpoolP256r1(self): ), ) + @pytest.mark.slow def test_brainpoolP384r1(self): self._do( curve=curve_brainpoolp384r1, @@ -2189,6 +2248,7 @@ def test_brainpoolP384r1(self): ), ) + @pytest.mark.slow def test_brainpoolP512r1(self): self._do( curve=curve_brainpoolp512r1, @@ -2293,6 +2353,7 @@ def test_brainpoolP256r1(self): ), ) + @pytest.mark.slow def test_brainpoolP384r1(self): self._do( curve=curve_brainpoolp384r1, @@ -2339,6 +2400,7 @@ def test_brainpoolP384r1(self): ), ) + @pytest.mark.slow def test_brainpoolP512r1(self): self._do( curve=curve_brainpoolp512r1,