Skip to content

Commit 23a5b65

Browse files
authored
Merge pull request #213 from tomato42/precompute-updates
Precompute-related updates
2 parents 73f3d3e + 0dc17f0 commit 23a5b65

File tree

6 files changed

+179
-85
lines changed

6 files changed

+179
-85
lines changed

.travis.yml

+10-6
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,23 @@ matrix:
4444
dist: bionic
4545
sudo: true
4646
- python: 3.8
47+
env: TOX_ENV=py38
48+
dist: bionic
49+
sudo: true
50+
- python: 3.9
4751
env: TOX_ENV=codechecks
4852
dist: bionic
4953
sudo: true
50-
- python: 3.8
51-
env: TOX_ENV=py38
54+
- python: 3.9
55+
env: TOX_ENV=py39
5256
dist: bionic
5357
sudo: true
54-
- python: 3.8
55-
env: TOX_ENV=gmpypy38
58+
- python: 3.9
59+
env: TOX_ENV=gmpypy39
5660
dist: bionic
5761
sudo: true
58-
- python: 3.8
59-
env: TOX_ENV=gmpy2py38
62+
- python: 3.9
63+
env: TOX_ENV=gmpy2py39
6064
dist: bionic
6165
sudo: true
6266
- python: nightly

src/ecdsa/ellipticcurve.py

+33-33
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def __init__(self, curve, x, y, z, order=None, generator=False):
163163
self.__curve = curve
164164
# since it's generally better (faster) to use scaled points vs unscaled
165165
# ones, use writer-biased RWLock for locking:
166-
self._scale_lock = RWLock()
166+
self._update_lock = RWLock()
167167
if GMPY:
168168
self.__x = mpz(x)
169169
self.__y = mpz(y)
@@ -180,16 +180,16 @@ def __init__(self, curve, x, y, z, order=None, generator=False):
180180
def _maybe_precompute(self):
181181
if self.__generator:
182182
# since we lack promotion of read-locks to write-locks, we do a
183-
# "acquire-read-lock check, acquire-write-lock plus recheck" cycle
183+
# "acquire-read-lock, check, acquire-write-lock plus recheck" cycle
184184
try:
185-
self._scale_lock.reader_acquire()
185+
self._update_lock.reader_acquire()
186186
if self.__precompute:
187187
return
188188
finally:
189-
self._scale_lock.reader_release()
189+
self._update_lock.reader_release()
190190

191191
try:
192-
self._scale_lock.writer_acquire()
192+
self._update_lock.writer_acquire()
193193
if self.__precompute:
194194
return
195195
order = self.__order
@@ -208,38 +208,38 @@ def _maybe_precompute(self):
208208
self.__precompute.append((doubler.x(), doubler.y()))
209209

210210
finally:
211-
self._scale_lock.writer_release()
211+
self._update_lock.writer_release()
212212

213213
def __getstate__(self):
214214
try:
215-
self._scale_lock.reader_acquire()
215+
self._update_lock.reader_acquire()
216216
state = self.__dict__.copy()
217217
finally:
218-
self._scale_lock.reader_release()
219-
del state["_scale_lock"]
218+
self._update_lock.reader_release()
219+
del state["_update_lock"]
220220
return state
221221

222222
def __setstate__(self, state):
223223
self.__dict__.update(state)
224-
self._scale_lock = RWLock()
224+
self._update_lock = RWLock()
225225

226226
def __eq__(self, other):
227227
"""Compare two points with each-other."""
228228
try:
229-
self._scale_lock.reader_acquire()
229+
self._update_lock.reader_acquire()
230230
if other is INFINITY:
231231
return not self.__y or not self.__z
232232
x1, y1, z1 = self.__x, self.__y, self.__z
233233
finally:
234-
self._scale_lock.reader_release()
234+
self._update_lock.reader_release()
235235
if isinstance(other, Point):
236236
x2, y2, z2 = other.x(), other.y(), 1
237237
elif isinstance(other, PointJacobi):
238238
try:
239-
other._scale_lock.reader_acquire()
239+
other._update_lock.reader_acquire()
240240
x2, y2, z2 = other.__x, other.__y, other.__z
241241
finally:
242-
other._scale_lock.reader_release()
242+
other._update_lock.reader_release()
243243
else:
244244
return NotImplemented
245245
if self.__curve != other.curve():
@@ -277,13 +277,13 @@ def x(self):
277277
and then x() and y() on the returned instance.
278278
"""
279279
try:
280-
self._scale_lock.reader_acquire()
280+
self._update_lock.reader_acquire()
281281
if self.__z == 1:
282282
return self.__x
283283
x = self.__x
284284
z = self.__z
285285
finally:
286-
self._scale_lock.reader_release()
286+
self._update_lock.reader_release()
287287
p = self.__curve.p()
288288
z = numbertheory.inverse_mod(z, p)
289289
return x * z ** 2 % p
@@ -298,13 +298,13 @@ def y(self):
298298
and then x() and y() on the returned instance.
299299
"""
300300
try:
301-
self._scale_lock.reader_acquire()
301+
self._update_lock.reader_acquire()
302302
if self.__z == 1:
303303
return self.__y
304304
y = self.__y
305305
z = self.__z
306306
finally:
307-
self._scale_lock.reader_release()
307+
self._update_lock.reader_release()
308308
p = self.__curve.p()
309309
z = numbertheory.inverse_mod(z, p)
310310
return y * z ** 3 % p
@@ -316,14 +316,14 @@ def scale(self):
316316
Modifies point in place, returns self.
317317
"""
318318
try:
319-
self._scale_lock.reader_acquire()
319+
self._update_lock.reader_acquire()
320320
if self.__z == 1:
321321
return self
322322
finally:
323-
self._scale_lock.reader_release()
323+
self._update_lock.reader_release()
324324

325325
try:
326-
self._scale_lock.writer_acquire()
326+
self._update_lock.writer_acquire()
327327
# scaling already scaled point is safe (as inverse of 1 is 1) and
328328
# quick so we don't need to optimise for the unlikely event when
329329
# two threads hit the lock at the same time
@@ -336,7 +336,7 @@ def scale(self):
336336
# true only after all values were already updated
337337
self.__z = 1
338338
finally:
339-
self._scale_lock.writer_release()
339+
self._update_lock.writer_release()
340340
return self
341341

342342
def to_affine(self):
@@ -415,10 +415,10 @@ def double(self):
415415
p, a = self.__curve.p(), self.__curve.a()
416416

417417
try:
418-
self._scale_lock.reader_acquire()
418+
self._update_lock.reader_acquire()
419419
X1, Y1, Z1 = self.__x, self.__y, self.__z
420420
finally:
421-
self._scale_lock.reader_release()
421+
self._update_lock.reader_release()
422422

423423
X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a)
424424

@@ -533,15 +533,15 @@ def __add__(self, other):
533533

534534
p = self.__curve.p()
535535
try:
536-
self._scale_lock.reader_acquire()
536+
self._update_lock.reader_acquire()
537537
X1, Y1, Z1 = self.__x, self.__y, self.__z
538538
finally:
539-
self._scale_lock.reader_release()
539+
self._update_lock.reader_release()
540540
try:
541-
other._scale_lock.reader_acquire()
541+
other._update_lock.reader_acquire()
542542
X2, Y2, Z2 = other.__x, other.__y, other.__z
543543
finally:
544-
other._scale_lock.reader_release()
544+
other._update_lock.reader_release()
545545
X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p)
546546

547547
if not Y3 or not Z3:
@@ -632,10 +632,10 @@ def _leftmost_bit(x):
632632

633633
def mul_add(self, self_mul, other, other_mul):
634634
"""
635-
Do two multiplications at the same time, add results.
635+
Do two multiplications at the same time, add results.
636636
637-
calculates self*self_mul + other*other_mul
638-
"""
637+
calculates self*self_mul + other*other_mul
638+
"""
639639
if other is INFINITY or other_mul == 0:
640640
return self * self_mul
641641
if self_mul == 0:
@@ -688,12 +688,12 @@ def mul_add(self, self_mul, other, other_mul):
688688
def __neg__(self):
689689
"""Return negated point."""
690690
try:
691-
self._scale_lock.reader_acquire()
691+
self._update_lock.reader_acquire()
692692
return PointJacobi(
693693
self.__curve, self.__x, -self.__y, self.__z, self.__order
694694
)
695695
finally:
696-
self._scale_lock.reader_release()
696+
self._update_lock.reader_release()
697697

698698

699699
class Point(object):

src/ecdsa/keys.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,33 @@ def from_public_point(
201201
self.pubkey.order = curve.order
202202
return self
203203

204-
def precompute(self):
204+
def precompute(self, lazy=False):
205+
"""
206+
Precompute multiplication tables for faster signature verification.
207+
208+
Calling this method will cause the library to precompute the
209+
scalar multiplication tables, used in signature verification.
210+
While it's an expensive operation (comparable to performing
211+
as many signatures as the bit size of the curve, i.e. 256 for NIST256p)
212+
it speeds up verification 2 times. You should call this method
213+
if you expect to verify hundreds of signatures (or more) using the same
214+
VerifyingKey object.
215+
216+
Note: You should call this method only once, this method generates a
217+
new precomputation table every time it's called.
218+
219+
:param bool lazy: whether to calculate the precomputation table now
220+
(if set to False) or if it should be delayed to the time of first
221+
use (when set to True)
222+
"""
205223
self.pubkey.point = ellipticcurve.PointJacobi.from_affine(
206224
self.pubkey.point, True
207225
)
226+
# as precomputation in now delayed to the time of first use of the
227+
# point and we were asked specifically to precompute now, make
228+
# sure the precomputation is performed now to preserve the behaviour
229+
if not lazy:
230+
self.pubkey.point * 2
208231

209232
@staticmethod
210233
def _from_raw_encoding(string, curve):

0 commit comments

Comments
 (0)