39
39
from gmpy2 import mpz
40
40
41
41
GMPY = True
42
- except ImportError :
42
+ except ImportError : # pragma: no branch
43
43
try :
44
44
from gmpy import mpz
45
45
57
57
class CurveFp (object ):
58
58
"""Elliptic Curve over the field of integers modulo a prime."""
59
59
60
- if GMPY :
60
+ if GMPY : # pragma: no branch
61
61
62
62
def __init__ (self , p , a , b , h = None ):
63
63
"""
@@ -75,7 +75,7 @@ def __init__(self, p, a, b, h=None):
75
75
# gmpy with it
76
76
self .__h = h
77
77
78
- else :
78
+ else : # pragma: no branch
79
79
80
80
def __init__ (self , p , a , b , h = None ):
81
81
"""
@@ -164,12 +164,12 @@ def __init__(self, curve, x, y, z, order=None, generator=False):
164
164
# since it's generally better (faster) to use scaled points vs unscaled
165
165
# ones, use writer-biased RWLock for locking:
166
166
self ._update_lock = RWLock ()
167
- if GMPY :
167
+ if GMPY : # pragma: no branch
168
168
self .__x = mpz (x )
169
169
self .__y = mpz (y )
170
170
self .__z = mpz (z )
171
171
self .__order = order and mpz (order )
172
- else :
172
+ else : # pragma: no branch
173
173
self .__x = x
174
174
self .__y = y
175
175
self .__z = z
@@ -359,7 +359,8 @@ def from_affine(point, generator=False):
359
359
point .curve (), point .x (), point .y (), 1 , point .order (), generator
360
360
)
361
361
362
- # plese note that all the methods that use the equations from hyperelliptic
362
+ # please note that all the methods that use the equations from
363
+ # hyperelliptic
363
364
# are formatted in a way to maximise performance.
364
365
# Things that make code faster: multiplying instead of taking to the power
365
366
# (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and
@@ -389,7 +390,7 @@ def _double(self, X1, Y1, Z1, p, a):
389
390
"""Add a point to itself, arbitrary z."""
390
391
if Z1 == 1 :
391
392
return self ._double_with_z_1 (X1 , Y1 , p , a )
392
- if not Z1 :
393
+ if not Y1 or not Z1 :
393
394
return 0 , 0 , 1
394
395
# after:
395
396
# http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
@@ -579,11 +580,11 @@ def _naf(mult):
579
580
if mult % 2 :
580
581
nd = mult % 4
581
582
if nd >= 2 :
582
- nd = nd - 4
583
- ret += [ nd ]
583
+ nd -= 4
584
+ ret . append ( nd )
584
585
mult -= nd
585
586
else :
586
- ret += [ 0 ]
587
+ ret . append ( 0 )
587
588
mult //= 2
588
589
return ret
589
590
@@ -621,15 +622,6 @@ def __mul__(self, other):
621
622
622
623
return PointJacobi (self .__curve , X3 , Y3 , Z3 , self .__order )
623
624
624
- @staticmethod
625
- def _leftmost_bit (x ):
626
- """Return integer with the same magnitude as x but only one bit set"""
627
- assert x > 0
628
- result = 1
629
- while result <= x :
630
- result = 2 * result
631
- return result // 2
632
-
633
625
def mul_add (self , self_mul , other , other_mul ):
634
626
"""
635
627
Do two multiplications at the same time, add results.
@@ -643,7 +635,7 @@ def mul_add(self, self_mul, other, other_mul):
643
635
if not isinstance (other , PointJacobi ):
644
636
other = PointJacobi .from_affine (other )
645
637
# when the points have precomputed answers, then multiplying them alone
646
- # is faster (as it uses NAF)
638
+ # is faster (as it uses NAF and no point doublings )
647
639
self ._maybe_precompute ()
648
640
other ._maybe_precompute ()
649
641
if self .__precompute and other .__precompute :
@@ -653,32 +645,76 @@ def mul_add(self, self_mul, other, other_mul):
653
645
self_mul = self_mul % self .__order
654
646
other_mul = other_mul % self .__order
655
647
656
- i = self . _leftmost_bit ( max ( self_mul , other_mul )) * 2
648
+ # (X3, Y3, Z3) is the accumulator
657
649
X3 , Y3 , Z3 = 0 , 0 , 1
658
650
p , a = self .__curve .p (), self .__curve .a ()
659
- self = self .scale ()
660
- # after scaling, point is immutable, no need for locking
661
- X1 , Y1 = self .__x , self .__y
662
- other = other .scale ()
663
- X2 , Y2 = other .__x , other .__y
664
- both = self + other
665
- if both is INFINITY :
666
- X4 , Y4 = 0 , 0
667
- else :
668
- both .scale ()
669
- X4 , Y4 = both .__x , both .__y
651
+
652
+ # as we have 6 unique points to work with, we can't scale all of them,
653
+ # but do scale the ones that are used most often
654
+ # (post scale() points are immutable so no need for locking)
655
+ self .scale ()
656
+ X1 , Y1 , Z1 = self .__x , self .__y , self .__z
657
+ other .scale ()
658
+ X2 , Y2 , Z2 = other .__x , other .__y , other .__z
659
+
670
660
_double = self ._double
671
661
_add = self ._add
672
- while i > 1 :
662
+
663
+ # with NAF we have 3 options: no add, subtract, add
664
+ # so with 2 points, we have 9 combinations:
665
+ # 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B
666
+ # so we need 4 combined points:
667
+ mAmB_X , mAmB_Y , mAmB_Z = _add (X1 , - Y1 , Z1 , X2 , - Y2 , Z2 , p )
668
+ pAmB_X , pAmB_Y , pAmB_Z = _add (X1 , Y1 , Z1 , X2 , - Y2 , Z2 , p )
669
+ mApB_X , mApB_Y , mApB_Z = _add (X1 , - Y1 , Z1 , X2 , Y2 , Z2 , p )
670
+ pApB_X , pApB_Y , pApB_Z = _add (X1 , Y1 , Z1 , X2 , Y2 , Z2 , p )
671
+ # when the self and other sum to infinity, we need to add them
672
+ # one by one to get correct result but as that's very unlikely to
673
+ # happen in regular operation, we don't need to optimise this case
674
+ if not pApB_Y or not pApB_Z :
675
+ return self * self_mul + other * other_mul
676
+
677
+ # gmp object creation has cumulatively higher overhead than the
678
+ # speedup we get from calculating the NAF using gmp so ensure use
679
+ # of int()
680
+ self_naf = list (reversed (self ._naf (int (self_mul ))))
681
+ other_naf = list (reversed (self ._naf (int (other_mul ))))
682
+ # ensure that the lists are the same length (zip() will truncate
683
+ # longer one otherwise)
684
+ if len (self_naf ) < len (other_naf ):
685
+ self_naf = [0 ] * (len (other_naf )- len (self_naf )) + self_naf
686
+ elif len (self_naf ) > len (other_naf ):
687
+ other_naf = [0 ] * (len (self_naf )- len (other_naf )) + other_naf
688
+
689
+ for A , B in zip (self_naf , other_naf ):
673
690
X3 , Y3 , Z3 = _double (X3 , Y3 , Z3 , p , a )
674
- i = i // 2
675
691
676
- if self_mul & i and other_mul & i :
677
- X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , X4 , Y4 , 1 , p )
678
- elif self_mul & i :
679
- X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , X1 , Y1 , 1 , p )
680
- elif other_mul & i :
681
- X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , X2 , Y2 , 1 , p )
692
+ # conditions ordered from most to least likely
693
+ if A == 0 :
694
+ if B == 0 :
695
+ pass
696
+ elif B < 0 :
697
+ X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , X2 , - Y2 , Z2 , p )
698
+ else :
699
+ assert B > 0
700
+ X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , X2 , Y2 , Z2 , p )
701
+ elif A < 0 :
702
+ if B == 0 :
703
+ X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , X1 , - Y1 , Z1 , p )
704
+ elif B < 0 :
705
+ X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , mAmB_X , mAmB_Y , mAmB_Z , p )
706
+ else :
707
+ assert B > 0
708
+ X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , mApB_X , mApB_Y , mApB_Z , p )
709
+ else :
710
+ assert A > 0
711
+ if B == 0 :
712
+ X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , X1 , Y1 , Z1 , p )
713
+ elif B < 0 :
714
+ X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , pAmB_X , pAmB_Y , pAmB_Z , p )
715
+ else :
716
+ assert B > 0
717
+ X3 , Y3 , Z3 = _add (X3 , Y3 , Z3 , pApB_X , pApB_Y , pApB_Z , p )
682
718
683
719
if not Y3 or not Z3 :
684
720
return INFINITY
0 commit comments