]> Git Repo - secp256k1.git/commitdiff
Constant-time behaviour test using valgrind memtest.
authorGregory Maxwell <[email protected]>
Wed, 8 Jan 2020 11:56:15 +0000 (11:56 +0000)
committerGregory Maxwell <[email protected]>
Mon, 24 Feb 2020 18:59:30 +0000 (18:59 +0000)
Valgrind does bit-level tracking of the "uninitialized" status of memory,
 property tracks memory which is tainted by any uninitialized memory, and
 warns if any branch or array access depends on an uninitialized bit.

That is exactly the verification we need on secret data to test for
 constant-time behaviour. All we need to do is tell valgrind our
 secret key is actually uninitialized memory.

This adds a valgrind_ctime_test which is compiled if valgrind is installed:

Run it with libtool --mode=execute:
$ libtool --mode=execute valgrind ./valgrind_ctime_test

.gitignore
Makefile.am
configure.ac
src/valgrind_ctime_test.c [new file with mode: 0644]

index 55d325aeefa9c084a06e78808ddde841d702538c..cb4331aa90a2a212fc1473dc8fea50354554fd2a 100644 (file)
@@ -9,6 +9,7 @@ bench_internal
 tests
 exhaustive_tests
 gen_context
+valgrind_ctime_test
 *.exe
 *.so
 *.a
index 79dcf6e2f7c87b9145fa58f4017f12d611587207..e73b1baf3815e3f8777fa94364ff50d2c6824628 100644 (file)
@@ -93,6 +93,12 @@ if USE_TESTS
 noinst_PROGRAMS += tests
 tests_SOURCES = src/tests.c
 tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
+if VALGRIND_ENABLED
+tests_CPPFLAGS += -DVALGRIND
+noinst_PROGRAMS += valgrind_ctime_test
+valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
+valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
+endif
 if !ENABLE_COVERAGE
 tests_CPPFLAGS += -DVERIFY
 endif
index a2023aba436119f07f7ead15ba25734653a01ba3..e4929d6604c8cb9abdf3a0defae4c7cdda4a5558 100644 (file)
@@ -556,6 +556,7 @@ echo "  scalar                  = $set_scalar"
 echo "  ecmult window size      = $set_ecmult_window"
 echo "  ecmult gen prec. bits   = $set_ecmult_gen_precision"
 echo
+echo "  valgrind                = $enable_valgrind"
 echo "  CC                      = $CC"
 echo "  CFLAGS                  = $CFLAGS"
 echo "  CPPFLAGS                = $CPPFLAGS"
diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c
new file mode 100644 (file)
index 0000000..04c06d4
--- /dev/null
@@ -0,0 +1,100 @@
+/**********************************************************************
+ * Copyright (c) 2020 Gregory Maxwell                                 *
+ * Distributed under the MIT software license, see the accompanying   *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#include <valgrind/memcheck.h>
+#include "include/secp256k1.h"
+#include "util.h"
+
+#if ENABLE_MODULE_ECDH
+# include "include/secp256k1_ecdh.h"
+#endif
+
+int main(void) {
+    secp256k1_context* ctx;
+    secp256k1_ecdsa_signature signature;
+    secp256k1_pubkey pubkey;
+    size_t siglen = 74;
+    size_t outputlen = 33;
+    int i;
+    int ret;
+    unsigned char msg[32];
+    unsigned char key[32];
+    unsigned char sig[74];
+    unsigned char spubkey[33];
+
+    if (!RUNNING_ON_VALGRIND) {
+        fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
+        fprintf(stderr, "Usage: libtool --mode=execute valgrind ./valgrind_ctime_test\n");
+        exit(1);
+    }
+
+    /** In theory, testing with a single secret input should be sufficient:
+     *  If control flow depended on secrets the tool would generate an error.
+     */
+    for (i = 0; i < 32; i++) {
+        key[i] = i + 65;
+    }
+    for (i = 0; i < 32; i++) {
+        msg[i] = i + 1;
+    }
+
+    ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_DECLASSIFY);
+
+    /* Test keygen. */
+    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key);
+    VALGRIND_MAKE_MEM_DEFINED(&pubkey, sizeof(secp256k1_pubkey));
+    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    CHECK(ret);
+    CHECK(secp256k1_ec_pubkey_serialize(ctx, spubkey, &outputlen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
+
+    /* Test signing. */
+    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    ret = secp256k1_ecdsa_sign(ctx, &signature, msg, key, NULL, NULL);
+    VALGRIND_MAKE_MEM_DEFINED(&signature, sizeof(secp256k1_ecdsa_signature));
+    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    CHECK(ret);
+    CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature));
+
+#if ENABLE_MODULE_ECDH
+    /* Test ECDH. */
+    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    ret = secp256k1_ecdh(ctx, msg, &pubkey, key, NULL, NULL);
+    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    CHECK(ret == 1);
+#endif
+
+    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    ret = secp256k1_ec_seckey_verify(ctx, key);
+    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    CHECK(ret == 1);
+
+    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    ret = secp256k1_ec_privkey_negate(ctx, key);
+    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    CHECK(ret == 1);
+
+    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
+    ret = secp256k1_ec_privkey_tweak_add(ctx, key, msg);
+    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    CHECK(ret == 1);
+
+    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
+    ret = secp256k1_ec_privkey_tweak_mul(ctx, key, msg);
+    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    CHECK(ret == 1);
+
+    /* Test context randomisation. Do this last because it leaves the context tainted. */
+    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    ret = secp256k1_context_randomize(ctx, key);
+    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    CHECK(ret);
+
+    secp256k1_context_destroy(ctx);
+    return 0;
+}
This page took 0.0314140000000001 seconds and 4 git commands to generate.