[Pkg-bitcoin-commits] [libsecp256k1] 03/27: Add exhaustive test for group functions on a low-order subgroup

Jonas Smedegaard dr at jones.dk
Tue Jan 10 21:47:16 UTC 2017


This is an automated email from the git hooks/post-receive script.

js pushed a commit to branch master
in repository libsecp256k1.

commit 20b8877be1554b7584e85ff577e4fca8e5e9a76c
Author: Andrew Poelstra <apoelstra at wpsoftware.net>
Date:   Thu Sep 17 18:54:52 2015 -0500

    Add exhaustive test for group functions on a low-order subgroup
    
    We observe that when changing the b-value in the elliptic curve formula
    `y^2 = x^3 + ax + b`, the group law is unchanged. Therefore our functions
    for secp256k1 will be correct if and only if they are correct when applied
    to the curve defined by `y^2 = x^3 + 4` defined over the same field. This
    curve has a point P of order 199.
    
    This commit adds a test which computes the subgroup generated by P and
    exhaustively checks that addition of every pair of points gives the correct
    result.
    
    Unfortunately we cannot test const-time scalar multiplication by the same
    mechanism. The reason is that these ecmult functions both compute a wNAF
    representation of the scalar, and this representation is tied to the order
    of the group.
    
    Testing with the incomplete version of gej_add_ge (found in 5de4c5dff^)
    shows that this detects the incompleteness when adding P - 106P, which
    is exactly what we expected since 106 is a cube root of 1 mod 199.
---
 .gitignore             |   1 +
 Makefile.am            |  13 +++-
 configure.ac           |   6 ++
 src/ecmult_impl.h      |  12 ++++
 src/field.h            |   5 ++
 src/group_impl.h       |  25 +++++++-
 src/tests_exhaustive.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 229 insertions(+), 4 deletions(-)

diff --git a/.gitignore b/.gitignore
index efb277d..87fea16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ bench_schnorr_verify
 bench_recover
 bench_internal
 tests
+exhaustive_tests
 gen_context
 *.exe
 *.so
diff --git a/Makefile.am b/Makefile.am
index 7461ff3..df2017a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -87,13 +87,23 @@ bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
 bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
 endif
 
+TESTS =
 if USE_TESTS
 noinst_PROGRAMS += tests
 tests_SOURCES = src/tests.c
 tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
 tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
 tests_LDFLAGS = -static
-TESTS = tests
+TESTS += tests
+endif
+
+if USE_EXHAUSTIVE_TESTS
+noinst_PROGRAMS += exhaustive_tests
+exhaustive_tests_SOURCES = src/tests_exhaustive.c
+exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src $(SECP_INCLUDES)
+exhaustive_tests_LDADD = $(SECP_LIBS)
+exhaustive_tests_LDFLAGS = -static
+TESTS += exhaustive_tests
 endif
 
 JAVAROOT=src/java
@@ -140,6 +150,7 @@ $(gen_context_BIN): $(gen_context_OBJECTS)
 
 $(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h
 $(tests_OBJECTS): src/ecmult_static_context.h
+$(exhaustive_tests_OBJECTS): src/ecmult_static_context.h
 $(bench_internal_OBJECTS): src/ecmult_static_context.h
 
 src/ecmult_static_context.h: $(gen_context_BIN)
diff --git a/configure.ac b/configure.ac
index 1933f71..ec50ffe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -104,6 +104,11 @@ AC_ARG_ENABLE(experimental,
     [use_experimental=$enableval],
     [use_experimental=no])
 
+AC_ARG_ENABLE(exhaustive_tests,
+    AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]),
+    [use_exhaustive_tests=$enableval],
+    [use_exhaustive_tests=yes])
+
 AC_ARG_ENABLE(endomorphism,
     AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
     [use_endomorphism=$enableval],
@@ -456,6 +461,7 @@ AC_SUBST(SECP_LIBS)
 AC_SUBST(SECP_TEST_LIBS)
 AC_SUBST(SECP_TEST_INCLUDES)
 AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
+AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
 AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
 AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
 AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h
index d1e83bd..8ed19b7 100644
--- a/src/ecmult_impl.h
+++ b/src/ecmult_impl.h
@@ -7,6 +7,8 @@
 #ifndef _SECP256K1_ECMULT_IMPL_H_
 #define _SECP256K1_ECMULT_IMPL_H_
 
+#include <string.h>
+
 #include "group.h"
 #include "scalar.h"
 #include "ecmult.h"
@@ -16,6 +18,15 @@
 /* optimal for 128-bit and 256-bit exponents. */
 #define WINDOW_A 5
 
+#if defined(EXHAUSTIVE_TEST_ORDER)
+#  if EXHAUSTIVE_TEST_ORDER > 128
+#    define WINDOW_G 8
+#  elif EXHAUSTIVE_TEST_ORDER > 8
+#    define WINDOW_G 4
+#  else
+#    define WINDOW_G 2
+#  endif
+#else
 /** larger numbers may result in slightly better performance, at the cost of
     exponentially larger precomputed tables. */
 #ifdef USE_ENDOMORPHISM
@@ -25,6 +36,7 @@
 /** One table for window size 16: 1.375 MiB. */
 #define WINDOW_G 16
 #endif
+#endif
 
 /** The number of entries a table with precomputed multiples needs to have. */
 #define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
diff --git a/src/field.h b/src/field.h
index 4daefc4..bbb1ee8 100644
--- a/src/field.h
+++ b/src/field.h
@@ -30,6 +30,8 @@
 #error "Please select field implementation"
 #endif
 
+#include "util.h"
+
 /** Normalize a field element. */
 static void secp256k1_fe_normalize(secp256k1_fe *r);
 
@@ -50,6 +52,9 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r);
 /** Set a field element equal to a small integer. Resulting field element is normalized. */
 static void secp256k1_fe_set_int(secp256k1_fe *r, int a);
 
+/** Sets a field element equal to zero, initializing all fields. */
+static void secp256k1_fe_clear(secp256k1_fe *a);
+
 /** Verify whether a field element is zero. Requires the input to be normalized. */
 static int secp256k1_fe_is_zero(const secp256k1_fe *a);
 
diff --git a/src/group_impl.h b/src/group_impl.h
index 18f2b37..97d5118 100644
--- a/src/group_impl.h
+++ b/src/group_impl.h
@@ -11,6 +11,18 @@
 #include "field.h"
 #include "group.h"
 
+#if defined(EXHAUSTIVE_TEST_ORDER)
+#  if EXHAUSTIVE_TEST_ORDER == 199
+const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
+    0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
+    0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
+    0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
+    0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
+);
+#  else
+#    error No known generator for the specified exhaustive test group order.
+#  endif
+#else
 /** Generator for secp256k1, value 'g' defined in
  *  "Standards for Efficient Cryptography" (SEC2) 2.7.1.
  */
@@ -20,6 +32,7 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
     0x483ADA77UL, 0x26A3C465UL, 0x5DA4FBFCUL, 0x0E1108A8UL,
     0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
 );
+#endif
 
 static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
     secp256k1_fe zi2;
@@ -145,9 +158,15 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
 
 static void secp256k1_gej_set_infinity(secp256k1_gej *r) {
     r->infinity = 1;
-    secp256k1_fe_set_int(&r->x, 0);
-    secp256k1_fe_set_int(&r->y, 0);
-    secp256k1_fe_set_int(&r->z, 0);
+    secp256k1_fe_clear(&r->x);
+    secp256k1_fe_clear(&r->y);
+    secp256k1_fe_clear(&r->z);
+}
+
+static void secp256k1_ge_set_infinity(secp256k1_ge *r) {
+    r->infinity = 1;
+    secp256k1_fe_clear(&r->x);
+    secp256k1_fe_clear(&r->y);
 }
 
 static void secp256k1_gej_clear(secp256k1_gej *r) {
diff --git a/src/tests_exhaustive.c b/src/tests_exhaustive.c
new file mode 100644
index 0000000..7a916e9
--- /dev/null
+++ b/src/tests_exhaustive.c
@@ -0,0 +1,171 @@
+/**********************************************************************
+ * Copyright (c) 2015 Andrew Poelstra                                 *
+ * Distributed under the MIT software license, see the accompanying   *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#if defined HAVE_CONFIG_H
+#include "libsecp256k1-config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <time.h>
+
+#ifndef EXHAUSTIVE_TEST_ORDER
+#define EXHAUSTIVE_TEST_ORDER 199
+#endif
+
+#include "include/secp256k1.h"
+#include "group.h"
+#include "secp256k1.c"
+#include "testrand_impl.h"
+
+/** stolen from tests.c */
+void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
+    CHECK(a->infinity == b->infinity);
+    if (a->infinity) {
+        return;
+    }
+    CHECK(secp256k1_fe_equal_var(&a->x, &b->x));
+    CHECK(secp256k1_fe_equal_var(&a->y, &b->y));
+}
+
+void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) {
+    secp256k1_fe z2s;
+    secp256k1_fe u1, u2, s1, s2;
+    CHECK(a->infinity == b->infinity);
+    if (a->infinity) {
+        return;
+    }
+    /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */
+    secp256k1_fe_sqr(&z2s, &b->z);
+    secp256k1_fe_mul(&u1, &a->x, &z2s);
+    u2 = b->x; secp256k1_fe_normalize_weak(&u2);
+    secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z);
+    s2 = b->y; secp256k1_fe_normalize_weak(&s2);
+    CHECK(secp256k1_fe_equal_var(&u1, &u2));
+    CHECK(secp256k1_fe_equal_var(&s1, &s2));
+}
+
+void random_fe(secp256k1_fe *x) {
+    unsigned char bin[32];
+    do {
+        secp256k1_rand256(bin);
+        if (secp256k1_fe_set_b32(x, bin)) {
+            return;
+        }
+    } while(1);
+}
+/** END stolen from tests.c */
+
+void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
+    int i, j;
+
+    /* Sanity-check (and check infinity functions) */
+    CHECK(secp256k1_ge_is_infinity(&group[0]));
+    CHECK(secp256k1_gej_is_infinity(&groupj[0]));
+    for (i = 1; i < order; i++) {
+        CHECK(!secp256k1_ge_is_infinity(&group[i]));
+        CHECK(!secp256k1_gej_is_infinity(&groupj[i]));
+    }
+
+    /* Check all addition formulae */
+    for (j = 0; j < order; j++) {
+        secp256k1_fe fe_inv;
+        secp256k1_fe_inv(&fe_inv, &groupj[j].z);
+        for (i = 0; i < order; i++) {
+            secp256k1_ge zless_gej;
+            secp256k1_gej tmp;
+            /* add_var */
+            secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL);
+            ge_equals_gej(&group[(i + j) % order], &tmp);
+            /* add_ge */
+            if (j > 0) {
+                secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]);
+                ge_equals_gej(&group[(i + j) % order], &tmp);
+            }
+            /* add_ge_var */
+            secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL);
+            ge_equals_gej(&group[(i + j) % order], &tmp);
+            /* add_zinv_var */
+            zless_gej.infinity = groupj[j].infinity;
+            zless_gej.x = groupj[j].x;
+            zless_gej.y = groupj[j].y;
+            secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv);
+            ge_equals_gej(&group[(i + j) % order], &tmp);
+        }
+    }
+
+    /* Check doubling */
+    for (i = 0; i < order; i++) {
+        secp256k1_gej tmp;
+        if (i > 0) {
+            secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL);
+            ge_equals_gej(&group[(2 * i) % order], &tmp);
+        }
+        secp256k1_gej_double_var(&tmp, &groupj[i], NULL);
+        ge_equals_gej(&group[(2 * i) % order], &tmp);
+    }
+
+    /* Check negation */
+    for (i = 1; i < order; i++) {
+        secp256k1_ge tmp;
+        secp256k1_gej tmpj;
+        secp256k1_ge_neg(&tmp, &group[i]);
+        ge_equals_ge(&group[order - i], &tmp);
+        secp256k1_gej_neg(&tmpj, &groupj[i]);
+        ge_equals_gej(&group[order - i], &tmpj);
+    }
+}
+
+void test_exhaustive_ecmult(secp256k1_context *ctx, secp256k1_ge *group, secp256k1_gej *groupj, int order) {
+    int i, j;
+    const int r_log = secp256k1_rand32() % order;  /* TODO be less biased */
+    for (j = 0; j < order; j++) {
+        for (i = 0; i < order; i++) {
+            secp256k1_gej tmp;
+            secp256k1_scalar na, ng;
+            secp256k1_scalar_set_int(&na, i);
+            secp256k1_scalar_set_int(&ng, j);
+
+            secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng);
+            ge_equals_gej(&group[(i * r_log + j) % order], &tmp);
+
+            /* TODO we cannot exhaustively test ecmult_const as it does a scalar
+             * negation for even numbers, and our code is not designed to handle
+             * such a small scalar modulus. */
+        }
+    }
+}
+
+int main(void) {
+    int i;
+    secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER];
+    secp256k1_ge group[EXHAUSTIVE_TEST_ORDER];
+
+    /* Build context */
+    secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+
+    /* TODO set z = 1, then do num_tests runs with random z values */
+
+    /* Generate the entire group */
+    secp256k1_ge_set_infinity(&group[0]);
+    secp256k1_gej_set_infinity(&groupj[0]);
+    for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
+        secp256k1_fe z;
+        random_fe(&z);
+
+        secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g);
+        secp256k1_ge_set_gej(&group[i], &groupj[i]);
+        secp256k1_gej_rescale(&groupj[i], &z);
+    }
+
+    /* Run the tests */
+    test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER);
+    test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER);
+
+    return 0;
+}
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-bitcoin/libsecp256k1.git



More information about the Pkg-bitcoin-commits mailing list