A naive implementation of memcmp()
goes something like this (from this answer):
int memcmp_test(const char *cs_in, const char *ct_in, size_t n)
{
size_t i;
const unsigned char * cs = (const unsigned char*) cs_in;
const unsigned char * ct = (const unsigned char*) ct_in;
for (i = 0; i < n; i++, cs++, ct++)
{
if (*cs < *ct)
{
return -1;
}
else if (*cs > *ct)
{
return 1;
}
}
return 0;
}
Here the blocks traversal is stopped once the first mismatching byte is found. This can be no good for cryptographic applications because it makes the execution time dependent on the blocks content and this could allow for timing attacks. So OpenSSL uses this (taken from here):
int CRYPTO_memcmp(const void *in_a, const void *in_b, size_t len)
{
size_t i;
const unsigned char *a = in_a;
const unsigned char *b = in_b;
unsigned char x = 0;
for (i = 0; i < len; i++)
x |= a[i] ^ b[i];
return x;
}
There're no break
s or return
s in the middle, so this code will have to traverse the entire length of blocks. At least this is the intent.
Now here's one usage example (from here):
static int des_ede3_unwrap(EVP_CIPHER_CTX *ctx,
unsigned char *out, const unsigned char *in, size_t inl)
{
unsigned char icv[8], iv[8], sha1tmp[SHA_DIGEST_LENGTH];
//whatever, unrelated then...
if (!CRYPTO_memcmp(sha1tmp, icv, 8))
rv = inl - 16;
//whatever, unrelated
}
Now with link-time code generation (Visual C++ LTCG) or link-time optimization (gcc LTO) the compiler is able to see both CRYPTO_memcmp()
implementation and the invocation site (even if they are in different translation units). It can see that the invocation site does not use the actual value, it just compares it to null. So it is free to transform CRYPTO_memcmp()
such that it return immediately once it find the first mismatching pair of bytes and the "secure" version of memcmp()
is no longer secure.
How can memcmp()
be implemented such that the a standard compliant compiler will not transform it into version that helps timing attacks?
#pragma
. It's obviously compiler dependent. – Stervolatile
counter would help? – Adlibvolatile
somewhere. That doesn't mean that it is impossible and a future version / different vendor might do it. Work with the compiler (using the tools [pragma...] it provides), not against it. The compiler is your friend who rewrites your code in a more efficient way. If you insist on your code being the way you want it, you have to tell him. – Sterint memcmp()
like function that returns <0, 0 >0 on less then, equal or greater or something different likebool memeq()
that returns true/false on match/mis-match? 2) Must data beconst
or can it be manipulated and returned to the same state on function exit? – Perimeterconst
and the result should be<0
,0
, or>0
as with usualmemcmp()
. – AdlibCRYPTO_memcmp()
, could not the variableunsigned char x
be madevolatile unsigned char x
to prevent the complier from transformingCRYPTO_memcmp()
? – Perimeterx
volatile - but that makes code about two times slower. The second one is to cast the pointers intovolatile unsigned char*
and access through them which is just as fast but is not fully Standard compliant (see this https://mcmap.net/q/477518/-is-it-possible-to-guarantee-code-doing-memory-writes-is-not-optimized-away-in-c/57428). – Adlib