Skip to content

Commit

Permalink
[SECP256K1] Improve field/scalar inverse tests
Browse files Browse the repository at this point in the history
Summary:
```
Add a new run_inverse_tests that replaces all existing field/scalar
inverse tests, and tests a few identities for fixed inputs, small
numbers (-999...999), random inputs (structured and unstructured), as
well as comparing with the output of secp256k1_fe_inv_all_var.
```

Partial backport of [[bitcoin-core/secp256k1#831 | secp256k1#831]]:
bitcoin-core/secp256k1@aa9cc52

Depends on D9405.

Test Plan:
  ninja check-secp256k1

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D9406
  • Loading branch information
sipa authored and Fabcien committed Apr 14, 2021
1 parent 1893050 commit f66a53c
Showing 1 changed file with 164 additions and 61 deletions.
225 changes: 164 additions & 61 deletions src/tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -1418,33 +1418,6 @@ void scalar_test(void) {
}
#endif

{
/* Test that scalar inverses are equal to the inverse of their number modulo the order. */
if (!secp256k1_scalar_is_zero(&s)) {
secp256k1_scalar inv;
#ifndef USE_NUM_NONE
secp256k1_num invnum;
secp256k1_num invnum2;
#endif
secp256k1_scalar_inverse(&inv, &s);
#ifndef USE_NUM_NONE
secp256k1_num_mod_inverse(&invnum, &snum, &order);
secp256k1_scalar_get_num(&invnum2, &inv);
CHECK(secp256k1_num_eq(&invnum, &invnum2));
#endif
secp256k1_scalar_mul(&inv, &inv, &s);
/* Multiplying a scalar with its inverse must result in one. */
CHECK(secp256k1_scalar_is_one(&inv));
secp256k1_scalar_inverse(&inv, &inv);
/* Inverting one must result in one. */
CHECK(secp256k1_scalar_is_one(&inv));
#ifndef USE_NUM_NONE
secp256k1_scalar_get_num(&invnum, &inv);
CHECK(secp256k1_num_is_one(&invnum));
#endif
}
}

{
/* Test commutativity of add. */
secp256k1_scalar r1, r2;
Expand Down Expand Up @@ -2275,13 +2248,6 @@ int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
return secp256k1_fe_equal_var(&an, &bn);
}

int check_fe_inverse(const secp256k1_fe *a, const secp256k1_fe *ai) {
secp256k1_fe x;
secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
secp256k1_fe_mul(&x, a, ai);
return check_fe_equal(&x, &one);
}

void run_field_convert(void) {
static const unsigned char b32[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
Expand Down Expand Up @@ -2401,30 +2367,6 @@ void run_field_misc(void) {
}
}

void run_field_inv(void) {
secp256k1_fe x, xi, xii;
int i;
for (i = 0; i < 10*count; i++) {
random_fe_non_zero(&x);
secp256k1_fe_inv(&xi, &x);
CHECK(check_fe_inverse(&x, &xi));
secp256k1_fe_inv(&xii, &xi);
CHECK(check_fe_equal(&x, &xii));
}
}

void run_field_inv_var(void) {
secp256k1_fe x, xi, xii;
int i;
for (i = 0; i < 10*count; i++) {
random_fe_non_zero(&x);
secp256k1_fe_inv_var(&xi, &x);
CHECK(check_fe_inverse(&x, &xi));
secp256k1_fe_inv_var(&xii, &xi);
CHECK(check_fe_equal(&x, &xii));
}
}

void run_sqr(void) {
secp256k1_fe x, s;

Expand Down Expand Up @@ -2489,6 +2431,169 @@ void run_sqrt(void) {
}
}

/***** FIELD/SCALAR INVERSE TESTS *****/

static const secp256k1_scalar scalar_minus_one = SECP256K1_SCALAR_CONST(
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE,
0xBAAEDCE6, 0xAF48A03B, 0xBFD25E8C, 0xD0364140
);

static const secp256k1_fe fe_minus_one = SECP256K1_FE_CONST(
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFC2E
);

/* These tests test the following identities:
*
* for x==0: 1/x == 0
* for x!=0: x*(1/x) == 1
* for x!=0 and x!=1: 1/(1/x - 1) + 1 == -1/(x-1)
*/

void test_inverse_scalar(secp256k1_scalar* out, const secp256k1_scalar* x, int var)
{
secp256k1_scalar l, r, t;

(var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&l, x); /* l = 1/x */
if (out) *out = l;
if (secp256k1_scalar_is_zero(x)) {
CHECK(secp256k1_scalar_is_zero(&l));
return;
}
secp256k1_scalar_mul(&t, x, &l); /* t = x*(1/x) */
CHECK(secp256k1_scalar_is_one(&t)); /* x*(1/x) == 1 */
secp256k1_scalar_add(&r, x, &scalar_minus_one); /* r = x-1 */
if (secp256k1_scalar_is_zero(&r)) return;
(var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&r, &r); /* r = 1/(x-1) */
secp256k1_scalar_add(&l, &scalar_minus_one, &l); /* l = 1/x-1 */
(var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&l, &l); /* l = 1/(1/x-1) */
secp256k1_scalar_add(&l, &l, &secp256k1_scalar_one); /* l = 1/(1/x-1)+1 */
secp256k1_scalar_add(&l, &r, &l); /* l = 1/(1/x-1)+1 + 1/(x-1) */
CHECK(secp256k1_scalar_is_zero(&l)); /* l == 0 */
}

void test_inverse_field(secp256k1_fe* out, const secp256k1_fe* x, int var)
{
secp256k1_fe l, r, t;

(var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&l, x) ; /* l = 1/x */
if (out) *out = l;
t = *x; /* t = x */
if (secp256k1_fe_normalizes_to_zero_var(&t)) {
CHECK(secp256k1_fe_normalizes_to_zero(&l));
return;
}
secp256k1_fe_mul(&t, x, &l); /* t = x*(1/x) */
secp256k1_fe_add(&t, &fe_minus_one); /* t = x*(1/x)-1 */
CHECK(secp256k1_fe_normalizes_to_zero(&t)); /* x*(1/x)-1 == 0 */
r = *x; /* r = x */
secp256k1_fe_add(&r, &fe_minus_one); /* r = x-1 */
if (secp256k1_fe_normalizes_to_zero_var(&r)) return;
(var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&r, &r); /* r = 1/(x-1) */
secp256k1_fe_add(&l, &fe_minus_one); /* l = 1/x-1 */
(var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&l, &l); /* l = 1/(1/x-1) */
secp256k1_fe_add(&l, &secp256k1_fe_one); /* l = 1/(1/x-1)+1 */
secp256k1_fe_add(&l, &r); /* l = 1/(1/x-1)+1 + 1/(x-1) */
CHECK(secp256k1_fe_normalizes_to_zero_var(&l)); /* l == 0 */
}

void run_inverse_tests(void)
{
/* Fixed test cases for field inverses: pairs of (x, 1/x) mod p. */
static const secp256k1_fe fe_cases[][2] = {
/* 0 */
{SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0),
SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)},
/* 1 */
{SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1),
SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1)},
/* -1 */
{SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc2e),
SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc2e)},
/* 2 */
{SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2),
SECP256K1_FE_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ffffe18)},
/* 2**128 */
{SECP256K1_FE_CONST(0, 0, 0, 1, 0, 0, 0, 0),
SECP256K1_FE_CONST(0xbcb223fe, 0xdc24a059, 0xd838091d, 0xd2253530, 0xffffffff, 0xffffffff, 0xffffffff, 0x434dd931)},
/* Input known to need 637 divsteps */
{SECP256K1_FE_CONST(0xe34e9c95, 0x6bee8a84, 0x0dcb632a, 0xdb8a1320, 0x66885408, 0x06f3f996, 0x7c11ca84, 0x19199ec3),
SECP256K1_FE_CONST(0xbd2cbd8f, 0x1c536828, 0x9bccda44, 0x2582ac0c, 0x870152b0, 0x8a3f09fb, 0x1aaadf92, 0x19b618e5)}
};
/* Fixed test cases for scalar inverses: pairs of (x, 1/x) mod n. */
static const secp256k1_scalar scalar_cases[][2] = {
/* 0 */
{SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0),
SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0)},
/* 1 */
{SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1),
SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1)},
/* -1 */
{SECP256K1_SCALAR_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xbaaedce6, 0xaf48a03b, 0xbfd25e8c, 0xd0364140),
SECP256K1_SCALAR_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xbaaedce6, 0xaf48a03b, 0xbfd25e8c, 0xd0364140)},
/* 2 */
{SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 2),
SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x5d576e73, 0x57a4501d, 0xdfe92f46, 0x681b20a1)},
/* 2**128 */
{SECP256K1_SCALAR_CONST(0, 0, 0, 1, 0, 0, 0, 0),
SECP256K1_SCALAR_CONST(0x50a51ac8, 0x34b9ec24, 0x4b0dff66, 0x5588b13e, 0x9984d5b3, 0xcf80ef0f, 0xd6a23766, 0xa3ee9f22)},
/* Input known to need 635 divsteps */
{SECP256K1_SCALAR_CONST(0xcb9f1d35, 0xdd4416c2, 0xcd71bf3f, 0x6365da66, 0x3c9b3376, 0x8feb7ae9, 0x32a5ef60, 0x19199ec3),
SECP256K1_SCALAR_CONST(0x1d7c7bba, 0xf1893d53, 0xb834bd09, 0x36b411dc, 0x42c2e42f, 0xec72c428, 0x5e189791, 0x8e9bc708)}
};
int i, var, testrand;
unsigned char b32[32];
secp256k1_fe x_fe;
secp256k1_scalar x_scalar;
memset(b32, 0, sizeof(b32));
/* Test fixed test cases through test_inverse_{scalar,field}, both ways. */
for (i = 0; (size_t)i < sizeof(fe_cases)/sizeof(fe_cases[0]); ++i) {
for (var = 0; var <= 1; ++var) {
test_inverse_field(&x_fe, &fe_cases[i][0], var);
check_fe_equal(&x_fe, &fe_cases[i][1]);
test_inverse_field(&x_fe, &fe_cases[i][1], var);
check_fe_equal(&x_fe, &fe_cases[i][0]);
}
}
for (i = 0; (size_t)i < sizeof(scalar_cases)/sizeof(scalar_cases[0]); ++i) {
for (var = 0; var <= 1; ++var) {
test_inverse_scalar(&x_scalar, &scalar_cases[i][0], var);
CHECK(secp256k1_scalar_eq(&x_scalar, &scalar_cases[i][1]));
test_inverse_scalar(&x_scalar, &scalar_cases[i][1], var);
CHECK(secp256k1_scalar_eq(&x_scalar, &scalar_cases[i][0]));
}
}
/* Test inputs 0..999 and their respective negations. */
for (i = 0; i < 1000; ++i) {
b32[31] = i & 0xff;
b32[30] = (i >> 8) & 0xff;
secp256k1_scalar_set_b32(&x_scalar, b32, NULL);
secp256k1_fe_set_b32(&x_fe, b32);
for (var = 0; var <= 1; ++var) {
test_inverse_scalar(NULL, &x_scalar, var);
test_inverse_field(NULL, &x_fe, var);
}
secp256k1_scalar_negate(&x_scalar, &x_scalar);
secp256k1_fe_negate(&x_fe, &x_fe, 1);
for (var = 0; var <= 1; ++var) {
test_inverse_scalar(NULL, &x_scalar, var);
test_inverse_field(NULL, &x_fe, var);
}
}
/* test 128*count random inputs; half with testrand256_test, half with testrand256 */
for (testrand = 0; testrand <= 1; ++testrand) {
for (i = 0; i < 64 * count; ++i) {
(testrand ? secp256k1_testrand256_test : secp256k1_testrand256)(b32);
secp256k1_scalar_set_b32(&x_scalar, b32, NULL);
secp256k1_fe_set_b32(&x_fe, b32);
for (var = 0; var <= 1; ++var) {
test_inverse_scalar(NULL, &x_scalar, var);
test_inverse_field(NULL, &x_fe, var);
}
}
}
}

/***** GROUP TESTS *****/

void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
Expand Down Expand Up @@ -6077,8 +6182,8 @@ int main(int argc, char **argv) {
run_rand_int();

run_ctz_tests();

run_modinv_tests();
run_inverse_tests();

run_sha256_tests();
run_hmac_sha256_tests();
Expand All @@ -6093,8 +6198,6 @@ int main(int argc, char **argv) {
run_scalar_tests();

/* field tests */
run_field_inv();
run_field_inv_var();
run_field_misc();
run_field_convert();
run_sqr();
Expand Down

0 comments on commit f66a53c

Please sign in to comment.