C ++ Crypto: ตอนที่ 2- HMAC

Aug 26 2020

กำลังมองหาห้องสมุด Crypto ที่ทันสมัย
ไม่พบสิ่งที่ดี

ฉันรู้ว่าฉันอาจจะทำผิดทั้งหมดจึงทำงานกับฉันที่นี่ จะมีบทวิจารณ์ที่แตกต่างกันสี่แบบสำหรับโครงสร้างสี่แบบที่สร้างต่อกัน:

  1. แฮช
  2. แฮชคีย์
  3. รหัสผ่าน
  4. การตอบสนองความท้าทายเค็ม

โครงสร้างข้อมูลและการดำเนินงานที่นำเสนอในคำถามเหล่านี้จะขึ้นอยู่กับ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

คำตอบ

1 G.Sliepen Aug 27 2020 at 04:33

หลีกเลี่ยงการใช้ประเภทเดียวกันเพื่อเก็บผลลัพธ์ของไดเจสต์ธรรมดาและ HMAC

คุณไม่สามารถ (หรืออย่างน้อยก็ไม่ควรจะสามารถ) เปรียบเทียบ HMAC กับไดเจสต์ธรรมดาได้ ดังนั้นจะเป็นการดีถ้าระบบประเภทสามารถจับข้อผิดพลาดที่อาจเกิดขึ้นได้ แทนที่จะมีDigestStore<Hash>คลาสที่ใช้สำหรับไดเจสต์ธรรมดาและ HMACs ฉันก็แค่มีDigest<Hash>และHMAC<Hash>แต่ละคลาสเก็บผลลัพธ์ของตัวเองโดยตรง

อนุญาตให้เพิ่มข้อมูลลงใน HMAC ได้หลายขั้นตอน

ดังที่ได้กล่าวไว้ในบทวิจารณ์สำหรับส่วนที่ 1 ไม่ใช่เรื่องแปลกที่จะต้องเพิ่มข้อมูลหลายชิ้นที่ไม่สัมพันธ์กันเพื่อเพิ่มลงใน HMAC ดังนั้นมีฟังก์ชันสมาชิกadd()ที่สามารถอัปเดต HMAC ได้ นี่หมายถึงการแบ่งการสร้าง HMAC ออกเป็นสามส่วน:

  1. มีการเตรียมวัสดุสำคัญเป็นส่วนหนึ่งของตัวสร้าง
  2. ข้อความจะถูกเพิ่มลงในแฮชไม่ว่าจะในครั้งเดียวหรือใช้การเรียกฟังก์ชันหลาย ๆ
  3. คำนวณค่าสุดท้ายแล้ว

ฉันจะจัดโครงสร้างชั้นเรียนดังนี้:

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()ฟังก์ชัน