Add new HashMap_Get() which uses a constant time memory comparison function.
lib/misc/utilMem.c:
Constant time memory and string comparison functions.
/*********************************************************
- * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
+ * Copyright (C) 2009-2018 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
#include "hashMap.h"
#include "clamped.h"
+#include "util.h"
#ifdef VMX86_SERVER
#include "aioMgr.h"
#include "iovector.h"
static void CalculateEntrySize(struct HashMap *map);
static void GetEntry(struct HashMap *map, uint32 index, HashMapEntryHeader **header, void **key, void **data);
static uint32 ComputeHash(struct HashMap *map, const void *key);
-static Bool LookupKey(struct HashMap* map, const void *key, HashMapEntryHeader **header, void **data, uint32 *freeIndex);
+static Bool LookupKey(struct HashMap* map, const void *key, Bool constTimeLookup, HashMapEntryHeader **header, void **data, uint32 *freeIndex);
static Bool CompareKeys(struct HashMap *map, const void *key, const void *compare);
+static Bool ConstTimeCompareKeys(struct HashMap *map, const void *key, const void *compare);
static Bool NeedsResize(struct HashMap *map);
static void Resize(struct HashMap *map);
INLINE void EnsureSanity(HashMap *map);
HashMapEntryHeader *header;
void *tableData;
- if (!LookupKey(map, key, &header, &tableData, &freeIndex)) {
+ if (!LookupKey(map, key, FALSE, &header, &tableData, &freeIndex)) {
uint32 hash = ComputeHash(map, key);
void *tableKey;
if (NeedsResize(map)) {
Resize(map);
- if (LookupKey(map, key, &header, &tableData, &freeIndex)) {
+ if (LookupKey(map, key, FALSE, &header, &tableData, &freeIndex)) {
/*
* Somehow our key appeared after resizing the table.
*/
uint32 freeIndex;
HashMapEntryHeader *header;
- if (LookupKey(map, key, &header, &data, &freeIndex)) {
+ if (LookupKey(map, key, FALSE, &header, &data, &freeIndex)) {
+ return data;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * HashMap_ConstTimeGet --
+ *
+ * Timing attack safe version of HashMap_Get. This will call LookupKey with
+ * the constTime flag set to 1 which will do the memory comparison with
+ * Util_ConstTimeMemDiff instead of memcmp. Note that there is a bit of a
+ * time penalty associated with this so only use this if you are looking up
+ * sensitive information.
+ *
+ * Results:
+ * Returns a pointer to the data that was previously stored by HashMap_Put or
+ * NULL if the key wasn't found.
+ *
+ * Side Effects:
+ * None.
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+void *
+HashMap_ConstTimeGet(struct HashMap *map, // IN
+ const void *key) // IN
+{
+ void *data;
+ uint32 freeIndex;
+ HashMapEntryHeader *header;
+
+ if (LookupKey(map, key, TRUE, &header, &data, &freeIndex)) {
return data;
}
HashMapEntryHeader *header;
void *tableData;
- if (!LookupKey(map, key, &header, &tableData, &freeIndex)) {
+ if (!LookupKey(map, key, FALSE, &header, &tableData, &freeIndex)) {
return FALSE;
}
* Use linear probing to find a free space in the table or the data that
* we're interested in.
*
+ * Call this function with constTimeLookup = TRUE to use a timing attack
+ * safe version of memcmp while comparing keys.
+ *
* Returns:
* - TRUE if the key was found in the table, FALSE otherwise.
* - Returns the entry header on header, data pointer on data and the first
Bool
LookupKey(struct HashMap* map, // IN
const void *key, // IN
+ Bool constTimeLookup, // IN
HashMapEntryHeader **header, // OUT
void **data, // OUT
uint32 *freeIndex) // OUT
break;
case HashMapState_FILLED:
if ((*header)->hash == hash) {
- found = CompareKeys(map, key, tableKey);
+ /*
+ * There is some performance penalty to doing a constant time
+ * comparison, so only use that version if it's been explicitly
+ * asked for.
+ */
+ if (constTimeLookup) {
+ found = ConstTimeCompareKeys(map, key, tableKey);
+ } else {
+ found = CompareKeys(map, key, tableKey);
+ }
if (found) {
done = TRUE;
}
}
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * ConstTimeCompareKeys --
+ *
+ * Timing attack safe version of CompareKeys. Instead of calling memcmp,
+ * which will return after the first character that doesn't match, this
+ * calls a constant time memory comparison function.
+ *
+ * Results:
+ * Returns TRUE if the two keys are binary equal over the length specified
+ * in the map. FALSE if the two keys are different.
+ *
+ * Side Effects:
+ * None.
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+Bool
+ConstTimeCompareKeys(struct HashMap *map, // IN
+ const void *key, // IN
+ const void *compare) // IN
+{
+ return Util_ConstTimeMemDiff(key, compare, map->keySize) == 0;
+}
+
+
/*
* ----------------------------------------------------------------------------
*
case HashMapState_DELETED:
continue;
case HashMapState_FILLED:
- if (!LookupKey(map, oldKey, &newHeader, &newData, &freeIndex)) {
+ if (!LookupKey(map, oldKey, FALSE, &newHeader, &newData, &freeIndex)) {
GetEntry(map, freeIndex, &newHeader, &newKey, &newData);
newHeader->hash = oldHeader->hash;
/*********************************************************
- * Copyright (C) 2009-2017 VMware, Inc. All rights reserved.
+ * Copyright (C) 2009-2018 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
void HashMap_DestroyMap(HashMap *map);
Bool HashMap_Put(HashMap *map, const void *key, const void *data);
void *HashMap_Get(HashMap *map, const void *key);
+void *HashMap_ConstTimeGet(struct HashMap *map, const void *key);
void HashMap_Clear(HashMap *map);
Bool HashMap_Remove(HashMap *map, const void *key);
uint32 HashMap_Count(HashMap *map);
/*********************************************************
- * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
+ * Copyright (C) 1998-2018 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
void *Util_Memdup(const void *src, size_t size);
void *Util_Memcpy(void *dest, const void *src, size_t count);
+Bool Util_ConstTimeMemDiff(const void *secret, const void *guess, size_t len);
+Bool Util_ConstTimeStrDiff(const char *secret, const char *guess);
+
#ifndef VMKBOOT
/*
/*********************************************************
- * Copyright (C) 2009-2017 VMware, Inc. All rights reserved.
+ * Copyright (C) 2009-2018 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
#endif
static NORETURN void UtilAllocationFailure0(void);
-static NORETURN void UtilAllocationFailure1(int bugNumber,
+static NORETURN void UtilAllocationFailure1(int bugNumber,
const char *file, int lineno);
+Bool UtilConstTimeMemDiff(const void *secret, const void *guess, size_t len, size_t *diffCount);
+Bool UtilConstTimeStrDiff(const char *secret, const char *guess, size_t *diffCount);
+
static void
UtilAllocationFailure0(void)
memcpy(dest, src, count);
return dest;
}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * UtilConstTimeMemDiff --
+ *
+ * The implementation of a constant time memory comparison. Unlike
+ * memcmp, this function does not return early if it finds a mismatch.
+ * It always examines the entire 'secret' and 'guess' buffers, so that
+ * the time spent in this function is constant for buffers of the same
+ * given 'len'. (We don't attempt to make the time invariant for
+ * different buffer lengths.)
+ *
+ * The reason why this function is externally visible (not static)
+ * and has a 'diffCount' argument is to try to prevent aggressive
+ * compiler optimization levels from short-circuiting the inner loop.
+ * The possibility of a call from outside this module with a non-NULL
+ * diffCount pointer prevents that optimization. If we didn't have
+ * to worry about that then we wouldn't need this function; we could
+ * have put the implementation directly into Util_ConstTimeMemDiff.
+ *
+ * Results:
+ * Returns true if the buffers differ, false if they are identical.
+ * If diffCount is non-NULL, sets *diffCount to the total number of
+ * differences between the buffers.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+UtilConstTimeMemDiff(const void *secret, // IN
+ const void *guess, // IN
+ size_t len, // IN
+ size_t *diffCount) // OUT: optional
+{
+ const char *secretChar = secret;
+ const char *guessChar = guess;
+
+ size_t numDiffs = 0;
+
+ while (len--) {
+ numDiffs += !!(*secretChar ^ *guessChar);
+ ++secretChar;
+ ++guessChar;
+ }
+
+ if (diffCount != NULL) {
+ *diffCount = numDiffs;
+ }
+ return numDiffs != 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Util_ConstTimeMemDiff --
+ *
+ * Performs a constant time memory comparison.
+ *
+ * The return values are chosen to make this as close as possible to
+ * a drop-in replacement for memcmp, so we return false (0) if the
+ * buffers match (ie there are zero differences) and true (1) if the
+ * buffers differ.
+ *
+ * Results:
+ * Returns zero if the buffers are identical, 1 if they differ.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+Util_ConstTimeMemDiff(const void *secret, // IN
+ const void *guess, // IN
+ size_t len) // IN
+{
+ return UtilConstTimeMemDiff(secret, guess, len, NULL);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * UtilConstTimeStrDiff --
+ *
+ * The implementation of a constant time string comparison. Unlike
+ * strcmp, this function does not return early if it finds a mismatch.
+ * It always compares the entire 'secret' string against however much
+ * of the 'guess' string is required for that comparison, so that the
+ * time spent in this function is constant for secrets of the same
+ * length. (We don't attempt to make the time invariant for secrets
+ * of different lengths.)
+ *
+ * The reason why this function is externally visible (not static)
+ * and has a 'diffCount' argument is to try to prevent aggressive
+ * compiler optimization levels from short-circuiting the inner
+ * loop. The possibility of a call from outside this module with a
+ * non-NULL diffCount pointer prevents that optimization. If we
+ * didn't have to worry about that then we wouldn't need this
+ * function; we could have put the implementation directly into
+ * Util_ConstTimeStrDiff.
+ *
+ * Results:
+ * Returns true if the strings differ, false if they are identical.
+ * If diffCount is non-NULL, sets *diffCount to the total number of
+ * differences between the strings.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+UtilConstTimeStrDiff(const char *secret, // IN
+ const char *guess, // IN
+ size_t *diffCount) // OUT: optional
+{
+ size_t numDiffs = 0;
+
+ do {
+ numDiffs += !!(*secret ^ *guess);
+ guess += !!(*guess);
+ } while (*secret++);
+
+ if (diffCount != NULL) {
+ *diffCount = numDiffs;
+ }
+ return numDiffs != 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Util_ConstTimeStrDiff --
+ *
+ * The implementation of a constant time string comparison.
+ *
+ * The return values are chosen to make this as close as possible
+ * to a drop-in replacement for strcmp, so we return 0 if
+ * the buffers match (ie there are zero differences) and 1
+ * if the buffers differ.
+ *
+ * Results:
+ * Returns zero if the strings are identical, 1 if they differ.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+Bool
+Util_ConstTimeStrDiff(const char *secret, // IN
+ const char *guess) // IN
+{
+ return UtilConstTimeStrDiff(secret, guess, NULL);
+}