C ++ Crypto: ตอนที่ 2- HMAC
กำลังมองหาห้องสมุด Crypto ที่ทันสมัย
ไม่พบสิ่งที่ดี
ฉันรู้ว่าฉันอาจจะทำผิดทั้งหมดจึงทำงานกับฉันที่นี่ จะมีบทวิจารณ์ที่แตกต่างกันสี่แบบสำหรับโครงสร้างสี่แบบที่สร้างต่อกัน:
- แฮช
- แฮชคีย์
- รหัสผ่าน
- การตอบสนองความท้าทายเค็ม
โครงสร้างข้อมูลและการดำเนินงานที่นำเสนอในคำถามเหล่านี้จะขึ้นอยู่กับRFC2104และโพสต์ในcodeproject
บทวิจารณ์นี้มีไว้สำหรับการใช้งาน HMAC นี่เป็นเทคนิคในการแฮชรหัสผ่านโดยใช้คีย์
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
คำตอบ
หลีกเลี่ยงการใช้ประเภทเดียวกันเพื่อเก็บผลลัพธ์ของไดเจสต์ธรรมดาและ 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ออบเจ็กต์และมีวิธีรับการอ้างอิง const ไปยังข้อมูลที่จัดเก็บคุณก็ไม่จำเป็นต้องมีhashUnsafe()ฟังก์ชัน