Skip to content

Commit 1147a52

Browse files
gmaxwelldeadalnix
authored andcommitted
Constant-time behaviour test using valgrind memtest.
Summary: * 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 * Run valgrind_ctime_test in travis This is a backport of secp256k1 [[bitcoin-core/secp256k1#708 | PR708]] Test Plan: make check Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Subscribers: Fabien Differential Revision: https://reviews.bitcoinabc.org/D5580
1 parent 3b7267b commit 1147a52

File tree

6 files changed

+118
-2
lines changed

6 files changed

+118
-2
lines changed

src/secp256k1/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ bench_internal
1010
tests
1111
exhaustive_tests
1212
gen_context
13+
valgrind_ctime_test
1314
*.exe
1415
*.so
1516
*.a

src/secp256k1/.travis.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ addons:
55
apt:
66
packages:
77
- gcc-multilib
8-
- libgmp-dev:i386
8+
- libc6-dbg:i386
99
- libgmp-dev
10+
- libgmp-dev:i386
11+
- libtool-bin
1012
- ninja-build
1113
- valgrind
1214
install:
@@ -38,6 +40,7 @@ env:
3840
- JNI=no
3941
- OPENSSL_TESTS=auto
4042
- MULTISET=no
43+
- CTIMETEST=yes
4144
jobs:
4245
- SCALAR=32bit RECOVERY=yes
4346
- SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes MULTISET=yes
@@ -51,7 +54,7 @@ env:
5154
- BIGNUM=no
5255
- BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes MULTISET=yes
5356
- BIGNUM=no STATICPRECOMPUTATION=no
54-
- AUTOTOOLS_TARGET=distcheck CMAKE_TARGET=install
57+
- AUTOTOOLS_TARGET=distcheck CMAKE_TARGET=install CTIMETEST=
5558
- AUTOTOOLS_EXTRA_FLAGS=CPPFLAGS=-DDETERMINISTIC CMAKE_EXTRA_FLAGS=-DCMAKE_C_FLAGS=-DDETERMINISTIC
5659
- AUTOTOOLS_EXTRA_FLAGS=CFLAGS=-O0 CMAKE_EXTRA_FLAGS=-DCMAKE_BUILD_TYPE=Debug
5760
- AUTOTOOLS_TARGET=check-java CMAKE_TARGET=check-secp256k1-java JNI=yes ECDH=yes EXPERIMENTAL=yes

src/secp256k1/Makefile.am

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ if USE_TESTS
103103
noinst_PROGRAMS += tests
104104
tests_SOURCES = src/tests.c
105105
tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
106+
if VALGRIND_ENABLED
107+
tests_CPPFLAGS += -DVALGRIND
108+
noinst_PROGRAMS += valgrind_ctime_test
109+
valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
110+
valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
111+
endif
106112
if !ENABLE_COVERAGE
107113
tests_CPPFLAGS += -DVERIFY
108114
endif

src/secp256k1/configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,7 @@ echo " scalar = $set_scalar"
612612
echo " ecmult window size = $set_ecmult_window"
613613
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
614614
echo
615+
echo " valgrind = $enable_valgrind"
615616
echo " CC = $CC"
616617
echo " CFLAGS = $CFLAGS"
617618
echo " CPPFLAGS = $CPPFLAGS"
Lines changed: 100 additions & 0 deletions
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+
}

src/secp256k1/travis/build_autotools.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,14 @@ pushd buildautotools
3838
print_logs() {
3939
cat tests.log || :
4040
cat exhaustive_tests.log || :
41+
cat valgrind_ctime_test.log || :
4142
}
4243
trap 'print_logs' ERR
4344

4445
make -j2 $AUTOTOOLS_TARGET
4546

47+
if [ -n "$CTIMETEST" ]; then
48+
libtool --mode=execute valgrind ./valgrind_ctime_test &> valgrind_ctime_test.log
49+
fi
50+
4651
popd

0 commit comments

Comments
 (0)