РЕДАКТИРОВАТЬ: Оригинальный ответ ниже - все еще стоит прочитать IMO, но о времени атаки ...
Страница, на которую вы ссылаетесь, дает некоторые интересные моменты об оптимизации компилятора.Учитывая, что вы знаете, что два байтовых массива будут одинаковой длины (при условии, что размер контрольной суммы не является особо секретным, вы можете сразу же вернуться, если длины разные), вы можете попробовать что-то вроде этого:
public static bool CompareArraysExhaustively(byte[] first, byte[] second)
{
if (first.Length != second.Length)
{
return false;
}
bool ret = true;
for (int i = 0; i < first.Length; i++)
{
ret = ret & (first[i] == second[i]);
}
return ret;
}
Теперь это все равно не займет одинаковое количество времени для всех входных данных - например, если два массива находятся в кеше L1, скорее всего, это будет быстрее, чем если быдолжен быть извлечен из основной памяти.Однако я подозреваю, что это вряд ли вызовет серьезную проблему с точки зрения безопасности.
Это нормально?Кто знает.Разные процессоры и разные версии CLR могут занимать разное количество времени для операции &
в зависимости от двух операндов.По сути, это то же самое, что и вывод страницы, на которую вы ссылались, - что она, вероятно, так же хороша, как мы получим переносимым способом, но для этого потребуется проверка на каждой платформе, на которой вы пытаетесь работать.
По крайней мере, приведенный выше код использует только относительно простые операции.Я бы лично не использовал здесь операции LINQ, поскольку в некоторых случаях может происходить хитрая оптимизация.Я не думаю, что будет в этом случае - или их будет легко победить - но вам, по крайней мере, придется думать о них.С помощью приведенного выше кода, по крайней мере, существует достаточно тесная связь между исходным кодом и IL - оставляя «только» JIT-компилятор и оптимизацию процессора, чтобы беспокоиться о:)
Оригинальный ответ
Существует одна существенная проблема: для предоставления контрольной суммы необходимо иметь строку, форма которой в кодировке UTF-8 совпадает с контрольной суммой.Существует множество байтовых последовательностей, которые просто не представляют кодированный в UTF-8 текст.По сути, попытка кодировать произвольные двоичные данные в виде текста с использованием UTF-8 - плохая идея.
Base64, с другой стороны, в основном предназначен для этого:
static bool VerifyIntegrity(string secret, string checksum, string data)
{
// Verify HMAC-SHA256 Checksum
byte[] key = Encoding.UTF8.GetBytes(secret);
byte[] value = Encoding.UTF8.GetBytes(data);
byte[] checksumBytes = Convert.FromBase64String(checksum);
using (var hmac = new HMACSHA256(key))
{
byte[] expectedBytes = hmac.ComputeHash(value);
return checksumBytes.SequenceEqual(expectedBytes);
}
}
С другой стороны, вместо использования SequenceEqual в байтовом массиве, вы можете Base64 кодировать фактический хеш и посмотреть, соответствует ли это:
static bool VerifyIntegrity(string secret, string checksum, string data)
{
// Verify HMAC-SHA256 Checksum
byte[] key = Encoding.UTF8.GetBytes(secret);
byte[] value = Encoding.UTF8.GetBytes(data);
using (var hmac = new HMACSHA256(key))
{
return checksum == Convert.ToBase64String(hmac.ComputeHash(value));
}
}
Я незнать что-нибудь лучше в рамках.Было бы не сложно написать специализированный оператор SequenceEqual
для массивов (или общих реализаций ICollection<T>
), который сначала проверял бы одинаковую длину ... но, учитывая, что хэши короткие, я бы об этом не беспокоился.