load("rustsecp256k1_v0_9_2_params.sage") MAX_ORDER = 1000 # Set of (curve) orders we have encountered so far. orders_done = set() # 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. 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(): assert E.is_isomorphic(C) print("- Isomorphic to secp256k1") print() continue print("- Finding prime subgroups") # Map from group_order to a set of independent generators for that order. curve_gens = {} 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 if f > MAX_ORDER: print(f" - Subgroup of size {f}: too large") continue # 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 cbrts: if l*gen == E(BETA*gen[0], gen[1]): lam = l break if lam is None: no_endo_count += 1 else: sol_count += 1 solutions.setdefault(f, []).append((b, int(gen[0]), int(gen[1]), gen, lam)) print(f" - Found {sol_count} generators (plus {no_endo_count} without endomorphism)") 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(f"#define SECP256K1_B {int(b)}") 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(solutions.keys()): b, _, _, _, lam = min(solutions[f]) print(f"# {'if' if first else 'elif'} EXHAUSTIVE_TEST_ORDER == {f}") first = False print() print(f"static const rustsecp256k1_v0_9_2_ge rustsecp256k1_v0_9_2_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 rustsecp256k1_v0_9_2_ge rustsecp256k1_v0_9_2_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("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */") first = True 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("/* End of section generated by sage/gen_exhaustive_groups.sage. */")