Skip to content

Commit 2cee5fd

Browse files
committed
exhaustive tests: add recovery module
1 parent 678b0e5 commit 2cee5fd

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

src/tests_exhaustive.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
#include "secp256k1.c"
2727
#include "testrand_impl.h"
2828

29+
#ifdef ENABLE_MODULE_RECOVERY
30+
#include "src/modules/recovery/main_impl.h"
31+
#include "include/secp256k1_recovery.h"
32+
#endif
33+
2934
/** stolen from tests.c */
3035
void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
3136
CHECK(a->infinity == b->infinity);
@@ -282,6 +287,130 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou
282287
*/
283288
}
284289

290+
#ifdef ENABLE_MODULE_RECOVERY
291+
void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
292+
int i, j, k;
293+
294+
/* Loop */
295+
for (i = 1; i < order; i++) { /* message */
296+
for (j = 1; j < order; j++) { /* key */
297+
for (k = 1; k < order; k++) { /* nonce */
298+
const int starting_k = k;
299+
secp256k1_fe r_dot_y_normalized;
300+
secp256k1_ecdsa_recoverable_signature rsig;
301+
secp256k1_ecdsa_signature sig;
302+
secp256k1_scalar sk, msg, r, s, expected_r;
303+
unsigned char sk32[32], msg32[32];
304+
int expected_recid;
305+
int recid;
306+
secp256k1_scalar_set_int(&msg, i);
307+
secp256k1_scalar_set_int(&sk, j);
308+
secp256k1_scalar_get_b32(sk32, &sk);
309+
secp256k1_scalar_get_b32(msg32, &msg);
310+
311+
secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
312+
313+
/* Check directly */
314+
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig);
315+
r_from_k(&expected_r, group, k);
316+
CHECK(r == expected_r);
317+
CHECK((k * s) % order == (i + r * j) % order ||
318+
(k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
319+
/* In computing the recid, there is an overflow condition that is disabled in
320+
* scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value
321+
* will exceed the group order, and our signing code always holds out for r
322+
* values that don't overflow, so with a proper overflow check the tests would
323+
* loop indefinitely. */
324+
r_dot_y_normalized = group[k].y;
325+
secp256k1_fe_normalize(&r_dot_y_normalized);
326+
/* Also the recovery id is flipped depending if we hit the low-s branch */
327+
if ((k * s) % order == (i + r * j) % order) {
328+
expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0;
329+
} else {
330+
expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1;
331+
}
332+
CHECK(recid == expected_recid);
333+
334+
/* Convert to a standard sig then check */
335+
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
336+
secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
337+
/* Note that we compute expected_r *after* signing -- this is important
338+
* because our nonce-computing function function might change k during
339+
* signing. */
340+
r_from_k(&expected_r, group, k);
341+
CHECK(r == expected_r);
342+
CHECK((k * s) % order == (i + r * j) % order ||
343+
(k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
344+
345+
/* Overflow means we've tried every possible nonce */
346+
if (k < starting_k) {
347+
break;
348+
}
349+
}
350+
}
351+
}
352+
}
353+
354+
void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
355+
/* This is essentially a copy of test_exhaustive_verify, with recovery added */
356+
int s, r, msg, key;
357+
for (s = 1; s < order; s++) {
358+
for (r = 1; r < order; r++) {
359+
for (msg = 1; msg < order; msg++) {
360+
for (key = 1; key < order; key++) {
361+
secp256k1_ge nonconst_ge;
362+
secp256k1_ecdsa_recoverable_signature rsig;
363+
secp256k1_ecdsa_signature sig;
364+
secp256k1_pubkey pk;
365+
secp256k1_scalar sk_s, msg_s, r_s, s_s;
366+
secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
367+
int recid = 0;
368+
int k, should_verify;
369+
unsigned char msg32[32];
370+
371+
secp256k1_scalar_set_int(&s_s, s);
372+
secp256k1_scalar_set_int(&r_s, r);
373+
secp256k1_scalar_set_int(&msg_s, msg);
374+
secp256k1_scalar_set_int(&sk_s, key);
375+
secp256k1_scalar_get_b32(msg32, &msg_s);
376+
377+
/* Verify by hand */
378+
/* Run through every k value that gives us this r and check that *one* works.
379+
* Note there could be none, there could be multiple, ECDSA is weird. */
380+
should_verify = 0;
381+
for (k = 0; k < order; k++) {
382+
secp256k1_scalar check_x_s;
383+
r_from_k(&check_x_s, group, k);
384+
if (r_s == check_x_s) {
385+
secp256k1_scalar_set_int(&s_times_k_s, k);
386+
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
387+
secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
388+
secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
389+
should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
390+
}
391+
}
392+
/* nb we have a "high s" rule */
393+
should_verify &= !secp256k1_scalar_is_high(&s_s);
394+
395+
/* We would like to try recovering the pubkey and checking that it matches,
396+
* but pubkey recovery is impossible in the exhaustive tests (the reason
397+
* being that there are 12 nonzero r values, 12 nonzero points, and no
398+
* overlap between the sets, so there are no valid signatures). */
399+
400+
/* Verify by converting to a standard signature and calling verify */
401+
secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid);
402+
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
403+
memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
404+
secp256k1_pubkey_save(&pk, &nonconst_ge);
405+
CHECK(should_verify ==
406+
secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
407+
}
408+
}
409+
}
410+
}
411+
}
412+
#endif
413+
285414
int main(void) {
286415
int i;
287416
secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER];
@@ -330,6 +459,12 @@ int main(void) {
330459
test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
331460
test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
332461

462+
#ifdef ENABLE_MODULE_RECOVERY
463+
test_exhaustive_recovery_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
464+
test_exhaustive_recovery_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
465+
#endif
466+
467+
secp256k1_context_destroy(ctx);
333468
return 0;
334469
}
335470

0 commit comments

Comments
 (0)