diff --git a/src/libsodium/include/sodium/utils.h b/src/libsodium/include/sodium/utils.h index 4384262f..d837fa42 100644 --- a/src/libsodium/include/sodium/utils.h +++ b/src/libsodium/include/sodium/utils.h @@ -19,7 +19,8 @@ extern "C" { SODIUM_EXPORT void sodium_memzero(void * const pnt, const size_t len); -/* WARNING: sodium_memcmp() must be used to verify if two secret keys +/* + * WARNING: sodium_memcmp() must be used to verify if two secret keys * are equal, in constant time. * It returns 0 if the keys are equal, and -1 if they differ. * This function is not designed for lexicographical comparisons. @@ -27,6 +28,19 @@ void sodium_memzero(void * const pnt, const size_t len); SODIUM_EXPORT int sodium_memcmp(const void * const b1_, const void * const b2_, size_t len); +/* + * sodium_compare() returns -1 if b1_ < b2_, 1 if b1_ > b2_ and 0 if b1_ == b2_ + * It is suitable for lexicographical comparisons, or to compare nonces + * and counters stored in little-endian format. + * However, it is slower than sodium_memcmp(). + */ +SODIUM_EXPORT +int sodium_compare(const unsigned char *b1_, const unsigned char *b2_, + size_t len); + +SODIUM_EXPORT +void sodium_increment(unsigned char *n, const size_t nlen); + SODIUM_EXPORT char *sodium_bin2hex(char * const hex, const size_t hex_maxlen, const unsigned char * const bin, const size_t bin_len); @@ -95,9 +109,6 @@ int sodium_mprotect_readonly(void *ptr); SODIUM_EXPORT int sodium_mprotect_readwrite(void *ptr); -SODIUM_EXPORT -void sodium_increment(unsigned char *n, const size_t nlen); - /* -------- */ int _sodium_alloc_init(void); diff --git a/src/libsodium/sodium/utils.c b/src/libsodium/sodium/utils.c index 5b6afa0d..ca0f6d77 100644 --- a/src/libsodium/sodium/utils.c +++ b/src/libsodium/sodium/utils.c @@ -116,6 +116,42 @@ sodium_memcmp(const void * const b1_, const void * const b2_, size_t len) return (int) ((1 & ((d - 1) >> 8)) - 1); } +#ifdef HAVE_WEAK_SYMBOLS +__attribute__((weak)) void +_sodium_dummy_symbol_to_prevent_compare_lto(const unsigned char *b1, + const unsigned char *b2, + const size_t len) +{ + (void) b1; + (void) b2; + (void) len; +} +#endif + +int +sodium_compare(const unsigned char *b1_, const unsigned char *b2_, size_t len) +{ +#ifdef HAVE_WEAK_SYMBOLS + const unsigned char *b1 = b1_; + const unsigned char *b2 = b2_; +#else + const volatile unsigned char *b1 = (const volatile unsigned char *) b1_; + const volatile unsigned char *b2 = (const volatile unsigned char *) b2_; +#endif + unsigned char gt = 0U; + unsigned char eq = 1U; + size_t i; + +#if HAVE_WEAK_SYMBOLS + _sodium_dummy_symbol_to_prevent_compare_lto(b1, b2, len); +#endif + for (i = (size_t) 0U; i < len; i++) { + gt |= ((b2[i] - b1[i]) >> 8) & eq; + eq &= ((b2[i] ^ b1[i]) - 1) >> 8; + } + return (int) (gt + gt + eq) - 1; +} + /* Derived from original code by CodesInChaos */ char * sodium_bin2hex(char * const hex, const size_t hex_maxlen, diff --git a/test/default/sodium_utils.c b/test/default/sodium_utils.c index 99f28a23..247e4fde 100644 --- a/test/default/sodium_utils.c +++ b/test/default/sodium_utils.c @@ -13,6 +13,7 @@ int main(void) const char *hex; const char *hex_end; size_t bin_len; + int i; randombytes_buf(buf1, sizeof buf1); memcpy(buf2, buf1, sizeof buf2); @@ -83,5 +84,18 @@ int main(void) sodium_increment(nonce, sizeof nonce); printf("%s\n", sodium_bin2hex(nonce_hex, sizeof nonce_hex, nonce, sizeof nonce)); + for (i = 0; i < 1000; i++) { + bin_len = (size_t) randombytes_uniform(sizeof buf1); + randombytes_buf(buf1, bin_len); + randombytes_buf(buf2, bin_len); + if (memcmp(buf1, buf2, bin_len) * + sodium_compare(buf1, buf2, bin_len) < 0) { + printf("sodium_compare() failure with length=%zu\n", bin_len); + } + memcpy(buf1, buf2, bin_len); + if (sodium_compare(buf1, buf2, bin_len)) { + printf("sodium_compare() equality failure with length=%zu\n", bin_len); + } + } return 0; }