3939 from gmpy2 import mpz
4040
4141 GMPY = True
42- except ImportError :
42+ except ImportError : # pragma: no branch
4343 try :
4444 from gmpy import mpz
4545
5757class CurveFp (object ):
5858 """Elliptic Curve over the field of integers modulo a prime."""
5959
60- if GMPY :
60+ if GMPY : # pragma: no branch
6161
6262 def __init__ (self , p , a , b , h = None ):
6363 """
@@ -75,7 +75,7 @@ def __init__(self, p, a, b, h=None):
7575 # gmpy with it
7676 self .__h = h
7777
78- else :
78+ else : # pragma: no branch
7979
8080 def __init__ (self , p , a , b , h = None ):
8181 """
@@ -164,12 +164,12 @@ def __init__(self, curve, x, y, z, order=None, generator=False):
164164 # since it's generally better (faster) to use scaled points vs unscaled
165165 # ones, use writer-biased RWLock for locking:
166166 self ._update_lock = RWLock ()
167- if GMPY :
167+ if GMPY : # pragma: no branch
168168 self .__x = mpz (x )
169169 self .__y = mpz (y )
170170 self .__z = mpz (z )
171171 self .__order = order and mpz (order )
172- else :
172+ else : # pragma: no branch
173173 self .__x = x
174174 self .__y = y
175175 self .__z = z
@@ -359,7 +359,8 @@ def from_affine(point, generator=False):
359359 point .curve (), point .x (), point .y (), 1 , point .order (), generator
360360 )
361361
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
363364 # are formatted in a way to maximise performance.
364365 # Things that make code faster: multiplying instead of taking to the power
365366 # (`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):
389390 """Add a point to itself, arbitrary z."""
390391 if Z1 == 1 :
391392 return self ._double_with_z_1 (X1 , Y1 , p , a )
392- if not Z1 :
393+ if not Y1 or not Z1 :
393394 return 0 , 0 , 1
394395 # after:
395396 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
@@ -579,11 +580,11 @@ def _naf(mult):
579580 if mult % 2 :
580581 nd = mult % 4
581582 if nd >= 2 :
582- nd = nd - 4
583- ret += [ nd ]
583+ nd -= 4
584+ ret . append ( nd )
584585 mult -= nd
585586 else :
586- ret += [ 0 ]
587+ ret . append ( 0 )
587588 mult //= 2
588589 return ret
589590
@@ -621,15 +622,6 @@ def __mul__(self, other):
621622
622623 return PointJacobi (self .__curve , X3 , Y3 , Z3 , self .__order )
623624
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-
633625 def mul_add (self , self_mul , other , other_mul ):
634626 """
635627 Do two multiplications at the same time, add results.
@@ -643,7 +635,7 @@ def mul_add(self, self_mul, other, other_mul):
643635 if not isinstance (other , PointJacobi ):
644636 other = PointJacobi .from_affine (other )
645637 # 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 )
647639 self ._maybe_precompute ()
648640 other ._maybe_precompute ()
649641 if self .__precompute and other .__precompute :
@@ -653,32 +645,76 @@ def mul_add(self, self_mul, other, other_mul):
653645 self_mul = self_mul % self .__order
654646 other_mul = other_mul % self .__order
655647
656- i = self . _leftmost_bit ( max ( self_mul , other_mul )) * 2
648+ # (X3, Y3, Z3) is the accumulator
657649 X3 , Y3 , Z3 = 0 , 0 , 1
658650 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+
670660 _double = self ._double
671661 _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 ):
673690 X3 , Y3 , Z3 = _double (X3 , Y3 , Z3 , p , a )
674- i = i // 2
675691
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 )
682718
683719 if not Y3 or not Z3 :
684720 return INFINITY
0 commit comments