Skip to content

Commit 714fb2c

Browse files
stratosphersipa
andcommitted
test: Add python ellswift implementation to test framework
Co-authored-by: Pieter Wuille <[email protected]>
1 parent 626d346 commit 714fb2c

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2022 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Test-only Elligator Swift implementation
6+
7+
WARNING: This code is slow and uses bad randomness.
8+
Do not use for anything but tests."""
9+
10+
import random
11+
12+
from test_framework.secp256k1 import FE, G, GE
13+
14+
# Precomputed constant square root of -3 (mod p).
15+
MINUS_3_SQRT = FE(-3).sqrt()
16+
17+
def xswiftec(u, t):
18+
"""Decode field elements (u, t) to an X coordinate on the curve."""
19+
if u == 0:
20+
u = FE(1)
21+
if t == 0:
22+
t = FE(1)
23+
if u**3 + t**2 + 7 == 0:
24+
t = 2 * t
25+
X = (u**3 + 7 - t**2) / (2 * t)
26+
Y = (X + t) / (MINUS_3_SQRT * u)
27+
for x in (u + 4 * Y**2, (-X / Y - u) / 2, (X / Y - u) / 2):
28+
if GE.is_valid_x(x):
29+
return x
30+
assert False
31+
32+
def xswiftec_inv(x, u, case):
33+
"""Given x and u, find t such that xswiftec(u, t) = x, or return None.
34+
35+
Case selects which of the up to 8 results to return."""
36+
37+
if case & 2 == 0:
38+
if GE.is_valid_x(-x - u):
39+
return None
40+
v = x
41+
s = -(u**3 + 7) / (u**2 + u*v + v**2)
42+
else:
43+
s = x - u
44+
if s == 0:
45+
return None
46+
r = (-s * (4 * (u**3 + 7) + 3 * s * u**2)).sqrt()
47+
if r is None:
48+
return None
49+
if case & 1 and r == 0:
50+
return None
51+
v = (-u + r / s) / 2
52+
w = s.sqrt()
53+
if w is None:
54+
return None
55+
if case & 5 == 0:
56+
return -w * (u * (1 - MINUS_3_SQRT) / 2 + v)
57+
if case & 5 == 1:
58+
return w * (u * (1 + MINUS_3_SQRT) / 2 + v)
59+
if case & 5 == 4:
60+
return w * (u * (1 - MINUS_3_SQRT) / 2 + v)
61+
if case & 5 == 5:
62+
return -w * (u * (1 + MINUS_3_SQRT) / 2 + v)
63+
64+
def xelligatorswift(x):
65+
"""Given a field element X on the curve, find (u, t) that encode them."""
66+
assert GE.is_valid_x(x)
67+
while True:
68+
u = FE(random.randrange(1, FE.SIZE))
69+
case = random.randrange(0, 8)
70+
t = xswiftec_inv(x, u, case)
71+
if t is not None:
72+
return u, t
73+
74+
def ellswift_create():
75+
"""Generate a (privkey, ellswift_pubkey) pair."""
76+
priv = random.randrange(1, GE.ORDER)
77+
u, t = xelligatorswift((priv * G).x)
78+
return priv.to_bytes(32, 'big'), u.to_bytes() + t.to_bytes()
79+
80+
def ellswift_ecdh_xonly(pubkey_theirs, privkey):
81+
"""Compute X coordinate of shared ECDH point between ellswift pubkey and privkey."""
82+
u = FE(int.from_bytes(pubkey_theirs[:32], 'big'))
83+
t = FE(int.from_bytes(pubkey_theirs[32:], 'big'))
84+
d = int.from_bytes(privkey, 'big')
85+
return (d * GE.lift_x(xswiftec(u, t))).x.to_bytes()

0 commit comments

Comments
 (0)