Lenstras 属性错误的椭圆曲线问题

Lenstras Elliptic curve problem with attribute error

Lenstra elliptic-curve factorization algorithm 编写了一些代码,我对此非常满意。但是,它只在某些时候有效。添加点的函数应该 return 一个整数或一个点。如果它 returns 是一个整数 d,Lenstra 代码块应该简单地打印 gcd(d,n) 并退出。似乎有时它不会退出,然后尝试将一个整数放入 add 函数中,然后由于属性错误而失败。我试过修补但似乎无法纠正它。

有人可以告诉我发生了什么事或如何更正代码吗?我很乐意回答任何问题,因为我不是程序员而且我知道我的代码远非整洁。

from sympy import mod_inverse
import math
import secrets
from collections import namedtuple
Point = namedtuple("Point", "x y")


def point_valid(P,a,b,p):
    O = 'Inf_Point'
    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


def inverse_point(P,p):
    O = 'Inf_Point'
    # Just calculates the inverse point
    if P == O:
        return P
    return Point(P.x, (-P.y) % p)


def point_add(P, Q, a, b, p):
    O = 'Inf_Point'
    # Checking that the points are valid if not raise an exception error
    if not (point_valid(P,a,b,p) and point_valid(Q,a,b,p)):
        raise ValueError("Invalid inputs")

    if P == O:
        R = Q
    elif Q == O:
        R = P
    elif Q == inverse_point(P,p):
        R = O
    else:
        if P == Q:
            try:
                inv = mod_inverse(2 * P.y,p)
            except ValueError:
                return 2 * P.y
            dydx = (3 * P.x**2 + a) * inv
        else:
            try:
                inv =  mod_inverse(Q.x - P.x,p)
            except ValueError:
                return Q.x - P.x
            dydx = (Q.y - P.y) * inv
        x = (dydx**2 - P.x - Q.x) % p
        y = (dydx * (P.x - x) - P.y) % p
        R = Point(x, y)

    # Making sure the result is on the curve
    assert point_valid(R,a,b,p)
    # Returning the result
    return R


def point_multiply(P,n,a,b,p):
    O = 'Inf_Point'
    Q = P
    R = O
    while n > 0:
        if n % 2 == 1:
            R = point_add(R,Q,a,b,p)
        Q = point_add(Q,Q,a,b,p)
        n = math.floor(n/2)
        if n > 0:
            continue
    return R



def random_curve(N):
    while True:
        A = secrets.randbelow(N)
        x = secrets.randbelow(N)
        y = secrets.randbelow(N)
        P = Point(x,y)
        B = (y**2 - x**3 - A*x) % N

        if (4*A**3 + 27*B**2) % N != 0:
            return (A,B,P)


def lenstra(N,B):
    a,b,P = random_curve(N)
    for i in range(2,B+1):
        if type(P)== int:
            d = math.gcd(P,N)
            if d < N:
                return d
            elif d == N:
                print('start again')
        Q = point_multiply(P,i,a,b,N)
        P = Q

print(lenstra(8453621,15))

大多数时候这会 运行 很好,return 一个整数除数;但是,有时会生成一条曲线,出现以下错误:

Traceback (most recent call last):
  File "C:/Users/conta/PycharmProjects/Cryptography/Lenstras_Elliptic_Factor.py", line 99, in <module>
Point(x=6653683, y=2444813)
    print(lenstra(8453621,15))
Point(x=1943642, y=922559)
  File "C:/Users/conta/PycharmProjects/Cryptography/Lenstras_Elliptic_Factor.py", line 96, in lenstra
    Q = point_multiply(P,i,a,b,N)
  File "C:/Users/conta/PycharmProjects/Cryptography/Lenstras_Elliptic_Factor.py", line 65, in point_multiply
    R = point_add(R,Q,a,b,p)
  File "C:/Users/conta/PycharmProjects/Cryptography/Lenstras_Elliptic_Factor.py", line 27, in point_add
    if not (point_valid(P,a,b,p) and point_valid(Q,a,b,p)):
  File "C:/Users/conta/PycharmProjects/Cryptography/Lenstras_Elliptic_Factor.py", line 13, in point_valid
    return (P.y**2 - (P.x**3 + a*P.x + b)) % p == 0 and 0 <= P.x < p and 0 <= P.y < p
AttributeError: 'int' object has no attribute 'y'

您可以通过将参数设置为 a= 6518263 b=1551605 P = Point(x=6433033, y=7097171) 而不是使用随机曲线来重现此错误。它失败了,因为当 P = 11 它不打印并退出时——它似乎试图尝试以 11 作为参数调用 point_multiply 函数。我似乎无法阻止这种行为,我已经尝试了很多方法。

我发现如果我添加这个:

 if type(Q) == int:
    return Q

到函数 point_add() 的开头,然后它似乎按预期工作,尽管我觉得这不是理想的。

您没有检查 point_multiply 到 return 中的两个 point_add 调用的结果 如果 int 而不是点 returned.

我将提出一些有点非正统的建议,这些建议会冒犯一些编程纯粹主义者。 我认为您应该在找到可能的因素时使用 Exception 发出信号。 在我看来,这将使您的代码更加清晰易懂,并且因为找到了 factor 是一个 "exceptional" 条件,它不会损害性能。这是代码 使用 NotInvertibleError 用户定义异常的最小修改,它 还纠正了一两个错误。

import math
import secrets
from collections import namedtuple
import sympy

Point = namedtuple("Point", "x y")


class NotInvertibleError(Exception):
    def __init__(self, value):
        self.value = value


def mod_inverse(a, m):
    try:
        return sympy.mod_inverse(a, m)
    except ValueError:
        raise NotInvertibleError(a)


def point_valid(P, a, b, p):
    O = 'Inf_Point'
    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


def inverse_point(P, p):
    O = 'Inf_Point'
    # Just calculates the inverse point
    if P == O:
        return P
    return Point(P.x, (-P.y) % p)


def point_add(P, Q, a, b, p):
    O = 'Inf_Point'
    # Checking that the points are valid if not raise an exception error
    if not (point_valid(P, a, b, p) and point_valid(Q, a, b, p)):
        raise ValueError("Invalid inputs")

    if P == O:
        R = Q
    elif Q == O:
        R = P
    elif Q == inverse_point(P, p):
        R = O
    else:
        if P == Q:
            inv = mod_inverse(2 * P.y, p)
            dydx = (3 * P.x ** 2 + a) * inv
        else:
            inv = mod_inverse(Q.x - P.x, p)
            dydx = (Q.y - P.y) * inv
        x = (dydx ** 2 - P.x - Q.x) % p
        y = (dydx * (P.x - x) - P.y) % p
        R = Point(x, y)

    # Making sure the result is on the curve
    assert point_valid(R, a, b, p)
    # Returning the result
    return R


def point_multiply(P, n, a, b, p):
    O = 'Inf_Point'
    Q = P
    R = O
    while n > 0:
        if n % 2 == 1:
            R = point_add(R, Q, a, b, p)
        Q = point_add(Q, Q, a, b, p)
        n = math.floor(n / 2)
        if n > 0:
            continue
    return R


def random_curve(N):
    while True:
        A = secrets.randbelow(N)
        x = secrets.randbelow(N)
        y = secrets.randbelow(N)
        P = Point(x, y)
        B = (y ** 2 - x ** 3 - A * x) % N

        if (4 * A ** 3 + 27 * B ** 2) % N != 0:
            return (A, B, P)


def lenstra(N, B):
    while True:
        a, b, P = random_curve(N)
        try:
            for i in range(2, B + 1):
                Q = point_multiply(P, i, a, b, N)
                P = Q
        except NotInvertibleError as e:
            d = math.gcd(e.value, N)
            if d < N:
                return d
            elif d == N:
                print("start again")



while True:
    print(lenstra(8453621, 15))

我强烈反对对 returning 值使用异常, 但对于某些因式分解算法,包括 Lenstra 的 Elliptic 曲线分解异常条件,无法计算逆, 是触发因素发现的原因,因此很自然地 使用一些附加信息传播异常。