mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-30 15:43:32 +01:00
Add a data-independent variant of memcmp and a d-i memeq function.
The tor_memcmp code is by Robert Ransom, and the tor_memeq code is by me. Both incorporate some ideas from DJB's stuff.
This commit is contained in:
parent
8a36f21251
commit
4b19730c82
7
changes/bug3122_memcmp
Normal file
7
changes/bug3122_memcmp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
o Security fixes
|
||||||
|
- Replace all potentially sensitive memory comparison operations
|
||||||
|
with versions whose runtime does not depend on the data being
|
||||||
|
compared. This will help resist a class of attacks where an
|
||||||
|
adversary can use variations in timing information to learn
|
||||||
|
sensitive data. Fix for one case of bug 3122. (Safe memcmp
|
||||||
|
implementation by Robert Ransom based partially on code by DJB.)
|
18
configure.in
18
configure.in
@ -626,6 +626,24 @@ if test "$tor_cv_twos_complement" != no ; then
|
|||||||
[Define to 1 iff we represent negative integers with two's complement])
|
[Define to 1 iff we represent negative integers with two's complement])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# What does shifting a negative value do?
|
||||||
|
AC_CACHE_CHECK([whether right-shift on negative values does sign-extension], tor_cv_sign_extend,
|
||||||
|
[AC_RUN_IFELSE([AC_LANG_SOURCE(
|
||||||
|
[[int main () { int okay = (-60 >> 8) == -1; return okay ? 0 : 1; }]])],
|
||||||
|
[tor_cv_sign_extend=yes],
|
||||||
|
[tor_cv_sign_extend=no],
|
||||||
|
[tor_cv_sign_extend=cross])])
|
||||||
|
|
||||||
|
if test "$tor_cv_sign_extend" = cross ; then
|
||||||
|
# Cross-compiling; let's hope that the target isn't raving mad.
|
||||||
|
AC_MSG_NOTICE([Cross-compiling: we'll assume that right-shifting negative integers causes sign-extension])
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$tor_cv_sign_extend" != no ; then
|
||||||
|
AC_DEFINE([RSHIFT_DOES_SIGN_EXTEND], 1,
|
||||||
|
[Define to 1 iff right-shifting a negative value performs sign-extension])
|
||||||
|
fi
|
||||||
|
|
||||||
# Whether we should use the dmalloc memory allocation debugging library.
|
# Whether we should use the dmalloc memory allocation debugging library.
|
||||||
AC_MSG_CHECKING(whether to use dmalloc (debug memory allocation library))
|
AC_MSG_CHECKING(whether to use dmalloc (debug memory allocation library))
|
||||||
AC_ARG_WITH(dmalloc,
|
AC_ARG_WITH(dmalloc,
|
||||||
|
@ -10,7 +10,7 @@ libor_extra_source=
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
libor_a_SOURCES = address.c log.c util.c compat.c container.c mempool.c \
|
libor_a_SOURCES = address.c log.c util.c compat.c container.c mempool.c \
|
||||||
memarea.c $(libor_extra_source)
|
memarea.c di_ops.c $(libor_extra_source)
|
||||||
libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
|
libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
|
||||||
|
|
||||||
noinst_HEADERS = address.h log.h crypto.h test.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h ciphers.inc
|
noinst_HEADERS = address.h log.h crypto.h test.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h di_ops.h ciphers.inc
|
||||||
|
133
src/common/di_ops.c
Normal file
133
src/common/di_ops.c
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/* Copyright (c) 2011, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file di_ops.c
|
||||||
|
* \brief Functions for data-independent operations
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "orconfig.h"
|
||||||
|
#include "di_ops.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timing-safe version of memcmp. As memcmp, compare the <b>sz</b> bytes
|
||||||
|
* at <b>a</b> with the <b>sz</b> bytes at <b>, and returns less than 0 if the
|
||||||
|
* bytes at <b>a</b> lexically precede those at <b>b</b>, 0 if the byte ranges
|
||||||
|
* are equal, and greater than zero if the bytes at <b>a</b> lexically follow
|
||||||
|
* those at <b>.
|
||||||
|
*
|
||||||
|
* This implementation differs from memcmp in that its timing behavior is not
|
||||||
|
* data-dependent: it should return in the same amount of time regardless of
|
||||||
|
* the contents of <b>a</b> and <b>b</b>.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
tor_memcmp(const void *a, const void *b, size_t len)
|
||||||
|
{
|
||||||
|
const uint8_t *x = a;
|
||||||
|
const uint8_t *y = b;
|
||||||
|
size_t i = len;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
/* This loop goes from the end of the arrays to the start. At the
|
||||||
|
* start of every iteration, before we decrement i, we have set
|
||||||
|
* "retval" equal to the result of memcmp(a+i,b+i,len-i). During the
|
||||||
|
* loop, we update retval by leaving it unchanged if x[i]==y[i] and
|
||||||
|
* setting it to x[i]-y[i] if x[i]!= y[i].
|
||||||
|
*
|
||||||
|
* The following assumes we are on a system with two's-complement
|
||||||
|
* arithmetic. We check for this at configure-time with the check
|
||||||
|
* that sets USING_TWOS_COMPLEMENT. If we aren't two's complement, then
|
||||||
|
* torint.h will stop compilation with an error.
|
||||||
|
*/
|
||||||
|
while (i--) {
|
||||||
|
int v1 = x[i];
|
||||||
|
int v2 = y[i];
|
||||||
|
int equal_p = v1 ^ v2;
|
||||||
|
|
||||||
|
/* The following sets bits 8 and above of equal_p to 'equal_p ==
|
||||||
|
* 0', and thus to v1 == v2. (To see this, note that if v1 ==
|
||||||
|
* v2, then v1^v2 == equal_p == 0, so equal_p-1 == -1, which is the
|
||||||
|
* same as ~0 on a two's-complement machine. Then note that if
|
||||||
|
* v1 != v2, then 0 < v1 ^ v2 < 256, so 0 <= equal_p - 1 < 255.)
|
||||||
|
*/
|
||||||
|
--equal_p;
|
||||||
|
|
||||||
|
equal_p >>= 8;
|
||||||
|
/* Thanks to (sign-preserving) arithmetic shift, equal_p is now
|
||||||
|
* equal to -(v1 == v2), which is exactly what we need below.
|
||||||
|
* (Since we're assuming two's-complement arithmetic, -1 is the
|
||||||
|
* same as ~0 (all bits set).)
|
||||||
|
*
|
||||||
|
* (The result of an arithmetic shift on a negative value is
|
||||||
|
* actually implementation-defined in standard C. So how do we
|
||||||
|
* get away with assuming it? Easy. We check.) */
|
||||||
|
#if ((-60 >> 8) != -1)
|
||||||
|
#error "According to cpp, right-shift doesn't perform sign-extension."
|
||||||
|
#endif
|
||||||
|
#ifndef RSHIFT_DOES_SIGN_EXTEND
|
||||||
|
#error "According to configure, right-shift doesn't perform sign-extension."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If v1 == v2, equal_p is ~0, so this will leave retval
|
||||||
|
* unchanged; otherwise, equal_p is 0, so this will zero it. */
|
||||||
|
retval &= equal_p;
|
||||||
|
|
||||||
|
/* If v1 == v2, then this adds 0, and leaves retval unchanged.
|
||||||
|
* Otherwise, we just zeroed retval, so this sets it to v1 - v2. */
|
||||||
|
retval += (v1 - v2);
|
||||||
|
|
||||||
|
/* There. Now retval is equal to its previous value if v1 == v2, and
|
||||||
|
* equal to v1 - v2 if v1 != v2. */
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timing-safe memory comparison. Return true if the <b>sz</b> bytes at
|
||||||
|
* <b>a</b> are the same as the <b>sz</b> bytes at <b>, and 0 otherwise.
|
||||||
|
*
|
||||||
|
* This implementation differs from !memcmp(a,b,sz) in that its timing
|
||||||
|
* behavior is not data-dependent: it should return in the same amount of time
|
||||||
|
* regardless of the contents of <b>a</b> and <b>b</b>. It differs from
|
||||||
|
* !tor_memcmp(a,b,sz) by being faster.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
tor_memeq(const void *a, const void *b, size_t sz)
|
||||||
|
{
|
||||||
|
/* Treat a and b as byte ranges. */
|
||||||
|
const uint8_t *ba = a, *bb = b;
|
||||||
|
uint32_t any_difference = 0;
|
||||||
|
while (sz--) {
|
||||||
|
/* Set byte_diff to all of those bits that are different in *ba and *bb,
|
||||||
|
* and advance both ba and bb. */
|
||||||
|
const uint8_t byte_diff = *ba++ ^ *bb++;
|
||||||
|
|
||||||
|
/* Set bits in any_difference if they are set in byte_diff. */
|
||||||
|
any_difference |= byte_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now any_difference is 0 if there are no bits different between
|
||||||
|
* a and b, and is nonzero if there are bits different between a
|
||||||
|
* and b. Now for paranoia's sake, let's convert it to 0 or 1.
|
||||||
|
*
|
||||||
|
* (If we say "!any_difference", the compiler might get smart enough
|
||||||
|
* to optimize-out our data-independence stuff above.)
|
||||||
|
*
|
||||||
|
* To unpack:
|
||||||
|
*
|
||||||
|
* If any_difference == 0:
|
||||||
|
* any_difference - 1 == ~0
|
||||||
|
* (any_difference - 1) >> 8 == 0x00ffffff
|
||||||
|
* 1 & ((any_difference - 1) >> 8) == 1
|
||||||
|
*
|
||||||
|
* If any_difference != 0:
|
||||||
|
* 0 < any_difference < 256, so
|
||||||
|
* 0 < any_difference - 1 < 255
|
||||||
|
* (any_difference - 1) >> 8 == 0
|
||||||
|
* 1 & ((any_difference - 1) >> 8) == 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 1 & ((any_difference - 1) >> 8);
|
||||||
|
}
|
||||||
|
|
28
src/common/di_ops.h
Normal file
28
src/common/di_ops.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/* Copyright (c) 2003-2004, Roger Dingledine
|
||||||
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||||
|
* Copyright (c) 2007-2011, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file di_ops.h
|
||||||
|
* \brief Headers for di_ops.c
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef TOR_DI_OPS_H
|
||||||
|
#define TOR_DI_OPS_H
|
||||||
|
|
||||||
|
#include "orconfig.h"
|
||||||
|
#include "torint.h"
|
||||||
|
|
||||||
|
int tor_memcmp(const void *a, const void *b, size_t sz);
|
||||||
|
int tor_memeq(const void *a, const void *b, size_t sz);
|
||||||
|
#define tor_memneq(a,b,sz) (!tor_memeq((a),(b),(sz)))
|
||||||
|
|
||||||
|
/** Alias for the platform's memcmp() function. This function is
|
||||||
|
* <em>not</em> data-independent: we define this alias so that we can
|
||||||
|
* mark cases where we are deliberately using a data-dependent memcmp()
|
||||||
|
* implementation.
|
||||||
|
*/
|
||||||
|
#define fast_memcmp(a,b,c) (memcmp((a),(b),(c)))
|
||||||
|
|
||||||
|
#endif
|
@ -43,6 +43,7 @@ const char tor_svn_revision[] = "";
|
|||||||
#include "torgzip.h"
|
#include "torgzip.h"
|
||||||
#include "mempool.h"
|
#include "mempool.h"
|
||||||
#include "memarea.h"
|
#include "memarea.h"
|
||||||
|
#include "di_ops.h"
|
||||||
|
|
||||||
#ifdef USE_DMALLOC
|
#ifdef USE_DMALLOC
|
||||||
#include <dmalloc.h>
|
#include <dmalloc.h>
|
||||||
@ -1331,6 +1332,59 @@ test_util(void)
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_util_di_ops(void)
|
||||||
|
{
|
||||||
|
#define LT -1
|
||||||
|
#define GT 1
|
||||||
|
#define EQ 0
|
||||||
|
const struct {
|
||||||
|
const char *a; int want_sign; const char *b;
|
||||||
|
} examples[] = {
|
||||||
|
{ "Foo", EQ, "Foo" },
|
||||||
|
{ "foo", GT, "bar", },
|
||||||
|
{ "foobar", EQ ,"foobar" },
|
||||||
|
{ "foobar", LT, "foobaw" },
|
||||||
|
{ "foobar", GT, "f00bar" },
|
||||||
|
{ "foobar", GT, "boobar" },
|
||||||
|
{ "", EQ, "" },
|
||||||
|
{ NULL, 0, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; examples[i].a; ++i) {
|
||||||
|
size_t len = strlen(examples[i].a);
|
||||||
|
int eq1, eq2, neq1, neq2, cmp1, cmp2;
|
||||||
|
test_eq(len, strlen(examples[i].b));
|
||||||
|
/* We do all of the operations, with operands in both orders. */
|
||||||
|
eq1 = tor_memeq(examples[i].a, examples[i].b, len);
|
||||||
|
eq2 = tor_memeq(examples[i].b, examples[i].a, len);
|
||||||
|
neq1 = tor_memneq(examples[i].a, examples[i].b, len);
|
||||||
|
neq2 = tor_memneq(examples[i].b, examples[i].a, len);
|
||||||
|
cmp1 = tor_memcmp(examples[i].a, examples[i].b, len);
|
||||||
|
cmp2 = tor_memcmp(examples[i].b, examples[i].a, len);
|
||||||
|
|
||||||
|
/* Check for correctness of cmp1 */
|
||||||
|
if (cmp1 < 0 && examples[i].want_sign != LT)
|
||||||
|
test_fail();
|
||||||
|
else if (cmp1 > 0 && examples[i].want_sign != GT)
|
||||||
|
test_fail();
|
||||||
|
else if (cmp1 == 0 && examples[i].want_sign != EQ)
|
||||||
|
test_fail();
|
||||||
|
|
||||||
|
/* Check for consistency of everything else with cmp1 */
|
||||||
|
test_eq(eq1, eq2);
|
||||||
|
test_eq(neq1, neq2);
|
||||||
|
test_eq(cmp1, -cmp2);
|
||||||
|
test_eq(eq1, cmp1 == 0);
|
||||||
|
test_eq(neq1, !eq1);
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
/** Helper: assert that IPv6 addresses <b>a</b> and <b>b</b> are the same. On
|
/** Helper: assert that IPv6 addresses <b>a</b> and <b>b</b> are the same. On
|
||||||
* failure, reports an error, describing the addresses as <b>e1</b> and
|
* failure, reports an error, describing the addresses as <b>e1</b> and
|
||||||
* <b>e2</b>, and reporting the line number as <b>line</b>. */
|
* <b>e2</b>, and reporting the line number as <b>line</b>. */
|
||||||
@ -4753,6 +4807,7 @@ static struct {
|
|||||||
SUBENT(crypto, aes_iv),
|
SUBENT(crypto, aes_iv),
|
||||||
SUBENT(crypto, base32_decode),
|
SUBENT(crypto, base32_decode),
|
||||||
ENT(util),
|
ENT(util),
|
||||||
|
SUBENT(util, di_ops),
|
||||||
SUBENT(util, ip6_helpers),
|
SUBENT(util, ip6_helpers),
|
||||||
SUBENT(util, gzip),
|
SUBENT(util, gzip),
|
||||||
SUBENT(util, datadir),
|
SUBENT(util, datadir),
|
||||||
|
Loading…
Reference in New Issue
Block a user