Skip to content

Commit 64d4a89

Browse files
committed
Add class for field elements and ellsq mapping functions
- source: https://github.com/sipa/writeups/tree/main/elligator-square-for-bn - f maps maps every field element to a curve point - r is a partial reverse function which can map a curve point to a field element
1 parent 1a32739 commit 64d4a89

File tree

2 files changed

+101
-1
lines changed

2 files changed

+101
-1
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Source: https://github.com/sipa/writeups/tree/main/elligator-square-for-bn
2+
"""Test-only Elligator Squared implementation
3+
4+
WARNING: This code is slow, uses bad randomness, does not properly protect
5+
keys, and is trivially vulnerable to side channel attacks. Do not use for
6+
anything but tests."""
7+
8+
from .key import fe
9+
10+
C1 = fe(-3).sqrt()
11+
C2 = (C1 - fe(1)) / fe(2)
12+
B = fe(7)
13+
14+
def f(u):
15+
"""Forward mapping function"""
16+
s = u**2
17+
x1 = C2 - C1*s / (fe(1)+B+s)
18+
g1 = x1**3 + B
19+
if g1.is_square():
20+
x, g = x1, g1
21+
else:
22+
x2 = -x1 - fe(1)
23+
g2 = x2**3 + B
24+
if g2.is_square():
25+
x, g = x2, g2
26+
else:
27+
x3 = fe(1) - (fe(1)+B+s)**2 / (fe(3)*s)
28+
g3 = x3**3 + B
29+
x, g = x3, g3
30+
y = g.sqrt()
31+
if y.is_odd() == u.is_odd():
32+
return (x, y)
33+
else:
34+
return (x, -y)
35+
36+
def r(x,y,i):
37+
"""Reverse mapping function"""
38+
if i == 0 or i == 1:
39+
z = fe(2)*x + fe(1)
40+
t1 = C1 - z
41+
t2 = C1 + z
42+
if not (t1*t2).is_square():
43+
return None
44+
if i == 0:
45+
if t2 == fe(0):
46+
return None
47+
if t1 == fe(0) and y.is_odd():
48+
return None
49+
u = ((fe(1)+B)*t1/t2).sqrt()
50+
else:
51+
x1 = -x-fe(1)
52+
if (x1**3 + B).is_square():
53+
return None
54+
u = ((fe(1)+B)*t2/t1).sqrt()
55+
else:
56+
z = fe(2) - fe(4)*B - fe(6)*x
57+
if not (z**2 - fe(16)*(B+fe(1))**2).is_square():
58+
return None
59+
if i == 2:
60+
s = (z + (z**2 - fe(16)*(B+fe(1))**2).sqrt()) / fe(4)
61+
else:
62+
if z**2 == fe(16)*(B+fe(1))**2:
63+
return None
64+
s = (z - (z**2 - fe(16)*(B+fe(1))**2).sqrt()) / fe(4)
65+
if not s.is_square():
66+
return None
67+
x1 = C2 - C1*s / (fe(1)+B+s)
68+
if (x1**3 + B).is_square():
69+
return None
70+
u = s.sqrt()
71+
if y.is_odd() == u.is_odd():
72+
return u
73+
else:
74+
return -u

test/functional/test_framework/key.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,33 @@ def modsqrt(a, p):
6060
return sqrt
6161
return None
6262

63+
SECP256K1_FIELD_SIZE = 2**256 - 2**32 - 977
64+
65+
class fe:
66+
"""Prime field over 2^256 - 2^32 - 977"""
67+
p = SECP256K1_FIELD_SIZE
68+
def __init__(self, x):
69+
self.val = x % self.p
70+
71+
def __add__ (self, o): return fe(self.val + o.val)
72+
def __eq__ (self, o): return self.val == o.val
73+
def __hash__ (self ): return id(self)
74+
def __mul__ (self, o): return fe(self.val * o.val)
75+
def __neg__ (self ): return fe(-self.val)
76+
def __pow__ (self, s): return fe(pow(self.val, s, self.p))
77+
def __sub__ (self, o): return fe(self.val - o.val)
78+
def __truediv__ (self, o): return fe(self.val * o.invert().val)
79+
80+
def invert (self ): return fe(pow(self.val, self.p-2, self.p))
81+
def is_odd(self): return (self.val & 1) != 0
82+
def is_square(self):
83+
ret_val = modsqrt(self.val, fe.p)
84+
if ret_val is not None:
85+
return True
86+
return False
87+
def sqrt(self):
88+
return fe(modsqrt(self.val, fe.p))
89+
6390
class EllipticCurve:
6491
def __init__(self, p, a, b):
6592
"""Initialize elliptic curve y^2 = x^3 + a*x + b over GF(p)."""
@@ -219,7 +246,6 @@ def mul(self, ps):
219246
r = self.add(r, p)
220247
return r
221248

222-
SECP256K1_FIELD_SIZE = 2**256 - 2**32 - 977
223249
SECP256K1 = EllipticCurve(SECP256K1_FIELD_SIZE, 0, 7)
224250
SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1)
225251
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

0 commit comments

Comments
 (0)