Merge bitcoin-core/secp256k1#1192: Switch to exhaustive groups with small B coefficient
ce60785b26
Introduce SECP256K1_B macro for curve b coefficient (Pieter Wuille)4934aa7995
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: ACKce60785b26
also ran the exhaustive tests with the group of size 7 apoelstra: ACKce60785b26
Tree-SHA512: 006041189d18319ddb9c0ed54e479f393b83ab2a368d198bd24860d1d2574c0c1a311aea24fbef2e74bb7859a687dfc803b9e963e6dc5c61cb707e20f52b5a70
This commit is contained in:
commit
a01a7d86dc
|
@ -1,124 +1,156 @@
|
|||
load("secp256k1_params.sage")
|
||||
|
||||
MAX_ORDER = 1000
|
||||
|
||||
# Set of (curve) orders we have encountered so far.
|
||||
orders_done = set()
|
||||
results = {}
|
||||
first = True
|
||||
|
||||
# Map from (subgroup) orders to [b, int(gen.x), int(gen.y), gen, lambda] for those subgroups.
|
||||
solutions = {}
|
||||
|
||||
# Iterate over curves of the form y^2 = x^3 + B.
|
||||
for b in range(1, P):
|
||||
# There are only 6 curves (up to isomorphism) of the form y^2=x^3+B. Stop once we have tried all.
|
||||
# There are only 6 curves (up to isomorphism) of the form y^2 = x^3 + B. Stop once we have tried all.
|
||||
if len(orders_done) == 6:
|
||||
break
|
||||
|
||||
E = EllipticCurve(F, [0, b])
|
||||
print("Analyzing curve y^2 = x^3 + %i" % b)
|
||||
n = E.order()
|
||||
|
||||
# Skip curves with an order we've already tried
|
||||
if n in orders_done:
|
||||
print("- Isomorphic to earlier curve")
|
||||
print()
|
||||
continue
|
||||
orders_done.add(n)
|
||||
|
||||
# Skip curves isomorphic to the real secp256k1
|
||||
if n.is_pseudoprime():
|
||||
print(" - Isomorphic to secp256k1")
|
||||
assert E.is_isomorphic(C)
|
||||
print("- Isomorphic to secp256k1")
|
||||
print()
|
||||
continue
|
||||
|
||||
print("- Finding subgroups")
|
||||
print("- Finding prime subgroups")
|
||||
|
||||
# Find what prime subgroups exist
|
||||
for f, _ in n.factor():
|
||||
print("- Analyzing subgroup of order %i" % f)
|
||||
# Skip subgroups of order >1000
|
||||
if f < 4 or f > 1000:
|
||||
print(" - Bad size")
|
||||
continue
|
||||
# Map from group_order to a set of independent generators for that order.
|
||||
curve_gens = {}
|
||||
|
||||
# Iterate over X coordinates until we find one that is on the curve, has order f,
|
||||
# and for which curve isomorphism exists that maps it to X coordinate 1.
|
||||
for x in range(1, P):
|
||||
# Skip X coordinates not on the curve, and construct the full point otherwise.
|
||||
if not E.is_x_coord(x):
|
||||
for g in E.gens():
|
||||
# Find what prime subgroups of group generated by g exist.
|
||||
g_order = g.order()
|
||||
for f, _ in g.order().factor():
|
||||
# Skip subgroups that have bad size.
|
||||
if f < 4:
|
||||
print(f" - Subgroup of size {f}: too small")
|
||||
continue
|
||||
G = E.lift_x(F(x))
|
||||
|
||||
print(" - Analyzing (multiples of) point with X=%i" % x)
|
||||
|
||||
# Skip points whose order is not a multiple of f. Project the point to have
|
||||
# order f otherwise.
|
||||
if (G.order() % f):
|
||||
print(" - Bad order")
|
||||
if f > MAX_ORDER:
|
||||
print(f" - Subgroup of size {f}: too large")
|
||||
continue
|
||||
G = G * (G.order() // f)
|
||||
|
||||
# Construct a generator for that subgroup.
|
||||
gen = g * (g_order // f)
|
||||
assert(gen.order() == f)
|
||||
|
||||
# Add to set the minimal multiple of gen.
|
||||
curve_gens.setdefault(f, set()).add(min([j*gen for j in range(1, f)]))
|
||||
print(f" - Subgroup of size {f}: ok")
|
||||
|
||||
for f in sorted(curve_gens.keys()):
|
||||
print(f"- Constructing group of order {f}")
|
||||
cbrts = sorted([int(c) for c in Integers(f)(1).nth_root(3, all=true) if c != 1])
|
||||
gens = list(curve_gens[f])
|
||||
sol_count = 0
|
||||
no_endo_count = 0
|
||||
|
||||
# Consider all non-zero linear combinations of the independent generators.
|
||||
for j in range(1, f**len(gens)):
|
||||
gen = sum(gens[k] * ((j // f**k) % f) for k in range(len(gens)))
|
||||
assert not gen.is_zero()
|
||||
assert (f*gen).is_zero()
|
||||
|
||||
# Find lambda for endomorphism. Skip if none can be found.
|
||||
lam = None
|
||||
for l in Integers(f)(1).nth_root(3, all=True):
|
||||
if int(l)*G == E(BETA*G[0], G[1]):
|
||||
lam = int(l)
|
||||
for l in cbrts:
|
||||
if l*gen == E(BETA*gen[0], gen[1]):
|
||||
lam = l
|
||||
break
|
||||
|
||||
if lam is None:
|
||||
print(" - No endomorphism for this subgroup")
|
||||
break
|
||||
no_endo_count += 1
|
||||
else:
|
||||
sol_count += 1
|
||||
solutions.setdefault(f, []).append((b, int(gen[0]), int(gen[1]), gen, lam))
|
||||
|
||||
# Now look for an isomorphism of the curve that gives this point an X
|
||||
# coordinate equal to 1.
|
||||
# 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.
|
||||
# So look for m=a^2=1/x.
|
||||
m = F(1)/G[0]
|
||||
if not m.is_square():
|
||||
print(" - No curve isomorphism maps it to a point with X=1")
|
||||
continue
|
||||
a = m.sqrt()
|
||||
rb = a^6*b
|
||||
RE = EllipticCurve(F, [0, rb])
|
||||
print(f" - Found {sol_count} generators (plus {no_endo_count} without endomorphism)")
|
||||
|
||||
# Use as generator twice the image of G under the above isormorphism.
|
||||
# This means that generator*(1/2 mod f) will have X coordinate 1.
|
||||
RG = RE(1, a^3*G[1]) * 2
|
||||
# And even Y coordinate.
|
||||
if int(RG[1]) % 2:
|
||||
RG = -RG
|
||||
assert(RG.order() == f)
|
||||
assert(lam*RG == RE(BETA*RG[0], RG[1]))
|
||||
print()
|
||||
|
||||
# We have found curve RE:y^2=x^3+rb with generator RG of order f. Remember it
|
||||
results[f] = {"b": rb, "G": RG, "lambda": lam}
|
||||
print(" - Found solution")
|
||||
break
|
||||
def output_generator(g, name):
|
||||
print(f"#define {name} SECP256K1_GE_CONST(\\")
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
|
||||
print(")")
|
||||
|
||||
print("")
|
||||
def output_b(b):
|
||||
print(f"#define SECP256K1_B {int(b)}")
|
||||
|
||||
print("")
|
||||
print("")
|
||||
print("/* To be put in src/group_impl.h: */")
|
||||
print()
|
||||
print("To be put in src/group_impl.h:")
|
||||
print()
|
||||
print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */")
|
||||
for f in sorted(solutions.keys()):
|
||||
# Use as generator/2 the one with lowest b, and lowest (x, y) generator (interpreted as non-negative integers).
|
||||
b, _, _, HALF_G, lam = min(solutions[f])
|
||||
output_generator(2 * HALF_G, f"SECP256K1_G_ORDER_{f}")
|
||||
print("/** Generator for secp256k1, value 'g' defined in")
|
||||
print(" * \"Standards for Efficient Cryptography\" (SEC2) 2.7.1.")
|
||||
print(" */")
|
||||
output_generator(G, "SECP256K1_G")
|
||||
print("/* These exhaustive group test orders and generators are chosen such that:")
|
||||
print(" * - The field size is equal to that of secp256k1, so field code is the same.")
|
||||
print(" * - The curve equation is of the form y^2=x^3+B for some small constant B.")
|
||||
print(" * - The subgroup has a generator 2*P, where P.x is as small as possible.")
|
||||
print(f" * - The subgroup has size less than {MAX_ORDER} to permit exhaustive testing.")
|
||||
print(" * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).")
|
||||
print(" */")
|
||||
print("#if defined(EXHAUSTIVE_TEST_ORDER)")
|
||||
first = True
|
||||
for f in sorted(results.keys()):
|
||||
b = results[f]["b"]
|
||||
G = results[f]["G"]
|
||||
print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f))
|
||||
for f in sorted(solutions.keys()):
|
||||
b, _, _, _, lam = min(solutions[f])
|
||||
print(f"# {'if' if first else 'elif'} EXHAUSTIVE_TEST_ORDER == {f}")
|
||||
first = False
|
||||
print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(")
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(G[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
|
||||
print(");")
|
||||
print("static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(")
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
|
||||
print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
|
||||
print(");")
|
||||
print()
|
||||
print(f"static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_{f};")
|
||||
output_b(b)
|
||||
print()
|
||||
print("# else")
|
||||
print("# error No known generator for the specified exhaustive test group order.")
|
||||
print("# endif")
|
||||
print("#else")
|
||||
print()
|
||||
print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;")
|
||||
output_b(7)
|
||||
print()
|
||||
print("#endif")
|
||||
print("/* End of section generated by sage/gen_exhaustive_groups.sage. */")
|
||||
|
||||
print("")
|
||||
print("")
|
||||
print("/* To be put in src/scalar_impl.h: */")
|
||||
|
||||
print()
|
||||
print()
|
||||
print("To be put in src/scalar_impl.h:")
|
||||
print()
|
||||
print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */")
|
||||
first = True
|
||||
for f in sorted(results.keys()):
|
||||
lam = results[f]["lambda"]
|
||||
for f in sorted(solutions.keys()):
|
||||
_, _, _, _, lam = min(solutions[f])
|
||||
print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f))
|
||||
first = False
|
||||
print("# define EXHAUSTIVE_TEST_LAMBDA %i" % lam)
|
||||
print("# else")
|
||||
print("# error No known lambda for the specified exhaustive test group order.")
|
||||
print("# endif")
|
||||
print("")
|
||||
print("/* End of section generated by sage/gen_exhaustive_groups.sage. */")
|
||||
|
|
|
@ -10,59 +10,69 @@
|
|||
#include "field.h"
|
||||
#include "group.h"
|
||||
|
||||
/* Begin of section generated by sage/gen_exhaustive_groups.sage. */
|
||||
#define SECP256K1_G_ORDER_7 SECP256K1_GE_CONST(\
|
||||
0x66625d13, 0x317ffe44, 0x63d32cff, 0x1ca02b9b,\
|
||||
0xe5c6d070, 0x50b4b05e, 0x81cc30db, 0xf5166f0a,\
|
||||
0x1e60e897, 0xa7c00c7c, 0x2df53eb6, 0x98274ff4,\
|
||||
0x64252f42, 0x8ca44e17, 0x3b25418c, 0xff4ab0cf\
|
||||
)
|
||||
#define SECP256K1_G_ORDER_13 SECP256K1_GE_CONST(\
|
||||
0xc3459c3d, 0x35326167, 0xcd86cce8, 0x07a2417f,\
|
||||
0x5b8bd567, 0xde8538ee, 0x0d507b0c, 0xd128f5bb,\
|
||||
0x8e467fec, 0xcd30000a, 0x6cc1184e, 0x25d382c2,\
|
||||
0xa2f4494e, 0x2fbe9abc, 0x8b64abac, 0xd005fb24\
|
||||
0xa2482ff8, 0x4bf34edf, 0xa51262fd, 0xe57921db,\
|
||||
0xe0dd2cb7, 0xa5914790, 0xbc71631f, 0xc09704fb,\
|
||||
0x942536cb, 0xa3e49492, 0x3a701cc3, 0xee3e443f,\
|
||||
0xdf182aa9, 0x15b8aa6a, 0x166d3b19, 0xba84b045\
|
||||
)
|
||||
#define SECP256K1_G_ORDER_199 SECP256K1_GE_CONST(\
|
||||
0x226e653f, 0xc8df7744, 0x9bacbf12, 0x7d1dcbf9,\
|
||||
0x87f05b2a, 0xe7edbd28, 0x1f564575, 0xc48dcf18,\
|
||||
0xa13872c2, 0xe933bb17, 0x5d9ffd5b, 0xb5b6e10c,\
|
||||
0x57fe3c00, 0xbaaaa15a, 0xe003ec3e, 0x9c269bae\
|
||||
0x7fb07b5c, 0xd07c3bda, 0x553902e2, 0x7a87ea2c,\
|
||||
0x35108a7f, 0x051f41e5, 0xb76abad5, 0x1f2703ad,\
|
||||
0x0a251539, 0x5b4c4438, 0x952a634f, 0xac10dd4d,\
|
||||
0x6d6f4745, 0x98990c27, 0x3a4f3116, 0xd32ff969\
|
||||
)
|
||||
/** Generator for secp256k1, value 'g' defined in
|
||||
* "Standards for Efficient Cryptography" (SEC2) 2.7.1.
|
||||
*/
|
||||
#define SECP256K1_G SECP256K1_GE_CONST(\
|
||||
0x79BE667EUL, 0xF9DCBBACUL, 0x55A06295UL, 0xCE870B07UL,\
|
||||
0x029BFCDBUL, 0x2DCE28D9UL, 0x59F2815BUL, 0x16F81798UL,\
|
||||
0x483ADA77UL, 0x26A3C465UL, 0x5DA4FBFCUL, 0x0E1108A8UL,\
|
||||
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL\
|
||||
0x79be667e, 0xf9dcbbac, 0x55a06295, 0xce870b07,\
|
||||
0x029bfcdb, 0x2dce28d9, 0x59f2815b, 0x16f81798,\
|
||||
0x483ada77, 0x26a3c465, 0x5da4fbfc, 0x0e1108a8,\
|
||||
0xfd17b448, 0xa6855419, 0x9c47d08f, 0xfb10d4b8\
|
||||
)
|
||||
/* These exhaustive group test orders and generators are chosen such that:
|
||||
* - The field size is equal to that of secp256k1, so field code is the same.
|
||||
* - The curve equation is of the form y^2=x^3+B for some constant B.
|
||||
* - The subgroup has a generator 2*P, where P.x=1.
|
||||
* - The curve equation is of the form y^2=x^3+B for some small constant B.
|
||||
* - The subgroup has a generator 2*P, where P.x is as small as possible.
|
||||
* - The subgroup has size less than 1000 to permit exhaustive testing.
|
||||
* - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).
|
||||
*
|
||||
* These parameters are generated using sage/gen_exhaustive_groups.sage.
|
||||
*/
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
# if EXHAUSTIVE_TEST_ORDER == 13
|
||||
# if EXHAUSTIVE_TEST_ORDER == 7
|
||||
|
||||
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_7;
|
||||
#define SECP256K1_B 6
|
||||
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 13
|
||||
|
||||
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_13;
|
||||
#define SECP256K1_B 2
|
||||
|
||||
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
|
||||
0x3d3486b2, 0x159a9ca5, 0xc75638be, 0xb23a69bc,
|
||||
0x946a45ab, 0x24801247, 0xb4ed2b8e, 0x26b6a417
|
||||
);
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 199
|
||||
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_199;
|
||||
|
||||
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
|
||||
0x2cca28fa, 0xfc614b80, 0x2a3db42b, 0x00ba00b1,
|
||||
0xbea8d943, 0xdace9ab2, 0x9536daea, 0x0074defb
|
||||
);
|
||||
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_199;
|
||||
#define SECP256K1_B 4
|
||||
|
||||
# else
|
||||
# error No known generator for the specified exhaustive test group order.
|
||||
# endif
|
||||
#else
|
||||
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;
|
||||
|
||||
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 7);
|
||||
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;
|
||||
#define SECP256K1_B 7
|
||||
|
||||
#endif
|
||||
/* End of section generated by sage/gen_exhaustive_groups.sage. */
|
||||
|
||||
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, SECP256K1_B);
|
||||
|
||||
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
|
||||
secp256k1_fe zi2;
|
||||
|
|
|
@ -43,8 +43,7 @@ static void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const se
|
|||
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
|
||||
/* The recid's second bit is for conveying overflow (R.x value >= group order).
|
||||
* In the actual secp256k1 this is an astronomically unlikely event, but in the
|
||||
* small group used here, it will be the case for all points except the ones where
|
||||
* R.x=1 (which the group is specifically selected to have).
|
||||
* small group used here, it will almost certainly be the case for all points.
|
||||
* Note that this isn't actually useful; full recovery would need to convey
|
||||
* floor(R.x / group_order), but only one bit is used as that is sufficient
|
||||
* in the real group. */
|
||||
|
|
|
@ -13,7 +13,9 @@ extern "C" {
|
|||
|
||||
#include "group.h"
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
#if EXHAUSTIVE_TEST_ORDER == 13
|
||||
# if EXHAUSTIVE_TEST_ORDER == 7
|
||||
# define WINDOW_G 3
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 13
|
||||
# define WINDOW_G 4
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 199
|
||||
# define WINDOW_G 8
|
||||
|
|
|
@ -33,15 +33,18 @@ static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned c
|
|||
return (!overflow) & (!secp256k1_scalar_is_zero(r));
|
||||
}
|
||||
|
||||
/* These parameters are generated using sage/gen_exhaustive_groups.sage. */
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
# if EXHAUSTIVE_TEST_ORDER == 13
|
||||
/* Begin of section generated by sage/gen_exhaustive_groups.sage. */
|
||||
# if EXHAUSTIVE_TEST_ORDER == 7
|
||||
# define EXHAUSTIVE_TEST_LAMBDA 2
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 13
|
||||
# define EXHAUSTIVE_TEST_LAMBDA 9
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 199
|
||||
# define EXHAUSTIVE_TEST_LAMBDA 92
|
||||
# else
|
||||
# error No known lambda for the specified exhaustive test group order.
|
||||
# endif
|
||||
/* End of section generated by sage/gen_exhaustive_groups.sage. */
|
||||
|
||||
/**
|
||||
* Find r1 and r2 given k, such that r1 + r2 * lambda == k mod n; unlike in the
|
||||
|
|
Loading…
Reference in New Issue