|
1 | 1 | load("secp256k1_params.sage")
|
2 | 2 |
|
| 3 | +MAX_ORDER = 1000 |
| 4 | + |
| 5 | +# Set of (curve) orders we have encountered so far. |
3 | 6 | 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. |
6 | 12 | 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. |
8 | 14 | if len(orders_done) == 6:
|
9 | 15 | break
|
10 | 16 |
|
11 | 17 | E = EllipticCurve(F, [0, b])
|
12 | 18 | print("Analyzing curve y^2 = x^3 + %i" % b)
|
13 | 19 | n = E.order()
|
| 20 | + |
14 | 21 | # Skip curves with an order we've already tried
|
15 | 22 | if n in orders_done:
|
16 | 23 | print("- Isomorphic to earlier curve")
|
| 24 | + print() |
17 | 25 | continue
|
18 | 26 | orders_done.add(n)
|
| 27 | + |
19 | 28 | # Skip curves isomorphic to the real secp256k1
|
20 | 29 | if n.is_pseudoprime():
|
21 |
| - print(" - Isomorphic to secp256k1") |
| 30 | + assert E.is_isomorphic(C) |
| 31 | + print("- Isomorphic to secp256k1") |
| 32 | + print() |
22 | 33 | continue
|
23 | 34 |
|
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") |
41 | 36 |
|
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 = {} |
43 | 39 |
|
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") |
48 | 50 | 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() |
50 | 72 |
|
51 | 73 | # Find lambda for endomorphism. Skip if none can be found.
|
52 | 74 | 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 |
56 | 78 | break
|
| 79 | + |
57 | 80 | 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)") |
92 | 121 | 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}") |
97 | 125 | 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() |
108 | 130 | print("# else")
|
109 | 131 | print("# error No known generator for the specified exhaustive test group order.")
|
110 | 132 | 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 | + |
111 | 141 |
|
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. */") |
115 | 147 | 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]) |
118 | 150 | print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f))
|
119 | 151 | first = False
|
120 | 152 | print("# define EXHAUSTIVE_TEST_LAMBDA %i" % lam)
|
121 | 153 | print("# else")
|
122 | 154 | print("# error No known lambda for the specified exhaustive test group order.")
|
123 | 155 | print("# endif")
|
124 |
| -print("") |
| 156 | +print("/* End of section generated by sage/gen_exhaustive_groups.sage. */") |
0 commit comments