diff --git a/ChangeLog b/ChangeLog index 136fc78f2c2604071a55063bd7159bf8ac28dccc..5cc379018947ea013feba536d1282f7a0fd7ce5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -49,6 +49,28 @@ * tools/sexp-conv-test: Likewise. * tools/pkcs1-conv-test: Likewise. +2023-08-05 Niels Möller <nisse@lysator.liu.se> + + * testsuite/testutils.c (mark_bytes_undefined) + (mark_bytes_defined): New functions. Update side-channel related + tests to use them. + (main): Check environment variable NETTLE_TEST_SIDE_CHANNEL. + (test_side_channel): New global variable. + + * testsuite/sc-valgrind.sh (with_valgrind): New file, new shell + utility function. + + * testsuite/sc-pkcs1-sec-decrypt-test: New test, for side channel + silence. + * testsuite/sc-memeql-test: Likewise. + * testsuite/sc-gcm-test: Likewise. + * testsuite/sc-cnd-memcpy-test: Likewise. + * testsuite/rsa-sec-decrypt-test: Likewise. + + * rsa-sec-decrypt.c (_rsa_sec_decrypt): New internal function, + without input range checks. + (rsa_sec_decrypt): Use it. + 2023-08-02 Niels Möller <nisse@lysator.liu.se> * configure.ac: Replace obsoleted macros, require autoconf-2.69, diff --git a/rsa-internal.h b/rsa-internal.h index f66a7df049b675ac471d345d56000a2180fc31f6..ed4ebe887397e0588b772bd982884fb61b810369 100644 --- a/rsa-internal.h +++ b/rsa-internal.h @@ -44,6 +44,7 @@ #define _rsa_sec_compute_root_itch _nettle_rsa_sec_compute_root_itch #define _rsa_sec_compute_root _nettle_rsa_sec_compute_root #define _rsa_sec_compute_root_tr _nettle_rsa_sec_compute_root_tr +#define _rsa_sec_decrypt _nettle_rsa_sec_decrypt /* Internal functions. */ int @@ -85,4 +86,13 @@ _rsa_sec_compute_root_tr(const struct rsa_public_key *pub, void *random_ctx, nettle_random_func *random, mp_limb_t *x, const mp_limb_t *m); +/* Variant without range check of the input, to ease testing for + side-channel silence. */ +int +_rsa_sec_decrypt (const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t length, uint8_t *message, + const mpz_t gibberish); + #endif /* NETTLE_RSA_INTERNAL_H_INCLUDED */ diff --git a/rsa-sec-decrypt.c b/rsa-sec-decrypt.c index 4c98958dd52863dfc2682dfa60532bd4980f44d8..e2f953e280f5ff3178876b142e073c4b43a309ea 100644 --- a/rsa-sec-decrypt.c +++ b/rsa-sec-decrypt.c @@ -44,21 +44,19 @@ #include "gmp-glue.h" +/* Variant without range check of the input, to ease testing for + side-channel silence. */ int -rsa_sec_decrypt(const struct rsa_public_key *pub, - const struct rsa_private_key *key, - void *random_ctx, nettle_random_func *random, - size_t length, uint8_t *message, - const mpz_t gibberish) +_rsa_sec_decrypt (const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t length, uint8_t *message, + const mpz_t gibberish) { TMP_GMP_DECL (m, mp_limb_t); TMP_GMP_DECL (em, uint8_t); int res; - /* First check that input is in range. */ - if (mpz_sgn (gibberish) < 0 || mpz_cmp (gibberish, pub->n) >= 0) - return 0; - TMP_GMP_ALLOC (m, mpz_size(pub->n)); TMP_GMP_ALLOC (em, key->size); @@ -78,3 +76,16 @@ rsa_sec_decrypt(const struct rsa_public_key *pub, return res; } +int +rsa_sec_decrypt (const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t length, uint8_t *message, + const mpz_t gibberish) +{ + /* First check that input is in range. */ + if (mpz_sgn (gibberish) < 0 || mpz_cmp (gibberish, pub->n) >= 0) + return 0; + + return _rsa_sec_decrypt (pub, key, random_ctx, random, length, message, gibberish); +} diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index 2aa1dd810d918649be28929b05048e65f02ea93a..ff491c4a34972207c709c3dc486564a5022bf1ba 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -66,7 +66,9 @@ TS_HOGWEED = $(TS_HOGWEED_SOURCES:.c=$(EXEEXT)) TS_C = $(TS_NETTLE) @IF_HOGWEED@ $(TS_HOGWEED) TS_CXX = @IF_CXX@ $(CXX_SOURCES:.cxx=$(EXEEXT)) TARGETS = $(TS_C) $(TS_CXX) -TS_SH = symbols-test +TS_SC = sc-cnd-memcpy-test sc-gcm-test sc-memeql-test \ + @IF_HOGWEED@ sc-pkcs1-sec-decrypt-test sc-rsa-sec-decrypt-test +TS_SH = $(TS_SC) symbols-test TS_ALL = $(TARGETS) $(TS_SH) @IF_DLOPEN_TEST@ dlopen-test$(EXEEXT) TS_FAT = $(patsubst %, %$(EXEEXT), aes-test cbc-test \ @@ -127,7 +129,7 @@ $(TARGETS) $(EXTRA_TARGETS): testutils.$(OBJEXT) ../nettle-internal.$(OBJEXT) \ # data. VALGRIND = valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes @IF_ASM@ --partial-loads-ok=yes -check: $(TS_ALL) +check: $(TS_ALL) $(TS_ALL:sc-%=%) TEST_SHLIB_DIR="$(TEST_SHLIB_DIR)" \ srcdir="$(srcdir)" \ EMULATOR="$(EMULATOR)" NM="$(NM)" EXEEXT="$(EXEEXT)" \ diff --git a/testsuite/cnd-memcpy-test.c b/testsuite/cnd-memcpy-test.c index 6e5db3413dfbe05127db79ce2748e6a6aa232563..466608d3b539c7294d1aed8f1879e5d4859da474 100644 --- a/testsuite/cnd-memcpy-test.c +++ b/testsuite/cnd-memcpy-test.c @@ -2,24 +2,19 @@ #include "knuth-lfib.h" #include "memops.h" -#if HAVE_VALGRIND_MEMCHECK_H -# include <valgrind/memcheck.h> static void cnd_memcpy_for_test(int cnd, void *dst, const void *src, size_t n) { /* Makes valgrind trigger on any branches depending on the input data. */ - VALGRIND_MAKE_MEM_UNDEFINED (dst, n); - VALGRIND_MAKE_MEM_UNDEFINED (src, n); - VALGRIND_MAKE_MEM_UNDEFINED (&cnd, sizeof(cnd)); + mark_bytes_undefined (n, dst); + mark_bytes_undefined (n, src); + mark_bytes_undefined (sizeof(cnd), &cnd); cnd_memcpy (cnd, dst, src, n); - VALGRIND_MAKE_MEM_DEFINED (src, n); - VALGRIND_MAKE_MEM_DEFINED (dst, n); + mark_bytes_defined (n, src); + mark_bytes_defined (n, dst); } -#else -#define cnd_memcpy_for_test cnd_memcpy -#endif #define MAX_SIZE 50 void diff --git a/testsuite/gcm-test.c b/testsuite/gcm-test.c index bc555d60819bff5ae078df9743e891e76115e9a3..023ff6f62f9ce589e6f06fb8ea32c55db53a99eb 100644 --- a/testsuite/gcm-test.c +++ b/testsuite/gcm-test.c @@ -6,13 +6,6 @@ #include "gcm.h" #include "ghash-internal.h" -#if HAVE_VALGRIND_MEMCHECK_H -# include <valgrind/memcheck.h> -#else -# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) -# define VALGRIND_MAKE_MEM_DEFINED(p, n) -#endif - static void test_gcm_hash (const struct tstring *msg, const struct tstring *ref) { @@ -49,19 +42,19 @@ test_ghash_internal (const struct tstring *key, struct gcm_key gcm_key; union nettle_block16 state; - /* Use VALGRIND_MAKE_MEM_DEFINED to mark inputs as "undefined", to - get valgrind to warn about any branches or memory accesses - depending on secret data. */ + /* Mark inputs as "undefined" to valgrind, to get warnings about any + branches or memory accesses depending on secret data. */ memcpy (state.b, key->data, GCM_BLOCK_SIZE); - VALGRIND_MAKE_MEM_UNDEFINED (&state, sizeof(state)); + mark_bytes_undefined (sizeof(state), &state); _ghash_set_key (&gcm_key, &state); memcpy (state.b, iv->data, GCM_BLOCK_SIZE); - VALGRIND_MAKE_MEM_UNDEFINED (&state, sizeof(state)); - VALGRIND_MAKE_MEM_UNDEFINED (message->data, message->length); + mark_bytes_undefined (sizeof(state), &state); + mark_bytes_undefined (message->length, message->data); _ghash_update (&gcm_key, &state, message->length / GCM_BLOCK_SIZE, message->data); - VALGRIND_MAKE_MEM_DEFINED (&state, sizeof(state)); - VALGRIND_MAKE_MEM_DEFINED (message->data, message->length); + mark_bytes_defined (sizeof(state), &state); + mark_bytes_defined (message->length, message->data); + if (!MEMEQ(GCM_BLOCK_SIZE, state.b, digest->data)) { fprintf (stderr, "gcm_hash (internal) failed\n"); diff --git a/testsuite/memeql-test.c b/testsuite/memeql-test.c index 356671d6018bb61ab135749d1d0ad45aefd3abd5..98cd8a0cd12a9c5c5e6550ff31b96835c4e1e8ae 100644 --- a/testsuite/memeql-test.c +++ b/testsuite/memeql-test.c @@ -2,8 +2,6 @@ #include "knuth-lfib.h" #include "memops.h" -#if HAVE_VALGRIND_MEMCHECK_H -# include <valgrind/memcheck.h> static int memeql_sec_for_test(const void *a, const void *b, size_t n) { @@ -11,16 +9,13 @@ memeql_sec_for_test(const void *a, const void *b, size_t n) /* Makes valgrind trigger on any branches depending on the input data. */ - VALGRIND_MAKE_MEM_UNDEFINED (a, n); - VALGRIND_MAKE_MEM_UNDEFINED (b, n); + mark_bytes_undefined (n, a); + mark_bytes_undefined (n, b); res = memeql_sec (a, b, n); - VALGRIND_MAKE_MEM_DEFINED (&res, sizeof(res)); + mark_bytes_defined (sizeof(res), &res); return res; } -#else -#define memeql_sec_for_test memeql_sec -#endif #define MAX_SIZE 50 void diff --git a/testsuite/pkcs1-sec-decrypt-test.c b/testsuite/pkcs1-sec-decrypt-test.c index c7fcdcb602c3fc5e0dc927b26f97f47fcf6624b8..28189382a4ef12b40f79cd1bd244c058ee990388 100644 --- a/testsuite/pkcs1-sec-decrypt-test.c +++ b/testsuite/pkcs1-sec-decrypt-test.c @@ -2,28 +2,23 @@ #include "pkcs1-internal.h" -#if HAVE_VALGRIND_MEMCHECK_H -# include <valgrind/memcheck.h> static int pkcs1_decrypt_for_test(size_t msg_len, uint8_t *msg, size_t pad_len, uint8_t *pad) { int ret; - VALGRIND_MAKE_MEM_UNDEFINED (msg, msg_len); - VALGRIND_MAKE_MEM_UNDEFINED (pad, pad_len); + mark_bytes_undefined (msg_len, msg); + mark_bytes_undefined (pad_len, pad); ret = _pkcs1_sec_decrypt (msg_len, msg, pad_len, pad); - VALGRIND_MAKE_MEM_DEFINED (msg, msg_len); - VALGRIND_MAKE_MEM_DEFINED (pad, pad_len); - VALGRIND_MAKE_MEM_DEFINED (&ret, sizeof (ret)); + mark_bytes_defined (msg_len, msg); + mark_bytes_defined (pad_len, pad); + mark_bytes_defined (sizeof (ret), &ret); return ret; } -#else -#define pkcs1_decrypt_for_test _pkcs1_sec_decrypt -#endif void test_main(void) diff --git a/testsuite/rsa-sec-decrypt-test.c b/testsuite/rsa-sec-decrypt-test.c index be7ab5fb57e49bbad6cb5a12b13bc12e33af2121..f257723bb5dfef31d1a6a6e83fd2068a2b8e31a4 100644 --- a/testsuite/rsa-sec-decrypt-test.c +++ b/testsuite/rsa-sec-decrypt-test.c @@ -1,17 +1,15 @@ #include "testutils.h" #include "rsa.h" +#include "rsa-internal.h" #include "knuth-lfib.h" -#if HAVE_VALGRIND_MEMCHECK_H -# include <valgrind/memcheck.h> +#define MARK_MPZ_LIMBS_UNDEFINED(x) \ + mark_bytes_undefined (mpz_size (x) * sizeof (mp_limb_t), mpz_limbs_read (x)) + +#define MARK_MPZ_LIMBS_DEFINED(x) \ + mark_bytes_defined (mpz_size (x) * sizeof (mp_limb_t), mpz_limbs_read (x)) -#define MARK_MPZ_LIMBS_UNDEFINED(parm) \ - VALGRIND_MAKE_MEM_UNDEFINED (mpz_limbs_read (parm), \ - mpz_size (parm) * sizeof (mp_limb_t)) -#define MARK_MPZ_LIMBS_DEFINED(parm) \ - VALGRIND_MAKE_MEM_DEFINED (mpz_limbs_read (parm), \ - mpz_size (parm) * sizeof (mp_limb_t)) static int rsa_decrypt_for_test(const struct rsa_public_key *pub, const struct rsa_private_key *key, @@ -20,6 +18,9 @@ rsa_decrypt_for_test(const struct rsa_public_key *pub, const mpz_t gibberish) { int ret; + if (!test_side_channel) + return rsa_sec_decrypt (pub, key, random_ctx, random, length, message, gibberish); + /* Makes valgrind trigger on any branches depending on the input data. Except that (i) we have to allow rsa_sec_compute_root_tr to check that p and q are odd, (ii) mpn_sec_div_r may leak @@ -27,20 +28,21 @@ rsa_decrypt_for_test(const struct rsa_public_key *pub, normalization check and table lookup in invert_limb, and (iii) mpn_sec_powm may leak information about the least significant bits of p and q, due to table lookup in binvert_limb. */ - VALGRIND_MAKE_MEM_UNDEFINED (message, length); + mark_bytes_undefined (length, message); MARK_MPZ_LIMBS_UNDEFINED(gibberish); MARK_MPZ_LIMBS_UNDEFINED(key->a); MARK_MPZ_LIMBS_UNDEFINED(key->b); MARK_MPZ_LIMBS_UNDEFINED(key->c); - VALGRIND_MAKE_MEM_UNDEFINED(mpz_limbs_read (key->p) + 1, - (mpz_size (key->p) - 3) * sizeof(mp_limb_t)); - VALGRIND_MAKE_MEM_UNDEFINED(mpz_limbs_read (key->q) + 1, - (mpz_size (key->q) - 3) * sizeof(mp_limb_t)); + mark_bytes_undefined ((mpz_size (key->p) - 3) * sizeof(mp_limb_t), + mpz_limbs_read (key->p) + 1); + mark_bytes_undefined((mpz_size (key->q) - 3) * sizeof(mp_limb_t), + mpz_limbs_read (key->q) + 1); - ret = rsa_sec_decrypt (pub, key, random_ctx, random, length, message, gibberish); + /* Call variant not checking that 0 <= gibberish < n. */ + ret = _rsa_sec_decrypt (pub, key, random_ctx, random, length, message, gibberish); - VALGRIND_MAKE_MEM_DEFINED (message, length); - VALGRIND_MAKE_MEM_DEFINED (&ret, sizeof(ret)); + mark_bytes_defined (length, message); + mark_bytes_defined (sizeof(ret), &ret); MARK_MPZ_LIMBS_DEFINED(gibberish); MARK_MPZ_LIMBS_DEFINED(key->a); MARK_MPZ_LIMBS_DEFINED(key->b); @@ -50,9 +52,6 @@ rsa_decrypt_for_test(const struct rsa_public_key *pub, return ret; } -#else -#define rsa_decrypt_for_test rsa_sec_decrypt -#endif #define PAYLOAD_SIZE 50 #define DECRYPTED_SIZE 256 diff --git a/testsuite/sc-cnd-memcpy-test b/testsuite/sc-cnd-memcpy-test new file mode 100755 index 0000000000000000000000000000000000000000..056bbdaab060392d2a5a168ec09981f62e1d6e2a --- /dev/null +++ b/testsuite/sc-cnd-memcpy-test @@ -0,0 +1,6 @@ +#! /bin/sh + +srcdir=`dirname $0` +. "${srcdir}/sc-valgrind.sh" + +with_valgrind ./cnd-memcpy-test diff --git a/testsuite/sc-gcm-test b/testsuite/sc-gcm-test new file mode 100755 index 0000000000000000000000000000000000000000..57e39511feadecf50e5570b6f91d63c00436452a --- /dev/null +++ b/testsuite/sc-gcm-test @@ -0,0 +1,6 @@ +#! /bin/sh + +srcdir=`dirname $0` +. "${srcdir}/sc-valgrind.sh" + +with_valgrind ./gcm-test diff --git a/testsuite/sc-memeql-test b/testsuite/sc-memeql-test new file mode 100755 index 0000000000000000000000000000000000000000..a6dfcbefa18fb5eef30a7ebdccfe7d77d74a0de7 --- /dev/null +++ b/testsuite/sc-memeql-test @@ -0,0 +1,6 @@ +#! /bin/sh + +srcdir=`dirname $0` +. "${srcdir}/sc-valgrind.sh" + +with_valgrind ./memeql-test diff --git a/testsuite/sc-pkcs1-sec-decrypt-test b/testsuite/sc-pkcs1-sec-decrypt-test new file mode 100755 index 0000000000000000000000000000000000000000..2e0ddc34ca1af4bdf069c70a2bd20a0c0d9523d0 --- /dev/null +++ b/testsuite/sc-pkcs1-sec-decrypt-test @@ -0,0 +1,6 @@ +#! /bin/sh + +srcdir=`dirname $0` +. "${srcdir}/sc-valgrind.sh" + +with_valgrind ./pkcs1-sec-decrypt-test diff --git a/testsuite/sc-rsa-sec-decrypt-test b/testsuite/sc-rsa-sec-decrypt-test new file mode 100755 index 0000000000000000000000000000000000000000..0453ce2525c44f619fe9aac6db74b50edc742d06 --- /dev/null +++ b/testsuite/sc-rsa-sec-decrypt-test @@ -0,0 +1,6 @@ +#! /bin/sh + +srcdir=`dirname $0` +. "${srcdir}/sc-valgrind.sh" + +with_valgrind ./rsa-sec-decrypt-test diff --git a/testsuite/sc-valgrind.sh b/testsuite/sc-valgrind.sh new file mode 100644 index 0000000000000000000000000000000000000000..39e2e941797232f0145e2ec6016a1d9612d7dbeb --- /dev/null +++ b/testsuite/sc-valgrind.sh @@ -0,0 +1,7 @@ +# To setup a test to check for branches or memory accesses depending on secret data, +# using valgrind. + +with_valgrind () { + type valgrind >/dev/null || exit 77 + NETTLE_TEST_SIDE_CHANNEL=1 valgrind -q --error-exitcode=1 "$@" +} diff --git a/testsuite/testutils.c b/testsuite/testutils.c index 3420ae9d20ae97131977201f8857a6f68c48b856..1a8d10a9f9f56e65c613c85d0da2fac0eefbaf70 100644 --- a/testsuite/testutils.c +++ b/testsuite/testutils.c @@ -15,6 +15,11 @@ #include <ctype.h> #include <sys/time.h> +#if HAVE_VALGRIND_MEMCHECK_H +# include <valgrind/memcheck.h> +# include <valgrind/valgrind.h> +#endif + void die(const char *format, ...) { @@ -119,6 +124,28 @@ print_hex(size_t length, const uint8_t *data) int verbose = 0; +#if HAVE_VALGRIND_MEMCHECK_H +int test_side_channel = 0; + +void +mark_bytes_undefined (size_t size, const void *p) +{ + if (test_side_channel) + VALGRIND_MAKE_MEM_UNDEFINED(p, size); +} +void +mark_bytes_defined (size_t size, const void *p) +{ + if (test_side_channel) + VALGRIND_MAKE_MEM_DEFINED(p, size); +} +#else +void +mark_bytes_undefined (size_t size, const void *p) {} +void +mark_bytes_defined (size_t size, const void *p) {} +#endif + int main(int argc, char **argv) { @@ -134,6 +161,15 @@ main(int argc, char **argv) } } + if (getenv("NETTLE_TEST_SIDE_CHANNEL")) + { +#if HAVE_VALGRIND_MEMCHECK_H + if (RUNNING_ON_VALGRIND) + test_side_channel = 1; + else +#endif + SKIP(); + } test_main(); tstring_clear(); diff --git a/testsuite/testutils.h b/testsuite/testutils.h index 687bcd7311b446130a837bbfd1f7ea5137633b96..97710fc93817bfebdbdd8c235dfb3a5c7778e0a8 100644 --- a/testsuite/testutils.h +++ b/testsuite/testutils.h @@ -73,11 +73,20 @@ tstring_print_hex(const struct tstring *s); void print_hex(size_t length, const uint8_t *data); +/* If side-channel tests are requested, attach valgrind annotations on + given memory area. */ +void +mark_bytes_undefined (size_t size, const void *p); + +void +mark_bytes_defined (size_t size, const void *p); + /* The main program */ void test_main(void); extern int verbose; +extern int test_side_channel; typedef void nettle_encrypt_message_func(void *ctx,