Giacomo Casoni
Giacomo Casoni

Reputation: 427

Elliptic curve double and add implementation in python

I am trying to implement the "double and add" algorithm to quickly multiply points on an elliptic curve in Python (3, please).

Based off this previous answer (about addition and doubling),

Elliptic curve point addition over a finite field in Python

the Wikipedia page,

https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication

and my textbook (Information Security, by Mark Stamp), I came up with the following code:

def point_add(N_x, N_y, Q_x, Q_y, p):
    m = (Q_y - N_y) * pow((Q_x-N_x), p-2, p)
    ret_x = (m ** 2 - N_x - Q_x) % p
    ret_y = (m*(N_x - ret_x) - N_y) % p
    return ret_x, ret_y


def point_double(N_x, N_y, a, p):
    m = (3*(N_x ** 2)+a) * pow(2*N_y, p-2, p)   
    ret_x = (m ** 2 - N_x - Q_x) % p
    ret_y = (m*(N_x - ret_x) - N_y) % p
    return ret_x, ret_y


multiplier_A = 44
multiplier_B = 57
a = 10
b = -21
p = 41
N_x = 3
N_y = 6
Q_x = 0
Q_y = 0

multiplier_A = list(bin(multiplier_A)[2:])
multiplier_B = list(bin(multiplier_B)[2:])

for x_a in multiplier_A:
    if x_a == '1':
        Q_x, Q_y = point_add(N_x, N_y, Q_x, Q_y, p)
    N_x, N_y = point_double(N_x, N_y, a, p)

print(Q_x, Q_y)

Which sure enough returns a wrong result.

Where do I go wrong? A new implementation from scratch is also more than welcome.

UPDATE

I want to multiply the point (3,6) times multiplier_A and multiplier_B. For now I'm trying to only do multiplier_A(3,6).

a, b, and p are the elliptic curve paramethers

Upvotes: 1

Views: 4296

Answers (1)

Arty
Arty

Reputation: 16737

Both of your functions are totally correct, except small typo that point_double() should have N_x instead of Q_x inside its body, because there is no Q_x argument in function (Q_x is undefined).

I checked them against my own implementation that I used many times in different algorithms, my implementation is called add_dbl() in code below.

You can see below that I used standard Secp256k1 elliptic curve, that is default curve for BitCoin.

Function test() generates 200 random points and does addition and doubling with both my and yours implementations, and checks if they give equal results.

Code doesn't have any non-standard dependecies, just imports standard random module.

Try it online!

def get_y(A, B, N, x):
    assert N % 4 == 3, N
    y2 = x ** 3 + A * x + B
    y = pow(y2, (N + 1) // 4, N)
    if pow(y, 2, N) != y2 % N:
        return None
    return y

def add_dbl(A, N, Px, Py, Qx = None, Qy = None):
    if Qx is None and Qy is None:
        Qx, Qy = Px, Py
    if Px == Qx and Py == Qy:
        s = ((Px * Px * 3 + A) * pow(Py * 2, -1, N)) % N
    else:
        s = ((Py - Qy) * pow(Px - Qx, -1, N)) % N
    x = (s * s - Px - Qx) % N
    y = (s * (Px - x) - Py) % N
    return x, y

def base_point():
    # https://en.bitcoin.it/wiki/Secp256k1
    # https://www.secg.org/sec2-v2.pdf
    A = 0
    B = 7
    N = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F
    x = 0x79BE667E_F9DCBBAC_55A06295_CE870B07_029BFCDB_2DCE28D9_59F2815B_16F81798
    y = 0x483ADA77_26A3C465_5DA4FBFC_0E1108A8_FD17B448_A6855419_9C47D08F_FB10D4B8
    q = 0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_BAAEDCE6_AF48A03B_BFD25E8C_D0364141
    return A, B, N, x, y

def point_add(N_x, N_y, Q_x, Q_y, p):
    m = (Q_y - N_y) * pow((Q_x-N_x), p-2, p)
    ret_x = (m ** 2 - N_x - Q_x) % p
    ret_y = (m*(N_x - ret_x) - N_y) % p
    return ret_x, ret_y

def point_double(N_x, N_y, a, p):
    m = (3*(N_x ** 2)+a) * pow(2*N_y, p-2, p)   
    ret_x = (m ** 2 - N_x - N_x) % p
    ret_y = (m*(N_x - ret_x) - N_y) % p
    return ret_x, ret_y

def test():
    import random
    pb = base_point()
    A, B, N, *_ = pb
    for itest in range(200):
        while True:
            x0, x1 = [random.randrange(1, N) for i in range(2)]
            y0, y1 = [get_y(A, B, N, x) for x in (x0, x1)]
            if y0 is not None and y1 is not None:
                break
        
        add_a = add_dbl(A, N, x0, y0, x1, y1)
        add_b = point_add(x0, y0, x1, y1, N)
        assert add_a == add_b
        
        dbl_a = add_dbl(A, N, x0, y0)
        dbl_b = point_double(x0, y0, A, N)
        assert dbl_a == dbl_b

if __name__ == '__main__':
    test()

Upvotes: 0

Related Questions