C ++ Crypto: Часть 2 - HMAC
Ищу современные библиотеки Crypto.
Не нашел ничего хорошего.
Я знаю, что, вероятно, сделал все неправильно, поэтому работайте со мной здесь. Будет четыре разных обзора четырех структур, которые опираются друг на друга:
- Хеширование
- Хешированный ключ
- Ключ пароля
- Ответ на соленый вызов
Структуры данных и реализация, представленные в этих вопросах, основаны на RFC2104 и этой публикации на codeproject .
Этот обзор предназначен для реализации HMAC. Это метод хеширования пароля с помощью ключа.
Пример использования:
Digest<HMac<Sha1>> digest;
HMac<Sha1> hasher;
hasher.hash("This is the Key", "This is the message", digest);
hmac.h
#ifndef THORS_ANVIL_CRYPTO_HMAC_H
#define THORS_ANVIL_CRYPTO_HMAC_H
#include "hash.h"
// HMAC: Keyed-Hashing for Message Authentication RFC-2104
namespace ThorsAnvil::Crypto
{
// Look in hash.h for good examples of THash
// ThorsAnvil::Crypto::Sha1
template<typename THash>
struct HMac
{
static constexpr std::size_t digestSize = THash::digestSize;
using Hash = THash;
using DigestStore = typename Hash::DigestStore;
void hash(std::string_view key, std::string_view message, DigestStore& digest)
{
Hash hasher;
enum { BLOCK_SIZE = 64 };
/* STEP 1 */
std::array<Byte, BLOCK_SIZE> SHA1_Key{'\x00'};
if (key.size() > BLOCK_SIZE)
{
hasher.hashUnsafe(key, &SHA1_Key[0]);
}
else
{
std::copy(std::begin(key), std::end(key), &SHA1_Key[0]);
}
/* STEP 2 */
std::string ipad;
std::string opad;
ipad.reserve(BLOCK_SIZE + std::size(message));
opad.reserve(BLOCK_SIZE + digestSize);
ipad.resize(BLOCK_SIZE, '\x36');
opad.resize(BLOCK_SIZE, '\x5c');
for (int i=0; i< BLOCK_SIZE; i++)
{
ipad[i] ^= SHA1_Key[i];
opad[i] ^= SHA1_Key[i];
}
/* STEP 3 */
std::copy(std::begin(message), std::end(message), std::back_inserter(ipad));
/* STEP 4 */
opad.resize(BLOCK_SIZE + digestSize);
hasher.hashUnsafe(ipad, reinterpret_cast<Byte*>(&opad[BLOCK_SIZE]));
/* STEP 5 */
// Moved XOR of opad to STEP 2
/* STEP 6 */
// Don't need to copy the hash of ipad onto opad as we hashed
// into the correct destination.
/*STEP 7 */
hasher.hash(opad, digest);
}
};
}
#endif
Ответы
Избегайте использования одного и того же типа для хранения результатов простых дайджестов и HMAC.
Вы не можете (или, по крайней мере, никогда не должны иметь возможности) сравнивать HMAC с простым дайджестом. Было бы хорошо, если бы система типов могла уловить эту потенциальную ошибку. Вместо того , чтобы иметь DigestStore<Hash>класс , который используется как для обычных сборников и HMACs, я бы просто Digest<Hash>и HMAC<Hash>каждый магазин своего собственного результата напрямую.
Разрешить добавление данных в HMAC за несколько шагов
Как упоминалось в обзоре к части 1, нередко приходится добавлять несколько несмежных частей данных для добавления в HMAC, поэтому необходимо иметь функцию-член, add()которая может обновлять HMAC. Это означало бы разделение создания HMAC на три части:
- Основной материал готовится в рамках конструктора.
- Сообщение добавляется в хэш за один раз или с использованием нескольких вызовов функций.
- Окончательное значение рассчитывается
Я бы структурировал класс так:
template<typename Hash>
class HMAC {
Digest<Hash> outer_digest;
Digest<Hash> inner_digest;
public:
HMAC(std::string_view key) {
// Add key XOR opad to outer_digest
// Add key XOR ipad to inner_digest
}
// Convenience constructor to do a one-shot HMAC creation
HMAC(std::string_view key, std::string_view message): HMAC(key) {
add(message);
finish();
}
void add(std::string_view message) {
// Add message to inner_digest
}
void finish() {
// Finish inner_digest, add it to outer_digest
// Finish outer_digest
}
// Something to get the bits out
const auto &get() {
return outer_digest.get();
}
};
Вы также можете добавить способ запретить finish()вызов более одного раза.
Избегайте небезопасных операций
Ваши собственные слова:
Я просто ненавижу читать эти плохо написанные C ++ (именно поэтому я начал этот хак) проекты, которые являются дрянной оберткой вокруг C, вместо того, чтобы использовать хорошую безопасность типов и красивые чистые интерфейсы и методы C ++.
Вам нужна хорошая безопасность типов, но я также думаю, что вам нужна хорошая безопасность в целом. Создание небезопасных функций противоречит этой цели. Если вы храните результаты хеширования в Digestобъекте и имеете способ получить константную ссылку на данные, которые он хранит, тогда вам не нужна hashUnsafe()функция.