|  | 
| 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 | 
|  | 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)) | 
| 86 | 85 | 
 | 
| 87 |  | -    print("") | 
|  | 86 | +        print(f"  - Found {sol_count} generators (plus {no_endo_count} without endomorphism)") | 
| 88 | 87 | 
 | 
| 89 |  | -print("") | 
| 90 |  | -print("") | 
| 91 |  | -print("/* To be put in src/group_impl.h: */") | 
| 92 |  | -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)) | 
| 97 |  | -    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(");") | 
|  | 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): | 
| 104 | 99 |     print("static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(") | 
| 105 | 100 |     print("    0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) | 
| 106 | 101 |     print("    0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(b) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) | 
| 107 | 102 |     print(");") | 
|  | 103 | + | 
|  | 104 | +print() | 
|  | 105 | +print("To be put in src/group_impl.h:") | 
|  | 106 | +print() | 
|  | 107 | +print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */") | 
|  | 108 | +for f in sorted(solutions.keys()): | 
|  | 109 | +    # Use as generator/2 the one with lowest b, and lowest (x, y) generator (interpreted as non-negative integers). | 
|  | 110 | +    b, _, _, HALF_G, lam = min(solutions[f]) | 
|  | 111 | +    output_generator(2 * HALF_G, f"SECP256K1_G_ORDER_{f}") | 
|  | 112 | +print("/** Generator for secp256k1, value 'g' defined in") | 
|  | 113 | +print(" *  \"Standards for Efficient Cryptography\" (SEC2) 2.7.1.") | 
|  | 114 | +print(" */") | 
|  | 115 | +output_generator(G, "SECP256K1_G") | 
|  | 116 | +print("/* These exhaustive group test orders and generators are chosen such that:") | 
|  | 117 | +print(" * - The field size is equal to that of secp256k1, so field code is the same.") | 
|  | 118 | +print(" * - The curve equation is of the form y^2=x^3+B for some small constant B.") | 
|  | 119 | +print(" * - The subgroup has a generator 2*P, where P.x is as small as possible.") | 
|  | 120 | +print(f" * - The subgroup has size less than {MAX_ORDER} to permit exhaustive testing.") | 
|  | 121 | +print(" * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).") | 
|  | 122 | +print(" */") | 
|  | 123 | +print("#if defined(EXHAUSTIVE_TEST_ORDER)") | 
|  | 124 | +first = True | 
|  | 125 | +for f in sorted(solutions.keys()): | 
|  | 126 | +    b, _, _, _, lam = min(solutions[f]) | 
|  | 127 | +    print(f"#  {'if' if first else 'elif'} EXHAUSTIVE_TEST_ORDER == {f}") | 
|  | 128 | +    first = False | 
|  | 129 | +    print() | 
|  | 130 | +    print(f"static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_{f};") | 
|  | 131 | +    print("") | 
|  | 132 | +    output_b(b) | 
|  | 133 | +    print() | 
| 108 | 134 | print("#  else") | 
| 109 | 135 | print("#    error No known generator for the specified exhaustive test group order.") | 
| 110 | 136 | print("#  endif") | 
| 111 |  | - | 
|  | 137 | +print("#else") | 
|  | 138 | +print() | 
|  | 139 | +print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;") | 
| 112 | 140 | print("") | 
| 113 |  | -print("") | 
| 114 |  | -print("/* To be put in src/scalar_impl.h: */") | 
|  | 141 | +output_b(7) | 
|  | 142 | +print() | 
|  | 143 | +print("#endif") | 
|  | 144 | +print("/* End of section generated by sage/gen_exhaustive_groups.sage. */") | 
|  | 145 | + | 
|  | 146 | + | 
|  | 147 | +print() | 
|  | 148 | +print() | 
|  | 149 | +print("To be put in src/scalar_impl.h:") | 
|  | 150 | +print() | 
|  | 151 | +print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */") | 
| 115 | 152 | first = True | 
| 116 |  | -for f in sorted(results.keys()): | 
| 117 |  | -    lam = results[f]["lambda"] | 
|  | 153 | +for f in sorted(solutions.keys()): | 
|  | 154 | +    _, _, _, _, lam = min(solutions[f]) | 
| 118 | 155 |     print("#  %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f)) | 
| 119 | 156 |     first = False | 
| 120 | 157 |     print("#    define EXHAUSTIVE_TEST_LAMBDA %i" % lam) | 
| 121 | 158 | print("#  else") | 
| 122 | 159 | print("#    error No known lambda for the specified exhaustive test group order.") | 
| 123 | 160 | print("#  endif") | 
| 124 |  | -print("") | 
|  | 161 | +print("/* End of section generated by sage/gen_exhaustive_groups.sage. */") | 
0 commit comments