]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
memory: Add helper function to conditionally copy data in constant time
authorTobias Brunner <tobias@strongswan.org>
Tue, 29 Oct 2024 17:33:00 +0000 (18:33 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 22 Nov 2024 13:09:53 +0000 (14:09 +0100)
src/libstrongswan/tests/suites/test_utils.c
src/libstrongswan/utils/utils/memory.c
src/libstrongswan/utils/utils/memory.h

index b2118c5e4bdb21a8a3d2a3bb7711ab52b46974d0..7cda4acecd959ec278af124025c7dcd2fa1dddfc 100644 (file)
@@ -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);
index c1a4d6eecd12ae2e4910d8e9085a664b1b629768..39b6bac6bd76308464b40497924980ec174f73a0 100644 (file)
@@ -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.
  */
index 79c37fb2d3466c9d2c2ecca6a54f725cdb0d44c9..c69b36759d5f0d0593d8b85bc56b59d938cc9a2b 100644 (file)
@@ -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