Skip to content

Commit

Permalink
Switch to exhaustive groups with small B coefficient
Browse files Browse the repository at this point in the history
  • Loading branch information
sipa committed Jan 11, 2023
1 parent cc3b8a4 commit ab8bbfe
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 95 deletions.
158 changes: 85 additions & 73 deletions sage/gen_exhaustive_groups.sage
Original file line number Diff line number Diff line change
@@ -1,124 +1,136 @@
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.
gens = {}

# 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")
continue
orders_done.add(n)

# Skip curves isomorphic to the real secp256k1
if n.is_pseudoprime():
print(" - Isomorphic to secp256k1")
continue

print("- Finding 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

# 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):
# Iterate over the generators of this curve.
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():
print("- Analyzing subgroup of order %i" % f)
# Skip subgroups that are too large.
if f < 4 or f > MAX_ORDER:
print(" - Bad size")
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")
continue
G = G * (G.order() // f)
# Construct a generator for that subgroup.
gen = g * (g_order // f)
assert(gen.order() == f)

# 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]):
if int(l)*gen == E(BETA*gen[0], gen[1]):
lam = int(l)
break
if lam is None:
print(" - No endomorphism for this subgroup")
print(" - No endomorphism for this subgroup")
break

# 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])

# 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]))

# 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
# Add to gens all the generators for that subgroup.
gens.setdefault(f, [])
gens[f].extend((b, int(P[0]), int(P[1]), P, lam) for P in [n*gen for n in range(1, f)])
print(" - Found solutions")

print("")

print("")
print("")
print("/* To be put in src/group_impl.h: */")
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))
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(");")
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(")")

def output_b(b):
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()
print("To be put in src/group_impl.h:")
print()
print("/* Begin of section generated by sage/gen_exhaustive_groups.size. */")
for f in sorted(gens.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(gens[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(gens.keys()):
b, _, _, _, lam = min(gens[f])
print(f"# {'if' if first else 'elif'} EXHAUSTIVE_TEST_ORDER == {f}")
first = False
print(f"static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_{f};")
print("")
output_b(b)
print("# else")
print("# error No known generator for the specified exhaustive test group order.")
print("# endif")

print("")
print("#else")
print()
print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;")
print("")
print("/* To be put in src/scalar_impl.h: */")
output_b(7)
print()
print("#endif")
print("/* End of section generated by sage/gen_exhaustive_groups.size. */")


print()
print()
print("To be put in src/scalar_impl.h:")
print()
print("/* Begin of section generated by sage/gen_exhaustive_groups.size. */")
first = True
for f in sorted(results.keys()):
lam = results[f]["lambda"]
for f in sorted(gens.keys()):
_, _, _, _, lam = min(gens[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.size. */")
47 changes: 26 additions & 21 deletions src/group_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,59 +10,64 @@
#include "field.h"
#include "group.h"

/* Begin of section generated by sage/gen_exhaustive_groups.size. */
#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
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_13;

static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
0x3d3486b2, 0x159a9ca5, 0xc75638be, 0xb23a69bc,
0x946a45ab, 0x24801247, 0xb4ed2b8e, 0x26b6a417
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000002
);
# 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
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000004
);
# 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_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000007
);

#endif
/* End of section generated by sage/gen_exhaustive_groups.size. */

static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
secp256k1_fe zi2;
Expand Down
3 changes: 2 additions & 1 deletion src/scalar_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@ 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)
/* Begin of section generated by sage/gen_exhaustive_groups.size. */
# if 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.size. */

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

0 comments on commit ab8bbfe

Please sign in to comment.