changeset 21951:3ef988b95d96

lib: Add mem_equals_timing_safe()
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Sun, 09 Apr 2017 00:49:37 +0300
parents 86b0d2fb44d3
children e19e015f7449
files src/lib/strfuncs.c src/lib/strfuncs.h src/lib/test-strfuncs.c
diffstat 3 files changed, 45 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib/strfuncs.c	Mon Apr 10 20:53:29 2017 +0300
+++ b/src/lib/strfuncs.c	Sun Apr 09 00:49:37 2017 +0300
@@ -15,6 +15,8 @@
 
 const unsigned char uchar_nul = '\0';
 
+volatile int timing_safety_unoptimization;
+
 int i_snprintf(char *dest, size_t max_chars, const char *format, ...)
 {
 	va_list args;
@@ -474,6 +476,21 @@
 	return strcasecmp(*p1, *p2);
 }
 
+bool mem_equals_timing_safe(const void *p1, const void *p2, size_t size)
+{
+	const unsigned char *s1 = p1, *s2 = p2;
+	size_t i;
+	int ret = 0;
+
+	for (i = 0; i < size; 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;
+}
+
 static char **
 split_str(pool_t pool, const char *data, const char *separators, int spaces)
 {
--- a/src/lib/strfuncs.h	Mon Apr 10 20:53:29 2017 +0300
+++ b/src/lib/strfuncs.h	Sun Apr 09 00:49:37 2017 +0300
@@ -66,6 +66,9 @@
 int i_memcasecmp(const void *p1, const void *p2, size_t size) ATTR_PURE;
 int i_strcmp_p(const char *const *p1, const char *const *p2) ATTR_PURE;
 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);
 
 static inline char *i_strchr_to_next(const char *str, char chr)
 {
--- a/src/lib/test-strfuncs.c	Mon Apr 10 20:53:29 2017 +0300
+++ b/src/lib/test-strfuncs.c	Sun Apr 09 00:49:37 2017 +0300
@@ -236,6 +236,30 @@
 	test_end();
 }
 
+static void test_mem_equals_timing_safe(void)
+{
+	const struct {
+		const char *a, *b;
+	} tests[] = {
+		{ "", "" },
+		{ "a", "a" },
+		{ "b", "a" },
+		{ "ab", "ab" },
+		{ "ab", "ba" },
+		{ "ab", "bc" },
+	};
+	test_begin("mem_equals_timing_safe()");
+	for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+		size_t len = strlen(tests[i].a);
+		i_assert(len == strlen(tests[i].b));
+		test_assert((memcmp(tests[i].a, tests[i].b, len) == 0) ==
+			    mem_equals_timing_safe(tests[i].a, tests[i].b, len));
+		test_assert((memcmp(tests[i].a, tests[i].b, len) == 0) ==
+			    mem_equals_timing_safe(tests[i].b, tests[i].a, len));
+	}
+	test_end();
+}
+
 void test_strfuncs(void)
 {
 	test_p_strarray_dup();
@@ -248,4 +272,5 @@
 	test_t_str_rtrim();
 	test_t_strarray_join();
 	test_p_array_const_string_join();
+	test_mem_equals_timing_safe();
 }