I'm writing a ProtectedPtr class that protects objects in memory using Windows Crypto API, and I've run into a problem creating a generic constant time compare function. My current code:
template <class T>
bool operator==(volatile const ProtectedPtr& other)
{
std::size_t thisDataSize = sizeof(*protectedData) / sizeof(T);
std::size_t otherDataSize = sizeof(*other) / sizeof(T);
volatile auto thisData = (byte*)getEncyptedData();
volatile auto otherData = (byte*)other.getEncyptedData();
if (thisDataSize != otherDataSize)
return false;
volatile int result = 0;
for (int i = 0; i < thisDataSize; i++)
result |= thisData[i] ^ otherData[i];
return result == 0;
}
getEncryptedData function:
std::unique_ptr<T> protectedData;
const T& getEncyptedData() const
{
ProtectMemory(true);
return *protectedData;
}
The problem is casting to byte*. When using this class with strings, my compiler complains that strings can't be casted to byte pointers. I was thinking maybe trying to base my function off of Go's ConstantTimeByteEq function, but it still brings me back to my original problem of converting a template type to an int or something that I can preform binary manipulation on.
Go's ConstantTimeByteEq function:
func ConstantTimeByteEq(x, y uint8) int {
z := ^(x ^ y)
z &= z >> 4
z &= z >> 2
z &= z >> 1
return int(z)
}
How can I easily convert a template type into something that can have binary manipulation easily preformed on it?
UPDATE Working generic constant compare function based on suggestions from lockcmpxchg8b:
//only works on primative types, and types that don't have
//internal pointers pointing to dynamically allocated data
byte* serialize()
{
const size_t size = sizeof(*protectedData);
byte* out = new byte[size];
ProtectMemory(false);
memcpy(out, &(*protectedData), size);
ProtectMemory(true);
return out;
}
bool operator==(ProtectedPtr& other)
{
if (sizeof(*protectedData) != sizeof(*other))
return false;
volatile auto thisData = serialize();
volatile auto otherData = other.serialize();
volatile int result = 0;
for (int i = 0; i < sizeof(*protectedData); i++)
result |= thisData[i] ^ otherData[i];
//wipe the unencrypted copies of the data
SecureZeroMemory(thisData, sizeof(thisData));
SecureZeroMemory(otherData, sizeof(otherData));
return result == 0;
}
Generally, what you're trying to accomplish in your current code is called Format Preserving Encryption. I.e., to encrypt a
std::string
such that the resulting ciphertext is also a validstd::string
. This is much harder than letting the encryption process convert from the original type to a flat array of bytes.To do the conversion to a flat array, declare a second template argument for a "Serializer" object, that knows how to serialize objects of type T into an array of unsigned char. You could default it to a generic
sizeof
/memcpy
serializer that would work for all primitve types.Here's an example for
std::string
.Once you've reduced the objects down to a flat array of
unsigned char
s, then your given constant-time compare algorithm will work just fine.Here's a really dumbed-down version of your example code using the serializer above.
Output:
Edit: answering request for a generic serializer for primtive/flat types. Consider this as pseudocode, because I'm typing it into a browser without testing. I'm not sure if that's the right template syntax.