]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add str_equals_timing_almost_safe()
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 7 Apr 2020 16:16:10 +0000 (19:16 +0300)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Wed, 15 Apr 2020 10:57:57 +0000 (10:57 +0000)
src/lib/strfuncs.c
src/lib/strfuncs.h
src/lib/test-strfuncs.c

index cf98e127535761ecc29f0829fc8b54f379aa36c3..49fe81872fa8a383ab56d03563ad228ab65ecc60 100644 (file)
@@ -583,6 +583,21 @@ bool mem_equals_timing_safe(const void *p1, const void *p2, size_t size)
        return ret == 0;
 }
 
+bool str_equals_timing_almost_safe(const char *s1, const char *s2)
+{
+       size_t i;
+       int ret = 0;
+
+       for (i = 0; s1[i] != '\0' && s2[i] != '\0'; i++)
+               ret |= s1[i] ^ s2[i];
+       ret |= s1[i] ^ s2[i];
+
+       /* make sure the compiler optimizer doesn't try to break out of the
+          above loop early. */
+       timing_safety_unoptimization = ret;
+       return ret == 0;
+}
+
 size_t
 str_match(const char *p1, const char *p2)
 {
index bfd2bd17b3e1c6103946aba21061db7ce1f09e9d..0fef770ac854e9881178ec13d60097289f955f5a 100644 (file)
@@ -80,6 +80,11 @@ int i_strcasecmp_p(const char *const *p1, const char *const *p2) ATTR_PURE;
 /* Returns TRUE if the two memory areas are equal. This function is safe
    against timing attacks, so it compares all the bytes every time. */
 bool mem_equals_timing_safe(const void *p1, const void *p2, size_t size);
+/* Returns TRUE if the two strings are equal. Similar to
+   mem_equals_timing_safe() this function is safe against timing attacks when
+   the string lengths are the same. If not, the length of the secret string may
+   be leaked, but otherwise the contents won't be. */
+bool str_equals_timing_almost_safe(const char *s1, const char *s2);
 
 size_t str_match(const char *p1, const char *p2) ATTR_PURE;
 static inline ATTR_PURE bool str_begins(const char *haystack, const char *needle)
index a2ea3073768eab0c21bb0aa3b2bfb9f31420fe1f..5db27110379bbb59708a0f972a2025ac5c7512bb 100644 (file)
@@ -387,6 +387,32 @@ static void test_mem_equals_timing_safe(void)
        test_end();
 }
 
+static void test_str_equals_timing_almost_safe(void)
+{
+       const struct {
+               const char *a, *b;
+       } tests[] = {
+               { "", "" },
+               { "a", "a" },
+               { "b", "a" },
+               { "ab", "ab" },
+               { "ab", "ba" },
+               { "ab", "bc" },
+               { "a", "" },
+               { "a", "ab" },
+               { "a", "abc" },
+               { "ab", "abc" },
+       };
+       test_begin("str_equals_timing_almost_safe()");
+       for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+               test_assert((strcmp(tests[i].a, tests[i].b) == 0) ==
+                           str_equals_timing_almost_safe(tests[i].a, tests[i].b));
+               test_assert((strcmp(tests[i].a, tests[i].b) == 0) ==
+                           str_equals_timing_almost_safe(tests[i].b, tests[i].a));
+       }
+       test_end();
+}
+
 static void test_dec2str_buf(void)
 {
        const uintmax_t test_input[] = {
@@ -462,6 +488,7 @@ void test_strfuncs(void)
        test_t_strarray_join();
        test_p_array_const_string_join();
        test_mem_equals_timing_safe();
+       test_str_equals_timing_almost_safe();
        test_dec2str_buf();
        test_str_match();
 }