Skip to content

Instantly share code, notes, and snippets.

@khaotik
Created June 11, 2021 14:49
Show Gist options
  • Save khaotik/d7c8becf4f582444c2ecf8e68e46d37f to your computer and use it in GitHub Desktop.
Save khaotik/d7c8becf4f582444c2ecf8e68e46d37f to your computer and use it in GitHub Desktop.
minimal python implementation of secp256k1 curve
#!/usr/bin/env python
'''
minimal reference implementation of bitcoin's secp256k1 with general prime field
requires pari && cypari2 for certain operations.
'''
class EcPoint:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return '{}({},{})'.format(type(self).__name__, hex(self.x), hex(self.y))
def __iter__(self):
yield self.x
yield self.y
def _calcSecp256k1GroupOrder(modp:int):
import cypari2
pari = cypari2.Pari()
ec = pari.ellinit([0,0,0,0,7], modp)
return pari.ellcard(ec)
ec_t = EcPoint
class Secp256k1:
modp:int
_gorder:int
G:ec_t
iG:ec_t
def __init__(self,
modp:int=None, G:ec_t=None):
btc_gorder = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
if modp is None:
modp = btc_gorder
self.modp = modp
if G is None:
if modp == btc_gorder:
G = EcPoint(
0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)
else:
self.modp = modp
G = self.randomPoint()
self.G = G
self.iG = self.inv(G)
def randomPoint(self):
import cypari2
pari = cypari2.Pari()
ec = pari.ellinit([0,0,0,0,7], self.modp)
x,y = pari.random(ec)
return ec_t(int(x),int(y))
def groupOrder(self):
ret = getattr(self,'_gorder',None)
if ret is None:
ret = _calcSecp256k1GroupOrder(self.modp)
self._gorder = ret
return ret
def mul(self, A:ec_t, B:ec_t):
modp = self.modp
if (A==1):
return B
if (B==1):
return A
Ax,Ay = A
Bx,By = B
if Ax != Bx:
s = ((Ay - By) * pow((Ax - Bx), -1, modp)) % modp
elif Ay == By:
s = (3*Ax*Ax * pow(2*Ay, -1, modp)) % modp
else:
return 1
Cx = (s*s - Ax - Bx) % modp
Cy = (s*(Ax - Cx) - Ay)
Cy = Cy % modp
return ec_t(Cx, Cy)
def inv(self, A:ec_t):
if A==1:
return 1
x,y = A
return ec_t(x,(-y)%self.modp)
def square(self, A:ec_t):
if A==1:
return 1
modp = self.modp
x,y = A
s = (3*x*x * pow(2*y, -1, modp)) % modp
Cx = (s*s - x - x) % modp
Cy = (s*(x - Cx) - y) % modp
return ec_t(Cx, Cy)
def pow(self, A:ec_t, k:int):
if k==0:
return 1
modp = self.modp
# k %= modp
nbits = k.bit_length()
ret = ec_t(A.x, A.y)
for i in range(nbits-1, 0, -1):
ret = self.square(ret)
if ((1<<(i-1)) & k):
ret = self.mul(ret, A)
return ret
def exp(self, i:int):
return self.pow(self.G, i)
secp256k1 = Secp256k1()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment