这里有几个问题。首先是您使用了错误的公式:那些是求和的否定的公式,或者等效地说,是通过P和Q的直线上的曲线的第三个点。与您在维基百科上链接的公式进行比较,您会发现您对于Z.y
的值是它们所拥有的值的否定。
第二个问题是您的公式没有考虑原点:如果P是群法则中Q的逆,则P + Q将是原点,它不位于曲线的仿射部分,因此不能被描述为一对(x, y)
坐标。因此,您需要某种方式来指定原点。
让我们编写一些代码。首先,我们需要一种表示曲线上点的方法。我们使用字符串'Origin'
表示原点,并使用简单的命名元组表示椭圆曲线仿射部分上的点。
from collections import namedtuple
Point = namedtuple("Point", "x y")
O = 'Origin'
为了举例说明,我们选择了一个特定的椭圆曲线和素数。这个素数应该大于3
才能使加法公式有效。我们还编写了一个函数来检查一个特定点是否是曲线上的有效表示。这将有助于检查我们在实现加法公式时是否犯了任何错误。
# Choose a particular curve and prime. We assume that p > 3.
p = 15733
a = 1
b = 3
def valid(P):
"""
Determine whether we have a valid representation of a point
on our curve. We assume that the x and y coordinates
are always reduced modulo p, so that we can compare
two points for equality with a simple ==.
"""
if P == O:
return True
else:
return (
(P.y**2 - (P.x**3 + a*P.x + b)) % p == 0 and
0 <= P.x < p and 0 <= P.y < p)
要对p
取模的除法,您需要某种方式来计算p
的逆元。在这里,一个简单且相当高效的技巧是使用Python pow
函数的三个参数版本:根据费马小定理,pow(a, p-2, p)
将给出a
关于p
的逆元(前提是该逆元存在-也就是说,a
不可被p
整除)。
def inv_mod_p(x):
"""
Compute an inverse for x modulo p, assuming that x
is not divisible by p.
"""
if x % p == 0:
raise ZeroDivisionError("Impossible inverse")
return pow(x, p-2, p)
最后,这里是计算椭圆曲线求反和加法的两个函数。加法函数直接基于您提供的公式(在更正了 Z.y
的符号之后),利用 inv_mod_p
执行模 p
的除法,并对计算出的 x
和 y
坐标进行最终的模 p
减法约减。
def ec_inv(P):
"""
Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b.
"""
if P == O:
return P
return Point(P.x, (-P.y)%p)
def ec_add(P, Q):
"""
Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b.
"""
if not (valid(P) and valid(Q)):
raise ValueError("Invalid inputs")
if P == O:
result = Q
elif Q == O:
result = P
elif Q == ec_inv(P):
result = O
else:
if P == Q:
dydx = (3 * P.x**2 + a) * inv_mod_p(2 * P.y)
else:
dydx = (Q.y - P.y) * inv_mod_p(Q.x - P.x)
x = (dydx**2 - P.x - Q.x) % p
y = (dydx * (P.x - x) - P.y) % p
result = Point(x, y)
assert valid(result)
return result
我们可以通过在曲线上创建一些点并检查它们是否遵守预期的算术法则来检查上面的代码。
P = Point(6, 15)
Q = Point(8, 1267)
R = Point(2, 3103)
TwoP = ec_add(P, P)
ThreeP = ec_add(TwoP, P)
assert ec_add(P, ThreeP) == ec_add(TwoP, TwoP)
assert ec_add(P, ec_add(Q, R)) == ec_add(ec_add(P, Q), R)