#include "printf-format-fix.h"
#include "strfuncs.h"
#include "array.h"
+#include "hash.h"
+#include "hmac.h"
+#include "sha2.h"
#include <stdio.h>
#include <limits.h>
return ret == 0;
}
+bool str_equals_hash_timing_safe(const char *s1, const char *s2)
+{
+ struct hmac_context ctx;
+ unsigned char digest1[SHA256_RESULTLEN];
+ unsigned char digest2[SHA256_RESULTLEN];
+
+ /* Compare HMAC-SHA256 digests of the inputs rather than the inputs
+ themselves. The digest length is constant, so the subsequent
+ mem_equals_timing_safe() leaks no length information. The HMAC
+ times depend on the input lengths, but each side's length is either
+ a deployment constant (for the secret) or already known to the
+ attacker (for their own input), so neither leaks a useful signal.
+ hash_iv keys the HMAC to prevent precomputation. */
+ hmac_init(&ctx, (const unsigned char *)&hash_iv, sizeof(hash_iv),
+ &hash_method_sha256);
+ hmac_update(&ctx, s1, strlen(s1));
+ hmac_final(&ctx, digest1);
+
+ hmac_init(&ctx, (const unsigned char *)&hash_iv, sizeof(hash_iv),
+ &hash_method_sha256);
+ hmac_update(&ctx, s2, strlen(s2));
+ hmac_final(&ctx, digest2);
+
+ return mem_equals_timing_safe(digest1, digest2, sizeof(digest1));
+}
+
size_t
str_match(const char *p1, const char *p2)
{
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);
+/* Returns TRUE if the two strings are equal. Safe against timing attacks:
+ neither the contents nor the length of either string is leaked.
+ Implemented by HMAC-SHA256ing both inputs under a random per-process key
+ and comparing the fixed-length digests. */
+bool str_equals_hash_timing_safe(const char *s1, const char *s2);
size_t str_match(const char *p1, const char *p2) ATTR_PURE;
size_t str_match_icase(const char *p1, const char *p2) ATTR_PURE;
test_end();
}
+static void test_str_equals_hash_timing_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_hash_timing_safe()");
+ for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+ test_assert((strcmp(tests[i].a, tests[i].b) == 0) ==
+ str_equals_hash_timing_safe(tests[i].a, tests[i].b));
+ test_assert((strcmp(tests[i].a, tests[i].b) == 0) ==
+ str_equals_hash_timing_safe(tests[i].b, tests[i].a));
+ }
+ test_end();
+}
+
static void test_dec2str_buf(void)
{
const uintmax_t test_input[] = {
test_p_array_const_string_join();
test_mem_equals_timing_safe();
test_str_equals_timing_almost_safe();
+ test_str_equals_hash_timing_safe();
test_dec2str_buf();
test_str_match();
test_str_match_icase();