Skip to content

Commit ae40e87

Browse files
committed
Change exhaustive test groups so they have a point with X=1
This enables testing overflow is correctly encoded in the recid, and likely triggers more edge cases.
1 parent 9fb066e commit ae40e87

File tree

3 files changed

+120
-45
lines changed

3 files changed

+120
-45
lines changed

src/group_impl.h

Lines changed: 109 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,52 +11,125 @@
1111
#include "field.h"
1212
#include "group.h"
1313

14-
/* These points can be generated in sage as follows:
14+
/* These exhaustive group test orders and generators are chosen such that:
15+
* - The field size is equal to that of secp256k1, so field code is the same.
16+
* - The curve equation is of the form y^2=x^3+B for some constant B.
17+
* - The order is less than 1000 to permit exhaustive testing.
18+
* - The order is a multiple of 3 plus 1, enabling the endomorphism optimization.
19+
* - The generator is 2*P, where P has X coefficient equal to 1.
1520
*
16-
* 0. Setup a worksheet with the following parameters.
17-
* b = 4 # whatever secp256k1_fe_const_b will be set to
18-
* F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
19-
* C = EllipticCurve ([F (0), F (b)])
21+
* They can generated with the following Sage code:
2022
*
21-
* 1. Determine all the small orders available to you. (If there are
22-
* no satisfactory ones, go back and change b.)
23-
* print C.order().factor(limit=1000)
23+
* # Define field size and field
24+
* P = 2^256 - 2^32 - 977
25+
* F = GF(P)
2426
*
25-
* 2. Choose an order as one of the prime factors listed in the above step.
26-
* (You can also multiply some to get a composite order, though the
27-
* tests will crash trying to invert scalars during signing.) We take a
28-
* random point and scale it to drop its order to the desired value.
29-
* There is some probability this won't work; just try again.
30-
* order = 199
31-
* P = C.random_point()
32-
* P = (int(P.order()) / int(order)) * P
33-
* assert(P.order() == order)
27+
* orders_done = set()
28+
* first = True
29+
* for b in range(1, P):
30+
* E = EllipticCurve(F, [0, b])
31+
* n = E.order()
32+
* # Skip curves isomorphic to the real secp256k1
33+
* if n.is_pseudoprime():
34+
* continue
35+
* # Skip curves with an order we've already tried
36+
* if n in orders_done:
37+
* continue
38+
* orders_done.add(n)
3439
*
35-
* 3. Print the values. You'll need to use a vim macro or something to
36-
* split the hex output into 4-byte chunks.
37-
* print "%x %x" % P.xy()
40+
* # Find what prime subgroups exist
41+
* for f, _ in n.factor():
42+
* # Skip subgroups of order >1000
43+
* if f > 1000:
44+
* continue
45+
* # Skip subgroups that are not 3n+1 (needed for endomorphism)
46+
* if f % 3 != 1:
47+
* continue
48+
*
49+
* for x in range(1, P):
50+
* if (F(x)^3+F(b)).is_square():
51+
* # X coordinate x exists on curve y^2=x^3+b
52+
* G = E.lift_x(F(x))
53+
* # We need a point whose order is at (a multiple of) our desired order
54+
* if (G.order() % f):
55+
* continue
56+
* # Project into subgroup of desired order
57+
* G = G * (G.order() // f)
58+
* assert G.order() == f
59+
*
60+
* # Now look for an isomorphism of the curve that gives this generator X
61+
* # coordinate equal to 1.
62+
* # 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.
63+
* # So look for m=a^2=1/x.
64+
* m = F(1)/G[0]
65+
* if not m.is_square():
66+
* continue
67+
* rb = b*m^3
68+
* RE = EllipticCurve(F, [0, rb])
69+
* # Use as generator twice the point with this low X coordinate (like secp256k1!)
70+
* RG = RE.lift_x(1) * 2
71+
* # And even Y coordinate.
72+
* if int(RG[1]) % 2:
73+
* RG = -RG
74+
*
75+
* if first:
76+
* print("# if EXHAUSTIVE_TEST_ORDER == %i" % f)
77+
* first = False
78+
* else:
79+
* print("# elif EXHAUSTIVE_TEST_ORDER == %i" % f)
80+
* print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(")
81+
* print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(RG[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
82+
* print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(RG[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
83+
* print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(RG[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
84+
* print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(RG[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
85+
* print(");")
86+
* print("static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(")
87+
* print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x," % tuple((int(rb) >> (32 * (7 - i))) & 0xffffffff for i in range(4)))
88+
* print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x" % tuple((int(rb) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8)))
89+
* print(");")
90+
* break
91+
*
92+
* if len(orders_done) == 5:
93+
* break
94+
*
95+
* print("# else")
96+
* print("# error No known generator for the specified exhaustive test group order.")
97+
* print("# endif")
3898
*/
3999
#if defined(EXHAUSTIVE_TEST_ORDER)
40-
# if EXHAUSTIVE_TEST_ORDER == 199
100+
# if EXHAUSTIVE_TEST_ORDER == 13
41101
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
42-
0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
43-
0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
44-
0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
45-
0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
102+
0xc3459c3d, 0x35326167, 0xcd86cce8, 0x07a2417f,
103+
0x5b8bd567, 0xde8538ee, 0x0d507b0c, 0xd128f5bb,
104+
0x8e467fec, 0xcd30000a, 0x6cc1184e, 0x25d382c2,
105+
0xa2f4494e, 0x2fbe9abc, 0x8b64abac, 0xd005fb24
46106
);
47-
48-
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 4);
49-
50-
# elif EXHAUSTIVE_TEST_ORDER == 13
107+
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
108+
0x3d3486b2, 0x159a9ca5, 0xc75638be, 0xb23a69bc,
109+
0x946a45ab, 0x24801247, 0xb4ed2b8e, 0x26b6a417
110+
);
111+
# elif EXHAUSTIVE_TEST_ORDER == 199
51112
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
52-
0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
53-
0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
54-
0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
55-
0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
113+
0x226e653f, 0xc8df7744, 0x9bacbf12, 0x7d1dcbf9,
114+
0x87f05b2a, 0xe7edbd28, 0x1f564575, 0xc48dcf18,
115+
0xa13872c2, 0xe933bb17, 0x5d9ffd5b, 0xb5b6e10c,
116+
0x57fe3c00, 0xbaaaa15a, 0xe003ec3e, 0x9c269bae
117+
);
118+
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
119+
0x2cca28fa, 0xfc614b80, 0x2a3db42b, 0x00ba00b1,
120+
0xbea8d943, 0xdace9ab2, 0x9536daea, 0x0074defb
121+
);
122+
# elif EXHAUSTIVE_TEST_ORDER == 7
123+
static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
124+
0x3629054c, 0x34e70740, 0x5d009072, 0x3172630c,
125+
0x3b93b9fe, 0x53d21c61, 0x3fd29d22, 0xed48a4ca,
126+
0x165c881d, 0x0a95ff40, 0xc81b3e2d, 0xa9be43e0,
127+
0xca5a8953, 0xa2fd0196, 0xf718e5cb, 0x21d245d8
128+
);
129+
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(
130+
0x168b921c, 0xe98a84e1, 0x2e977057, 0x29f0a9c1,
131+
0x5bce37e9, 0x94c30aed, 0xde3460ff, 0x71ac095f
56132
);
57-
58-
static const secp256k1_fe secp256k1_fe_const_b = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2);
59-
60133
# else
61134
# error No known generator for the specified exhaustive test group order.
62135
# endif

src/modules/recovery/tests_exhaustive_impl.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1
2525
unsigned char sk32[32], msg32[32];
2626
int expected_recid;
2727
int recid;
28+
int overflow;
2829
secp256k1_scalar_set_int(&msg, i);
2930
secp256k1_scalar_set_int(&sk, j);
3031
secp256k1_scalar_get_b32(sk32, &sk);
@@ -34,17 +35,18 @@ void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1
3435

3536
/* Check directly */
3637
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig);
37-
r_from_k(&expected_r, group, k);
38+
r_from_k(&expected_r, group, k, &overflow);
3839
CHECK(r == expected_r);
3940
CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
4041
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
4142
/* The recid's second bit is for conveying overflow (R.x value >= group order).
4243
* In the actual secp256k1 this is an astronomically unlikely event, but in the
43-
* small group used here, it will always be the case.
44+
* small group used here, it will be the case for all points except the ones where
45+
* R.x=1 (which the group is specifically selected to have).
4446
* Note that this isn't actually useful; full recovery would need to convey
4547
* floor(R.x / group_order), but only one bit is used as that is sufficient
4648
* in the real group. */
47-
expected_recid = 2;
49+
expected_recid = overflow ? 2 : 0;
4850
r_dot_y_normalized = group[k].y;
4951
secp256k1_fe_normalize(&r_dot_y_normalized);
5052
/* Also the recovery id is flipped depending if we hit the low-s branch */
@@ -61,7 +63,7 @@ void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1
6163
/* Note that we compute expected_r *after* signing -- this is important
6264
* because our nonce-computing function function might change k during
6365
* signing. */
64-
r_from_k(&expected_r, group, k);
66+
r_from_k(&expected_r, group, k, NULL);
6567
CHECK(r == expected_r);
6668
CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
6769
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
@@ -104,7 +106,7 @@ void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256
104106
should_verify = 0;
105107
for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) {
106108
secp256k1_scalar check_x_s;
107-
r_from_k(&check_x_s, group, k);
109+
r_from_k(&check_x_s, group, k, NULL);
108110
if (r_s == check_x_s) {
109111
secp256k1_scalar_set_int(&s_times_k_s, k);
110112
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);

src/tests_exhaustive.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,14 @@ void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_
216216
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
217217
}
218218

219-
void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
219+
void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k, int* overflow) {
220220
secp256k1_fe x;
221221
unsigned char x_bin[32];
222222
k %= EXHAUSTIVE_TEST_ORDER;
223223
x = group[k].x;
224224
secp256k1_fe_normalize(&x);
225225
secp256k1_fe_get_b32(x_bin, &x);
226-
secp256k1_scalar_set_b32(r, x_bin, NULL);
226+
secp256k1_scalar_set_b32(r, x_bin, overflow);
227227
}
228228

229229
void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group) {
@@ -251,7 +251,7 @@ void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *gr
251251
should_verify = 0;
252252
for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) {
253253
secp256k1_scalar check_x_s;
254-
r_from_k(&check_x_s, group, k);
254+
r_from_k(&check_x_s, group, k, NULL);
255255
if (r_s == check_x_s) {
256256
secp256k1_scalar_set_int(&s_times_k_s, k);
257257
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
@@ -298,7 +298,7 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou
298298
/* Note that we compute expected_r *after* signing -- this is important
299299
* because our nonce-computing function function might change k during
300300
* signing. */
301-
r_from_k(&expected_r, group, k);
301+
r_from_k(&expected_r, group, k, NULL);
302302
CHECK(r == expected_r);
303303
CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
304304
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);

0 commit comments

Comments
 (0)