Skip to content

Commit feecedd

Browse files
authored
Merge pull request #127 from tomato42/jacobi
Turbocharge by using Jacobi representation, 2-ary NAF and precomputation
2 parents 34864b1 + a67da69 commit feecedd

18 files changed

+1258
-301
lines changed

.coveragerc

-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@ include =
55
src/ecdsa/*
66
omit =
77
src/ecdsa/_version.py
8-
src/ecdsa/test_*

.travis.yml

+27-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ dist: trusty
44
sudo: false
55
language: python
66
cache: pip
7+
addons:
8+
apt_packages:
9+
# needed for gmpy and gmpy2
10+
- libgmp-dev
11+
- libmpfr-dev
12+
- libmpc-dev
713
before_cache:
814
- rm -f $HOME/.cache/pip/log/debug.log
915
# place the slowest (instrumental and py2.6) first
@@ -15,8 +21,16 @@ matrix:
1521
env: TOX_ENV=py26
1622
- python: 2.7
1723
env: TOX_ENV=py27
24+
- python: 2.7
25+
env: TOX_ENV=py27_old_gmpy
26+
- python: 2.7
27+
env: TOX_ENV=py27_old_gmpy2
1828
- python: 2.7
1929
env: TOX_ENV=py27_old_six
30+
- python: 2.7
31+
env: TOX_ENV=gmpypy27
32+
- python: 2.7
33+
env: TOX_ENV=gmpy2py27
2034
- python: 3.3
2135
env: TOX_ENV=py33
2236
- python: 3.4
@@ -33,6 +47,14 @@ matrix:
3347
env: TOX_ENV=py38
3448
dist: xenial
3549
sudo: true
50+
- python: 3.8
51+
env: TOX_ENV=gmpypy38
52+
dist: xenial
53+
sudo: true
54+
- python: 3.8
55+
env: TOX_ENV=gmpy2py38
56+
dist: xenial
57+
sudo: true
3658
- python: nightly
3759
env: TOX_ENV=py
3860
dist: xenial
@@ -75,11 +97,15 @@ install:
7597
else
7698
travis_retry pip install -r build-requirements.txt;
7799
fi
100+
- if [[ $TOX_ENV =~ gmpy2 ]]; then travis_retry pip install gmpy2; fi
101+
- if [[ $TOX_ENV =~ gmpyp ]]; then travis_retry pip install gmpy; fi
78102
- if [[ $INSTRUMENTAL ]]; then travis_retry pip install instrumental; fi
79103
- pip list
80104
script:
81105
- if [[ $TOX_ENV ]]; then tox -e $TOX_ENV; fi
82-
- tox -e speed
106+
- if [[ $TOX_ENV =~ gmpy2 ]]; then tox -e speedgmpy2; fi
107+
- if [[ $TOX_ENV =~ gmpyp ]]; then tox -e speedgmpy; fi
108+
- if ! [[ $TOX_ENV =~ gmpy ]]; then tox -e speed; fi
83109
- |
84110
if [[ $INSTRUMENTAL && $TRAVIS_PULL_REQUEST != "false" ]]; then
85111
git checkout $PR_FIRST^

README.md

+70-18
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,38 @@ curves over prime fields.
3232

3333
This library uses only Python and the 'six' package. It is compatible with
3434
Python 2.6, 2.7 and 3.3+. It also supports execution on the alternative
35-
implementations like pypy and pypy3
35+
implementations like pypy and pypy3.
36+
37+
If `gmpy2` or `gmpy` is installed, they will be used for faster arithmetic.
38+
Either of them can be installed after this library is installed,
39+
`python-ecdsa` will detect their presence on start-up and use them
40+
automatically.
3641

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

46+
47+
## Installation
48+
49+
This library is available on PyPI, it's recommended to install it using `pip`:
50+
51+
```
52+
pip install ecdsa
53+
```
54+
55+
In case higher performance is wanted and using native code is not a problem,
56+
it's possible to specify installation together with `gmpy2`:
57+
58+
```
59+
pip install ecdsa[gmpy2]
60+
```
61+
62+
or (slower, legacy option):
63+
```
64+
pip install ecdsa[gmpy]
65+
```
66+
4167
## Speed
4268

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

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

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

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

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

106158
tox -e coverage
107159

108-
On an Intel Core i7 4790K @ 4.0GHz, the tests take about 150 seconds to execute.
160+
On an Intel Core i7 4790K @ 4.0GHz, the tests take about 16 seconds to execute.
109161
The test suite uses
110162
[`hypothesis`](https://github.com/HypothesisWorks/hypothesis) so there is some
111163
inherent variability in the test suite execution time.

setup.py

+4
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,8 @@
4141
"Programming Language :: Python :: 3.8",
4242
],
4343
install_requires=['six>=1.9.0'],
44+
extras_require={
45+
'gmpy2': 'gmpy2',
46+
'gmpy': 'gmpy',
47+
},
4448
)

speed.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,15 @@ def do(setup_statements, statement):
3030
S3 = "msg = six.b('msg')"
3131
S4 = "sig = sk.sign(msg)"
3232
S5 = "vk = sk.get_verifying_key()"
33-
S6 = "vk.verify(sig, msg)"
33+
S6 = "vk.precompute()"
34+
S7 = "vk.verify(sig, msg)"
3435
# We happen to know that .generate() also calculates the
3536
# verifying key, which is the time-consuming part. If the code
3637
# were changed to lazily calculate vk, we'd need to change this
3738
# benchmark to loop over S5 instead of S2
3839
keygen = do([S1], S2)
3940
sign = do([S1,S2,S3], S4)
40-
verf = do([S1,S2,S3,S4,S5], S6)
41+
verf = do([S1,S2,S3,S4,S5,S6], S7)
4142
import ecdsa
4243
c = getattr(ecdsa, curve)
4344
sig = ecdsa.SigningKey.generate(c).sign(six.b("msg"))

src/ecdsa/curves.py

+14
Original file line numberDiff line numberDiff line change
@@ -63,36 +63,50 @@ def __repr__(self):
6363
SECP256k1 = Curve("SECP256k1", ecdsa.curve_secp256k1,
6464
ecdsa.generator_secp256k1,
6565
(1, 3, 132, 0, 10), "secp256k1")
66+
67+
6668
BRAINPOOLP160r1 = Curve("BRAINPOOLP160r1",
6769
ecdsa.curve_brainpoolp160r1,
6870
ecdsa.generator_brainpoolp160r1,
6971
(1, 3, 36, 3, 3, 2, 8, 1, 1, 1),
7072
"brainpoolP160r1")
73+
74+
7175
BRAINPOOLP192r1 = Curve("BRAINPOOLP192r1",
7276
ecdsa.curve_brainpoolp192r1,
7377
ecdsa.generator_brainpoolp192r1,
7478
(1, 3, 36, 3, 3, 2, 8, 1, 1, 3),
7579
"brainpoolP192r1")
80+
81+
7682
BRAINPOOLP224r1 = Curve("BRAINPOOLP224r1",
7783
ecdsa.curve_brainpoolp224r1,
7884
ecdsa.generator_brainpoolp224r1,
7985
(1, 3, 36, 3, 3, 2, 8, 1, 1, 5),
8086
"brainpoolP224r1")
87+
88+
8189
BRAINPOOLP256r1 = Curve("BRAINPOOLP256r1",
8290
ecdsa.curve_brainpoolp256r1,
8391
ecdsa.generator_brainpoolp256r1,
8492
(1, 3, 36, 3, 3, 2, 8, 1, 1, 7),
8593
"brainpoolP256r1")
94+
95+
8696
BRAINPOOLP320r1 = Curve("BRAINPOOLP320r1",
8797
ecdsa.curve_brainpoolp320r1,
8898
ecdsa.generator_brainpoolp320r1,
8999
(1, 3, 36, 3, 3, 2, 8, 1, 1, 9),
90100
"brainpoolP320r1")
101+
102+
91103
BRAINPOOLP384r1 = Curve("BRAINPOOLP384r1",
92104
ecdsa.curve_brainpoolp384r1,
93105
ecdsa.generator_brainpoolp384r1,
94106
(1, 3, 36, 3, 3, 2, 8, 1, 1, 11),
95107
"brainpoolP384r1")
108+
109+
96110
BRAINPOOLP512r1 = Curve("BRAINPOOLP512r1",
97111
ecdsa.curve_brainpoolp512r1,
98112
ecdsa.generator_brainpoolp512r1,

0 commit comments

Comments
 (0)