|
26 | 26 | #include "secp256k1.c" |
27 | 27 | #include "testrand_impl.h" |
28 | 28 |
|
| 29 | +#ifdef ENABLE_MODULE_RECOVERY |
| 30 | +#include "src/modules/recovery/main_impl.h" |
| 31 | +#include "include/secp256k1_recovery.h" |
| 32 | +#endif |
| 33 | + |
29 | 34 | /** stolen from tests.c */ |
30 | 35 | void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { |
31 | 36 | CHECK(a->infinity == b->infinity); |
@@ -282,6 +287,130 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou |
282 | 287 | */ |
283 | 288 | } |
284 | 289 |
|
| 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 | + |
285 | 414 | int main(void) { |
286 | 415 | int i; |
287 | 416 | secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; |
@@ -330,6 +459,12 @@ int main(void) { |
330 | 459 | test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); |
331 | 460 | test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); |
332 | 461 |
|
| 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); |
333 | 468 | return 0; |
334 | 469 | } |
335 | 470 |
|
0 commit comments