from __future__ import division
try:
from gmpy2 import mpz
GMPY = True
except ImportError: try:
from gmpy import mpz
GMPY = True
except ImportError:
GMPY = False
from six import python_2_unicode_compatible
from . import numbertheory
from ._compat import normalise_bytes, int_to_bytes, bit_length, bytes_to_int
from .errors import MalformedPointError
from .util import orderlen, string_to_number, number_to_string
@python_2_unicode_compatible
class CurveFp(object):
if GMPY:
def __init__(self, p, a, b, h=None):
self.__p = mpz(p)
self.__a = mpz(a)
self.__b = mpz(b)
self.__h = h
else:
def __init__(self, p, a, b, h=None):
self.__p = p
self.__a = a
self.__b = b
self.__h = h
def __eq__(self, other):
if isinstance(other, CurveFp):
p = self.__p
return (
self.__p == other.__p
and self.__a % p == other.__a % p
and self.__b % p == other.__b % p
)
return NotImplemented
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash((self.__p, self.__a, self.__b))
def p(self):
return self.__p
def a(self):
return self.__a
def b(self):
return self.__b
def cofactor(self):
return self.__h
def contains_point(self, x, y):
return (y * y - ((x * x + self.__a) * x + self.__b)) % self.__p == 0
def __str__(self):
if self.__h is not None:
return "CurveFp(p={0}, a={1}, b={2}, h={3})".format(
self.__p,
self.__a,
self.__b,
self.__h,
)
return "CurveFp(p={0}, a={1}, b={2})".format(
self.__p,
self.__a,
self.__b,
)
class CurveEdTw(object):
if GMPY:
def __init__(self, p, a, d, h=None, hash_func=None):
self.__p = mpz(p)
self.__a = mpz(a)
self.__d = mpz(d)
self.__h = h
self.__hash_func = hash_func
else:
def __init__(self, p, a, d, h=None, hash_func=None):
self.__p = p
self.__a = a
self.__d = d
self.__h = h
self.__hash_func = hash_func
def __eq__(self, other):
if isinstance(other, CurveEdTw):
p = self.__p
return (
self.__p == other.__p
and self.__a % p == other.__a % p
and self.__d % p == other.__d % p
)
return NotImplemented
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash((self.__p, self.__a, self.__d))
def contains_point(self, x, y):
return (
self.__a * x * x + y * y - 1 - self.__d * x * x * y * y
) % self.__p == 0
def p(self):
return self.__p
def a(self):
return self.__a
def d(self):
return self.__d
def hash_func(self, data):
return self.__hash_func(data)
def cofactor(self):
return self.__h
def __str__(self):
if self.__h is not None:
return "CurveEdTw(p={0}, a={1}, d={2}, h={3})".format(
self.__p,
self.__a,
self.__d,
self.__h,
)
return "CurveEdTw(p={0}, a={1}, d={2})".format(
self.__p,
self.__a,
self.__d,
)
class AbstractPoint(object):
@staticmethod
def _from_raw_encoding(data, raw_encoding_length):
assert len(data) == raw_encoding_length
xs = data[: raw_encoding_length // 2]
ys = data[raw_encoding_length // 2 :]
assert len(xs) == raw_encoding_length // 2
assert len(ys) == raw_encoding_length // 2
coord_x = string_to_number(xs)
coord_y = string_to_number(ys)
return coord_x, coord_y
@staticmethod
def _from_compressed(data, curve):
if data[:1] not in (b"\x02", b"\x03"):
raise MalformedPointError("Malformed compressed point encoding")
is_even = data[:1] == b"\x02"
x = string_to_number(data[1:])
p = curve.p()
alpha = (pow(x, 3, p) + (curve.a() * x) + curve.b()) % p
try:
beta = numbertheory.square_root_mod_prime(alpha, p)
except numbertheory.Error as e:
raise MalformedPointError(
"Encoding does not correspond to a point on curve", e
)
if is_even == bool(beta & 1):
y = p - beta
else:
y = beta
return x, y
@classmethod
def _from_hybrid(cls, data, raw_encoding_length, validate_encoding):
assert data[:1] in (b"\x06", b"\x07")
x, y = cls._from_raw_encoding(data[1:], raw_encoding_length)
if validate_encoding and (
y & 1
and data[:1] != b"\x07"
or (not y & 1)
and data[:1] != b"\x06"
):
raise MalformedPointError("Inconsistent hybrid point encoding")
return x, y
@classmethod
def _from_edwards(cls, curve, data):
data = bytearray(data)
p = curve.p()
exp_len = (bit_length(p) + 1 + 7) // 8
if len(data) != exp_len:
raise MalformedPointError("Point length doesn't match the curve.")
x_0 = (data[-1] & 0x80) >> 7
data[-1] &= 0x80 - 1
y = bytes_to_int(data, "little")
if GMPY:
y = mpz(y)
x2 = (
(y * y - 1)
* numbertheory.inverse_mod(curve.d() * y * y - curve.a(), p)
% p
)
try:
x = numbertheory.square_root_mod_prime(x2, p)
except numbertheory.Error as e:
raise MalformedPointError(
"Encoding does not correspond to a point on curve", e
)
if x % 2 != x_0:
x = -x % p
return x, y
@classmethod
def from_bytes(
cls, curve, data, validate_encoding=True, valid_encodings=None
):
if not valid_encodings:
valid_encodings = set(
["uncompressed", "compressed", "hybrid", "raw"]
)
if not all(
i in set(("uncompressed", "compressed", "hybrid", "raw"))
for i in valid_encodings
):
raise ValueError(
"Only uncompressed, compressed, hybrid or raw encoding "
"supported."
)
data = normalise_bytes(data)
if isinstance(curve, CurveEdTw):
return cls._from_edwards(curve, data)
key_len = len(data)
raw_encoding_length = 2 * orderlen(curve.p())
if key_len == raw_encoding_length and "raw" in valid_encodings:
coord_x, coord_y = cls._from_raw_encoding(
data, raw_encoding_length
)
elif key_len == raw_encoding_length + 1 and (
"hybrid" in valid_encodings or "uncompressed" in valid_encodings
):
if data[:1] in (b"\x06", b"\x07") and "hybrid" in valid_encodings:
coord_x, coord_y = cls._from_hybrid(
data, raw_encoding_length, validate_encoding
)
elif data[:1] == b"\x04" and "uncompressed" in valid_encodings:
coord_x, coord_y = cls._from_raw_encoding(
data[1:], raw_encoding_length
)
else:
raise MalformedPointError(
"Invalid X9.62 encoding of the public point"
)
elif (
key_len == raw_encoding_length // 2 + 1
and "compressed" in valid_encodings
):
coord_x, coord_y = cls._from_compressed(data, curve)
else:
raise MalformedPointError(
"Length of string does not match lengths of "
"any of the enabled ({0}) encodings of the "
"curve.".format(", ".join(valid_encodings))
)
return coord_x, coord_y
def _raw_encode(self):
prime = self.curve().p()
x_str = number_to_string(self.x(), prime)
y_str = number_to_string(self.y(), prime)
return x_str + y_str
def _compressed_encode(self):
prime = self.curve().p()
x_str = number_to_string(self.x(), prime)
if self.y() & 1:
return b"\x03" + x_str
return b"\x02" + x_str
def _hybrid_encode(self):
raw_enc = self._raw_encode()
if self.y() & 1:
return b"\x07" + raw_enc
return b"\x06" + raw_enc
def _edwards_encode(self):
self.scale()
x, y, p = self.x(), self.y(), self.curve().p()
enc_len = (bit_length(p) + 1 + 7) // 8
y_str = int_to_bytes(y, enc_len, "little")
if x % 2:
y_str[-1] |= 0x80
return y_str
def to_bytes(self, encoding="raw"):
assert encoding in ("raw", "uncompressed", "compressed", "hybrid")
curve = self.curve()
if isinstance(curve, CurveEdTw):
return self._edwards_encode()
elif encoding == "raw":
return self._raw_encode()
elif encoding == "uncompressed":
return b"\x04" + self._raw_encode()
elif encoding == "hybrid":
return self._hybrid_encode()
else:
return self._compressed_encode()
@staticmethod
def _naf(mult):
ret = []
while mult:
if mult % 2:
nd = mult % 4
if nd >= 2:
nd -= 4
ret.append(nd)
mult -= nd
else:
ret.append(0)
mult //= 2
return ret
class PointJacobi(AbstractPoint):
def __init__(self, curve, x, y, z, order=None, generator=False):
super(PointJacobi, self).__init__()
self.__curve = curve
if GMPY: self.__coords = (mpz(x), mpz(y), mpz(z))
self.__order = order and mpz(order)
else: self.__coords = (x, y, z)
self.__order = order
self.__generator = generator
self.__precompute = []
@classmethod
def from_bytes(
cls,
curve,
data,
validate_encoding=True,
valid_encodings=None,
order=None,
generator=False,
):
coord_x, coord_y = super(PointJacobi, cls).from_bytes(
curve, data, validate_encoding, valid_encodings
)
return PointJacobi(curve, coord_x, coord_y, 1, order, generator)
def _maybe_precompute(self):
if not self.__generator or self.__precompute:
return
order = self.__order
assert order
precompute = []
i = 1
order *= 2
coord_x, coord_y, coord_z = self.__coords
doubler = PointJacobi(self.__curve, coord_x, coord_y, coord_z, order)
order *= 2
precompute.append((doubler.x(), doubler.y()))
while i < order:
i *= 2
doubler = doubler.double().scale()
precompute.append((doubler.x(), doubler.y()))
self.__precompute = precompute
def __getstate__(self):
state = self.__dict__.copy()
return state
def __setstate__(self, state):
self.__dict__.update(state)
def __eq__(self, other):
x1, y1, z1 = self.__coords
if other is INFINITY:
return not z1
if isinstance(other, Point):
x2, y2, z2 = other.x(), other.y(), 1
elif isinstance(other, PointJacobi):
x2, y2, z2 = other.__coords
else:
return NotImplemented
if self.__curve != other.curve():
return False
p = self.__curve.p()
zz1 = z1 * z1 % p
zz2 = z2 * z2 % p
return (x1 * zz2 - x2 * zz1) % p == 0 and (
y1 * zz2 * z2 - y2 * zz1 * z1
) % p == 0
def __ne__(self, other):
return not self == other
def order(self):
return self.__order
def curve(self):
return self.__curve
def x(self):
x, _, z = self.__coords
if z == 1:
return x
p = self.__curve.p()
z = numbertheory.inverse_mod(z, p)
return x * z**2 % p
def y(self):
_, y, z = self.__coords
if z == 1:
return y
p = self.__curve.p()
z = numbertheory.inverse_mod(z, p)
return y * z**3 % p
def scale(self):
x, y, z = self.__coords
if z == 1:
return self
p = self.__curve.p()
z_inv = numbertheory.inverse_mod(z, p)
zz_inv = z_inv * z_inv % p
x = x * zz_inv % p
y = y * zz_inv * z_inv % p
self.__coords = (x, y, 1)
return self
def to_affine(self):
_, _, z = self.__coords
p = self.__curve.p()
if not (z % p):
return INFINITY
self.scale()
x, y, z = self.__coords
assert z == 1
return Point(self.__curve, x, y, self.__order)
@staticmethod
def from_affine(point, generator=False):
return PointJacobi(
point.curve(), point.x(), point.y(), 1, point.order(), generator
)
def _double_with_z_1(self, X1, Y1, p, a):
XX, YY = X1 * X1 % p, Y1 * Y1 % p
if not YY:
return 0, 0, 0
YYYY = YY * YY % p
S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
M = 3 * XX + a
T = (M * M - 2 * S) % p
Y3 = (M * (S - T) - 8 * YYYY) % p
Z3 = 2 * Y1 % p
return T, Y3, Z3
def _double(self, X1, Y1, Z1, p, a):
if Z1 == 1:
return self._double_with_z_1(X1, Y1, p, a)
if not Z1:
return 0, 0, 0
XX, YY = X1 * X1 % p, Y1 * Y1 % p
if not YY:
return 0, 0, 0
YYYY = YY * YY % p
ZZ = Z1 * Z1 % p
S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p
M = (3 * XX + a * ZZ * ZZ) % p
T = (M * M - 2 * S) % p
Y3 = (M * (S - T) - 8 * YYYY) % p
Z3 = ((Y1 + Z1) ** 2 - YY - ZZ) % p
return T, Y3, Z3
def double(self):
X1, Y1, Z1 = self.__coords
if not Z1:
return INFINITY
p, a = self.__curve.p(), self.__curve.a()
X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a)
if not Z3:
return INFINITY
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
def _add_with_z_1(self, X1, Y1, X2, Y2, p):
H = X2 - X1
HH = H * H
I = 4 * HH % p
J = H * I
r = 2 * (Y2 - Y1)
if not H and not r:
return self._double_with_z_1(X1, Y1, p, self.__curve.a())
V = X1 * I
X3 = (r**2 - J - 2 * V) % p
Y3 = (r * (V - X3) - 2 * Y1 * J) % p
Z3 = 2 * H % p
return X3, Y3, Z3
def _add_with_z_eq(self, X1, Y1, Z1, X2, Y2, p):
A = (X2 - X1) ** 2 % p
B = X1 * A % p
C = X2 * A
D = (Y2 - Y1) ** 2 % p
if not A and not D:
return self._double(X1, Y1, Z1, p, self.__curve.a())
X3 = (D - B - C) % p
Y3 = ((Y2 - Y1) * (B - X3) - Y1 * (C - B)) % p
Z3 = Z1 * (X2 - X1) % p
return X3, Y3, Z3
def _add_with_z2_1(self, X1, Y1, Z1, X2, Y2, p):
Z1Z1 = Z1 * Z1 % p
U2, S2 = X2 * Z1Z1 % p, Y2 * Z1 * Z1Z1 % p
H = (U2 - X1) % p
HH = H * H % p
I = 4 * HH % p
J = H * I
r = 2 * (S2 - Y1) % p
if not r and not H:
return self._double_with_z_1(X2, Y2, p, self.__curve.a())
V = X1 * I
X3 = (r * r - J - 2 * V) % p
Y3 = (r * (V - X3) - 2 * Y1 * J) % p
Z3 = ((Z1 + H) ** 2 - Z1Z1 - HH) % p
return X3, Y3, Z3
def _add_with_z_ne(self, X1, Y1, Z1, X2, Y2, Z2, p):
Z1Z1 = Z1 * Z1 % p
Z2Z2 = Z2 * Z2 % p
U1 = X1 * Z2Z2 % p
U2 = X2 * Z1Z1 % p
S1 = Y1 * Z2 * Z2Z2 % p
S2 = Y2 * Z1 * Z1Z1 % p
H = U2 - U1
I = 4 * H * H % p
J = H * I % p
r = 2 * (S2 - S1) % p
if not H and not r:
return self._double(X1, Y1, Z1, p, self.__curve.a())
V = U1 * I
X3 = (r * r - J - 2 * V) % p
Y3 = (r * (V - X3) - 2 * S1 * J) % p
Z3 = ((Z1 + Z2) ** 2 - Z1Z1 - Z2Z2) * H % p
return X3, Y3, Z3
def __radd__(self, other):
return self + other
def _add(self, X1, Y1, Z1, X2, Y2, Z2, p):
if not Z1:
return X2 % p, Y2 % p, Z2 % p
if not Z2:
return X1 % p, Y1 % p, Z1 % p
if Z1 == Z2:
if Z1 == 1:
return self._add_with_z_1(X1, Y1, X2, Y2, p)
return self._add_with_z_eq(X1, Y1, Z1, X2, Y2, p)
if Z1 == 1:
return self._add_with_z2_1(X2, Y2, Z2, X1, Y1, p)
if Z2 == 1:
return self._add_with_z2_1(X1, Y1, Z1, X2, Y2, p)
return self._add_with_z_ne(X1, Y1, Z1, X2, Y2, Z2, p)
def __add__(self, other):
if self == INFINITY:
return other
if other == INFINITY:
return self
if isinstance(other, Point):
other = PointJacobi.from_affine(other)
if self.__curve != other.__curve:
raise ValueError("The other point is on different curve")
p = self.__curve.p()
X1, Y1, Z1 = self.__coords
X2, Y2, Z2 = other.__coords
X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p)
if not Z3:
return INFINITY
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
def __rmul__(self, other):
return self * other
def _mul_precompute(self, other):
X3, Y3, Z3, p = 0, 0, 0, self.__curve.p()
_add = self._add
for X2, Y2 in self.__precompute:
if other % 2:
if other % 4 >= 2:
other = (other + 1) // 2
X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p)
else:
other = (other - 1) // 2
X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p)
else:
other //= 2
if not Z3:
return INFINITY
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
def __mul__(self, other):
if not self.__coords[1] or not other:
return INFINITY
if other == 1:
return self
if self.__order:
other = other % (self.__order * 2)
self._maybe_precompute()
if self.__precompute:
return self._mul_precompute(other)
self = self.scale()
X2, Y2, _ = self.__coords
X3, Y3, Z3 = 0, 0, 0
p, a = self.__curve.p(), self.__curve.a()
_double = self._double
_add = self._add
for i in reversed(self._naf(other)):
X3, Y3, Z3 = _double(X3, Y3, Z3, p, a)
if i < 0:
X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p)
elif i > 0:
X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p)
if not Z3:
return INFINITY
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
def mul_add(self, self_mul, other, other_mul):
if other == INFINITY or other_mul == 0:
return self * self_mul
if self_mul == 0:
return other * other_mul
if not isinstance(other, PointJacobi):
other = PointJacobi.from_affine(other)
self._maybe_precompute()
other._maybe_precompute()
if self.__precompute and other.__precompute:
return self * self_mul + other * other_mul
if self.__order:
self_mul = self_mul % self.__order
other_mul = other_mul % self.__order
X3, Y3, Z3 = 0, 0, 0
p, a = self.__curve.p(), self.__curve.a()
self.scale()
X1, Y1, Z1 = self.__coords
other.scale()
X2, Y2, Z2 = other.__coords
_double = self._double
_add = self._add
mAmB_X, mAmB_Y, mAmB_Z = _add(X1, -Y1, Z1, X2, -Y2, Z2, p)
pAmB_X, pAmB_Y, pAmB_Z = _add(X1, Y1, Z1, X2, -Y2, Z2, p)
mApB_X, mApB_Y, mApB_Z = pAmB_X, -pAmB_Y, pAmB_Z
pApB_X, pApB_Y, pApB_Z = mAmB_X, -mAmB_Y, mAmB_Z
if not pApB_Z:
return self * self_mul + other * other_mul
self_naf = list(reversed(self._naf(int(self_mul))))
other_naf = list(reversed(self._naf(int(other_mul))))
if len(self_naf) < len(other_naf):
self_naf = [0] * (len(other_naf) - len(self_naf)) + self_naf
elif len(self_naf) > len(other_naf):
other_naf = [0] * (len(self_naf) - len(other_naf)) + other_naf
for A, B in zip(self_naf, other_naf):
X3, Y3, Z3 = _double(X3, Y3, Z3, p, a)
if A == 0:
if B == 0:
pass
elif B < 0:
X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, Z2, p)
else:
assert B > 0
X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, Z2, p)
elif A < 0:
if B == 0:
X3, Y3, Z3 = _add(X3, Y3, Z3, X1, -Y1, Z1, p)
elif B < 0:
X3, Y3, Z3 = _add(X3, Y3, Z3, mAmB_X, mAmB_Y, mAmB_Z, p)
else:
assert B > 0
X3, Y3, Z3 = _add(X3, Y3, Z3, mApB_X, mApB_Y, mApB_Z, p)
else:
assert A > 0
if B == 0:
X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, Z1, p)
elif B < 0:
X3, Y3, Z3 = _add(X3, Y3, Z3, pAmB_X, pAmB_Y, pAmB_Z, p)
else:
assert B > 0
X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p)
if not Z3:
return INFINITY
return PointJacobi(self.__curve, X3, Y3, Z3, self.__order)
def __neg__(self):
x, y, z = self.__coords
return PointJacobi(self.__curve, x, -y, z, self.__order)
class Point(AbstractPoint):
def __init__(self, curve, x, y, order=None):
super(Point, self).__init__()
self.__curve = curve
if GMPY:
self.__x = x and mpz(x)
self.__y = y and mpz(y)
self.__order = order and mpz(order)
else:
self.__x = x
self.__y = y
self.__order = order
if self.__curve:
assert self.__curve.contains_point(x, y)
if curve and curve.cofactor() != 1 and order:
assert self * order == INFINITY
@classmethod
def from_bytes(
cls,
curve,
data,
validate_encoding=True,
valid_encodings=None,
order=None,
):
coord_x, coord_y = super(Point, cls).from_bytes(
curve, data, validate_encoding, valid_encodings
)
return Point(curve, coord_x, coord_y, order)
def __eq__(self, other):
if other is INFINITY:
return self.__x is None or self.__y is None
if isinstance(other, Point):
return (
self.__curve == other.__curve
and self.__x == other.__x
and self.__y == other.__y
)
return NotImplemented
def __ne__(self, other):
return not self == other
def __neg__(self):
return Point(self.__curve, self.__x, self.__curve.p() - self.__y)
def __add__(self, other):
if not isinstance(other, Point):
return NotImplemented
if other == INFINITY:
return self
if self == INFINITY:
return other
assert self.__curve == other.__curve
if self.__x == other.__x:
if (self.__y + other.__y) % self.__curve.p() == 0:
return INFINITY
else:
return self.double()
p = self.__curve.p()
l = (
(other.__y - self.__y)
* numbertheory.inverse_mod(other.__x - self.__x, p)
) % p
x3 = (l * l - self.__x - other.__x) % p
y3 = (l * (self.__x - x3) - self.__y) % p
return Point(self.__curve, x3, y3)
def __mul__(self, other):
def leftmost_bit(x):
assert x > 0
result = 1
while result <= x:
result = 2 * result
return result // 2
e = other
if e == 0 or (self.__order and e % self.__order == 0):
return INFINITY
if self == INFINITY:
return INFINITY
if e < 0:
return (-self) * (-e)
e3 = 3 * e
negative_self = Point(
self.__curve,
self.__x,
(-self.__y) % self.__curve.p(),
self.__order,
)
i = leftmost_bit(e3) // 2
result = self
while i > 1:
result = result.double()
if (e3 & i) != 0 and (e & i) == 0:
result = result + self
if (e3 & i) == 0 and (e & i) != 0:
result = result + negative_self
i = i // 2
return result
def __rmul__(self, other):
return self * other
def __str__(self):
if self == INFINITY:
return "infinity"
return "(%d,%d)" % (self.__x, self.__y)
def double(self):
if self == INFINITY:
return INFINITY
p = self.__curve.p()
a = self.__curve.a()
l = (
(3 * self.__x * self.__x + a)
* numbertheory.inverse_mod(2 * self.__y, p)
) % p
if not l:
return INFINITY
x3 = (l * l - 2 * self.__x) % p
y3 = (l * (self.__x - x3) - self.__y) % p
return Point(self.__curve, x3, y3)
def x(self):
return self.__x
def y(self):
return self.__y
def curve(self):
return self.__curve
def order(self):
return self.__order
class PointEdwards(AbstractPoint):
def __init__(self, curve, x, y, z, t, order=None, generator=False):
super(PointEdwards, self).__init__()
self.__curve = curve
if GMPY: self.__coords = (mpz(x), mpz(y), mpz(z), mpz(t))
self.__order = order and mpz(order)
else: self.__coords = (x, y, z, t)
self.__order = order
self.__generator = generator
self.__precompute = []
@classmethod
def from_bytes(
cls,
curve,
data,
validate_encoding=None,
valid_encodings=None,
order=None,
generator=False,
):
coord_x, coord_y = super(PointEdwards, cls).from_bytes(
curve, data, validate_encoding, valid_encodings
)
return PointEdwards(
curve, coord_x, coord_y, 1, coord_x * coord_y, order, generator
)
def _maybe_precompute(self):
if not self.__generator or self.__precompute:
return self.__precompute
order = self.__order
assert order
precompute = []
i = 1
order *= 2
coord_x, coord_y, coord_z, coord_t = self.__coords
prime = self.__curve.p()
doubler = PointEdwards(
self.__curve, coord_x, coord_y, coord_z, coord_t, order
)
order *= 4
while i < order:
doubler = doubler.scale()
coord_x, coord_y = doubler.x(), doubler.y()
coord_t = coord_x * coord_y % prime
precompute.append((coord_x, coord_y, coord_t))
i *= 2
doubler = doubler.double()
self.__precompute = precompute
return self.__precompute
def x(self):
X1, _, Z1, _ = self.__coords
if Z1 == 1:
return X1
p = self.__curve.p()
z_inv = numbertheory.inverse_mod(Z1, p)
return X1 * z_inv % p
def y(self):
_, Y1, Z1, _ = self.__coords
if Z1 == 1:
return Y1
p = self.__curve.p()
z_inv = numbertheory.inverse_mod(Z1, p)
return Y1 * z_inv % p
def curve(self):
return self.__curve
def order(self):
return self.__order
def scale(self):
X1, Y1, Z1, _ = self.__coords
if Z1 == 1:
return self
p = self.__curve.p()
z_inv = numbertheory.inverse_mod(Z1, p)
x = X1 * z_inv % p
y = Y1 * z_inv % p
t = x * y % p
self.__coords = (x, y, 1, t)
return self
def __eq__(self, other):
x1, y1, z1, t1 = self.__coords
if other is INFINITY:
return not x1 or not t1
if isinstance(other, PointEdwards):
x2, y2, z2, t2 = other.__coords
else:
return NotImplemented
if self.__curve != other.curve():
return False
p = self.__curve.p()
xn1 = x1 * z2 % p
xn2 = x2 * z1 % p
yn1 = y1 * z2 % p
yn2 = y2 * z1 % p
return xn1 == xn2 and yn1 == yn2
def __ne__(self, other):
return not self == other
def _add(self, X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a):
A = X1 * X2 % p
B = Y1 * Y2 % p
C = Z1 * T2 % p
D = T1 * Z2 % p
E = D + C
F = ((X1 - Y1) * (X2 + Y2) + B - A) % p
G = B + a * A
H = D - C
if not H:
return self._double(X1, Y1, Z1, T1, p, a)
X3 = E * F % p
Y3 = G * H % p
T3 = E * H % p
Z3 = F * G % p
return X3, Y3, Z3, T3
def __add__(self, other):
if other == INFINITY:
return self
if (
not isinstance(other, PointEdwards)
or self.__curve != other.__curve
):
raise ValueError("The other point is on a different curve.")
p, a = self.__curve.p(), self.__curve.a()
X1, Y1, Z1, T1 = self.__coords
X2, Y2, Z2, T2 = other.__coords
X3, Y3, Z3, T3 = self._add(X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a)
if not X3 or not T3:
return INFINITY
return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
def __radd__(self, other):
return self + other
def _double(self, X1, Y1, Z1, T1, p, a):
A = X1 * X1 % p
B = Y1 * Y1 % p
C = 2 * Z1 * Z1 % p
D = a * A % p
E = ((X1 + Y1) * (X1 + Y1) - A - B) % p
G = D + B
F = G - C
H = D - B
X3 = E * F % p
Y3 = G * H % p
T3 = E * H % p
Z3 = F * G % p
return X3, Y3, Z3, T3
def double(self):
X1, Y1, Z1, T1 = self.__coords
if not X1 or not T1:
return INFINITY
p, a = self.__curve.p(), self.__curve.a()
X3, Y3, Z3, T3 = self._double(X1, Y1, Z1, T1, p, a)
if not X3 or not T3: return INFINITY
return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
def __rmul__(self, other):
return self * other
def _mul_precompute(self, other):
X3, Y3, Z3, T3, p, a = 0, 1, 1, 0, self.__curve.p(), self.__curve.a()
_add = self._add
for X2, Y2, T2 in self.__precompute:
rem = other % 4
if rem == 0 or rem == 2:
other //= 2
elif rem == 3:
other = (other + 1) // 2
X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, 1, -T2, p, a)
else:
assert rem == 1
other = (other - 1) // 2
X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, 1, T2, p, a)
if not X3 or not T3:
return INFINITY
return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
def __mul__(self, other):
X2, Y2, Z2, T2 = self.__coords
if not X2 or not T2 or not other:
return INFINITY
if other == 1:
return self
if self.__order:
other = other % (self.__order * 2)
if self._maybe_precompute():
return self._mul_precompute(other)
X3, Y3, Z3, T3 = 0, 1, 1, 0 p, a = self.__curve.p(), self.__curve.a()
_double = self._double
_add = self._add
for i in reversed(self._naf(other)):
X3, Y3, Z3, T3 = _double(X3, Y3, Z3, T3, p, a)
if i < 0:
X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, Z2, -T2, p, a)
elif i > 0:
X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, Z2, T2, p, a)
if not X3 or not T3:
return INFINITY
return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order)
INFINITY = Point(None, None, None)