Skip to content

Commit dab98fa

Browse files
committed
util: add function for constant-time memory comparison
Add a function to check if two buffers of the same length contain the same data, but do the comparison in a constant time with respect to the returned value to avoid creating a timing side channel, i.e. the time depends only on the buffer length, not on the content. Use the gnutls_memcmp() or nettle_memeql_sec() functions if available, otherwise use the same algorithm as nettle - bitwise ORing XORed data.
1 parent dd87381 commit dab98fa

File tree

4 files changed

+47
-1
lines changed

4 files changed

+47
-1
lines changed

configure

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
888888
HASH_OBJ="hash_nettle.o"
889889
HASH_LINK="$test_link"
890890
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
891+
add_def HAVE_NETTLE
891892
add_def FEAT_SECHASH
892893

893894
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
@@ -910,6 +911,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ];
910911
HASH_OBJ="hash_gnutls.o"
911912
HASH_LINK="$test_link"
912913
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
914+
add_def HAVE_GNUTLS
913915
add_def FEAT_SECHASH
914916

915917
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \

test/unit/util.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ handle_signal(int signal)
3232
void
3333
test_unit(void)
3434
{
35+
char buf[16], buf2[16], *s, *s2, *words[3];
3536
struct timespec ts, ts2, ts3, ts4;
36-
char buf[16], *s, *s2, *words[3];
3737
NTP_int64 ntp_ts, ntp_ts2, ntp_fuzz;
3838
NTP_int32 ntp32_ts;
3939
struct timeval tv;
@@ -797,5 +797,19 @@ test_unit(void)
797797
TEST_CHECK(strcmp(words[0], "a") == 0);
798798
TEST_CHECK(strcmp(words[1], "b") == 0);
799799

800+
for (i = 0; i < 1000; i++) {
801+
UTI_GetRandomBytes(buf, sizeof (buf));
802+
memcpy(buf2, buf, sizeof (buf));
803+
for (j = 0; j < sizeof (buf); j++)
804+
TEST_CHECK(UTI_IsMemoryEqual(buf, buf2, j));
805+
806+
for (j = 0; j < 8 * sizeof (buf); j++) {
807+
buf2[j / 8] ^= 1U << j % 8;
808+
TEST_CHECK(!UTI_IsMemoryEqual(buf, buf2, sizeof (buf)));
809+
buf2[j / 8] ^= 1U << j % 8;
810+
TEST_CHECK(UTI_IsMemoryEqual(buf, buf2, sizeof (buf)));
811+
}
812+
}
813+
800814
HSH_Finalise();
801815
}

util.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929

3030
#include "sysincl.h"
3131

32+
#if defined(HAVE_NETTLE)
33+
#include <nettle/memops.h>
34+
#elif defined(HAVE_GNUTLS)
35+
#include <gnutls/gnutls.h>
36+
#endif
37+
3238
#include "logging.h"
3339
#include "memory.h"
3440
#include "util.h"
@@ -1648,3 +1654,22 @@ UTI_SplitString(char *string, char **words, int max_saved_words)
16481654

16491655
return i;
16501656
}
1657+
1658+
/* ================================================== */
1659+
1660+
int
1661+
UTI_IsMemoryEqual(const void *s1, const void *s2, unsigned int len)
1662+
{
1663+
#if defined(HAVE_NETTLE)
1664+
return nettle_memeql_sec(s1, s2, len);
1665+
#elif defined(HAVE_GNUTLS)
1666+
return gnutls_memcmp(s1, s2, len) == 0;
1667+
#else
1668+
unsigned int i, x;
1669+
1670+
for (i = 0, x = 0; i < len; i++)
1671+
x |= ((const unsigned char *)s1)[i] ^ ((const unsigned char *)s2)[i];
1672+
1673+
return x == 0;
1674+
#endif
1675+
}

util.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ extern unsigned int UTI_HexToBytes(const char *hex, void *buf, unsigned int len)
257257
number of pointers to the words. */
258258
extern int UTI_SplitString(char *string, char **words, int max_saved_words);
259259

260+
/* Check if two buffers of the same length contain the same data, but do the
261+
comparison in constant time with respect to the returned value to avoid
262+
creating a timing side channel */
263+
extern int UTI_IsMemoryEqual(const void *s1, const void *s2, unsigned int len);
264+
260265
/* Macros to get maximum and minimum of two values */
261266
#ifdef MAX
262267
#undef MAX

0 commit comments

Comments
 (0)