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 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 _, _, z = self.__coords
727 p = self.__curve.p()
728 if not (z % p):
729 return INFINITY
730 self.scale()
731 x, y, z = self.__coords
732 assert z == 1
733 return Point(self.__curve, x, y, self.__order)
734
735 @staticmethod
736 def from_affine(point, generator=False):
737 """Create from an affine point.
738
739 :param bool generator: set to True to make the point to precalculate
740 multiplication table - useful for public point when verifying many
741 signatures (around 100 or so) or for generator points of a curve.
742 """
743 return PointJacobi(
744 point.curve(), point.x(), point.y(), 1, point.order(), generator
745 )
746
747 # please note that all the methods that use the equations from
748 # hyperelliptic
749 # are formatted in a way to maximise performance.
750 # Things that make code faster: multiplying instead of taking to the power
751 # (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and
752 # `pow(x, 4, p)`),
753 # multiple assignments at the same time (`x1, x2 = self.x1, self.x2` is
754 # faster than `x1 = self.x1; x2 = self.x2`),
755 # similarly, sometimes the `% p` is skipped if it makes the calculation
756 # faster and the result of calculation is later reduced modulo `p`
757
758 def _double_with_z_1(self, X1, Y1, p, a):
759 """Add a point to itself with z == 1."""
760 # after:
761 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl
762 XX, YY = X1 * X1 % p, Y1 * Y1 % p
763 if not YY:
764 return 0, 0, 0
765 YYYY = YY * YY % p
766 S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
767 M = 3 * XX + a
768 T = (M * M - 2 * S) % p
769 # X3 = T
770 Y3 = (M * (S - T) - 8 * YYYY) % p
771 Z3 = 2 * Y1 % p
772 return T, Y3, Z3
773
774 def _double(self, X1, Y1, Z1, p, a):
775 """Add a point to itself, arbitrary z."""
776 if Z1 == 1:
777 return self._double_with_z_1(X1, Y1, p, a)
778 if not Z1:
779 return 0, 0, 0
780 # after:
781 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
782 XX, YY = X1 * X1 % p, Y1 * Y1 % p
783 if not YY:
784 return 0, 0, 0
785 YYYY = YY * YY % p
786 ZZ = Z1 * Z1 % p
787 S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
788 M = (3 * XX + a * ZZ * ZZ) % p
789 T = (M * M - 2 * S) % p
790 # X3 = T
791 Y3 = (M * (S - T) - 8 * YYYY) % p
792 Z3 = ((Y1 + Z1) ** 2 - YY - ZZ) % p
793
794 return T, Y3, Z3
795
796 def double(self):
797 """Add a point to itself."""
798 X1, Y1, Z1 = self.__coords
799
800 if not Z1:
801 return INFINITY
802
803 p, a = self.__curve.p(), self.__curve.a()
804
805 X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a)
806
807 if not Z3:
808 return INFINITY
809 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
810
811 def _add_with_z_1(self, X1, Y1, X2, Y2, p):
812 """add points when both Z1 and Z2 equal 1"""
813 # after:
814 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-mmadd-2007-bl
815 H = X2 - X1
816 HH = H * H
817 I = 4 * HH % p
818 J = H * I
819 r = 2 * (Y2 - Y1)
820 if not H and not r:
821 return self._double_with_z_1(X1, Y1, p, self.__curve.a())
822 V = X1 * I
823 X3 = (r**2 - J - 2 * V) % p
824 Y3 = (r * (V - X3) - 2 * Y1 * J) % p
825 Z3 = 2 * H % p
826 return X3, Y3, Z3
827
828 def _add_with_z_eq(self, X1, Y1, Z1, X2, Y2, p):
829 """add points when Z1 == Z2"""
830 # after:
831 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-zadd-2007-m
832 A = (X2 - X1) ** 2 % p
833 B = X1 * A % p
834 C = X2 * A
835 D = (Y2 - Y1) ** 2 % p
836 if not A and not D:
837 return self._double(X1, Y1, Z1, p, self.__curve.a())
838 X3 = (D - B - C) % p
839 Y3 = ((Y2 - Y1) * (B - X3) - Y1 * (C - B)) % p
840 Z3 = Z1 * (X2 - X1) % p
841 return X3, Y3, Z3
842
843 def _add_with_z2_1(self, X1, Y1, Z1, X2, Y2, p):
844 """add points when Z2 == 1"""
845 # after:
846 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl
847 Z1Z1 = Z1 * Z1 % p
848 U2, S2 = X2 * Z1Z1 % p, Y2 * Z1 * Z1Z1 % p
849 H = (U2 - X1) % p
850 HH = H * H % p
851 I = 4 * HH % p
852 J = H * I
853 r = 2 * (S2 - Y1) % p
854 if not r and not H:
855 return self._double_with_z_1(X2, Y2, p, self.__curve.a())
856 V = X1 * I
857 X3 = (r * r - J - 2 * V) % p
858 Y3 = (r * (V - X3) - 2 * Y1 * J) % p
859 Z3 = ((Z1 + H) ** 2 - Z1Z1 - HH) % p
860 return X3, Y3, Z3
861
862 def _add_with_z_ne(self, X1, Y1, Z1, X2, Y2, Z2, p):
863 """add points with arbitrary z"""
864 # after:
865 # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl
866 Z1Z1 = Z1 * Z1 % p
867 Z2Z2 = Z2 * Z2 % p
868 U1 = X1 * Z2Z2 % p
869 U2 = X2 * Z1Z1 % p
870 S1 = Y1 * Z2 * Z2Z2 % p
871 S2 = Y2 * Z1 * Z1Z1 % p
872 H = U2 - U1
873 I = 4 * H * H % p
874 J = H * I % p
875 r = 2 * (S2 - S1) % p
876 if not H and not r:
877 return self._double(X1, Y1, Z1, p, self.__curve.a())
878 V = U1 * I
879 X3 = (r * r - J - 2 * V) % p
880 Y3 = (r * (V - X3) - 2 * S1 * J) % p
881 Z3 = ((Z1 + Z2) ** 2 - Z1Z1 - Z2Z2) * H % p
882
883 return X3, Y3, Z3
884
885 def __radd__(self, other):
886 """Add other to self."""
887 return self + other
888
889 def _add(self, X1, Y1, Z1, X2, Y2, Z2, p):
890 """add two points, select fastest method."""
891 if not Z1:
892 return X2 % p, Y2 % p, Z2 % p
893 if not Z2:
894 return X1 % p, Y1 % p, Z1 % p
895 if Z1 == Z2:
896 if Z1 == 1:
897 return self._add_with_z_1(X1, Y1, X2, Y2, p)
898 return self._add_with_z_eq(X1, Y1, Z1, X2, Y2, p)
899 if Z1 == 1:
900 return self._add_with_z2_1(X2, Y2, Z2, X1, Y1, p)
901 if Z2 == 1:
902 return self._add_with_z2_1(X1, Y1, Z1, X2, Y2, p)
903 return self._add_with_z_ne(X1, Y1, Z1, X2, Y2, Z2, p)
904
905 def __add__(self, other):
906 """Add two points on elliptic curve."""
907 if self == INFINITY:
908 return other
909 if other == INFINITY:
910 return self
911 if isinstance(other, Point):
912 other = PointJacobi.from_affine(other)
913 if self.__curve != other.__curve:
914 raise ValueError("The other point is on different curve")
915
916 p = self.__curve.p()
917 X1, Y1, Z1 = self.__coords
918 X2, Y2, Z2 = other.__coords
919
920 X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p)
921
922 if not Z3:
923 return INFINITY
924 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
925
926 def __rmul__(self, other):
927 """Multiply point by an integer."""
928 return self * other
929
930 def _mul_precompute(self, other):
931 """Multiply point by integer with precomputation table."""
932 X3, Y3, Z3, p = 0, 0, 0, self.__curve.p()
933 _add = self._add
934 for X2, Y2 in self.__precompute:
935 if other % 2:
936 if other % 4 >= 2:
937 other = (other + 1) // 2
938 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p)
939 else:
940 other = (other - 1) // 2
941 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p)
942 else:
943 other //= 2
944
945 if not Z3:
946 return INFINITY
947 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
948
949 def __mul__(self, other):
950 """Multiply point by an integer."""
951 if not self.__coords[1] or not other:
952 return INFINITY
953 if other == 1:
954 return self
955 if self.__order:
956 # order*2 as a protection for Minerva
957 other = other % (self.__order * 2)
958 self._maybe_precompute()
959 if self.__precompute:
960 return self._mul_precompute(other)
961
962 self = self.scale()
963 X2, Y2, _ = self.__coords
964 X3, Y3, Z3 = 0, 0, 0
965 p, a = self.__curve.p(), self.__curve.a()
966 _double = self._double
967 _add = self._add
968 # since adding points when at least one of them is scaled
969 # is quicker, reverse the NAF order
970 for i in reversed(self._naf(other)):
971 X3, Y3, Z3 = _double(X3, Y3, Z3, p, a)
972 if i < 0:
973 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p)
974 elif i > 0:
975 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p)
976
977 if not Z3:
978 return INFINITY
979
980 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
981
982 def mul_add(self, self_mul, other, other_mul):
983 """
984 Do two multiplications at the same time, add results.
985
986 calculates self*self_mul + other*other_mul
987 """
988 if other == INFINITY or other_mul == 0:
989 return self * self_mul
990 if self_mul == 0:
991 return other * other_mul
992 if not isinstance(other, PointJacobi):
993 other = PointJacobi.from_affine(other)
994 # when the points have precomputed answers, then multiplying them alone
995 # is faster (as it uses NAF and no point doublings)
996 self._maybe_precompute()
997 other._maybe_precompute()
998 if self.__precompute and other.__precompute:
999 return self * self_mul + other * other_mul
1000
1001 if self.__order:
1002 self_mul = self_mul % self.__order
1003 other_mul = other_mul % self.__order
1004
1005 # (X3, Y3, Z3) is the accumulator
1006 X3, Y3, Z3 = 0, 0, 0
1007 p, a = self.__curve.p(), self.__curve.a()
1008
1009 # as we have 6 unique points to work with, we can't scale all of them,
1010 # but do scale the ones that are used most often
1011 self.scale()
1012 X1, Y1, Z1 = self.__coords
1013 other.scale()
1014 X2, Y2, Z2 = other.__coords
1015
1016 _double = self._double
1017 _add = self._add
1018
1019 # with NAF we have 3 options: no add, subtract, add
1020 # so with 2 points, we have 9 combinations:
1021 # 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B
1022 # so we need 4 combined points:
1023 mAmB_X, mAmB_Y, mAmB_Z = _add(X1, -Y1, Z1, X2, -Y2, Z2, p)
1024 pAmB_X, pAmB_Y, pAmB_Z = _add(X1, Y1, Z1, X2, -Y2, Z2, p)
1025 mApB_X, mApB_Y, mApB_Z = pAmB_X, -pAmB_Y, pAmB_Z
1026 pApB_X, pApB_Y, pApB_Z = mAmB_X, -mAmB_Y, mAmB_Z
1027 # when the self and other sum to infinity, we need to add them
1028 # one by one to get correct result but as that's very unlikely to
1029 # happen in regular operation, we don't need to optimise this case
1030 if not pApB_Z:
1031 return self * self_mul + other * other_mul
1032
1033 # gmp object creation has cumulatively higher overhead than the
1034 # speedup we get from calculating the NAF using gmp so ensure use
1035 # of int()
1036 self_naf = list(reversed(self._naf(int(self_mul))))
1037 other_naf = list(reversed(self._naf(int(other_mul))))
1038 # ensure that the lists are the same length (zip() will truncate
1039 # longer one otherwise)
1040 if len(self_naf) < len(other_naf):
1041 self_naf = [0] * (len(other_naf) - len(self_naf)) + self_naf
1042 elif len(self_naf) > len(other_naf):
1043 other_naf = [0] * (len(self_naf) - len(other_naf)) + other_naf
1044
1045 for A, B in zip(self_naf, other_naf):
1046 X3, Y3, Z3 = _double(X3, Y3, Z3, p, a)
1047
1048 # conditions ordered from most to least likely
1049 if A == 0:
1050 if B == 0:
1051 pass
1052 elif B < 0:
1053 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, Z2, p)
1054 else:
1055 assert B > 0
1056 X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, Z2, p)
1057 elif A < 0:
1058 if B == 0:
1059 X3, Y3, Z3 = _add(X3, Y3, Z3, X1, -Y1, Z1, p)
1060 elif B < 0:
1061 X3, Y3, Z3 = _add(X3, Y3, Z3, mAmB_X, mAmB_Y, mAmB_Z, p)
1062 else:
1063 assert B > 0
1064 X3, Y3, Z3 = _add(X3, Y3, Z3, mApB_X, mApB_Y, mApB_Z, p)
1065 else:
1066 assert A > 0
1067 if B == 0:
1068 X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, Z1, p)
1069 elif B < 0:
1070 X3, Y3, Z3 = _add(X3, Y3, Z3, pAmB_X, pAmB_Y, pAmB_Z, p)
1071 else:
1072 assert B > 0
1073 X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p)
1074
1075 if not Z3:
1076 return INFINITY
1077
1078 return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
1079
1080 def __neg__(self):
1081 """Return negated point."""
1082 x, y, z = self.__coords
1083 return PointJacobi(self.__curve, x, -y, z, self.__order)
1084
1085
1086class Point(AbstractPoint):
1087 """A point on a short Weierstrass elliptic curve. Altering x and y is
1088 forbidden, but they can be read by the x() and y() methods."""
1089
1090 def __init__(self, curve, x, y, order=None):
1091 """curve, x, y, order; order (optional) is the order of this point."""
1092 super(Point, self).__init__()
1093 self.__curve = curve
1094 if GMPY:
1095 self.__x = x and mpz(x)
1096 self.__y = y and mpz(y)
1097 self.__order = order and mpz(order)
1098 else:
1099 self.__x = x
1100 self.__y = y
1101 self.__order = order
1102 # self.curve is allowed to be None only for INFINITY:
1103 if self.__curve:
1104 assert self.__curve.contains_point(x, y)
1105 # for curves with cofactor 1, all points that are on the curve are
1106 # scalar multiples of the base point, so performing multiplication is
1107 # not necessary to verify that. See Section 3.2.2.1 of SEC 1 v2
1108 if curve and curve.cofactor() != 1 and order:
1109 assert self * order == INFINITY
1110
1111 @classmethod
1112 def from_bytes(
1113 cls,
1114 curve,
1115 data,
1116 validate_encoding=True,
1117 valid_encodings=None,
1118 order=None,
1119 ):
1120 """
1121 Initialise the object from byte encoding of a point.
1122
1123 The method does accept and automatically detect the type of point
1124 encoding used. It supports the :term:`raw encoding`,
1125 :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings.
1126
1127 :param data: single point encoding of the public key
1128 :type data: :term:`bytes-like object`
1129 :param curve: the curve on which the public key is expected to lay
1130 :type curve: ~ecdsa.ellipticcurve.CurveFp
1131 :param validate_encoding: whether to verify that the encoding of the
1132 point is self-consistent, defaults to True, has effect only
1133 on ``hybrid`` encoding
1134 :type validate_encoding: bool
1135 :param valid_encodings: list of acceptable point encoding formats,
1136 supported ones are: :term:`uncompressed`, :term:`compressed`,
1137 :term:`hybrid`, and :term:`raw encoding` (specified with ``raw``
1138 name). All formats by default (specified with ``None``).
1139 :type valid_encodings: :term:`set-like object`
1140 :param int order: the point order, must be non zero when using
1141 generator=True
1142
1143 :raises `~ecdsa.errors.MalformedPointError`: if the public point does
1144 not lay on the curve or the encoding is invalid
1145
1146 :return: Point on curve
1147 :rtype: Point
1148 """
1149 coord_x, coord_y = super(Point, cls).from_bytes(
1150 curve, data, validate_encoding, valid_encodings
1151 )
1152 return Point(curve, coord_x, coord_y, order)
1153
1154 def __eq__(self, other):
1155 """Return True if the points are identical, False otherwise.
1156
1157 Note: only points that lay on the same curve can be equal.
1158 """
1159 if other is INFINITY:
1160 return self.__x is None or self.__y is None
1161 if isinstance(other, Point):
1162 return (
1163 self.__curve == other.__curve
1164 and self.__x == other.__x
1165 and self.__y == other.__y
1166 )
1167 return NotImplemented
1168
1169 def __ne__(self, other):
1170 """Returns False if points are identical, True otherwise."""
1171 return not self == other
1172
1173 def __neg__(self):
1174 return Point(self.__curve, self.__x, self.__curve.p() - self.__y)
1175
1176 def __add__(self, other):
1177 """Add one point to another point."""
1178
1179 # X9.62 B.3:
1180
1181 if not isinstance(other, Point):
1182 return NotImplemented
1183 if other == INFINITY:
1184 return self
1185 if self == INFINITY:
1186 return other
1187 assert self.__curve == other.__curve
1188 if self.__x == other.__x:
1189 if (self.__y + other.__y) % self.__curve.p() == 0:
1190 return INFINITY
1191 else:
1192 return self.double()
1193
1194 p = self.__curve.p()
1195
1196 l = (
1197 (other.__y - self.__y)
1198 * numbertheory.inverse_mod(other.__x - self.__x, p)
1199 ) % p
1200
1201 x3 = (l * l - self.__x - other.__x) % p
1202 y3 = (l * (self.__x - x3) - self.__y) % p
1203
1204 return Point(self.__curve, x3, y3)
1205
1206 def __mul__(self, other):
1207 """Multiply a point by an integer."""
1208
1209 def leftmost_bit(x):
1210 assert x > 0
1211 result = 1
1212 while result <= x:
1213 result = 2 * result
1214 return result // 2
1215
1216 e = other
1217 if e == 0 or (self.__order and e % self.__order == 0):
1218 return INFINITY
1219 if self == INFINITY:
1220 return INFINITY
1221 if e < 0:
1222 return (-self) * (-e)
1223
1224 # From X9.62 D.3.2:
1225
1226 e3 = 3 * e
1227 negative_self = Point(
1228 self.__curve,
1229 self.__x,
1230 (-self.__y) % self.__curve.p(),
1231 self.__order,
1232 )
1233 i = leftmost_bit(e3) // 2
1234 result = self
1235 # print("Multiplying %s by %d (e3 = %d):" % (self, other, e3))
1236 while i > 1:
1237 result = result.double()
1238 if (e3 & i) != 0 and (e & i) == 0:
1239 result = result + self
1240 if (e3 & i) == 0 and (e & i) != 0:
1241 result = result + negative_self
1242 # print(". . . i = %d, result = %s" % ( i, result ))
1243 i = i // 2
1244
1245 return result
1246
1247 def __rmul__(self, other):
1248 """Multiply a point by an integer."""
1249
1250 return self * other
1251
1252 def __str__(self):
1253 if self == INFINITY:
1254 return "infinity"
1255 return "(%d,%d)" % (self.__x, self.__y)
1256
1257 def double(self):
1258 """Return a new point that is twice the old."""
1259 if self == INFINITY:
1260 return INFINITY
1261
1262 # X9.62 B.3:
1263
1264 p = self.__curve.p()
1265 a = self.__curve.a()
1266
1267 l = (
1268 (3 * self.__x * self.__x + a)
1269 * numbertheory.inverse_mod(2 * self.__y, p)
1270 ) % p
1271
1272 if not l:
1273 return INFINITY
1274
1275 x3 = (l * l - 2 * self.__x) % p
1276 y3 = (l * (self.__x - x3) - self.__y) % p
1277
1278 return Point(self.__curve, x3, y3)
1279
1280 def x(self):
1281 return self.__x
1282
1283 def y(self):
1284 return self.__y
1285
1286 def curve(self):
1287 return self.__curve
1288
1289 def order(self):
1290 return self.__order
1291
1292
1293class PointEdwards(AbstractPoint):
1294 """Point on Twisted Edwards curve.
1295
1296 Internally represents the coordinates on the curve using four parameters,
1297 X, Y, Z, T. They correspond to affine parameters 'x' and 'y' like so:
1298
1299 x = X / Z
1300 y = Y / Z
1301 x*y = T / Z
1302 """
1303
1304 def __init__(self, curve, x, y, z, t, order=None, generator=False):
1305 """
1306 Initialise a point that uses the extended coordinates internally.
1307 """
1308 super(PointEdwards, self).__init__()
1309 self.__curve = curve
1310 if GMPY: # pragma: no branch
1311 self.__coords = (mpz(x), mpz(y), mpz(z), mpz(t))
1312 self.__order = order and mpz(order)
1313 else: # pragma: no branch
1314 self.__coords = (x, y, z, t)
1315 self.__order = order
1316 self.__generator = generator
1317 self.__precompute = []
1318
1319 @classmethod
1320 def from_bytes(
1321 cls,
1322 curve,
1323 data,
1324 validate_encoding=None,
1325 valid_encodings=None,
1326 order=None,
1327 generator=False,
1328 ):
1329 """
1330 Initialise the object from byte encoding of a point.
1331
1332 `validate_encoding` and `valid_encodings` are provided for
1333 compatibility with Weierstrass curves, they are ignored for Edwards
1334 points.
1335
1336 :param data: single point encoding of the public key
1337 :type data: :term:`bytes-like object`
1338 :param curve: the curve on which the public key is expected to lay
1339 :type curve: ecdsa.ellipticcurve.CurveEdTw
1340 :param None validate_encoding: Ignored, encoding is always validated
1341 :param None valid_encodings: Ignored, there is just one encoding
1342 supported
1343 :param int order: the point order, must be non zero when using
1344 generator=True
1345 :param bool generator: Flag to mark the point as a curve generator,
1346 this will cause the library to pre-compute some values to
1347 make repeated usages of the point much faster
1348
1349 :raises `~ecdsa.errors.MalformedPointError`: if the public point does
1350 not lay on the curve or the encoding is invalid
1351
1352 :return: Initialised point on an Edwards curve
1353 :rtype: PointEdwards
1354 """
1355 coord_x, coord_y = super(PointEdwards, cls).from_bytes(
1356 curve, data, validate_encoding, valid_encodings
1357 )
1358 return PointEdwards(
1359 curve, coord_x, coord_y, 1, coord_x * coord_y, order, generator
1360 )
1361
1362 def _maybe_precompute(self):
1363 if not self.__generator or self.__precompute:
1364 return self.__precompute
1365
1366 # since this code will execute just once, and it's fully deterministic,
1367 # depend on atomicity of the last assignment to switch from empty
1368 # self.__precompute to filled one and just ignore the unlikely
1369 # situation when two threads execute it at the same time (as it won't
1370 # lead to inconsistent __precompute)
1371 order = self.__order
1372 assert order
1373 precompute = []
1374 i = 1
1375 order *= 2
1376 coord_x, coord_y, coord_z, coord_t = self.__coords
1377 prime = self.__curve.p()
1378
1379 doubler = PointEdwards(
1380 self.__curve, coord_x, coord_y, coord_z, coord_t, order
1381 )
1382 # for "protection" against Minerva we need 1 or 2 more bits depending
1383 # on order bit size, but it's easier to just calculate one
1384 # point more always
1385 order *= 4
1386
1387 while i < order:
1388 doubler = doubler.scale()
1389 coord_x, coord_y = doubler.x(), doubler.y()
1390 coord_t = coord_x * coord_y % prime
1391 precompute.append((coord_x, coord_y, coord_t))
1392
1393 i *= 2
1394 doubler = doubler.double()
1395
1396 self.__precompute = precompute
1397 return self.__precompute
1398
1399 def x(self):
1400 """Return affine x coordinate."""
1401 X1, _, Z1, _ = self.__coords
1402 if Z1 == 1:
1403 return X1
1404 p = self.__curve.p()
1405 z_inv = numbertheory.inverse_mod(Z1, p)
1406 return X1 * z_inv % p
1407
1408 def y(self):
1409 """Return affine y coordinate."""
1410 _, Y1, Z1, _ = self.__coords
1411 if Z1 == 1:
1412 return Y1
1413 p = self.__curve.p()
1414 z_inv = numbertheory.inverse_mod(Z1, p)
1415 return Y1 * z_inv % p
1416
1417 def curve(self):
1418 """Return the curve of the point."""
1419 return self.__curve
1420
1421 def order(self):
1422 return self.__order
1423
1424 def scale(self):
1425 """
1426 Return point scaled so that z == 1.
1427
1428 Modifies point in place, returns self.
1429 """
1430 X1, Y1, Z1, _ = self.__coords
1431 if Z1 == 1:
1432 return self
1433
1434 p = self.__curve.p()
1435 z_inv = numbertheory.inverse_mod(Z1, p)
1436 x = X1 * z_inv % p
1437 y = Y1 * z_inv % p
1438 t = x * y % p
1439 self.__coords = (x, y, 1, t)
1440 return self
1441
1442 def __eq__(self, other):
1443 """Compare for equality two points with each-other.
1444
1445 Note: only points on the same curve can be equal.
1446 """
1447 x1, y1, z1, t1 = self.__coords
1448 if other is INFINITY:
1449 return not x1 or not t1
1450 if isinstance(other, PointEdwards):
1451 x2, y2, z2, t2 = other.__coords
1452 else:
1453 return NotImplemented
1454 if self.__curve != other.curve():
1455 return False
1456 p = self.__curve.p()
1457
1458 # cross multiply to eliminate divisions
1459 xn1 = x1 * z2 % p
1460 xn2 = x2 * z1 % p
1461 yn1 = y1 * z2 % p
1462 yn2 = y2 * z1 % p
1463 return xn1 == xn2 and yn1 == yn2
1464
1465 def __ne__(self, other):
1466 """Compare for inequality two points with each-other."""
1467 return not self == other
1468
1469 def _add(self, X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a):
1470 """add two points, assume sane parameters."""
1471 # after add-2008-hwcd-2
1472 # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html
1473 # NOTE: there are more efficient formulas for Z1 or Z2 == 1
1474 A = X1 * X2 % p
1475 B = Y1 * Y2 % p
1476 C = Z1 * T2 % p
1477 D = T1 * Z2 % p
1478 E = D + C
1479 F = ((X1 - Y1) * (X2 + Y2) + B - A) % p
1480 G = B + a * A
1481 H = D - C
1482 if not H:
1483 return self._double(X1, Y1, Z1, T1, p, a)
1484 X3 = E * F % p
1485 Y3 = G * H % p
1486 T3 = E * H % p
1487 Z3 = F * G % p
1488
1489 return X3, Y3, Z3, T3
1490
1491 def __add__(self, other):
1492 """Add point to another."""
1493 if other == INFINITY:
1494 return self
1495 if (
1496 not isinstance(other, PointEdwards)
1497 or self.__curve != other.__curve
1498 ):
1499 raise ValueError("The other point is on a different curve.")
1500
1501 p, a = self.__curve.p(), self.__curve.a()
1502 X1, Y1, Z1, T1 = self.__coords
1503 X2, Y2, Z2, T2 = other.__coords
1504
1505 X3, Y3, Z3, T3 = self._add(X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a)
1506
1507 if not X3 or not T3:
1508 return INFINITY
1509 return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
1510
1511 def __radd__(self, other):
1512 """Add other to self."""
1513 return self + other
1514
1515 def _double(self, X1, Y1, Z1, T1, p, a):
1516 """Double the point, assume sane parameters."""
1517 # after "dbl-2008-hwcd"
1518 # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html
1519 # NOTE: there are more efficient formulas for Z1 == 1
1520 A = X1 * X1 % p
1521 B = Y1 * Y1 % p
1522 C = 2 * Z1 * Z1 % p
1523 D = a * A % p
1524 E = ((X1 + Y1) * (X1 + Y1) - A - B) % p
1525 G = D + B
1526 F = G - C
1527 H = D - B
1528 X3 = E * F % p
1529 Y3 = G * H % p
1530 T3 = E * H % p
1531 Z3 = F * G % p
1532
1533 return X3, Y3, Z3, T3
1534
1535 def double(self):
1536 """Return point added to itself."""
1537 X1, Y1, Z1, T1 = self.__coords
1538
1539 if not X1 or not T1:
1540 return INFINITY
1541
1542 p, a = self.__curve.p(), self.__curve.a()
1543
1544 X3, Y3, Z3, T3 = self._double(X1, Y1, Z1, T1, p, a)
1545
1546 # both Ed25519 and Ed448 have prime order, so no point added to
1547 # itself will equal zero
1548 if not X3 or not T3: # pragma: no branch
1549 return INFINITY
1550 return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
1551
1552 def __rmul__(self, other):
1553 """Multiply point by an integer."""
1554 return self * other
1555
1556 def _mul_precompute(self, other):
1557 """Multiply point by integer with precomputation table."""
1558 X3, Y3, Z3, T3, p, a = 0, 1, 1, 0, self.__curve.p(), self.__curve.a()
1559 _add = self._add
1560 for X2, Y2, T2 in self.__precompute:
1561 rem = other % 4
1562 if rem == 0 or rem == 2:
1563 other //= 2
1564 elif rem == 3:
1565 other = (other + 1) // 2
1566 X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, 1, -T2, p, a)
1567 else:
1568 assert rem == 1
1569 other = (other - 1) // 2
1570 X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, 1, T2, p, a)
1571
1572 if not X3 or not T3:
1573 return INFINITY
1574
1575 return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
1576
1577 def __mul__(self, other):
1578 """Multiply point by an integer."""
1579 X2, Y2, Z2, T2 = self.__coords
1580 if not X2 or not T2 or not other:
1581 return INFINITY
1582 if other == 1:
1583 return self
1584 if self.__order:
1585 # order*2 as a "protection" for Minerva
1586 other = other % (self.__order * 2)
1587 if self._maybe_precompute():
1588 return self._mul_precompute(other)
1589
1590 X3, Y3, Z3, T3 = 0, 1, 1, 0 # INFINITY in extended coordinates
1591 p, a = self.__curve.p(), self.__curve.a()
1592 _double = self._double
1593 _add = self._add
1594
1595 for i in reversed(self._naf(other)):
1596 X3, Y3, Z3, T3 = _double(X3, Y3, Z3, T3, p, a)
1597 if i < 0:
1598 X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, Z2, -T2, p, a)
1599 elif i > 0:
1600 X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, Z2, T2, p, a)
1601
1602 if not X3 or not T3:
1603 return INFINITY
1604
1605 return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
1606
1607
1608# This one point is the Point At Infinity for all purposes:
1609INFINITY = Point(None, None, None)