From 930381228b726e72da39d3df232ef1caaab89fba Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 29 Oct 2024 18:33:00 +0100 Subject: [PATCH] memory: Add helper function to conditionally copy data in constant time --- src/libstrongswan/tests/suites/test_utils.c | 38 +++++++++++++++++++++ src/libstrongswan/utils/utils/memory.c | 21 ++++++++++++ src/libstrongswan/utils/utils/memory.h | 9 +++++ 3 files changed, 68 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c index b2118c5e4b..7cda4acecd 100644 --- a/src/libstrongswan/tests/suites/test_utils.c +++ b/src/libstrongswan/tests/suites/test_utils.c @@ -539,6 +539,40 @@ START_TEST(test_memeq_const) } END_TEST +/******************************************************************************* + * memcpy_cond + */ + +#define MEMCPY_COND_DEF "abcdef" +static struct { + char *a; + size_t n; + uint8_t cond; + char *res; +} memcpy_cond_data[] = { + {NULL, 0, 0, NULL}, + {NULL, 0, 1, NULL}, + {"foobar", 6, 0, MEMCPY_COND_DEF}, + {"foobar", 6, 1, "foobar"}, + {"foobar", 3, 0, MEMCPY_COND_DEF}, + {"foobar", 3, 1, "foodef"}, + {"\0bar", 4, 0, MEMCPY_COND_DEF}, + {"\0bar", 4, 1, "\0baref"}, + /* unpredictable results if condition is not 0 or 1 */ + {"foobar", 6, 5, "bkkfev"}, +}; + +START_TEST(test_memcpy_cond) +{ + char buf[BUF_LEN] = MEMCPY_COND_DEF; + + memcpy_cond(buf, memcpy_cond_data[_i].a, memcpy_cond_data[_i].n, + memcpy_cond_data[_i].cond); + ck_assert(memeq(buf, memcpy_cond_data[_i].res ?: MEMCPY_COND_DEF, + sizeof(MEMCPY_COND_DEF))); +} +END_TEST + /******************************************************************************* * memstr */ @@ -1346,6 +1380,10 @@ Suite *utils_suite_create() tcase_add_loop_test(tc, test_memeq_const, 0, countof(memeq_data)); suite_add_tcase(s, tc); + tc = tcase_create("memcpy_cond"); + tcase_add_loop_test(tc, test_memcpy_cond, 0, countof(memcpy_cond_data)); + suite_add_tcase(s, tc); + tc = tcase_create("memstr"); tcase_add_loop_test(tc, test_memstr, 0, countof(memstr_data)); suite_add_tcase(s, tc); diff --git a/src/libstrongswan/utils/utils/memory.c b/src/libstrongswan/utils/utils/memory.c index c1a4d6eecd..39b6bac6bd 100644 --- a/src/libstrongswan/utils/utils/memory.c +++ b/src/libstrongswan/utils/utils/memory.c @@ -90,6 +90,27 @@ bool memeq_const(const void *x, const void *y, size_t len) return !bad; } +/* + * Described in header + */ +void memcpy_cond(void *dst, const void *src, size_t n, uint8_t cond) +{ + uint8_t *a; + const uint8_t *b; + size_t i; + + a = (uint8_t*)dst; + b = (const uint8_t*)src; + + /* either 0xff or 0 */ + cond = -cond; + + for (i = 0; i < n; i++) + { + a[i] ^= cond & (a[i] ^ b[i]); + } +} + /** * Described in header. */ diff --git a/src/libstrongswan/utils/utils/memory.h b/src/libstrongswan/utils/utils/memory.h index 79c37fb2d3..c69b36759d 100644 --- a/src/libstrongswan/utils/utils/memory.h +++ b/src/libstrongswan/utils/utils/memory.h @@ -54,6 +54,15 @@ static inline void *memcpy_noop(void *dst, const void *src, size_t n) #endif #define memcpy(d,s,n) memcpy_noop(d,s,n) +/** + * Copies n bytes from src to dst if the given condition is 1, or leaves dst + * unchanged if it's 0 (but does write to it). + * + * Runs in constant time, so it's safe to conditionally copy data after a call + * to memeq_const() without any branching instructions. + */ +void memcpy_cond(void *dst, const void *src, size_t n, uint8_t cond); + /** * Calling memmove() with NULL pointers, even with n == 0, results in undefined * behavior according to the C standard. This version is guaranteed to not -- 2.47.3