aboutsummaryrefslogtreecommitdiff
path: root/src/secp256k1/sage/gen_exhaustive_groups.sage
blob: 070bc1285f7e145cb20d5ef2a2f25926f3f2f8d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
load("secp256k1_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 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("/* 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. */")