1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Implementation of elliptic curves, for cryptographic applications.
5#
6# This module doesn't provide any way to choose a random elliptic
7# curve, nor to verify that an elliptic curve was chosen randomly,
8# because one can simply use NIST's standard curves.
9#
10# Notes from X9.62-1998 (draft):
11# Nomenclature:
12# - Q is a public key.
13# The "Elliptic Curve Domain Parameters" include:
14# - q is the "field size", which in our case equals p.
15# - p is a big prime.
16# - G is a point of prime order (5.1.1.1).
17# - n is the order of G (5.1.1.1).
18# Public-key validation (5.2.2):
19# - Verify that Q is not the point at infinity.
20# - Verify that X_Q and Y_Q are in [0,p-1].
21# - Verify that Q is on the curve.
22# - Verify that nQ is the point at infinity.
23# Signature generation (5.3):
24# - Pick random k from [1,n-1].
25# Signature checking (5.4.2):
26# - Verify that r and s are in [1,n-1].
27#
28# Revision history:
29# 2005.12.31 - Initial version.
30# 2008.11.25 - Change CurveFp.is_on to contains_point.
31#
32# Written in 2005 by Peter Pearson and placed in the public domain.
33# Modified extensively as part of python-ecdsa.
34
35from __future__ import division
36
37try:
38 from gmpy2 import mpz
39
40 GMPY = True
41except ImportError: # pragma: no branch
42 try:
43 from gmpy import mpz
44
45 GMPY = True
46 except ImportError:
47 GMPY = False
48
49
50from six import python_2_unicode_compatible
51from . import numbertheory
52from ._compat import normalise_bytes, int_to_bytes, bit_length, bytes_to_int
53from .errors import MalformedPointError
54from .util import orderlen, string_to_number, number_to_string
55
56
57@python_2_unicode_compatible
58class CurveFp(object):
59 """
60 :term:`Short Weierstrass Elliptic Curve <short Weierstrass curve>` over a
61 prime field.
62 """
63
64 if GMPY: # pragma: no branch
65
66 def __init__(self, p, a, b, h=None):
67 """
68 The curve of points satisfying y^2 = x^3 + a*x + b (mod p).
69
70 h is an integer that is the cofactor of the elliptic curve domain
71 parameters; it is the number of points satisfying the elliptic
72 curve equation divided by the order of the base point. It is used
73 for selection of efficient algorithm for public point verification.
74 """
75 self.__p = mpz(p)
76 self.__a = mpz(a)
77 self.__b = mpz(b)
78 # h is not used in calculations and it can be None, so don't use
79 # gmpy with it
80 self.__h = h
81
82 else: # pragma: no branch
83
84 def __init__(self, p, a, b, h=None):
85 """
86 The curve of points satisfying y^2 = x^3 + a*x + b (mod p).
87
88 h is an integer that is the cofactor of the elliptic curve domain
89 parameters; it is the number of points satisfying the elliptic
90 curve equation divided by the order of the base point. It is used
91 for selection of efficient algorithm for public point verification.
92 """
93 self.__p = p
94 self.__a = a
95 self.__b = b
96 self.__h = h
97
98 def __eq__(self, other):
99 """Return True if other is an identical curve, False otherwise.
100
101 Note: the value of the cofactor of the curve is not taken into account
102 when comparing curves, as it's derived from the base point and
103 intrinsic curve characteristic (but it's complex to compute),
104 only the prime and curve parameters are considered.
105 """
106 if isinstance(other, CurveFp):
107 p = self.__p
108 return (
109 self.__p == other.__p
110 and self.__a % p == other.__a % p
111 and self.__b % p == other.__b % p
112 )
113 return NotImplemented
114
115 def __ne__(self, other):
116 """Return False if other is an identical curve, True otherwise."""
117 return not self == other
118
119 def __hash__(self):
120 return hash((self.__p, self.__a, self.__b))
121
122 def p(self):
123 return self.__p
124
125 def a(self):
126 return self.__a
127
128 def b(self):
129 return self.__b
130
131 def cofactor(self):
132 return self.__h
133
134 def contains_point(self, x, y):
135 """Is the point (x,y) on this curve?"""
136 return (y * y - ((x * x + self.__a) * x + self.__b)) % self.__p == 0
137
138 def __str__(self):
139 if self.__h is not None:
140 return "CurveFp(p={0}, a={1}, b={2}, h={3})".format(
141 self.__p,
142 self.__a,
143 self.__b,
144 self.__h,
145 )
146 return "CurveFp(p={0}, a={1}, b={2})".format(
147 self.__p,
148 self.__a,
149 self.__b,
150 )
151
152
153class CurveEdTw(object):
154 """Parameters for a Twisted Edwards Elliptic Curve"""
155
156 if GMPY: # pragma: no branch
157
158 def __init__(self, p, a, d, h=None, hash_func=None):
159 """
160 The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p).
161
162 h is the cofactor of the curve.
163 hash_func is the hash function associated with the curve
164 (like SHA-512 for Ed25519)
165 """
166 self.__p = mpz(p)
167 self.__a = mpz(a)
168 self.__d = mpz(d)
169 self.__h = h
170 self.__hash_func = hash_func
171
172 else:
173
174 def __init__(self, p, a, d, h=None, hash_func=None):
175 """
176 The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p).
177
178 h is the cofactor of the curve.
179 hash_func is the hash function associated with the curve
180 (like SHA-512 for Ed25519)
181 """
182 self.__p = p
183 self.__a = a
184 self.__d = d
185 self.__h = h
186 self.__hash_func = hash_func
187
188 def __eq__(self, other):
189 """Returns True if other is an identical curve."""
190 if isinstance(other, CurveEdTw):
191 p = self.__p
192 return (
193 self.__p == other.__p
194 and self.__a % p == other.__a % p
195 and self.__d % p == other.__d % p
196 )
197 return NotImplemented
198
199 def __ne__(self, other):
200 """Return False if the other is an identical curve, True otherwise."""
201 return not self == other
202
203 def __hash__(self):
204 return hash((self.__p, self.__a, self.__d))
205
206 def contains_point(self, x, y):
207 """Is the point (x, y) on this curve?"""
208 return (
209 self.__a * x * x + y * y - 1 - self.__d * x * x * y * y
210 ) % self.__p == 0
211
212 def p(self):
213 return self.__p
214
215 def a(self):
216 return self.__a
217
218 def d(self):
219 return self.__d
220
221 def hash_func(self, data):
222 return self.__hash_func(data)
223
224 def cofactor(self):
225 return self.__h
226
227 def __str__(self):
228 if self.__h is not None:
229 return "CurveEdTw(p={0}, a={1}, d={2}, h={3})".format(
230 self.__p,
231 self.__a,
232 self.__d,
233 self.__h,
234 )
235 return "CurveEdTw(p={0}, a={1}, d={2})".format(
236 self.__p,
237 self.__a,
238 self.__d,
239 )
240
241
242class AbstractPoint(object):
243 """Class for common methods of elliptic curve points."""
244
245 @staticmethod
246 def _from_raw_encoding(data, raw_encoding_length):
247 """
248 Decode public point from :term:`raw encoding`.
249
250 :term:`raw encoding` is the same as the :term:`uncompressed` encoding,
251 but without the 0x04 byte at the beginning.
252 """
253 # real assert, from_bytes() should not call us with different length
254 assert len(data) == raw_encoding_length
255 xs = data[: raw_encoding_length // 2]
256 ys = data[raw_encoding_length // 2 :]
257 # real assert, raw_encoding_length is calculated by multiplying an
258 # integer by two so it will always be even
259 assert len(xs) == raw_encoding_length // 2
260 assert len(ys) == raw_encoding_length // 2
261 coord_x = string_to_number(xs)
262 coord_y = string_to_number(ys)
263
264 return coord_x, coord_y
265
266 @staticmethod
267 def _from_compressed(data, curve):
268 """Decode public point from compressed encoding."""
269 if data[:1] not in (b"\x02", b"\x03"):
270 raise MalformedPointError("Malformed compressed point encoding")
271
272 is_even = data[:1] == b"\x02"
273 x = string_to_number(data[1:])
274 p = curve.p()
275 alpha = (pow(x, 3, p) + (curve.a() * x) + curve.b()) % p
276 try:
277 beta = numbertheory.square_root_mod_prime(alpha, p)
278 except numbertheory.Error as e:
279 raise MalformedPointError(
280 "Encoding does not correspond to a point on curve", e
281 )
282 if is_even == bool(beta & 1):
283 y = p - beta
284 else:
285 y = beta
286 return x, y
287
288 @classmethod
289 def _from_hybrid(cls, data, raw_encoding_length, validate_encoding):
290 """Decode public point from hybrid encoding."""
291 # real assert, from_bytes() should not call us with different types
292 assert data[:1] in (b"\x06", b"\x07")
293
294 # primarily use the uncompressed as it's easiest to handle
295 x, y = cls._from_raw_encoding(data[1:], raw_encoding_length)
296
297 # but validate if it's self-consistent if we're asked to do that
298 if validate_encoding and (
299 y & 1
300 and data[:1] != b"\x07"
301 or (not y & 1)
302 and data[:1] != b"\x06"
303 ):
304 raise MalformedPointError("Inconsistent hybrid point encoding")
305
306 return x, y
307
308 @classmethod
309 def _from_edwards(cls, curve, data):
310 """Decode a point on an Edwards curve."""
311 data = bytearray(data)
312 p = curve.p()
313 # add 1 for the sign bit and then round up
314 exp_len = (bit_length(p) + 1 + 7) // 8
315 if len(data) != exp_len:
316 raise MalformedPointError("Point length doesn't match the curve.")
317 x_0 = (data[-1] & 0x80) >> 7
318
319 data[-1] &= 0x80 - 1
320
321 y = bytes_to_int(data, "little")
322 if GMPY:
323 y = mpz(y)
324
325 x2 = (
326 (y * y - 1)
327 * numbertheory.inverse_mod(curve.d() * y * y - curve.a(), p)
328 % p
329 )
330
331 try:
332 x = numbertheory.square_root_mod_prime(x2, p)
333 except numbertheory.Error as e:
334 raise MalformedPointError(
335 "Encoding does not correspond to a point on curve", e
336 )
337
338 if x % 2 != x_0:
339 x = -x % p
340
341 return x, y
342
343 @classmethod
344 def from_bytes(
345 cls, curve, data, validate_encoding=True, valid_encodings=None
346 ):
347 """
348 Initialise the object from byte encoding of a point.
349
350 The method does accept and automatically detect the type of point
351 encoding used. It supports the :term:`raw encoding`,
352 :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings.
353
354 Note: generally you will want to call the ``from_bytes()`` method of
355 either a child class, PointJacobi or Point.
356
357 :param data: single point encoding of the public key
358 :type data: :term:`bytes-like object`
359 :param curve: the curve on which the public key is expected to lay
360 :type curve: ~ecdsa.ellipticcurve.CurveFp
361 :param validate_encoding: whether to verify that the encoding of the
362 point is self-consistent, defaults to True, has effect only
363 on ``hybrid`` encoding
364 :type validate_encoding: bool
365 :param valid_encodings: list of acceptable point encoding formats,
366 supported ones are: :term:`uncompressed`, :term:`compressed`,
367 :term:`hybrid`, and :term:`raw encoding` (specified with ``raw``
368 name). All formats by default (specified with ``None``).
369 :type valid_encodings: :term:`set-like object`
370
371 :raises `~ecdsa.errors.MalformedPointError`: if the public point does
372 not lay on the curve or the encoding is invalid
373
374 :return: x and y coordinates of the encoded point
375 :rtype: tuple(int, int)
376 """
377 if not valid_encodings:
378 valid_encodings = set(
379 ["uncompressed", "compressed", "hybrid", "raw"]
380 )
381 if not all(
382 i in set(("uncompressed", "compressed", "hybrid", "raw"))
383 for i in valid_encodings
384 ):
385 raise ValueError(
386 "Only uncompressed, compressed, hybrid or raw encoding "
387 "supported."
388 )
389 data = normalise_bytes(data)
390
391 if isinstance(curve, CurveEdTw):
392 return cls._from_edwards(curve, data)
393
394 key_len = len(data)
395 raw_encoding_length = 2 * orderlen(curve.p())
396 if key_len == raw_encoding_length and "raw" in valid_encodings:
397 coord_x, coord_y = cls._from_raw_encoding(
398 data, raw_encoding_length
399 )
400 elif key_len == raw_encoding_length + 1 and (
401 "hybrid" in valid_encodings or "uncompressed" in valid_encodings
402 ):
403 if data[:1] in (b"\x06", b"\x07") and "hybrid" in valid_encodings:
404 coord_x, coord_y = cls._from_hybrid(
405 data, raw_encoding_length, validate_encoding
406 )
407 elif data[:1] == b"\x04" and "uncompressed" in valid_encodings:
408 coord_x, coord_y = cls._from_raw_encoding(
409 data[1:], raw_encoding_length
410 )
411 else:
412 raise MalformedPointError(
413 "Invalid X9.62 encoding of the public point"
414 )
415 elif (
416 key_len == raw_encoding_length // 2 + 1
417 and "compressed" in valid_encodings
418 ):
419 coord_x, coord_y = cls._from_compressed(data, curve)
420 else:
421 raise MalformedPointError(
422 "Length of string does not match lengths of "
423 "any of the enabled ({0}) encodings of the "
424 "curve.".format(", ".join(valid_encodings))
425 )
426 return coord_x, coord_y
427
428 def _raw_encode(self):
429 """Convert the point to the :term:`raw encoding`."""
430 prime = self.curve().p()
431 x_str = number_to_string(self.x(), prime)
432 y_str = number_to_string(self.y(), prime)
433 return x_str + y_str
434
435 def _compressed_encode(self):
436 """Encode the point into the compressed form."""
437 prime = self.curve().p()
438 x_str = number_to_string(self.x(), prime)
439 if self.y() & 1:
440 return b"\x03" + x_str
441 return b"\x02" + x_str
442
443 def _hybrid_encode(self):
444 """Encode the point into the hybrid form."""
445 raw_enc = self._raw_encode()
446 if self.y() & 1:
447 return b"\x07" + raw_enc
448 return b"\x06" + raw_enc
449
450 def _edwards_encode(self):
451 """Encode the point according to RFC8032 encoding."""
452 self.scale()
453 x, y, p = self.x(), self.y(), self.curve().p()
454
455 # add 1 for the sign bit and then round up
456 enc_len = (bit_length(p) + 1 + 7) // 8
457 y_str = int_to_bytes(y, enc_len, "little")
458 if x % 2:
459 y_str[-1] |= 0x80
460 return y_str
461
462 def to_bytes(self, encoding="raw"):
463 """
464 Convert the point to a byte string.
465
466 The method by default uses the :term:`raw encoding` (specified
467 by `encoding="raw"`. It can also output points in :term:`uncompressed`,
468 :term:`compressed`, and :term:`hybrid` formats.
469
470 For points on Edwards curves `encoding` is ignored and only the
471 encoding defined in RFC 8032 is supported.
472
473 :return: :term:`raw encoding` of a public on the curve
474 :rtype: bytes
475 """
476 assert encoding in ("raw", "uncompressed", "compressed", "hybrid")
477 curve = self.curve()
478 if isinstance(curve, CurveEdTw):
479 return self._edwards_encode()
480 elif encoding == "raw":
481 return self._raw_encode()
482 elif encoding == "uncompressed":
483 return b"\x04" + self._raw_encode()
484 elif encoding == "hybrid":
485 return self._hybrid_encode()
486 else:
487 return self._compressed_encode()
488
489 @staticmethod
490 def _naf(mult):
491 """Calculate non-adjacent form of number."""
492 ret = []
493 while mult:
494 if mult % 2:
495 nd = mult % 4
496 if nd >= 2:
497 nd -= 4
498 ret.append(nd)
499 mult -= nd
500 else:
501 ret.append(0)
502 mult //= 2
503 return ret
504
505
506class PointJacobi(AbstractPoint):
507 """
508 Point on a short Weierstrass elliptic curve. Uses Jacobi coordinates.
509
510 In Jacobian coordinates, there are three parameters, X, Y and Z.
511 They correspond to affine parameters 'x' and 'y' like so:
512
513 x = X / Z²
514 y = Y / Z³
515 """
516
517 def __init__(self, curve, x, y, z, order=None, generator=False):
518 """
519 Initialise a point that uses Jacobi representation internally.
520
521 :param CurveFp curve: curve on which the point resides
522 :param int x: the X parameter of Jacobi representation (equal to x when
523 converting from affine coordinates
524 :param int y: the Y parameter of Jacobi representation (equal to y when
525 converting from affine coordinates
526 :param int z: the Z parameter of Jacobi representation (equal to 1 when
527 converting from affine coordinates
528 :param int order: the point order, must be non zero when using
529 generator=True
530 :param bool generator: the point provided is a curve generator, as
531 such, it will be commonly used with scalar multiplication. This will
532 cause to precompute multiplication table generation for it
533 """
534 super(PointJacobi, self).__init__()
535 self.__curve = curve
536 if GMPY: # pragma: no branch
537 self.__coords = (mpz(x), mpz(y), mpz(z))
538 self.__order = order and mpz(order)
539 else: # pragma: no branch
540 self.__coords = (x, y, z)
541 self.__order = order
542 self.__generator = generator
543 self.__precompute = []
544
545 @classmethod
546 def from_bytes(
547 cls,
548 curve,
549 data,
550 validate_encoding=True,
551 valid_encodings=None,
552 order=None,
553 generator=False,
554 ):
555 """
556 Initialise the object from byte encoding of a point.
557
558 The method does accept and automatically detect the type of point
559 encoding used. It supports the :term:`raw encoding`,
560 :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings.
561
562 :param data: single point encoding of the public key
563 :type data: :term:`bytes-like object`
564 :param curve: the curve on which the public key is expected to lay
565 :type curve: ~ecdsa.ellipticcurve.CurveFp
566 :param validate_encoding: whether to verify that the encoding of the
567 point is self-consistent, defaults to True, has effect only
568 on ``hybrid`` encoding
569 :type validate_encoding: bool
570 :param valid_encodings: list of acceptable point encoding formats,
571 supported ones are: :term:`uncompressed`, :term:`compressed`,
572 :term:`hybrid`, and :term:`raw encoding` (specified with ``raw``
573 name). All formats by default (specified with ``None``).
574 :type valid_encodings: :term:`set-like object`
575 :param int order: the point order, must be non zero when using
576 generator=True
577 :param bool generator: the point provided is a curve generator, as
578 such, it will be commonly used with scalar multiplication. This
579 will cause to precompute multiplication table generation for it
580
581 :raises `~ecdsa.errors.MalformedPointError`: if the public point does
582 not lay on the curve or the encoding is invalid
583
584 :return: Point on curve
585 :rtype: PointJacobi
586 """
587 coord_x, coord_y = super(PointJacobi, cls).from_bytes(
588 curve, data, validate_encoding, valid_encodings
589 )
590 return PointJacobi(curve, coord_x, coord_y, 1, order, generator)
591
592 def _maybe_precompute(self):
593 if not self.__generator or self.__precompute:
594 return
595
596 # since this code will execute just once, and it's fully deterministic,
597 # depend on atomicity of the last assignment to switch from empty
598 # self.__precompute to filled one and just ignore the unlikely
599 # situation when two threads execute it at the same time (as it won't
600 # lead to inconsistent __precompute)
601 order = self.__order
602 assert order
603 precompute = []
604 i = 1
605 order *= 2
606 coord_x, coord_y, coord_z = self.__coords
607 doubler = PointJacobi(self.__curve, coord_x, coord_y, coord_z, order)
608 order *= 2
609 precompute.append((doubler.x(), doubler.y()))
610
611 while i < order:
612 i *= 2
613 doubler = doubler.double().scale()
614 precompute.append((doubler.x(), doubler.y()))
615
616 self.__precompute = precompute
617
618 def __getstate__(self):
619 # while this code can execute at the same time as _maybe_precompute()
620 # is updating the __precompute or scale() is updating the __coords,
621 # there is no requirement for consistency between __coords and
622 # __precompute
623 state = self.__dict__.copy()
624 return state
625
626 def __setstate__(self, state):
627 self.__dict__.update(state)
628
629 def __eq__(self, other):
630 """Compare for equality two points with each-other.
631
632 Note: only points that lay on the same curve can be equal.
633 """
634 x1, y1, z1 = self.__coords
635 if other is INFINITY:
636 return not y1 or not z1
637 if isinstance(other, Point):
638 x2, y2, z2 = other.x(), other.y(), 1
639 elif isinstance(other, PointJacobi):
640 x2, y2, z2 = other.__coords
641 else:
642 return NotImplemented
643 if self.__curve != other.curve():
644 return False
645 p = self.__curve.p()
646
647 zz1 = z1 * z1 % p
648 zz2 = z2 * z2 % p
649
650 # compare the fractions by bringing them to the same denominator
651 # depend on short-circuit to save 4 multiplications in case of
652 # inequality
653 return (x1 * zz2 - x2 * zz1) % p == 0 and (
654 y1 * zz2 * z2 - y2 * zz1 * z1
655 ) % p == 0
656
657 def __ne__(self, other):
658 """Compare for inequality two points with each-other."""
659 return not self == other
660
661 def order(self):
662 """Return the order of the point.
663
664 None if it is undefined.
665 """
666 return self.__order
667
668 def curve(self):
669 """Return curve over which the point is defined."""
670 return self.__curve
671
672 def x(self):
673 """
674 Return affine x coordinate.
675
676 This method should be used only when the 'y' coordinate is not needed.
677 It's computationally more efficient to use `to_affine()` and then
678 call x() and y() on the returned instance. Or call `scale()`
679 and then x() and y() on the returned instance.
680 """
681 x, _, z = self.__coords
682 if z == 1:
683 return x
684 p = self.__curve.p()
685 z = numbertheory.inverse_mod(z, p)
686 return x * z**2 % p
687
688 def y(self):
689 """
690 Return affine y coordinate.
691
692 This method should be used only when the 'x' coordinate is not needed.
693 It's computationally more efficient to use `to_affine()` and then
694 call x() and y() on the returned instance. Or call `scale()`
695 and then x() and y() on the returned instance.
696 """
697 _, y, z = self.__coords
698 if z == 1:
699 return y
700 p = self.__curve.p()
701 z = numbertheory.inverse_mod(z, p)
702 return y * z**3 % p
703
704 def scale(self):
705 """
706 Return point scaled so that z == 1.
707
708 Modifies point in place, returns self.
709 """
710 x, y, z = self.__coords
711 if z == 1:
712 return self
713
714 # scaling is deterministic, so even if two threads execute the below
715 # code at the same time, they will set __coords to the same value
716 p = self.__curve.p()
717 z_inv = numbertheory.inverse_mod(z, p)
718 zz_inv = z_inv * z_inv % p
719 x = x * zz_inv % p
720 y = y * zz_inv * z_inv % p
721 self.__coords = (x, y, 1)
722 return self
723
724 def to_affine(self):
725 """Return point in affine form."""
726 _, y, z = self.__coords
727 if not y or not z:
728 return INFINITY
729 self.scale()
730 x, y, z = self.__coords
731 return Point(self.__curve, x, y, self.__order)
732
733 @staticmethod
734 def from_affine(point, generator=False):
735 """Create from an affine point.
736
737 :param bool generator: set to True to make the point to precalculate
738 multiplication table - useful for public point when verifying many
739 signatures (around 100 or so) or for generator points of a curve.
740 """
741 return PointJacobi(
742 point.curve(), point.x(), point.y(), 1, point.order(), generator
743 )
744
745 # please note that all the methods that use the equations from
746 # hyperelliptic
747 # are formatted in a way to maximise performance.
748 # Things that make code faster: multiplying instead of taking to the power
749 # (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and
750 # `pow(x, 4, p)`),
751 # multiple assignments at the same time (`x1, x2 = self.x1, self.x2` is
752 # faster than `x1 = self.x1; x2 = self.x2`),
753 # similarly, sometimes the `% p` is skipped if it makes the calculation
754 # faster and the result of calculation is later reduced modulo `p`
755
756 def _double_with_z_1(self, X1, Y1, p, a):
757 """Add a point to itself with z == 1."""
758 # after:
759 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl
760 XX, YY = X1 * X1 % p, Y1 * Y1 % p
761 if not YY:
762 return 0, 0, 1
763 YYYY = YY * YY % p
764 S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
765 M = 3 * XX + a
766 T = (M * M - 2 * S) % p
767 # X3 = T
768 Y3 = (M * (S - T) - 8 * YYYY) % p
769 Z3 = 2 * Y1 % p
770 return T, Y3, Z3
771
772 def _double(self, X1, Y1, Z1, p, a):
773 """Add a point to itself, arbitrary z."""
774 if Z1 == 1:
775 return self._double_with_z_1(X1, Y1, p, a)
776 if not Y1 or not Z1:
777 return 0, 0, 1
778 # after:
779 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
780 XX, YY = X1 * X1 % p, Y1 * Y1 % p
781 if not YY:
782 return 0, 0, 1
783 YYYY = YY * YY % p
784 ZZ = Z1 * Z1 % p
785 S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
786 M = (3 * XX + a * ZZ * ZZ) % p
787 T = (M * M - 2 * S) % p
788 # X3 = T
789 Y3 = (M * (S - T) - 8 * YYYY) % p
790 Z3 = ((Y1 + Z1) ** 2 - YY - ZZ) % p
791
792 return T, Y3, Z3
793
794 def double(self):
795 """Add a point to itself."""
796 X1, Y1, Z1 = self.__coords
797
798 if not Y1:
799 return INFINITY
800
801 p, a = self.__curve.p(), self.__curve.a()
802
803 X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a)
804
805 if not Y3 or not Z3:
806 return INFINITY
807 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
808
809 def _add_with_z_1(self, X1, Y1, X2, Y2, p):
810 """add points when both Z1 and Z2 equal 1"""
811 # after:
812 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-mmadd-2007-bl
813 H = X2 - X1
814 HH = H * H
815 I = 4 * HH % p
816 J = H * I
817 r = 2 * (Y2 - Y1)
818 if not H and not r:
819 return self._double_with_z_1(X1, Y1, p, self.__curve.a())
820 V = X1 * I
821 X3 = (r**2 - J - 2 * V) % p
822 Y3 = (r * (V - X3) - 2 * Y1 * J) % p
823 Z3 = 2 * H % p
824 return X3, Y3, Z3
825
826 def _add_with_z_eq(self, X1, Y1, Z1, X2, Y2, p):
827 """add points when Z1 == Z2"""
828 # after:
829 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-zadd-2007-m
830 A = (X2 - X1) ** 2 % p
831 B = X1 * A % p
832 C = X2 * A
833 D = (Y2 - Y1) ** 2 % p
834 if not A and not D:
835 return self._double(X1, Y1, Z1, p, self.__curve.a())
836 X3 = (D - B - C) % p
837 Y3 = ((Y2 - Y1) * (B - X3) - Y1 * (C - B)) % p
838 Z3 = Z1 * (X2 - X1) % p
839 return X3, Y3, Z3
840
841 def _add_with_z2_1(self, X1, Y1, Z1, X2, Y2, p):
842 """add points when Z2 == 1"""
843 # after:
844 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl
845 Z1Z1 = Z1 * Z1 % p
846 U2, S2 = X2 * Z1Z1 % p, Y2 * Z1 * Z1Z1 % p
847 H = (U2 - X1) % p
848 HH = H * H % p
849 I = 4 * HH % p
850 J = H * I
851 r = 2 * (S2 - Y1) % p
852 if not r and not H:
853 return self._double_with_z_1(X2, Y2, p, self.__curve.a())
854 V = X1 * I
855 X3 = (r * r - J - 2 * V) % p
856 Y3 = (r * (V - X3) - 2 * Y1 * J) % p
857 Z3 = ((Z1 + H) ** 2 - Z1Z1 - HH) % p
858 return X3, Y3, Z3
859
860 def _add_with_z_ne(self, X1, Y1, Z1, X2, Y2, Z2, p):
861 """add points with arbitrary z"""
862 # after:
863 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl
864 Z1Z1 = Z1 * Z1 % p
865 Z2Z2 = Z2 * Z2 % p
866 U1 = X1 * Z2Z2 % p
867 U2 = X2 * Z1Z1 % p
868 S1 = Y1 * Z2 * Z2Z2 % p
869 S2 = Y2 * Z1 * Z1Z1 % p
870 H = U2 - U1
871 I = 4 * H * H % p
872 J = H * I % p
873 r = 2 * (S2 - S1) % p
874 if not H and not r:
875 return self._double(X1, Y1, Z1, p, self.__curve.a())
876 V = U1 * I
877 X3 = (r * r - J - 2 * V) % p
878 Y3 = (r * (V - X3) - 2 * S1 * J) % p
879 Z3 = ((Z1 + Z2) ** 2 - Z1Z1 - Z2Z2) * H % p
880
881 return X3, Y3, Z3
882
883 def __radd__(self, other):
884 """Add other to self."""
885 return self + other
886
887 def _add(self, X1, Y1, Z1, X2, Y2, Z2, p):
888 """add two points, select fastest method."""
889 if not Y1 or not Z1:
890 return X2, Y2, Z2
891 if not Y2 or not Z2:
892 return X1, Y1, Z1
893 if Z1 == Z2:
894 if Z1 == 1:
895 return self._add_with_z_1(X1, Y1, X2, Y2, p)
896 return self._add_with_z_eq(X1, Y1, Z1, X2, Y2, p)
897 if Z1 == 1:
898 return self._add_with_z2_1(X2, Y2, Z2, X1, Y1, p)
899 if Z2 == 1:
900 return self._add_with_z2_1(X1, Y1, Z1, X2, Y2, p)
901 return self._add_with_z_ne(X1, Y1, Z1, X2, Y2, Z2, p)
902
903 def __add__(self, other):
904 """Add two points on elliptic curve."""
905 if self == INFINITY:
906 return other
907 if other == INFINITY:
908 return self
909 if isinstance(other, Point):
910 other = PointJacobi.from_affine(other)
911 if self.__curve != other.__curve:
912 raise ValueError("The other point is on different curve")
913
914 p = self.__curve.p()
915 X1, Y1, Z1 = self.__coords
916 X2, Y2, Z2 = other.__coords
917
918 X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p)
919
920 if not Y3 or not Z3:
921 return INFINITY
922 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
923
924 def __rmul__(self, other):
925 """Multiply point by an integer."""
926 return self * other
927
928 def _mul_precompute(self, other):
929 """Multiply point by integer with precomputation table."""
930 X3, Y3, Z3, p = 0, 0, 1, self.__curve.p()
931 _add = self._add
932 for X2, Y2 in self.__precompute:
933 if other % 2:
934 if other % 4 >= 2:
935 other = (other + 1) // 2
936 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p)
937 else:
938 other = (other - 1) // 2
939 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p)
940 else:
941 other //= 2
942
943 if not Y3 or not Z3:
944 return INFINITY
945 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
946
947 def __mul__(self, other):
948 """Multiply point by an integer."""
949 if not self.__coords[1] or not other:
950 return INFINITY
951 if other == 1:
952 return self
953 if self.__order:
954 # order*2 as a protection for Minerva
955 other = other % (self.__order * 2)
956 self._maybe_precompute()
957 if self.__precompute:
958 return self._mul_precompute(other)
959
960 self = self.scale()
961 X2, Y2, _ = self.__coords
962 X3, Y3, Z3 = 0, 0, 1
963 p, a = self.__curve.p(), self.__curve.a()
964 _double = self._double
965 _add = self._add
966 # since adding points when at least one of them is scaled
967 # is quicker, reverse the NAF order
968 for i in reversed(self._naf(other)):
969 X3, Y3, Z3 = _double(X3, Y3, Z3, p, a)
970 if i < 0:
971 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p)
972 elif i > 0:
973 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p)
974
975 if not Y3 or not Z3:
976 return INFINITY
977
978 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
979
980 def mul_add(self, self_mul, other, other_mul):
981 """
982 Do two multiplications at the same time, add results.
983
984 calculates self*self_mul + other*other_mul
985 """
986 if other == INFINITY or other_mul == 0:
987 return self * self_mul
988 if self_mul == 0:
989 return other * other_mul
990 if not isinstance(other, PointJacobi):
991 other = PointJacobi.from_affine(other)
992 # when the points have precomputed answers, then multiplying them alone
993 # is faster (as it uses NAF and no point doublings)
994 self._maybe_precompute()
995 other._maybe_precompute()
996 if self.__precompute and other.__precompute:
997 return self * self_mul + other * other_mul
998
999 if self.__order:
1000 self_mul = self_mul % self.__order
1001 other_mul = other_mul % self.__order
1002
1003 # (X3, Y3, Z3) is the accumulator
1004 X3, Y3, Z3 = 0, 0, 1
1005 p, a = self.__curve.p(), self.__curve.a()
1006
1007 # as we have 6 unique points to work with, we can't scale all of them,
1008 # but do scale the ones that are used most often
1009 self.scale()
1010 X1, Y1, Z1 = self.__coords
1011 other.scale()
1012 X2, Y2, Z2 = other.__coords
1013
1014 _double = self._double
1015 _add = self._add
1016
1017 # with NAF we have 3 options: no add, subtract, add
1018 # so with 2 points, we have 9 combinations:
1019 # 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B
1020 # so we need 4 combined points:
1021 mAmB_X, mAmB_Y, mAmB_Z = _add(X1, -Y1, Z1, X2, -Y2, Z2, p)
1022 pAmB_X, pAmB_Y, pAmB_Z = _add(X1, Y1, Z1, X2, -Y2, Z2, p)
1023 mApB_X, mApB_Y, mApB_Z = pAmB_X, -pAmB_Y, pAmB_Z
1024 pApB_X, pApB_Y, pApB_Z = mAmB_X, -mAmB_Y, mAmB_Z
1025 # when the self and other sum to infinity, we need to add them
1026 # one by one to get correct result but as that's very unlikely to
1027 # happen in regular operation, we don't need to optimise this case
1028 if not pApB_Y or not pApB_Z:
1029 return self * self_mul + other * other_mul
1030
1031 # gmp object creation has cumulatively higher overhead than the
1032 # speedup we get from calculating the NAF using gmp so ensure use
1033 # of int()
1034 self_naf = list(reversed(self._naf(int(self_mul))))
1035 other_naf = list(reversed(self._naf(int(other_mul))))
1036 # ensure that the lists are the same length (zip() will truncate
1037 # longer one otherwise)
1038 if len(self_naf) < len(other_naf):
1039 self_naf = [0] * (len(other_naf) - len(self_naf)) + self_naf
1040 elif len(self_naf) > len(other_naf):
1041 other_naf = [0] * (len(self_naf) - len(other_naf)) + other_naf
1042
1043 for A, B in zip(self_naf, other_naf):
1044 X3, Y3, Z3 = _double(X3, Y3, Z3, p, a)
1045
1046 # conditions ordered from most to least likely
1047 if A == 0:
1048 if B == 0:
1049 pass
1050 elif B < 0:
1051 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, Z2, p)
1052 else:
1053 assert B > 0
1054 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, Z2, p)
1055 elif A < 0:
1056 if B == 0:
1057 X3, Y3, Z3 = _add(X3, Y3, Z3, X1, -Y1, Z1, p)
1058 elif B < 0:
1059 X3, Y3, Z3 = _add(X3, Y3, Z3, mAmB_X, mAmB_Y, mAmB_Z, p)
1060 else:
1061 assert B > 0
1062 X3, Y3, Z3 = _add(X3, Y3, Z3, mApB_X, mApB_Y, mApB_Z, p)
1063 else:
1064 assert A > 0
1065 if B == 0:
1066 X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, Z1, p)
1067 elif B < 0:
1068 X3, Y3, Z3 = _add(X3, Y3, Z3, pAmB_X, pAmB_Y, pAmB_Z, p)
1069 else:
1070 assert B > 0
1071 X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p)
1072
1073 if not Y3 or not Z3:
1074 return INFINITY
1075
1076 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
1077
1078 def __neg__(self):
1079 """Return negated point."""
1080 x, y, z = self.__coords
1081 return PointJacobi(self.__curve, x, -y, z, self.__order)
1082
1083
1084class Point(AbstractPoint):
1085 """A point on a short Weierstrass elliptic curve. Altering x and y is
1086 forbidden, but they can be read by the x() and y() methods."""
1087
1088 def __init__(self, curve, x, y, order=None):
1089 """curve, x, y, order; order (optional) is the order of this point."""
1090 super(Point, self).__init__()
1091 self.__curve = curve
1092 if GMPY:
1093 self.__x = x and mpz(x)
1094 self.__y = y and mpz(y)
1095 self.__order = order and mpz(order)
1096 else:
1097 self.__x = x
1098 self.__y = y
1099 self.__order = order
1100 # self.curve is allowed to be None only for INFINITY:
1101 if self.__curve:
1102 assert self.__curve.contains_point(x, y)
1103 # for curves with cofactor 1, all points that are on the curve are
1104 # scalar multiples of the base point, so performing multiplication is
1105 # not necessary to verify that. See Section 3.2.2.1 of SEC 1 v2
1106 if curve and curve.cofactor() != 1 and order:
1107 assert self * order == INFINITY
1108
1109 @classmethod
1110 def from_bytes(
1111 cls,
1112 curve,
1113 data,
1114 validate_encoding=True,
1115 valid_encodings=None,
1116 order=None,
1117 ):
1118 """
1119 Initialise the object from byte encoding of a point.
1120
1121 The method does accept and automatically detect the type of point
1122 encoding used. It supports the :term:`raw encoding`,
1123 :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings.
1124
1125 :param data: single point encoding of the public key
1126 :type data: :term:`bytes-like object`
1127 :param curve: the curve on which the public key is expected to lay
1128 :type curve: ~ecdsa.ellipticcurve.CurveFp
1129 :param validate_encoding: whether to verify that the encoding of the
1130 point is self-consistent, defaults to True, has effect only
1131 on ``hybrid`` encoding
1132 :type validate_encoding: bool
1133 :param valid_encodings: list of acceptable point encoding formats,
1134 supported ones are: :term:`uncompressed`, :term:`compressed`,
1135 :term:`hybrid`, and :term:`raw encoding` (specified with ``raw``
1136 name). All formats by default (specified with ``None``).
1137 :type valid_encodings: :term:`set-like object`
1138 :param int order: the point order, must be non zero when using
1139 generator=True
1140
1141 :raises `~ecdsa.errors.MalformedPointError`: if the public point does
1142 not lay on the curve or the encoding is invalid
1143
1144 :return: Point on curve
1145 :rtype: Point
1146 """
1147 coord_x, coord_y = super(Point, cls).from_bytes(
1148 curve, data, validate_encoding, valid_encodings
1149 )
1150 return Point(curve, coord_x, coord_y, order)
1151
1152 def __eq__(self, other):
1153 """Return True if the points are identical, False otherwise.
1154
1155 Note: only points that lay on the same curve can be equal.
1156 """
1157 if isinstance(other, Point):
1158 return (
1159 self.__curve == other.__curve
1160 and self.__x == other.__x
1161 and self.__y == other.__y
1162 )
1163 return NotImplemented
1164
1165 def __ne__(self, other):
1166 """Returns False if points are identical, True otherwise."""
1167 return not self == other
1168
1169 def __neg__(self):
1170 return Point(self.__curve, self.__x, self.__curve.p() - self.__y)
1171
1172 def __add__(self, other):
1173 """Add one point to another point."""
1174
1175 # X9.62 B.3:
1176
1177 if not isinstance(other, Point):
1178 return NotImplemented
1179 if other == INFINITY:
1180 return self
1181 if self == INFINITY:
1182 return other
1183 assert self.__curve == other.__curve
1184 if self.__x == other.__x:
1185 if (self.__y + other.__y) % self.__curve.p() == 0:
1186 return INFINITY
1187 else:
1188 return self.double()
1189
1190 p = self.__curve.p()
1191
1192 l = (
1193 (other.__y - self.__y)
1194 * numbertheory.inverse_mod(other.__x - self.__x, p)
1195 ) % p
1196
1197 x3 = (l * l - self.__x - other.__x) % p
1198 y3 = (l * (self.__x - x3) - self.__y) % p
1199
1200 return Point(self.__curve, x3, y3)
1201
1202 def __mul__(self, other):
1203 """Multiply a point by an integer."""
1204
1205 def leftmost_bit(x):
1206 assert x > 0
1207 result = 1
1208 while result <= x:
1209 result = 2 * result
1210 return result // 2
1211
1212 e = other
1213 if e == 0 or (self.__order and e % self.__order == 0):
1214 return INFINITY
1215 if self == INFINITY:
1216 return INFINITY
1217 if e < 0:
1218 return (-self) * (-e)
1219
1220 # From X9.62 D.3.2:
1221
1222 e3 = 3 * e
1223 negative_self = Point(self.__curve, self.__x, -self.__y, self.__order)
1224 i = leftmost_bit(e3) // 2
1225 result = self
1226 # print("Multiplying %s by %d (e3 = %d):" % (self, other, e3))
1227 while i > 1:
1228 result = result.double()
1229 if (e3 & i) != 0 and (e & i) == 0:
1230 result = result + self
1231 if (e3 & i) == 0 and (e & i) != 0:
1232 result = result + negative_self
1233 # print(". . . i = %d, result = %s" % ( i, result ))
1234 i = i // 2
1235
1236 return result
1237
1238 def __rmul__(self, other):
1239 """Multiply a point by an integer."""
1240
1241 return self * other
1242
1243 def __str__(self):
1244 if self == INFINITY:
1245 return "infinity"
1246 return "(%d,%d)" % (self.__x, self.__y)
1247
1248 def double(self):
1249 """Return a new point that is twice the old."""
1250
1251 if self == INFINITY:
1252 return INFINITY
1253
1254 # X9.62 B.3:
1255
1256 p = self.__curve.p()
1257 a = self.__curve.a()
1258
1259 l = (
1260 (3 * self.__x * self.__x + a)
1261 * numbertheory.inverse_mod(2 * self.__y, p)
1262 ) % p
1263
1264 x3 = (l * l - 2 * self.__x) % p
1265 y3 = (l * (self.__x - x3) - self.__y) % p
1266
1267 return Point(self.__curve, x3, y3)
1268
1269 def x(self):
1270 return self.__x
1271
1272 def y(self):
1273 return self.__y
1274
1275 def curve(self):
1276 return self.__curve
1277
1278 def order(self):
1279 return self.__order
1280
1281
1282class PointEdwards(AbstractPoint):
1283 """Point on Twisted Edwards curve.
1284
1285 Internally represents the coordinates on the curve using four parameters,
1286 X, Y, Z, T. They correspond to affine parameters 'x' and 'y' like so:
1287
1288 x = X / Z
1289 y = Y / Z
1290 x*y = T / Z
1291 """
1292
1293 def __init__(self, curve, x, y, z, t, order=None, generator=False):
1294 """
1295 Initialise a point that uses the extended coordinates internally.
1296 """
1297 super(PointEdwards, self).__init__()
1298 self.__curve = curve
1299 if GMPY: # pragma: no branch
1300 self.__coords = (mpz(x), mpz(y), mpz(z), mpz(t))
1301 self.__order = order and mpz(order)
1302 else: # pragma: no branch
1303 self.__coords = (x, y, z, t)
1304 self.__order = order
1305 self.__generator = generator
1306 self.__precompute = []
1307
1308 @classmethod
1309 def from_bytes(
1310 cls,
1311 curve,
1312 data,
1313 validate_encoding=None,
1314 valid_encodings=None,
1315 order=None,
1316 generator=False,
1317 ):
1318 """
1319 Initialise the object from byte encoding of a point.
1320
1321 `validate_encoding` and `valid_encodings` are provided for
1322 compatibility with Weierstrass curves, they are ignored for Edwards
1323 points.
1324
1325 :param data: single point encoding of the public key
1326 :type data: :term:`bytes-like object`
1327 :param curve: the curve on which the public key is expected to lay
1328 :type curve: ecdsa.ellipticcurve.CurveEdTw
1329 :param None validate_encoding: Ignored, encoding is always validated
1330 :param None valid_encodings: Ignored, there is just one encoding
1331 supported
1332 :param int order: the point order, must be non zero when using
1333 generator=True
1334 :param bool generator: Flag to mark the point as a curve generator,
1335 this will cause the library to pre-compute some values to
1336 make repeated usages of the point much faster
1337
1338 :raises `~ecdsa.errors.MalformedPointError`: if the public point does
1339 not lay on the curve or the encoding is invalid
1340
1341 :return: Initialised point on an Edwards curve
1342 :rtype: PointEdwards
1343 """
1344 coord_x, coord_y = super(PointEdwards, cls).from_bytes(
1345 curve, data, validate_encoding, valid_encodings
1346 )
1347 return PointEdwards(
1348 curve, coord_x, coord_y, 1, coord_x * coord_y, order, generator
1349 )
1350
1351 def _maybe_precompute(self):
1352 if not self.__generator or self.__precompute:
1353 return self.__precompute
1354
1355 # since this code will execute just once, and it's fully deterministic,
1356 # depend on atomicity of the last assignment to switch from empty
1357 # self.__precompute to filled one and just ignore the unlikely
1358 # situation when two threads execute it at the same time (as it won't
1359 # lead to inconsistent __precompute)
1360 order = self.__order
1361 assert order
1362 precompute = []
1363 i = 1
1364 order *= 2
1365 coord_x, coord_y, coord_z, coord_t = self.__coords
1366 prime = self.__curve.p()
1367
1368 doubler = PointEdwards(
1369 self.__curve, coord_x, coord_y, coord_z, coord_t, order
1370 )
1371 # for "protection" against Minerva we need 1 or 2 more bits depending
1372 # on order bit size, but it's easier to just calculate one
1373 # point more always
1374 order *= 4
1375
1376 while i < order:
1377 doubler = doubler.scale()
1378 coord_x, coord_y = doubler.x(), doubler.y()
1379 coord_t = coord_x * coord_y % prime
1380 precompute.append((coord_x, coord_y, coord_t))
1381
1382 i *= 2
1383 doubler = doubler.double()
1384
1385 self.__precompute = precompute
1386 return self.__precompute
1387
1388 def x(self):
1389 """Return affine x coordinate."""
1390 X1, _, Z1, _ = self.__coords
1391 if Z1 == 1:
1392 return X1
1393 p = self.__curve.p()
1394 z_inv = numbertheory.inverse_mod(Z1, p)
1395 return X1 * z_inv % p
1396
1397 def y(self):
1398 """Return affine y coordinate."""
1399 _, Y1, Z1, _ = self.__coords
1400 if Z1 == 1:
1401 return Y1
1402 p = self.__curve.p()
1403 z_inv = numbertheory.inverse_mod(Z1, p)
1404 return Y1 * z_inv % p
1405
1406 def curve(self):
1407 """Return the curve of the point."""
1408 return self.__curve
1409
1410 def order(self):
1411 return self.__order
1412
1413 def scale(self):
1414 """
1415 Return point scaled so that z == 1.
1416
1417 Modifies point in place, returns self.
1418 """
1419 X1, Y1, Z1, _ = self.__coords
1420 if Z1 == 1:
1421 return self
1422
1423 p = self.__curve.p()
1424 z_inv = numbertheory.inverse_mod(Z1, p)
1425 x = X1 * z_inv % p
1426 y = Y1 * z_inv % p
1427 t = x * y % p
1428 self.__coords = (x, y, 1, t)
1429 return self
1430
1431 def __eq__(self, other):
1432 """Compare for equality two points with each-other.
1433
1434 Note: only points on the same curve can be equal.
1435 """
1436 x1, y1, z1, t1 = self.__coords
1437 if other is INFINITY:
1438 return not x1 or not t1
1439 if isinstance(other, PointEdwards):
1440 x2, y2, z2, t2 = other.__coords
1441 else:
1442 return NotImplemented
1443 if self.__curve != other.curve():
1444 return False
1445 p = self.__curve.p()
1446
1447 # cross multiply to eliminate divisions
1448 xn1 = x1 * z2 % p
1449 xn2 = x2 * z1 % p
1450 yn1 = y1 * z2 % p
1451 yn2 = y2 * z1 % p
1452 return xn1 == xn2 and yn1 == yn2
1453
1454 def __ne__(self, other):
1455 """Compare for inequality two points with each-other."""
1456 return not self == other
1457
1458 def _add(self, X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a):
1459 """add two points, assume sane parameters."""
1460 # after add-2008-hwcd-2
1461 # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html
1462 # NOTE: there are more efficient formulas for Z1 or Z2 == 1
1463 A = X1 * X2 % p
1464 B = Y1 * Y2 % p
1465 C = Z1 * T2 % p
1466 D = T1 * Z2 % p
1467 E = D + C
1468 F = ((X1 - Y1) * (X2 + Y2) + B - A) % p
1469 G = B + a * A
1470 H = D - C
1471 if not H:
1472 return self._double(X1, Y1, Z1, T1, p, a)
1473 X3 = E * F % p
1474 Y3 = G * H % p
1475 T3 = E * H % p
1476 Z3 = F * G % p
1477
1478 return X3, Y3, Z3, T3
1479
1480 def __add__(self, other):
1481 """Add point to another."""
1482 if other == INFINITY:
1483 return self
1484 if (
1485 not isinstance(other, PointEdwards)
1486 or self.__curve != other.__curve
1487 ):
1488 raise ValueError("The other point is on a different curve.")
1489
1490 p, a = self.__curve.p(), self.__curve.a()
1491 X1, Y1, Z1, T1 = self.__coords
1492 X2, Y2, Z2, T2 = other.__coords
1493
1494 X3, Y3, Z3, T3 = self._add(X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a)
1495
1496 if not X3 or not T3:
1497 return INFINITY
1498 return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
1499
1500 def __radd__(self, other):
1501 """Add other to self."""
1502 return self + other
1503
1504 def _double(self, X1, Y1, Z1, T1, p, a):
1505 """Double the point, assume sane parameters."""
1506 # after "dbl-2008-hwcd"
1507 # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html
1508 # NOTE: there are more efficient formulas for Z1 == 1
1509 A = X1 * X1 % p
1510 B = Y1 * Y1 % p
1511 C = 2 * Z1 * Z1 % p
1512 D = a * A % p
1513 E = ((X1 + Y1) * (X1 + Y1) - A - B) % p
1514 G = D + B
1515 F = G - C
1516 H = D - B
1517 X3 = E * F % p
1518 Y3 = G * H % p
1519 T3 = E * H % p
1520 Z3 = F * G % p
1521
1522 return X3, Y3, Z3, T3
1523
1524 def double(self):
1525 """Return point added to itself."""
1526 X1, Y1, Z1, T1 = self.__coords
1527
1528 if not X1 or not T1:
1529 return INFINITY
1530
1531 p, a = self.__curve.p(), self.__curve.a()
1532
1533 X3, Y3, Z3, T3 = self._double(X1, Y1, Z1, T1, p, a)
1534
1535 # both Ed25519 and Ed448 have prime order, so no point added to
1536 # itself will equal zero
1537 if not X3 or not T3: # pragma: no branch
1538 return INFINITY
1539 return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
1540
1541 def __rmul__(self, other):
1542 """Multiply point by an integer."""
1543 return self * other
1544
1545 def _mul_precompute(self, other):
1546 """Multiply point by integer with precomputation table."""
1547 X3, Y3, Z3, T3, p, a = 0, 1, 1, 0, self.__curve.p(), self.__curve.a()
1548 _add = self._add
1549 for X2, Y2, T2 in self.__precompute:
1550 rem = other % 4
1551 if rem == 0 or rem == 2:
1552 other //= 2
1553 elif rem == 3:
1554 other = (other + 1) // 2
1555 X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, 1, -T2, p, a)
1556 else:
1557 assert rem == 1
1558 other = (other - 1) // 2
1559 X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, 1, T2, p, a)
1560
1561 if not X3 or not T3:
1562 return INFINITY
1563
1564 return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
1565
1566 def __mul__(self, other):
1567 """Multiply point by an integer."""
1568 X2, Y2, Z2, T2 = self.__coords
1569 if not X2 or not T2 or not other:
1570 return INFINITY
1571 if other == 1:
1572 return self
1573 if self.__order:
1574 # order*2 as a "protection" for Minerva
1575 other = other % (self.__order * 2)
1576 if self._maybe_precompute():
1577 return self._mul_precompute(other)
1578
1579 X3, Y3, Z3, T3 = 0, 1, 1, 0 # INFINITY in extended coordinates
1580 p, a = self.__curve.p(), self.__curve.a()
1581 _double = self._double
1582 _add = self._add
1583
1584 for i in reversed(self._naf(other)):
1585 X3, Y3, Z3, T3 = _double(X3, Y3, Z3, T3, p, a)
1586 if i < 0:
1587 X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, Z2, -T2, p, a)
1588 elif i > 0:
1589 X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, Z2, T2, p, a)
1590
1591 if not X3 or not T3:
1592 return INFINITY
1593
1594 return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
1595
1596
1597# This one point is the Point At Infinity for all purposes:
1598INFINITY = Point(None, None, None)