Skip to content

Commit a01a7d8

Browse files
Merge bitcoin-core/secp256k1#1192: Switch to exhaustive groups with small B coefficient
ce60785 Introduce SECP256K1_B macro for curve b coefficient (Pieter Wuille) 4934aa7 Switch to exhaustive groups with small B coefficient (Pieter Wuille) Pull request description: This has the advantage that in the future, multiplication with B can be done using `secp256k1_fe_mul_int` rather than the slower `secp256k1_fe_mul`. ACKs for top commit: real-or-random: ACK ce60785 also ran the exhaustive tests with the group of size 7 apoelstra: ACK ce60785 Tree-SHA512: 006041189d18319ddb9c0ed54e479f393b83ab2a368d198bd24860d1d2574c0c1a311aea24fbef2e74bb7859a687dfc803b9e963e6dc5c61cb707e20f52b5a70
2 parents a7a7bfa + ce60785 commit a01a7d8

File tree

5 files changed

+161
-115
lines changed

5 files changed

+161
-115
lines changed

sage/gen_exhaustive_groups.sage

+116-84
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,156 @@
11
load("secp256k1_params.sage")
22

3+
MAX_ORDER = 1000
4+
5+
# Set of (curve) orders we have encountered so far.
36
orders_done = set()
4-
results = {}
5-
first = True
7+
8+
# Map from (subgroup) orders to [b, int(gen.x), int(gen.y), gen, lambda] for those subgroups.
9+
solutions = {}
10+
11+
# Iterate over curves of the form y^2 = x^3 + B.
612
for b in range(1, P):
7-
# There are only 6 curves (up to isomorphism) of the form y^2=x^3+B. Stop once we have tried all.
13+
# There are only 6 curves (up to isomorphism) of the form y^2 = x^3 + B. Stop once we have tried all.
814
if len(orders_done) == 6:
915
break
1016

1117
E = EllipticCurve(F, [0, b])
1218
print("Analyzing curve y^2 = x^3 + %i" % b)
1319
n = E.order()
20+
1421
# Skip curves with an order we've already tried
1522
if n in orders_done:
1623
print("- Isomorphic to earlier curve")
24+
print()
1725
continue
1826
orders_done.add(n)
27+
1928
# Skip curves isomorphic to the real secp256k1
2029
if n.is_pseudoprime():
21-
print(" - Isomorphic to secp256k1")
30+
assert E.is_isomorphic(C)
31+
print("- Isomorphic to secp256k1")
32+
print()
2233
continue
2334

24-
print("- Finding subgroups")
25-
26-
# Find what prime subgroups exist
27-
for f, _ in n.factor():
28-
print("- Analyzing subgroup of order %i" % f)
29-
# Skip subgroups of order >1000
30-
if f < 4 or f > 1000:
31-
print(" - Bad size")
32-
continue
33-
34-
# Iterate over X coordinates until we find one that is on the curve, has order f,
35-
# and for which curve isomorphism exists that maps it to X coordinate 1.
36-
for x in range(1, P):
37-
# Skip X coordinates not on the curve, and construct the full point otherwise.
38-
if not E.is_x_coord(x):
39-
continue
40-
G = E.lift_x(F(x))
35+
print("- Finding prime subgroups")
4136

42-
print(" - Analyzing (multiples of) point with X=%i" % x)
37+
# Map from group_order to a set of independent generators for that order.
38+
curve_gens = {}
4339

44-
# Skip points whose order is not a multiple of f. Project the point to have
45-
# order f otherwise.
46-
if (G.order() % f):
47-
print(" - Bad order")
40+
for g in E.gens():
41+
# Find what prime subgroups of group generated by g exist.
42+
g_order = g.order()
43+
for f, _ in g.order().factor():
44+
# Skip subgroups that have bad size.
45+
if f < 4:
46+
print(f" - Subgroup of size {f}: too small")
47+
continue
48+
if f > MAX_ORDER:
49+
print(f" - Subgroup of size {f}: too large")
4850
continue
49-
G = G * (G.order() // f)
51+
52+
# Construct a generator for that subgroup.
53+
gen = g * (g_order // f)
54+
assert(gen.order() == f)
55+
56+
# Add to set the minimal multiple of gen.
57+
curve_gens.setdefault(f, set()).add(min([j*gen for j in range(1, f)]))
58+
print(f" - Subgroup of size {f}: ok")
59+
60+
for f in sorted(curve_gens.keys()):
61+
print(f"- Constructing group of order {f}")
62+
cbrts = sorted([int(c) for c in Integers(f)(1).nth_root(3, all=true) if c != 1])
63+
gens = list(curve_gens[f])
64+
sol_count = 0
65+
no_endo_count = 0
66+
67+
# Consider all non-zero linear combinations of the independent generators.
68+
for j in range(1, f**len(gens)):
69+
gen = sum(gens[k] * ((j // f**k) % f) for k in range(len(gens)))
70+
assert not gen.is_zero()
71+
assert (f*gen).is_zero()
5072

5173
# Find lambda for endomorphism. Skip if none can be found.
5274
lam = None
53-
for l in Integers(f)(1).nth_root(3, all=True):
54-
if int(l)*G == E(BETA*G[0], G[1]):
55-
lam = int(l)
75+
for l in cbrts:
76+
if l*gen == E(BETA*gen[0], gen[1]):
77+
lam = l
5678
break
79+
5780
if lam is None:
58-
print(" - No endomorphism for this subgroup")
59-
break
60-
61-
# Now look for an isomorphism of the curve that gives this point an X
62-
# coordinate equal to 1.
63-
# If (x,y) is on y^2 = x^3 + b, then (a^2*x, a^3*y) is on y^2 = x^3 + a^6*b.
64-
# So look for m=a^2=1/x.
65-
m = F(1)/G[0]
66-
if not m.is_square():
67-
print(" - No curve isomorphism maps it to a point with X=1")
68-
continue
69-
a = m.sqrt()
70-
rb = a^6*b
71-
RE = EllipticCurve(F, [0, rb])
72-
73-
# Use as generator twice the image of G under the above isormorphism.
74-
# This means that generator*(1/2 mod f) will have X coordinate 1.
75-
RG = RE(1, a^3*G[1]) * 2
76-
# And even Y coordinate.
77-
if int(RG[1]) % 2:
78-
RG = -RG
79-
assert(RG.order() == f)
80-
assert(lam*RG == RE(BETA*RG[0], RG[1]))
81-
82-
# We have found curve RE:y^2=x^3+rb with generator RG of order f. Remember it
83-
results[f] = {"b": rb, "G": RG, "lambda": lam}
84-
print(" - Found solution")
85-
break
86-
87-
print("")
88-
89-
print("")
90-
print("")
91-
print("/* To be put in src/group_impl.h: */")
81+
no_endo_count += 1
82+
else:
83+
sol_count += 1
84+
solutions.setdefault(f, []).append((b, int(gen[0]), int(gen[1]), gen, lam))
85+
86+
print(f" - Found {sol_count} generators (plus {no_endo_count} without endomorphism)")
87+
88+
print()
89+
90+
def output_generator(g, name):
91+
print(f"#define {name} SECP256K1_GE_CONST(\\")
92+
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
93+
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
94+
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
95+
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
96+
print(")")
97+
98+
def output_b(b):
99+
print(f"#define SECP256K1_B {int(b)}")
100+
101+
print()
102+
print("To be put in src/group_impl.h:")
103+
print()
104+
print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */")
105+
for f in sorted(solutions.keys()):
106+
# Use as generator/2 the one with lowest b, and lowest (x, y) generator (interpreted as non-negative integers).
107+
b, _, _, HALF_G, lam = min(solutions[f])
108+
output_generator(2 * HALF_G, f"SECP256K1_G_ORDER_{f}")
109+
print("/** Generator for secp256k1, value 'g' defined in")
110+
print(" * \"Standards for Efficient Cryptography\" (SEC2) 2.7.1.")
111+
print(" */")
112+
output_generator(G, "SECP256K1_G")
113+
print("/* These exhaustive group test orders and generators are chosen such that:")
114+
print(" * - The field size is equal to that of secp256k1, so field code is the same.")
115+
print(" * - The curve equation is of the form y^2=x^3+B for some small constant B.")
116+
print(" * - The subgroup has a generator 2*P, where P.x is as small as possible.")
117+
print(f" * - The subgroup has size less than {MAX_ORDER} to permit exhaustive testing.")
118+
print(" * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).")
119+
print(" */")
120+
print("#if defined(EXHAUSTIVE_TEST_ORDER)")
92121
first = True
93-
for f in sorted(results.keys()):
94-
b = results[f]["b"]
95-
G = results[f]["G"]
96-
print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f))
122+
for f in sorted(solutions.keys()):
123+
b, _, _, _, lam = min(solutions[f])
124+
print(f"# {'if' if first else 'elif'} EXHAUSTIVE_TEST_ORDER == {f}")
97125
first = False
98-
print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(")
99-
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
100-
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
101-
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
102-
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
103-
print(");")
104-
print("static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(")
105-
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
106-
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
107-
print(");")
126+
print()
127+
print(f"static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_{f};")
128+
output_b(b)
129+
print()
108130
print("# else")
109131
print("# error No known generator for the specified exhaustive test group order.")
110132
print("# endif")
133+
print("#else")
134+
print()
135+
print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;")
136+
output_b(7)
137+
print()
138+
print("#endif")
139+
print("/* End of section generated by sage/gen_exhaustive_groups.sage. */")
140+
111141

112-
print("")
113-
print("")
114-
print("/* To be put in src/scalar_impl.h: */")
142+
print()
143+
print()
144+
print("To be put in src/scalar_impl.h:")
145+
print()
146+
print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */")
115147
first = True
116-
for f in sorted(results.keys()):
117-
lam = results[f]["lambda"]
148+
for f in sorted(solutions.keys()):
149+
_, _, _, _, lam = min(solutions[f])
118150
print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f))
119151
first = False
120152
print("# define EXHAUSTIVE_TEST_LAMBDA %i" % lam)
121153
print("# else")
122154
print("# error No known lambda for the specified exhaustive test group order.")
123155
print("# endif")
124-
print("")
156+
print("/* End of section generated by sage/gen_exhaustive_groups.sage. */")

src/group_impl.h

+36-26
Original file line numberDiff line numberDiff line change
@@ -10,59 +10,69 @@
1010
#include "field.h"
1111
#include "group.h"
1212

13+
/* Begin of section generated by sage/gen_exhaustive_groups.sage. */
14+
#define SECP256K1_G_ORDER_7 SECP256K1_GE_CONST(\
15+
0x66625d13, 0x317ffe44, 0x63d32cff, 0x1ca02b9b,\
16+
0xe5c6d070, 0x50b4b05e, 0x81cc30db, 0xf5166f0a,\
17+
0x1e60e897, 0xa7c00c7c, 0x2df53eb6, 0x98274ff4,\
18+
0x64252f42, 0x8ca44e17, 0x3b25418c, 0xff4ab0cf\
19+
)
1320
#define SECP256K1_G_ORDER_13 SECP256K1_GE_CONST(\
14-
0xc3459c3d, 0x35326167, 0xcd86cce8, 0x07a2417f,\
15-
0x5b8bd567, 0xde8538ee, 0x0d507b0c, 0xd128f5bb,\
16-
0x8e467fec, 0xcd30000a, 0x6cc1184e, 0x25d382c2,\
17-
0xa2f4494e, 0x2fbe9abc, 0x8b64abac, 0xd005fb24\
21+
0xa2482ff8, 0x4bf34edf, 0xa51262fd, 0xe57921db,\
22+
0xe0dd2cb7, 0xa5914790, 0xbc71631f, 0xc09704fb,\
23+
0x942536cb, 0xa3e49492, 0x3a701cc3, 0xee3e443f,\
24+
0xdf182aa9, 0x15b8aa6a, 0x166d3b19, 0xba84b045\
1825
)
1926
#define SECP256K1_G_ORDER_199 SECP256K1_GE_CONST(\
20-
0x226e653f, 0xc8df7744, 0x9bacbf12, 0x7d1dcbf9,\
21-
0x87f05b2a, 0xe7edbd28, 0x1f564575, 0xc48dcf18,\
22-
0xa13872c2, 0xe933bb17, 0x5d9ffd5b, 0xb5b6e10c,\
23-
0x57fe3c00, 0xbaaaa15a, 0xe003ec3e, 0x9c269bae\
27+
0x7fb07b5c, 0xd07c3bda, 0x553902e2, 0x7a87ea2c,\
28+
0x35108a7f, 0x051f41e5, 0xb76abad5, 0x1f2703ad,\
29+
0x0a251539, 0x5b4c4438, 0x952a634f, 0xac10dd4d,\
30+
0x6d6f4745, 0x98990c27, 0x3a4f3116, 0xd32ff969\
2431
)
2532
/** Generator for secp256k1, value 'g' defined in
2633
* "Standards for Efficient Cryptography" (SEC2) 2.7.1.
2734
*/
2835
#define SECP256K1_G SECP256K1_GE_CONST(\
29-
0x79BE667EUL, 0xF9DCBBACUL, 0x55A06295UL, 0xCE870B07UL,\
30-
0x029BFCDBUL, 0x2DCE28D9UL, 0x59F2815BUL, 0x16F81798UL,\
31-
0x483ADA77UL, 0x26A3C465UL, 0x5DA4FBFCUL, 0x0E1108A8UL,\
32-
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL\
36+
0x79be667e, 0xf9dcbbac, 0x55a06295, 0xce870b07,\
37+
0x029bfcdb, 0x2dce28d9, 0x59f2815b, 0x16f81798,\
38+
0x483ada77, 0x26a3c465, 0x5da4fbfc, 0x0e1108a8,\
39+
0xfd17b448, 0xa6855419, 0x9c47d08f, 0xfb10d4b8\
3340
)
3441
/* These exhaustive group test orders and generators are chosen such that:
3542
* - The field size is equal to that of secp256k1, so field code is the same.
36-
* - The curve equation is of the form y^2=x^3+B for some constant B.
37-
* - The subgroup has a generator 2*P, where P.x=1.
43+
* - The curve equation is of the form y^2=x^3+B for some small constant B.
44+
* - The subgroup has a generator 2*P, where P.x is as small as possible.
3845
* - The subgroup has size less than 1000 to permit exhaustive testing.
3946
* - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).
40-
*
41-
* These parameters are generated using sage/gen_exhaustive_groups.sage.
4247
*/
4348
#if defined(EXHAUSTIVE_TEST_ORDER)
44-
# if EXHAUSTIVE_TEST_ORDER == 13
49+
# if EXHAUSTIVE_TEST_ORDER == 7
50+
51+
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_7;
52+
#define SECP256K1_B 6
53+
54+
# elif EXHAUSTIVE_TEST_ORDER == 13
55+
4556
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_13;
57+
#define SECP256K1_B 2
4658

47-
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
48-
0x3d3486b2, 0x159a9ca5, 0xc75638be, 0xb23a69bc,
49-
0x946a45ab, 0x24801247, 0xb4ed2b8e, 0x26b6a417
50-
);
5159
# elif EXHAUSTIVE_TEST_ORDER == 199
60+
5261
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_199;
62+
#define SECP256K1_B 4
5363

54-
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
55-
0x2cca28fa, 0xfc614b80, 0x2a3db42b, 0x00ba00b1,
56-
0xbea8d943, 0xdace9ab2, 0x9536daea, 0x0074defb
57-
);
5864
# else
5965
# error No known generator for the specified exhaustive test group order.
6066
# endif
6167
#else
68+
6269
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;
70+
#define SECP256K1_B 7
6371

64-
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 7);
6572
#endif
73+
/* End of section generated by sage/gen_exhaustive_groups.sage. */
74+
75+
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, SECP256K1_B);
6676

6777
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
6878
secp256k1_fe zi2;

src/modules/recovery/tests_exhaustive_impl.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ static void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const se
4343
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
4444
/* The recid's second bit is for conveying overflow (R.x value >= group order).
4545
* In the actual secp256k1 this is an astronomically unlikely event, but in the
46-
* small group used here, it will be the case for all points except the ones where
47-
* R.x=1 (which the group is specifically selected to have).
46+
* small group used here, it will almost certainly be the case for all points.
4847
* Note that this isn't actually useful; full recovery would need to convey
4948
* floor(R.x / group_order), but only one bit is used as that is sufficient
5049
* in the real group. */

src/precomputed_ecmult.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ extern "C" {
1313

1414
#include "group.h"
1515
#if defined(EXHAUSTIVE_TEST_ORDER)
16-
#if EXHAUSTIVE_TEST_ORDER == 13
16+
# if EXHAUSTIVE_TEST_ORDER == 7
17+
# define WINDOW_G 3
18+
# elif EXHAUSTIVE_TEST_ORDER == 13
1719
# define WINDOW_G 4
1820
# elif EXHAUSTIVE_TEST_ORDER == 199
1921
# define WINDOW_G 8

src/scalar_impl.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,18 @@ static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned c
3333
return (!overflow) & (!secp256k1_scalar_is_zero(r));
3434
}
3535

36-
/* These parameters are generated using sage/gen_exhaustive_groups.sage. */
3736
#if defined(EXHAUSTIVE_TEST_ORDER)
38-
# if EXHAUSTIVE_TEST_ORDER == 13
37+
/* Begin of section generated by sage/gen_exhaustive_groups.sage. */
38+
# if EXHAUSTIVE_TEST_ORDER == 7
39+
# define EXHAUSTIVE_TEST_LAMBDA 2
40+
# elif EXHAUSTIVE_TEST_ORDER == 13
3941
# define EXHAUSTIVE_TEST_LAMBDA 9
4042
# elif EXHAUSTIVE_TEST_ORDER == 199
4143
# define EXHAUSTIVE_TEST_LAMBDA 92
4244
# else
4345
# error No known lambda for the specified exhaustive test group order.
4446
# endif
47+
/* End of section generated by sage/gen_exhaustive_groups.sage. */
4548

4649
/**
4750
* Find r1 and r2 given k, such that r1 + r2 * lambda == k mod n; unlike in the

0 commit comments

Comments
 (0)