Add sodium_compare()

A constant-time version of memcmp(), useful to compare nonces and counters
in little-endian format, that plays well with sodium_increment().

Unlike sodium_memcmp() which can compare anything for equality,
sodium_compare() is designed to compare things that are comparable, byte by
byte. Therefore, the prototype is slightly different: its arguments are
supposed to be `const unsigned char *`.

The names sodium_memcmp() and sodium_compare() are slightly confusing.
But we're not going to rename sodium_memcmp(), and I cannot think of a
better name for sodium_compare() than sodium_compare().
This commit is contained in:
Frank Denis
2015-10-17 21:25:30 +02:00
parent 2aef671fd9
commit d667efde68
3 changed files with 65 additions and 4 deletions
+15 -4
View File
@@ -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);
+36
View File
@@ -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,
+14
View File
@@ -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;
}