C ++ Crypto: Phần 2- HMAC
Tìm kiếm xung quanh các thư viện Crypto hiện đại.
Không thể tìm thấy bất cứ điều gì tốt.
Tôi biết có lẽ tôi đã làm điều này sai hoàn toàn vì vậy hãy làm việc với tôi ở đây. Sẽ có bốn đánh giá khác nhau cho bốn cấu trúc xây dựng dựa trên nhau:
- Băm
- Khoá băm
- Khóa mật khẩu
- Phản ứng thử thách muối
Cấu trúc dữ liệu và cách triển khai được trình bày trong những câu hỏi này dựa trên RFC2104 và bài đăng này trên codeproject .
Đánh giá này là để triển khai HMAC. Đây là một kỹ thuật để băm mật khẩu sử dụng Khóa.
Sử dụng Exmple:
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
Trả lời
Tránh sử dụng cùng một loại để lưu trữ kết quả phân tích đơn giản và HMAC
Bạn không thể (hoặc ít nhất là không bao giờ có thể) so sánh HMAC với một thông báo đơn giản. Vì vậy, sẽ tốt nếu hệ thống loại có thể bắt được lỗi tiềm ẩn đó. Thay vì có một DigestStore<Hash>lớp được sử dụng cho cả tiêu chuẩn đơn giản và HMAC, tôi sẽ chỉ có Digest<Hash>và HMAC<Hash>mỗi lớp lưu trữ trực tiếp kết quả của riêng chúng.
Cho phép thêm dữ liệu vào HMAC theo nhiều bước
Như đã đề cập trong bài đánh giá cho phần 1, không có gì lạ khi phải thêm nhiều phần dữ liệu không liên tục vào HMAC, vì vậy hãy có một chức năng thành viên add()có thể cập nhật HMAC. Điều này có nghĩa là chia việc tạo HMAC thành ba phần:
- Vật liệu quan trọng được chuẩn bị như một phần của cấu trúc
- Thông báo được thêm vào hàm băm, trong một lần hoặc sử dụng nhiều lệnh gọi hàm
- Giá trị cuối cùng được tính
Tôi sẽ cấu trúc lớp như vậy:
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();
}
};
Bạn cũng có thể muốn thêm một số cách để ngăn finish()bị gọi nhiều lần.
Tránh các hoạt động không an toàn
Lời nói của chính bạn:
Tôi chỉ ghét đọc những dự án C ++ được viết tồi tệ này (đó là lý do tại sao tôi bắt đầu hack này) những dự án khó hiểu xung quanh C hơn là sử dụng an toàn kiểu tốt, giao diện và kỹ thuật C ++ sạch đẹp.
Bạn muốn loại an toàn tốt, nhưng tôi cũng nghĩ rằng bạn muốn loại an toàn nói chung. Tạo các chức năng không an toàn đi ngược lại mục tiêu đó. Nếu bạn lưu trữ kết quả của một hàm băm trong một Digestđối tượng và có cách để lấy tham chiếu const đến dữ liệu mà nó lưu trữ, thì bạn không cần một hashUnsafe()hàm.