шаблоны — Общая функция сравнения постоянного времени Переполнение стека

Я пишу класс ProtectedPtr, который защищает объекты в памяти с помощью Windows Crypto API, и столкнулся с проблемой создания общей функции сравнения с постоянным временем. Мой текущий код:

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:

std::unique_ptr<T> protectedData;
const T& getEncyptedData() const
{
ProtectMemory(true);
return *protectedData;
}

Проблема в приведении к байту *. При использовании этого класса со строками мой компилятор жалуется, что строки не могут быть преобразованы в байтовые указатели. Я подумал, может быть, пытаясь основать свою функцию на функции Go ConstantTimeByteEq, но это все еще возвращает меня к моей первоначальной проблеме преобразования типа шаблона в int или что-то, на чем я могу предварительно выполнить бинарную манипуляцию.

Функция Go ConstantTimeByteEq:

func ConstantTimeByteEq(x, y uint8) int {
z := ^(x ^ y)
z &= z >> 4
z &= z >> 2
z &= z >> 1

return int(z)
}

Как я могу легко преобразовать тип шаблона во что-то, для чего можно легко выполнить бинарную манипуляцию?

ОБНОВИТЬ Рабочая универсальная функция сравнения констант, основанная на предложениях из 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;
}

0

Решение

Как правило, то, что вы пытаетесь достичь в своем текущем коде, называется Формат, сохраняющий шифрование. Т.е. для шифрования std::string так что полученный зашифрованный текст также является действительным std::string, Это гораздо сложнее, чем позволить процессу шифрования преобразовать исходный тип в плоский массив байтов.

Чтобы выполнить преобразование в плоский массив, объявите второй аргумент шаблона для объекта «Serializer», который знает, как сериализовать объекты типа T в массив без знака. Вы можете по умолчанию это универсальный sizeof/memcpy Сериализатор, который будет работать для всех типов примитивов.

Вот пример для std::string,

template <class T>
class Serializer
{
public:
virtual size_t serializedSize(const T& obj) const = 0;
virtual size_t serialize(const T& obj, unsigned char *out, size_t max) const = 0;
virtual void deserialize(const unsigned char *in, size_t len, T& out) const = 0;
};

class StringSerializer : public Serializer<std::string>
{
public:

size_t serializedSize(const std::string& obj) const {
return obj.length();
};

size_t serialize(const std::string& obj, unsigned char *out, size_t max) const {
if(max >= obj.length()){
memcpy(out, obj.c_str(), obj.length());
return obj.length();
}
throw std::runtime_error("overflow");
}

void deserialize(const unsigned char *in, size_t len, std::string& out) const {
out = std::string((const char *)in, (const char *)(in+len));
}
};

Как только вы уменьшили объекты до плоского массива unsigned chars, тогда ваш заданный алгоритм сравнения с постоянным временем будет работать просто отлично.

Вот действительно тупая версия вашего примера кода с использованием сериализатора выше.

template <class T, class S>
class Test
{
std::unique_ptr<unsigned char[]> protectedData;
size_t serSize;
public:
Test(const T& obj) : protectedData() {
S serializer;
size_t size = serializer.serializedSize(obj);

protectedData.reset(new unsigned char[size]);
serSize = serializer.serialize(obj, protectedData.get(), size);

// "Encrypt"for(size_t i=0; i< size; i++)
protectedData.get()[i] ^= 0xa5;
}

size_t getEncryptedLen() const {
return serSize;
}
const unsigned char *getEncryptedData() const {
return protectedData.get();
}

const T getPlaintextData() const {
S serializer;
T target;

//"Decrypt"for(size_t i=0; i< serSize; i++)
protectedData.get()[i] ^= 0xa5;

serializer.deserialize(protectedData.get(), serSize, target);
return target;
}
};

int main(int argc, char *argv[])
{
std::string data = "test";
Test<std::string, StringSerializer> tester(data);

const unsigned char *ptr = tester.getEncryptedData();
std::cout << "\"Encrypted\" bytes: ";
for(size_t i=0; i<tester.getEncryptedLen(); i++)
std::cout << std::setw(2) << std::hex << std::setfill('0') << (unsigned int)ptr[i] << " ";
std::cout << std::endl;

std::string recov = tester.getPlaintextData();

std::cout << "Recovered: " << recov << std::endl;
}

Выход:

$ ./a.out
"Encrypted" bytes: d1 c0 d6 d1
Recovered: test

Редактировать: ответ на запрос универсального сериализатора для примитивных / плоских типов. Считайте это псевдокодом, потому что я печатаю его в браузере без тестирования. Я не уверен, что это правильный синтаксис шаблона.

template<class T>
class PrimitiveSerializer : public Serializer<T>
{
public:

size_t serializedSize(const T& obj) const {
return sizeof obj;
};

size_t serialize(const T& obj, unsigned char *out, size_t max) const {
if(max >= sizeof obj){
memcpy(out, &obj, sizeof obj);
return sizeof obj;
}
throw std::runtime_error("overflow");
}

void deserialize(const unsigned char *in, size_t len, T& out) const {
if(len < sizeof out) {
throw std::runtime_error("underflow");
}
memcpy(&out, in, sizeof out);
}
};
1

Другие решения

Мне интересно, какую ошибку выдает компилятор.

Тем не менее, попробуйте привести к const char* или же const void*,

Другой проблемой может быть приведение 64-разрядного указателя к 8-разрядному. byte, Попробуйте привести к int, long, или же longlong

Изменить: на основе вашего отзыва, еще одно небольшое изменение:

volatile auto thisData = (byte*)&getEncyptedData();
volatile auto otherData = (byte*)&other.getEncyptedData();

(обратите внимание на амперсанды). Это позволит предыдущим приведениям работать

0