Skip to content

Commit c498366

Browse files
committed
Move exhaustive tests for recovery to module
1 parent be31791 commit c498366

File tree

3 files changed

+143
-128
lines changed

3 files changed

+143
-128
lines changed

src/modules/recovery/Makefile.am.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
include_HEADERS += include/secp256k1_recovery.h
22
noinst_HEADERS += src/modules/recovery/main_impl.h
33
noinst_HEADERS += src/modules/recovery/tests_impl.h
4+
noinst_HEADERS += src/modules/recovery/tests_exhaustive_impl.h
45
if USE_BENCHMARK
56
noinst_PROGRAMS += bench_recover
67
bench_recover_SOURCES = src/bench_recover.c
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**********************************************************************
2+
* Copyright (c) 2016 Andrew Poelstra *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
5+
**********************************************************************/
6+
7+
#ifndef SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
8+
#define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
9+
10+
#include "src/modules/recovery/main_impl.h"
11+
#include "include/secp256k1_recovery.h"
12+
13+
void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) {
14+
int i, j, k;
15+
16+
/* Loop */
17+
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { /* message */
18+
for (j = 1; j < EXHAUSTIVE_TEST_ORDER; j++) { /* key */
19+
for (k = 1; k < EXHAUSTIVE_TEST_ORDER; k++) { /* nonce */
20+
const int starting_k = k;
21+
secp256k1_fe r_dot_y_normalized;
22+
secp256k1_ecdsa_recoverable_signature rsig;
23+
secp256k1_ecdsa_signature sig;
24+
secp256k1_scalar sk, msg, r, s, expected_r;
25+
unsigned char sk32[32], msg32[32];
26+
int expected_recid;
27+
int recid;
28+
secp256k1_scalar_set_int(&msg, i);
29+
secp256k1_scalar_set_int(&sk, j);
30+
secp256k1_scalar_get_b32(sk32, &sk);
31+
secp256k1_scalar_get_b32(msg32, &msg);
32+
33+
secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
34+
35+
/* Check directly */
36+
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig);
37+
r_from_k(&expected_r, group, k);
38+
CHECK(r == expected_r);
39+
CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
40+
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
41+
/* In computing the recid, there is an overflow condition that is disabled in
42+
* scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value
43+
* will exceed the group order, and our signing code always holds out for r
44+
* values that don't overflow, so with a proper overflow check the tests would
45+
* loop indefinitely. */
46+
r_dot_y_normalized = group[k].y;
47+
secp256k1_fe_normalize(&r_dot_y_normalized);
48+
/* Also the recovery id is flipped depending if we hit the low-s branch */
49+
if ((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER) {
50+
expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0;
51+
} else {
52+
expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1;
53+
}
54+
CHECK(recid == expected_recid);
55+
56+
/* Convert to a standard sig then check */
57+
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
58+
secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
59+
/* Note that we compute expected_r *after* signing -- this is important
60+
* because our nonce-computing function function might change k during
61+
* signing. */
62+
r_from_k(&expected_r, group, k);
63+
CHECK(r == expected_r);
64+
CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
65+
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
66+
67+
/* Overflow means we've tried every possible nonce */
68+
if (k < starting_k) {
69+
break;
70+
}
71+
}
72+
}
73+
}
74+
}
75+
76+
void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group) {
77+
/* This is essentially a copy of test_exhaustive_verify, with recovery added */
78+
int s, r, msg, key;
79+
for (s = 1; s < EXHAUSTIVE_TEST_ORDER; s++) {
80+
for (r = 1; r < EXHAUSTIVE_TEST_ORDER; r++) {
81+
for (msg = 1; msg < EXHAUSTIVE_TEST_ORDER; msg++) {
82+
for (key = 1; key < EXHAUSTIVE_TEST_ORDER; key++) {
83+
secp256k1_ge nonconst_ge;
84+
secp256k1_ecdsa_recoverable_signature rsig;
85+
secp256k1_ecdsa_signature sig;
86+
secp256k1_pubkey pk;
87+
secp256k1_scalar sk_s, msg_s, r_s, s_s;
88+
secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
89+
int recid = 0;
90+
int k, should_verify;
91+
unsigned char msg32[32];
92+
93+
secp256k1_scalar_set_int(&s_s, s);
94+
secp256k1_scalar_set_int(&r_s, r);
95+
secp256k1_scalar_set_int(&msg_s, msg);
96+
secp256k1_scalar_set_int(&sk_s, key);
97+
secp256k1_scalar_get_b32(msg32, &msg_s);
98+
99+
/* Verify by hand */
100+
/* Run through every k value that gives us this r and check that *one* works.
101+
* Note there could be none, there could be multiple, ECDSA is weird. */
102+
should_verify = 0;
103+
for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) {
104+
secp256k1_scalar check_x_s;
105+
r_from_k(&check_x_s, group, k);
106+
if (r_s == check_x_s) {
107+
secp256k1_scalar_set_int(&s_times_k_s, k);
108+
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
109+
secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
110+
secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
111+
should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
112+
}
113+
}
114+
/* nb we have a "high s" rule */
115+
should_verify &= !secp256k1_scalar_is_high(&s_s);
116+
117+
/* We would like to try recovering the pubkey and checking that it matches,
118+
* but pubkey recovery is impossible in the exhaustive tests (the reason
119+
* being that there are 12 nonzero r values, 12 nonzero points, and no
120+
* overlap between the sets, so there are no valid signatures). */
121+
122+
/* Verify by converting to a standard signature and calling verify */
123+
secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid);
124+
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
125+
memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
126+
secp256k1_pubkey_save(&pk, &nonconst_ge);
127+
CHECK(should_verify ==
128+
secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
129+
}
130+
}
131+
}
132+
}
133+
}
134+
135+
static void test_exhaustive_recovery(const secp256k1_context *ctx, const secp256k1_ge *group) {
136+
test_exhaustive_recovery_sign(ctx, group);
137+
test_exhaustive_recovery_verify(ctx, group);
138+
}
139+
140+
#endif /* SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H */

src/tests_exhaustive.c

Lines changed: 2 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@
2727
#include "secp256k1.c"
2828
#include "testrand_impl.h"
2929

30-
#ifdef ENABLE_MODULE_RECOVERY
31-
#include "src/modules/recovery/main_impl.h"
32-
#include "include/secp256k1_recovery.h"
33-
#endif
34-
3530
/** stolen from tests.c */
3631
void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
3732
CHECK(a->infinity == b->infinity);
@@ -327,127 +322,7 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou
327322
}
328323

329324
#ifdef ENABLE_MODULE_RECOVERY
330-
void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) {
331-
int i, j, k;
332-
333-
/* Loop */
334-
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { /* message */
335-
for (j = 1; j < EXHAUSTIVE_TEST_ORDER; j++) { /* key */
336-
for (k = 1; k < EXHAUSTIVE_TEST_ORDER; k++) { /* nonce */
337-
const int starting_k = k;
338-
secp256k1_fe r_dot_y_normalized;
339-
secp256k1_ecdsa_recoverable_signature rsig;
340-
secp256k1_ecdsa_signature sig;
341-
secp256k1_scalar sk, msg, r, s, expected_r;
342-
unsigned char sk32[32], msg32[32];
343-
int expected_recid;
344-
int recid;
345-
secp256k1_scalar_set_int(&msg, i);
346-
secp256k1_scalar_set_int(&sk, j);
347-
secp256k1_scalar_get_b32(sk32, &sk);
348-
secp256k1_scalar_get_b32(msg32, &msg);
349-
350-
secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
351-
352-
/* Check directly */
353-
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig);
354-
r_from_k(&expected_r, group, k);
355-
CHECK(r == expected_r);
356-
CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
357-
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
358-
/* In computing the recid, there is an overflow condition that is disabled in
359-
* scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value
360-
* will exceed the group order, and our signing code always holds out for r
361-
* values that don't overflow, so with a proper overflow check the tests would
362-
* loop indefinitely. */
363-
r_dot_y_normalized = group[k].y;
364-
secp256k1_fe_normalize(&r_dot_y_normalized);
365-
/* Also the recovery id is flipped depending if we hit the low-s branch */
366-
if ((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER) {
367-
expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0;
368-
} else {
369-
expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1;
370-
}
371-
CHECK(recid == expected_recid);
372-
373-
/* Convert to a standard sig then check */
374-
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
375-
secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
376-
/* Note that we compute expected_r *after* signing -- this is important
377-
* because our nonce-computing function function might change k during
378-
* signing. */
379-
r_from_k(&expected_r, group, k);
380-
CHECK(r == expected_r);
381-
CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER ||
382-
(k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER);
383-
384-
/* Overflow means we've tried every possible nonce */
385-
if (k < starting_k) {
386-
break;
387-
}
388-
}
389-
}
390-
}
391-
}
392-
393-
void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group) {
394-
/* This is essentially a copy of test_exhaustive_verify, with recovery added */
395-
int s, r, msg, key;
396-
for (s = 1; s < EXHAUSTIVE_TEST_ORDER; s++) {
397-
for (r = 1; r < EXHAUSTIVE_TEST_ORDER; r++) {
398-
for (msg = 1; msg < EXHAUSTIVE_TEST_ORDER; msg++) {
399-
for (key = 1; key < EXHAUSTIVE_TEST_ORDER; key++) {
400-
secp256k1_ge nonconst_ge;
401-
secp256k1_ecdsa_recoverable_signature rsig;
402-
secp256k1_ecdsa_signature sig;
403-
secp256k1_pubkey pk;
404-
secp256k1_scalar sk_s, msg_s, r_s, s_s;
405-
secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
406-
int recid = 0;
407-
int k, should_verify;
408-
unsigned char msg32[32];
409-
410-
secp256k1_scalar_set_int(&s_s, s);
411-
secp256k1_scalar_set_int(&r_s, r);
412-
secp256k1_scalar_set_int(&msg_s, msg);
413-
secp256k1_scalar_set_int(&sk_s, key);
414-
secp256k1_scalar_get_b32(msg32, &msg_s);
415-
416-
/* Verify by hand */
417-
/* Run through every k value that gives us this r and check that *one* works.
418-
* Note there could be none, there could be multiple, ECDSA is weird. */
419-
should_verify = 0;
420-
for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) {
421-
secp256k1_scalar check_x_s;
422-
r_from_k(&check_x_s, group, k);
423-
if (r_s == check_x_s) {
424-
secp256k1_scalar_set_int(&s_times_k_s, k);
425-
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
426-
secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
427-
secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
428-
should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
429-
}
430-
}
431-
/* nb we have a "high s" rule */
432-
should_verify &= !secp256k1_scalar_is_high(&s_s);
433-
434-
/* We would like to try recovering the pubkey and checking that it matches,
435-
* but pubkey recovery is impossible in the exhaustive tests (the reason
436-
* being that there are 12 nonzero r values, 12 nonzero points, and no
437-
* overlap between the sets, so there are no valid signatures). */
438-
439-
/* Verify by converting to a standard signature and calling verify */
440-
secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid);
441-
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
442-
memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
443-
secp256k1_pubkey_save(&pk, &nonconst_ge);
444-
CHECK(should_verify ==
445-
secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
446-
}
447-
}
448-
}
449-
}
450-
}
325+
#include "src/modules/recovery/tests_exhaustive_impl.h"
451326
#endif
452327

453328
int main(void) {
@@ -500,8 +375,7 @@ int main(void) {
500375
test_exhaustive_verify(ctx, group);
501376

502377
#ifdef ENABLE_MODULE_RECOVERY
503-
test_exhaustive_recovery_sign(ctx, group);
504-
test_exhaustive_recovery_verify(ctx, group);
378+
test_exhaustive_recovery(ctx, group);
505379
#endif
506380

507381
secp256k1_context_destroy(ctx);

0 commit comments

Comments
 (0)