Skip to content

Commit

Permalink
Merge pull request #127 from tomato42/jacobi
Browse files Browse the repository at this point in the history
Turbocharge by using Jacobi representation, 2-ary NAF and precomputation
  • Loading branch information
tomato42 committed Dec 2, 2019
2 parents 34864b1 + a67da69 commit feecedd
Show file tree
Hide file tree
Showing 18 changed files with 1,258 additions and 301 deletions.
1 change: 0 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ include =
src/ecdsa/*
omit =
src/ecdsa/_version.py
src/ecdsa/test_*
28 changes: 27 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ dist: trusty
sudo: false
language: python
cache: pip
addons:
apt_packages:
# needed for gmpy and gmpy2
- libgmp-dev
- libmpfr-dev
- libmpc-dev
before_cache:
- rm -f $HOME/.cache/pip/log/debug.log
# place the slowest (instrumental and py2.6) first
Expand All @@ -15,8 +21,16 @@ matrix:
env: TOX_ENV=py26
- python: 2.7
env: TOX_ENV=py27
- python: 2.7
env: TOX_ENV=py27_old_gmpy
- python: 2.7
env: TOX_ENV=py27_old_gmpy2
- python: 2.7
env: TOX_ENV=py27_old_six
- python: 2.7
env: TOX_ENV=gmpypy27
- python: 2.7
env: TOX_ENV=gmpy2py27
- python: 3.3
env: TOX_ENV=py33
- python: 3.4
Expand All @@ -33,6 +47,14 @@ matrix:
env: TOX_ENV=py38
dist: xenial
sudo: true
- python: 3.8
env: TOX_ENV=gmpypy38
dist: xenial
sudo: true
- python: 3.8
env: TOX_ENV=gmpy2py38
dist: xenial
sudo: true
- python: nightly
env: TOX_ENV=py
dist: xenial
Expand Down Expand Up @@ -75,11 +97,15 @@ install:
else
travis_retry pip install -r build-requirements.txt;
fi
- if [[ $TOX_ENV =~ gmpy2 ]]; 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
- pip list
script:
- if [[ $TOX_ENV ]]; then tox -e $TOX_ENV; fi
- tox -e speed
- 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 [[ $INSTRUMENTAL && $TRAVIS_PULL_REQUEST != "false" ]]; then
git checkout $PR_FIRST^
Expand Down
88 changes: 70 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,38 @@ curves over prime fields.

This library uses only Python and the 'six' package. It is compatible with
Python 2.6, 2.7 and 3.3+. It also supports execution on the alternative
implementations like pypy and pypy3
implementations like pypy and pypy3.

If `gmpy2` or `gmpy` is installed, they will be used for faster arithmetic.
Either of them can be installed after this library is installed,
`python-ecdsa` will detect their presence on start-up and use them
automatically.

To run the OpenSSL compatibility tests, the 'openssl' tool must be in your
`PATH`. This release has been tested successfully against OpenSSL 0.9.8o,
1.0.0a, 1.0.2f and 1.1.1d (among others).


## Installation

This library is available on PyPI, it's recommended to install it using `pip`:

```
pip install ecdsa
```

In case higher performance is wanted and using native code is not a problem,
it's possible to specify installation together with `gmpy2`:

```
pip install ecdsa[gmpy2]
```

or (slower, legacy option):
```
pip install ecdsa[gmpy]
```

## Speed

The following table shows how long this library takes to generate keypairs
Expand All @@ -53,31 +79,55 @@ On an Intel Core i7 4790K @ 4.0GHz I'm getting the following performance:

```
siglen keygen keygen/s sign sign/s verify verify/s
NIST192p: 48 0.01534s 65.18 0.00833s 120.05 0.01601s 62.48
NIST224p: 56 0.02107s 47.46 0.01153s 86.74 0.02220s 45.05
NIST256p: 64 0.02824s 35.40 0.01523s 65.66 0.02965s 33.73
NIST384p: 96 0.06640s 15.06 0.03572s 27.99 0.06973s 14.34
NIST521p: 132 0.13150s 7.60 0.07094s 14.10 0.13869s 7.21
SECP256k1: 64 0.02807s 35.63 0.01525s 65.58 0.02964s 33.74
BRAINPOOLP160r1: 40 0.01100s 90.88 0.00564s 177.45 0.01053s 94.92
BRAINPOOLP192r1: 48 0.01633s 61.25 0.00833s 120.05 0.01591s 62.84
BRAINPOOLP224r1: 56 0.02261s 44.23 0.01153s 86.76 0.02234s 44.77
BRAINPOOLP256r1: 64 0.02997s 33.36 0.01532s 65.29 0.03084s 32.42
BRAINPOOLP320r1: 80 0.04774s 20.95 0.02447s 40.86 0.04717s 21.20
BRAINPOOLP384r1: 96 0.07007s 14.27 0.03566s 28.04 0.07056s 14.17
BRAINPOOLP512r1: 128 0.13390s 7.47 0.06766s 14.78 0.13425s 7.45
NIST192p: 48 0.00033s 3062.43 0.00036s 2748.08 0.00062s 1605.66
NIST224p: 56 0.00041s 2424.07 0.00046s 2196.71 0.00083s 1205.81
NIST256p: 64 0.00053s 1892.05 0.00058s 1735.23 0.00106s 944.82
NIST384p: 96 0.00110s 904.98 0.00118s 847.82 0.00217s 460.26
NIST521p: 132 0.00234s 428.24 0.00245s 408.54 0.00443s 225.95
SECP256k1: 64 0.00053s 1891.93 0.00058s 1734.46 0.00109s 913.35
BRAINPOOLP160r1: 40 0.00025s 3982.49 0.00029s 3490.15 0.00053s 1878.51
BRAINPOOLP192r1: 48 0.00032s 3086.07 0.00036s 2761.68 0.00063s 1578.22
BRAINPOOLP224r1: 56 0.00041s 2412.41 0.00046s 2185.52 0.00076s 1311.65
BRAINPOOLP256r1: 64 0.00054s 1866.84 0.00058s 1719.45 0.00110s 906.85
BRAINPOOLP320r1: 80 0.00077s 1306.97 0.00083s 1201.59 0.00158s 632.82
BRAINPOOLP384r1: 96 0.00112s 892.44 0.00119s 841.48 0.00229s 436.71
BRAINPOOLP512r1: 128 0.00214s 467.05 0.00226s 441.64 0.00422s 237.13
```
To test performance with `gmpy2` loaded, use `tox -e speedgmpy2`.
On the same machine I'm getting the following performance with `gmpy2`:
```
siglen keygen keygen/s sign sign/s verify verify/s
NIST192p: 48 0.00016s 6180.12 0.00017s 5846.30 0.00033s 3029.51
NIST224p: 56 0.00021s 4861.86 0.00021s 4662.63 0.00042s 2366.47
NIST256p: 64 0.00023s 4343.93 0.00024s 4152.79 0.00047s 2148.83
NIST384p: 96 0.00040s 2507.97 0.00041s 2435.99 0.00079s 1260.01
NIST521p: 132 0.00088s 1135.13 0.00089s 1121.94 0.00139s 721.07
SECP256k1: 64 0.00023s 4360.83 0.00024s 4147.61 0.00044s 2254.53
BRAINPOOLP160r1: 40 0.00014s 7261.37 0.00015s 6824.47 0.00031s 3248.21
BRAINPOOLP192r1: 48 0.00016s 6219.18 0.00017s 5862.93 0.00034s 2933.74
BRAINPOOLP224r1: 56 0.00021s 4876.48 0.00022s 4640.40 0.00041s 2413.48
BRAINPOOLP256r1: 64 0.00023s 4397.89 0.00024s 4178.48 0.00044s 2272.76
BRAINPOOLP320r1: 80 0.00031s 3246.64 0.00032s 3138.38 0.00063s 1593.14
BRAINPOOLP384r1: 96 0.00040s 2491.04 0.00041s 2421.67 0.00079s 1262.64
BRAINPOOLP512r1: 128 0.00062s 1618.30 0.00063s 1577.42 0.00125s 799.29
```

(there's also `gmpy` version, execute it using `tox -e speedgmpy`)

For comparison, a highly optimised implementation (including curve-specific
assemply) like OpenSSL provides following performance numbers on the same
machine. Run `openssl speed` to reproduce it:
assembly for some curves), like the one in OpenSSL, provides following
performance numbers on the same machine.
Run `openssl speed ecdsa` to reproduce it:
```
sign verify sign/s verify/s
192 bits ecdsa (nistp192) 0.0002s 0.0002s 4785.6 5380.7
224 bits ecdsa (nistp224) 0.0000s 0.0001s 22475.6 9822.0
256 bits ecdsa (nistp256) 0.0000s 0.0001s 45069.6 14166.6
384 bits ecdsa (nistp384) 0.0008s 0.0006s 1265.6 1648.1
521 bits ecdsa (nistp521) 0.0003s 0.0005s 3753.1 1819.5
256 bits ecdsa (brainpoolP256r1) 0.0003s 0.0003s 2983.5 3333.2
384 bits ecdsa (brainpoolP384r1) 0.0008s 0.0007s 1258.8 1528.1
512 bits ecdsa (brainpoolP512r1) 0.0015s 0.0012s 675.1 860.1
```

Keys and signature can be serialized in different ways (see Usage, below).
Expand All @@ -94,7 +144,9 @@ following lengths (in bytes):
In 2006, Peter Pearson announced his pure-python implementation of ECDSA in a
[message to sci.crypt][1], available from his [download site][2]. In 2010,
Brian Warner wrote a wrapper around this code, to make it a bit easier and
safer to use. You are looking at the README for this wrapper.
safer to use. Hubert Kario then included an implementation of elliptic curve
cryptography that uses Jacobian coordinates internally, improving performance
about 20-fold. You are looking at the README for this wrapper.

[1]: http://www.derkeiler.com/Newsgroups/sci.crypt/2006-01/msg00651.html
[2]: http://webpages.charter.net/curryfans/peter/downloads.html
Expand All @@ -105,7 +157,7 @@ To run the full test suite, do this:

tox -e coverage

On an Intel Core i7 4790K @ 4.0GHz, the tests take about 150 seconds to execute.
On an Intel Core i7 4790K @ 4.0GHz, the tests take about 16 seconds to execute.
The test suite uses
[`hypothesis`](https://github.com/HypothesisWorks/hypothesis) so there is some
inherent variability in the test suite execution time.
Expand Down
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@
"Programming Language :: Python :: 3.8",
],
install_requires=['six>=1.9.0'],
extras_require={
'gmpy2': 'gmpy2',
'gmpy': 'gmpy',
},
)
5 changes: 3 additions & 2 deletions speed.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ def do(setup_statements, statement):
S3 = "msg = six.b('msg')"
S4 = "sig = sk.sign(msg)"
S5 = "vk = sk.get_verifying_key()"
S6 = "vk.verify(sig, msg)"
S6 = "vk.precompute()"
S7 = "vk.verify(sig, msg)"
# We happen to know that .generate() also calculates the
# verifying key, which is the time-consuming part. If the code
# were changed to lazily calculate vk, we'd need to change this
# benchmark to loop over S5 instead of S2
keygen = do([S1], S2)
sign = do([S1,S2,S3], S4)
verf = do([S1,S2,S3,S4,S5], S6)
verf = do([S1,S2,S3,S4,S5,S6], S7)
import ecdsa
c = getattr(ecdsa, curve)
sig = ecdsa.SigningKey.generate(c).sign(six.b("msg"))
Expand Down
14 changes: 14 additions & 0 deletions src/ecdsa/curves.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,36 +63,50 @@ def __repr__(self):
SECP256k1 = Curve("SECP256k1", ecdsa.curve_secp256k1,
ecdsa.generator_secp256k1,
(1, 3, 132, 0, 10), "secp256k1")


BRAINPOOLP160r1 = Curve("BRAINPOOLP160r1",
ecdsa.curve_brainpoolp160r1,
ecdsa.generator_brainpoolp160r1,
(1, 3, 36, 3, 3, 2, 8, 1, 1, 1),
"brainpoolP160r1")


BRAINPOOLP192r1 = Curve("BRAINPOOLP192r1",
ecdsa.curve_brainpoolp192r1,
ecdsa.generator_brainpoolp192r1,
(1, 3, 36, 3, 3, 2, 8, 1, 1, 3),
"brainpoolP192r1")


BRAINPOOLP224r1 = Curve("BRAINPOOLP224r1",
ecdsa.curve_brainpoolp224r1,
ecdsa.generator_brainpoolp224r1,
(1, 3, 36, 3, 3, 2, 8, 1, 1, 5),
"brainpoolP224r1")


BRAINPOOLP256r1 = Curve("BRAINPOOLP256r1",
ecdsa.curve_brainpoolp256r1,
ecdsa.generator_brainpoolp256r1,
(1, 3, 36, 3, 3, 2, 8, 1, 1, 7),
"brainpoolP256r1")


BRAINPOOLP320r1 = Curve("BRAINPOOLP320r1",
ecdsa.curve_brainpoolp320r1,
ecdsa.generator_brainpoolp320r1,
(1, 3, 36, 3, 3, 2, 8, 1, 1, 9),
"brainpoolP320r1")


BRAINPOOLP384r1 = Curve("BRAINPOOLP384r1",
ecdsa.curve_brainpoolp384r1,
ecdsa.generator_brainpoolp384r1,
(1, 3, 36, 3, 3, 2, 8, 1, 1, 11),
"brainpoolP384r1")


BRAINPOOLP512r1 = Curve("BRAINPOOLP512r1",
ecdsa.curve_brainpoolp512r1,
ecdsa.generator_brainpoolp512r1,
Expand Down

0 comments on commit feecedd

Please sign in to comment.