diff --git a/ConstantTime.xs b/ConstantTime.xs index 79ab132..227d230 100644 --- a/ConstantTime.xs +++ b/ConstantTime.xs @@ -4,12 +4,21 @@ -static int do_compare(unsigned char *a, unsigned char *b, size_t n) { +static int do_compare(unsigned char *a, size_t a_len, unsigned char *b, size_t b_len) { size_t i; - unsigned char r = 0; - - for (i = 0; i < n; i++) { - r |= *a++ ^ *b++; + unsigned char *s; + unsigned char r; + uintptr_t mask; + + /* Orchestrate a dummy compare which never matches and whose run-time does + * not stand out if a_len != b_len */ + r = (a_len != b_len); + /* Branching-less: s = (r) ? b : a */ + mask = (uintptr_t)0u - r; + s = (unsigned char *)(((uintptr_t)b & mask) | ((uintptr_t)a & ~mask)); + + for (i = 0; i < b_len; i++) { + r |= *s++ ^ *b++; } return r; @@ -41,11 +50,7 @@ equals(a, b) ap = SvPV(a, alen); bp = SvPV(b, blen); - if (alen == blen) { - r = !do_compare(ap, bp, alen); - } else { - r = 0; - } + r = !do_compare(ap, alen, bp, blen); } else if (SvOK(a) || SvOK(b)) { r = 0; } else { diff --git a/lib/String/Compare/ConstantTime.pm b/lib/String/Compare/ConstantTime.pm index 7aef0de..d3db545 100644 --- a/lib/String/Compare/ConstantTime.pm +++ b/lib/String/Compare/ConstantTime.pm @@ -51,7 +51,7 @@ An example with HMACs: This module provides one function, C (not exported by default). -You should pass this function two strings of the same length. Just like perl's C, it will return true if they are string-wise identical and false otherwise. However, comparing any two differing strings of the same length will take a fixed amount of time. If the lengths of the strings are different, C will return false right away. +You should pass this function two strings of the same length. Just like perl's C, it will return true if they are string-wise identical and false otherwise. However, comparing any two differing strings of the same length will take a fixed amount of time. If the lengths of the strings are different, C will spend time proportional to a length of the user-supplied string and return false anyway. B: This does byte-wise comparison of the underlying string storage, meaning that comparing strings with non-ASCII data with different states of the internal UTF-8 flag is not reliable. You should always encode your data to bytes before comparing. @@ -62,8 +62,6 @@ Some programs take different amounts of time to run depending on the input value Most routines that compare strings (like perl's C and C and C's C and C) start scanning from the start of the strings and terminate as soon as they determine the strings won't match. This is good for efficiency but bad because it opens a timing side-channel. If one of the strings being compared is a secret and the other is controlled by some untrusted party, it is sometimes possible for this untrusted party to learn the secret using a timing side-channel. -If the lengths of the strings are different, because C returns false right away the size of the secret string may be leaked (but not its contents). - =head1 HMAC