Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
251 lines (239 sloc) 9.63 KB
#include <stddef.h>
#include <inttypes.h>
/*
* "constant time" memcmp. Time taken depends on the buffer length, of
* course, but not on the content of the buffers.
*
* Just like the ordinary memcmp function, the return value is
* tri-state: <0, 0, or >0. However, applications that need a
* constant-time memory comparison function usually need only a
* two-state result, signalling only whether the inputs were identical
* or different, but not signalling which of the inputs was larger.
* This code could be made significantly faster and simpler if the
* requirement for a tri-state result were removed.
*
* In order to protect against adversaries who can observe timing,
* cache hits or misses, page faults, etc., and who can use such
* observations to learn something about the relationship between the
* contents of the two buffers, we have to perform exactly the same
* instructions and memory accesses regardless of the contents of the
* buffers. We can't stop as soon as we find a difference, we can't
* take different conditional branches depending on the data, and we
* can't use different pointers or array indexes depending on the data.
*
* Further reading:
*
* .Rs
* .%A Paul C. Kocher
* .%T Timing Attacks on Implementations of Diffie-Hellman, RSA, DSS, and Other Systems
* .%D 1996
* .%J CRYPTO 1996
* .%P 104-113
* .%U http://www.cryptography.com/timingattack/paper.html
* .%U http://www.webcitation.org/query?url=http%3A%2F%2Fwww.cryptography.com%2Ftimingattack%2Fpaper.html&date=2012-10-17
* .Re
*
* .Rs
* .%A D. Boneh
* .%A D. Brumley
* .%T Remote timing attacks are practical
* .%D August 2003
* .%J Proceedings of the 12th Usenix Security Symposium, 2003
* .%U https://crypto.stanford.edu/~dabo/abstracts/ssl-timing.html
* .%U http://www.webcitation.org/query?url=https%3A%2F%2Fcrypto.stanford.edu%2F%7Edabo%2Fabstracts%2Fssl-timing.html&date=2012-10-17
* .%U http://www.webcitation.org/query?url=http%3A%2F%2Fcrypto.stanford.edu%2F%7Edabo%2Fpubs%2Fpapers%2Fssl-timing.pdf&date=2012-10-17
* .Es
*
* .Rs
* .%A Coda Hale
* .%T A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)
* .%D 13 Aug 2009
* .%U http://codahale.com/a-lesson-in-timing-attacks/
* .%U http://www.webcitation.org/query?url=http%3A%2F%2Fcodahale.com%2Fa-lesson-in-timing-attacks%2F&date=2012-10-17
* .Re
*
*/
/*
* A note on portability:
*
* We assume that char is exactly 8 bits, the same as uint8_t, and that
* integer types with exactly 16 bits and exactly 32 bits exist. (If
* there is ever a need to change this, then the actual requirement is
* that we need a type that is at least two bits wider than char, and
* another type that is at least two bits wider than that, or we need to
* fake it somehow.)
*
* We do not assume any particular size for the plain "int" type, except
* that it is at least 16 bits, as is guaranteed by the C language
* standard.
*
* We do not assume that signed integer overflow is harmless. We
* ensure that signed integer overflow does not occur, so that
* implementation-defined overflow behaviour is not invoked.
*
* We rely on the C standard's guarantees regarding the wraparound
* behaviour of unsigned integer arithmetic, and on the analagous
* guarantees regarding conversions from signed types to narrower
* unsigned types.
*
* We do not assume that the platform uses two's complement arithmetic.
*/
/*
* How hard do we have to try to prevent unwanted compiler optimisations?
*
* Try compiling with "#define USE_VOLATILE_TEMPORARY 0", and examine
* the compiler output. If the only conditional tests in the entire
* function are to test whether len is zero, then all is well, but try
* again with different optimisation flags to be sure. If the compiler
* emitted code with conditional tests that do anything other than
* testing whether len is zero, then that's a problem, so try again with
* "#define USE_VOLATILE_TEMPORARY 1". If it's still bad, then you are
* out of luck.
*/
#define USE_VOLATILE_TEMPORARY 0
int consttime_memcmp(const void *b1, const void *b2, size_t len)
{
const uint8_t *c1, *c2;
uint16_t d, r, m;
#if USE_VOLATILE_TEMPORARY
volatile uint16_t v;
#else
uint16_t v;
#endif
c1 = b1;
c2 = b2;
r = 0;
while (len) {
/*
* Take the low 8 bits of r (in the range 0x00 to 0xff,
* or 0 to 255);
* As explained elsewhere, the low 8 bits of r will be zero
* if and only if all bytes compared so far were identical;
* Zero-extend to a 16-bit type (in the range 0x0000 to
* 0x00ff);
* Add 255, yielding a result in the range 255 to 510;
* Save that in a volatile variable to prevent
* the compiler from trying any shortcuts (the
* use of a volatile variable depends on "#ifdef
* USE_VOLATILE_TEMPORARY", and most compilers won't
* need it);
* Divide by 256 yielding a result of 1 if the original
* value of r was non-zero, or 0 if r was zero;
* Subtract 1, yielding 0 if r was non-zero, or -1 if r
* was zero;
* Convert to uint16_t, yielding 0x0000 if r was
* non-zero, or 0xffff if r was zero;
* Save in m.
*/
v = ((uint16_t)(uint8_t)r)+255;
m = v/256-1;
/*
* Get the values from *c1 and *c2 as uint8_t (each will
* be in the range 0 to 255, or 0x00 to 0xff);
* Convert them to signed int values (still in the
* range 0 to 255);
* Subtract them using signed arithmetic, yielding a
* result in the range -255 to +255;
* Convert to uint16_t, yielding a result in the range
* 0xff01 to 0xffff (for what was previously -255 to
* -1), or 0, or in the range 0x0001 to 0x00ff (for what
* was previously +1 to +255).
*/
d = (uint16_t)((int)*c1 - (int)*c2);
/*
* If the low 8 bits of r were previously 0, then m
* is now 0xffff, so (d & m) is the same as d, so we
* effectively copy d to r;
* Otherwise, if r was previously non-zero, then m is
* now 0, so (d & m) is zero, so leave r unchanged.
* Note that the low 8 bits of d will be zero if and
* only if d == 0, which happens when *c1 == *c2.
* The low 8 bits of r are thus zero if and only if the
* entirety of r is zero, which happens if and only if
* all bytes compared so far were equal. As soon as a
* non-zero value is stored in r, it remains unchanged
* for the remainder of the loop.
*/
r |= (d & m);
/*
* Increment pointers, decrement length, and loop.
*/
++c1;
++c2;
--len;
}
/*
* At this point, r is an unsigned value, which will be 0 if the
* final result should be zero, or in the range 0x0001 to 0x00ff
* (1 to 255) if the final result should be positive, or in the
* range 0xff01 to 0xffff (65281 to 65535) if the final result
* should be negative.
*
* We want to convert the unsigned values in the range 0xff01
* to 0xffff to signed values in the range -255 to -1, while
* converting the other unsigned values to equivalent signed
* values (0, or +1 to +255).
*
* On a machine with two's complement arithmetic, simply copying
* the underlying bits (with sign extension if int is wider than
* 16 bits) would do the job, so something like this might work:
*
* return (int16_t)r;
*
* However, that invokes implementation-defined behaviour,
* because values larger than 32767 can't fit in a signed 16-bit
* integer without overflow.
*
* To avoid any implementation-defined behaviour, we go through
* these contortions:
*
* a. Calculate ((uint32_t)r + 0x8000). The cast to uint32_t
* it to prevent problems on platforms where int is narrower
* than 32 bits. If int is a larger than 32-bits, then the
* usual arithmetic conversions cause this addition to be
* done in unsigned int arithmetic. If int is 32 bits
* or narrower, then this addition is done in uint32_t
* arithmetic. In either case, no overflow or wraparound
* occurs, and the result from this step has a value that
* will be one of 0x00008000 (32768), or in the range
* 0x00008001 to 0x000080ff (32769 to 33023), or in the range
* 0x00017f01 to 0x00017fff (98049 to 98303).
*
* b. Cast the result from (a) to uint16_t. This effectively
* discards the high bits of the result, in a way that is
* well defined by the C language. The result from this step
* will be of type uint16_t, and its value will be one of
* 0x8000 (32768), or in the range 0x8001 to 0x80ff (32769 to
* 33023), or in the range 0x7f01 to 0x7fff (32513 to
* 32767).
*
* c. Cast the result from (b) to int32_t. We use int32_t
* instead of int because we need a type that's strictly
* larger than 16 bits, and the C standard allows
* implementations where int is only 16 bits. The result
* from this step will be of type int32_t, and its value wll
* be one of 0x00008000 (32768), or in the range 0x00008001
* to 0x000080ff (32769 to 33023), or in the range 0x00007f01
* to 0x00007fff (32513 to 32767).
*
* d. Take the result from (c) and subtract 0x8000 (32768) using
* signed int32_t arithmetic. The result from this step will
* be of type int32_t and the value will be one of
* 0x00000000 (0), or in the range 0x00000001 to 0x000000ff
* (+1 to +255), or in the range 0xffffff01 to 0xffffffff
* (-255 to -1).
*
* e. Cast the result from (d) to int. This does nothing
* interesting, except to make explicit what would have been
* implicit in the return statement. The final result is an
* int in the range -255 to +255.
*
* Unfortunately, compilers don't seem to be good at figuring
* out that most of this can be optimised away by careful choice
* of register width and sign extension.
*
*/
return (/*e*/ int)(/*d*/
(/*c*/ int32_t)(/*b*/ uint16_t)(/*a*/ (uint32_t)r + 0x8000)
- 0x8000);
}