diff --git a/src/field.h b/src/field.h index 067115d0a7..4e5488cf4c 100644 --- a/src/field.h +++ b/src/field.h @@ -42,6 +42,10 @@ void static secp256k1_fe_start(void); /** Unload field element precomputation data. */ void static secp256k1_fe_stop(void); +#ifdef VERIFY +int static secp256k1_fe_verify(const secp256k1_fe_t * a); +#endif + /** Normalize a field element. */ void static secp256k1_fe_normalize(secp256k1_fe_t *r); diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h index cc9c5fe1d9..d848196038 100644 --- a/src/field_5x52_impl.h +++ b/src/field_5x52_impl.h @@ -33,6 +33,25 @@ void static secp256k1_fe_inner_start(void) {} void static secp256k1_fe_inner_stop(void) {} +#ifdef VERIFY +int static secp256k1_fe_verify(const secp256k1_fe_t * a) { + const uint64_t *d = a->n; + int m = a->magnitude, r = 1; + r &= (d[0] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[1] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[2] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[3] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[4] <= 0x0FFFFFFFFFFFFULL * m); + if (a->normalized) { + r &= (m == 1); + if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { + r &= (d[0] < 0xFFFFEFFFFFC2FULL); + } + } + return r; +} +#endif + void static secp256k1_fe_normalize(secp256k1_fe_t *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; diff --git a/src/tests.c b/src/tests.c index fca2bed0a4..90c1dc8255 100644 --- a/src/tests.c +++ b/src/tests.c @@ -312,6 +312,29 @@ void run_field_inv_all_var() { } } +void run_sqr() { + secp256k1_fe_t x, s; + +#if defined(USE_FIELD_5X52) + // Known issue with reduction part of sqr. For simplicity, we trigger the problem here + // with "negative" powers of 2, but the problem exists for large ranges of values. + { + secp256k1_fe_set_int(&x, 1); + secp256k1_fe_negate(&x, &x, 1); + + for (int i=1; i<=512; ++i) { + secp256k1_fe_mul_int(&x, 2); + secp256k1_fe_normalize(&x); + secp256k1_fe_sqr(&s, &x); + if (!secp256k1_fe_verify(&s)) { + printf("%4i: %016llx %016llx %016llx %016llx %016llx\n", + i, s.n[4], s.n[3], s.n[2], s.n[1], s.n[0]); + } + } + } +#endif +} + void test_sqrt(const secp256k1_fe_t *a, const secp256k1_fe_t *k) { secp256k1_fe_t r1, r2; int v = secp256k1_fe_sqrt(&r1, a); @@ -609,6 +632,7 @@ int main(int argc, char **argv) { run_field_inv_var(); run_field_inv_all(); run_field_inv_all_var(); + run_sqr(); run_sqrt(); // ecmult tests