Skip to content

Commit 8ec9bc5

Browse files
committed
Add functions for encoding and decoding to bytes
Source: https://github.com/sipa/writeups/tree/main/elligator-square-for-bn
1 parent 2b49380 commit 8ec9bc5

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

test/functional/test_framework/ellsq.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import random
88
import unittest
99

10-
from .key import fe, SECP256K1, SECP256K1_G, SECP256K1_ORDER
10+
from .key import fe, ECPubKey, SECP256K1, SECP256K1_G, SECP256K1_ORDER
1111

1212
C1 = fe(-3).sqrt()
1313
C2 = (C1 - fe(1)) / fe(2)
@@ -141,6 +141,61 @@ def reverse_map(x, y, i):
141141
[(fe(0x3498662504b73c7c8cecb6c33cd493bdfc190e0f87d913d7ff9ad42e222bfe95), fe(0x245b3a61b8d46997f14f2fea2874899691eb32542b9907d65eb9d21d42454021)), [fe(0x7f556282c3dd9d263390d6bbddada698ab8fd7c7d1a06498f42b30437c8361ad), None , None , None ]]
142142
]
143143

144+
def encode(P):
145+
while True:
146+
u = fe(random.randrange(1, SECP256K1_ORDER))
147+
ge = forward_map(u)
148+
# convert ge to jacobian form for EC operations
149+
ge = (ge[0].val, ge[1].val, 1)
150+
T = SECP256K1.negate(ge)
151+
Q = SECP256K1.add(T, SECP256K1.affine(P))
152+
if SECP256K1.is_infinity(Q):
153+
Q = T
154+
j = random.randrange(4)
155+
Q = SECP256K1.affine(Q)
156+
v = reverse_map(fe(Q[0]), fe(Q[1]), j)
157+
if v is not None:
158+
return u, v
159+
160+
def decode(u, v):
161+
ge1 = forward_map(u)
162+
ge2 = forward_map(v)
163+
# convert ge1 and ge2 to jacobian form for EC operations
164+
T = ge1[0].val, ge1[1].val, 1
165+
S = ge2[0].val, ge2[1].val, 1
166+
P = SECP256K1.add(T, S)
167+
if SECP256K1.is_infinity(P):
168+
P = T
169+
P = SECP256K1.affine(P)
170+
return fe(P[0]), fe(P[1])
171+
172+
def ellsq_encode(pubkey):
173+
"""
174+
generates elligator squared encoding of pubkey
175+
Parameters:
176+
pubkey : ECPubKey object
177+
Returns: 64 bytes encoding
178+
"""
179+
ge = pubkey.get_group_element()
180+
u, v = encode((ge[0].val, ge[1].val, 1))
181+
return u.to_bytes() + v.to_bytes()
182+
183+
def ellsq_decode(enc):
184+
"""
185+
decodes elligator squared encoding to obtain pubkey
186+
Parameters:
187+
enc : 64 bytes encoding
188+
Returns: ECPubKey object
189+
"""
190+
x, y = decode(fe.from_bytes(enc[:32]), fe.from_bytes(enc[32:]))
191+
if y.val % 2 == 0:
192+
compressed_sec = b'\x02' + x.val.to_bytes(32, 'big')
193+
else:
194+
compressed_sec = b'\x03' + x.val.to_bytes(32, 'big')
195+
pubkey = ECPubKey()
196+
pubkey.set(compressed_sec)
197+
return pubkey
198+
144199
class TestFrameworkEllsq(unittest.TestCase):
145200
def test_fe_to_ge_to_fe(self):
146201
for i in range(100):

test/functional/test_framework/key.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,11 @@ def get_bytes(self):
298298
else:
299299
return bytes([0x04]) + p[0].to_bytes(32, 'big') + p[1].to_bytes(32, 'big')
300300

301+
def get_group_element(self):
302+
assert(self.valid)
303+
p = SECP256K1.affine(self.p)
304+
return fe(p[0]), fe(p[1])
305+
301306
def verify_ecdsa(self, sig, msg, low_s=True):
302307
"""Verify a strictly DER-encoded ECDSA signature against this pubkey.
303308

0 commit comments

Comments
 (0)