Skip to content

Commit 3d23022

Browse files
committed
Constant-time behaviour test using valgrind memtest.
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
1 parent 96d8ccb commit 3d23022

File tree

4 files changed

+108
-0
lines changed

4 files changed

+108
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ bench_internal
99
tests
1010
exhaustive_tests
1111
gen_context
12+
valgrind_ctime_test
1213
*.exe
1314
*.so
1415
*.a

Makefile.am

+6
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ if USE_TESTS
9393
noinst_PROGRAMS += tests
9494
tests_SOURCES = src/tests.c
9595
tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
96+
if VALGRIND_ENABLED
97+
tests_CPPFLAGS += -DVALGRIND
98+
noinst_PROGRAMS += valgrind_ctime_test
99+
valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
100+
valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
101+
endif
96102
if !ENABLE_COVERAGE
97103
tests_CPPFLAGS += -DVERIFY
98104
endif

configure.ac

+1
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ echo " scalar = $set_scalar"
556556
echo " ecmult window size = $set_ecmult_window"
557557
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
558558
echo
559+
echo " valgrind = $enable_valgrind"
559560
echo " CC = $CC"
560561
echo " CFLAGS = $CFLAGS"
561562
echo " CPPFLAGS = $CPPFLAGS"

src/valgrind_ctime_test.c

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**********************************************************************
2+
* Copyright (c) 2020 Gregory Maxwell *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
5+
**********************************************************************/
6+
7+
#include <valgrind/memcheck.h>
8+
#include "include/secp256k1.h"
9+
#include "util.h"
10+
11+
#if ENABLE_MODULE_ECDH
12+
# include "include/secp256k1_ecdh.h"
13+
#endif
14+
15+
int main(void) {
16+
secp256k1_context* ctx;
17+
secp256k1_ecdsa_signature signature;
18+
secp256k1_pubkey pubkey;
19+
size_t siglen = 74;
20+
size_t outputlen = 33;
21+
int i;
22+
int ret;
23+
unsigned char msg[32];
24+
unsigned char key[32];
25+
unsigned char sig[74];
26+
unsigned char spubkey[33];
27+
28+
if (!RUNNING_ON_VALGRIND) {
29+
fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
30+
fprintf(stderr, "Usage: libtool --mode=execute valgrind ./valgrind_ctime_test\n");
31+
exit(1);
32+
}
33+
34+
/** In theory, testing with a single secret input should be sufficient:
35+
* If control flow depended on secrets the tool would generate an error.
36+
*/
37+
for (i = 0; i < 32; i++) {
38+
key[i] = i + 65;
39+
}
40+
for (i = 0; i < 32; i++) {
41+
msg[i] = i + 1;
42+
}
43+
44+
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_DECLASSIFY);
45+
46+
/* Test keygen. */
47+
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
48+
ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key);
49+
VALGRIND_MAKE_MEM_DEFINED(&pubkey, sizeof(secp256k1_pubkey));
50+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
51+
CHECK(ret);
52+
CHECK(secp256k1_ec_pubkey_serialize(ctx, spubkey, &outputlen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
53+
54+
/* Test signing. */
55+
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
56+
ret = secp256k1_ecdsa_sign(ctx, &signature, msg, key, NULL, NULL);
57+
VALGRIND_MAKE_MEM_DEFINED(&signature, sizeof(secp256k1_ecdsa_signature));
58+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
59+
CHECK(ret);
60+
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature));
61+
62+
#if ENABLE_MODULE_ECDH
63+
/* Test ECDH. */
64+
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
65+
ret = secp256k1_ecdh(ctx, msg, &pubkey, key, NULL, NULL);
66+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
67+
CHECK(ret == 1);
68+
#endif
69+
70+
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
71+
ret = secp256k1_ec_seckey_verify(ctx, key);
72+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
73+
CHECK(ret == 1);
74+
75+
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
76+
ret = secp256k1_ec_privkey_negate(ctx, key);
77+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
78+
CHECK(ret == 1);
79+
80+
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
81+
VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
82+
ret = secp256k1_ec_privkey_tweak_add(ctx, key, msg);
83+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
84+
CHECK(ret == 1);
85+
86+
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
87+
VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
88+
ret = secp256k1_ec_privkey_tweak_mul(ctx, key, msg);
89+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
90+
CHECK(ret == 1);
91+
92+
/* Test context randomisation. Do this last because it leaves the context tainted. */
93+
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
94+
ret = secp256k1_context_randomize(ctx, key);
95+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
96+
CHECK(ret);
97+
98+
secp256k1_context_destroy(ctx);
99+
return 0;
100+
}

0 commit comments

Comments
 (0)