C ++ Crypto: ตอนที่ 1- แฮช
กำลังมองหาห้องสมุด Crypto ที่ทันสมัย
ไม่พบสิ่งที่ดี
ฉันรู้ว่าฉันอาจจะทำผิดทั้งหมดจึงทำงานกับฉันที่นี่ จะมีบทวิจารณ์ที่แตกต่างกันสี่แบบสำหรับโครงสร้างสี่แบบที่สร้างต่อกัน:
นี่คือรหัสการแฮชและมีกระดาษห่อหุ้มรอบ ๆ SHA-1 และ SHA-256 แต่รูปแบบนั้นง่ายพอที่เราจะขยายสำหรับกลไกการแฮชอื่น ๆ ได้
โครงสร้างข้อมูลและการดำเนินงานที่นำเสนอในคำถามเหล่านี้จะขึ้นอยู่กับRFC2104และโพสต์ในcodeproject
ตัวอย่างการใช้งาน:
DigestStore<Sha1> hash; // <- destination of hash
Sha1 hasher;
hasher.hash("This string can be hashsed", hash);
แฮช
#ifndef THORS_ANVIL_CRYPTO_HASH_H
#define THORS_ANVIL_CRYPTO_HASH_H
#ifdef __APPLE__
#define COMMON_DIGEST_FOR_OPENSSL
#include <CommonCrypto/CommonDigest.h>
#define THOR_SHA1(data, len, dst) CC_SHA1(data, len, dst)
#define THOR_SHA256(data, len, dst) CC_SHA256(data, len, dst)
#else
#include <openssl/sha.h>
#define THOR_SHA1(data, len, dst) SHA1(data, len, dst)
#define THOR_SHA256(data, len, dst) SHA256(data, len, dst)
#endif
#include <string>
#include <array>
//
// Wrapper for sha1 and sha256 hashing algorithms
//
// Provides a simple wrapper class with the appropriates types and size
// for the resulting "digest" object. Also provides several type safe
// versions of the hashing algorithm to allow multiple know types to
// be safely hashed.
namespace ThorsAnvil::Crypto
{
using Byte = char unsigned;
using DigestPtr = Byte*;
template<typename Hash>
using Digest = typename Hash::DigestStore;
template<std::size_t size>
class DigestStore
{
std::array<Byte, size> data;
public:
using iterator = typename std::array<Byte, size>::iterator;
operator Digest() {return &data[0];}
std::string_view view() {return std::string_view(reinterpret_cast<char const*>(&data[0]), std::size(data));}
Byte& operator[](std::size_t i) {return data[i];}
iterator begin() {return std::begin(data);}
iterator end() {return std::end(data);}
};
// These versions of the hashing function are good for hashing short
// amounts of text. Use these for passwords and validation hashes
// do not use them for hashing large documents.
struct Sha1
{
static constexpr std::size_t digestSize = SHA_DIGEST_LENGTH;
using DigestStore = DigestStore<SHA_DIGEST_LENGTH>;
void hash(DigestStore& src, DigestStore& dst) {THOR_SHA1(src, SHA_DIGEST_LENGTH, dst);}
void hash(std::string_view src, DigestStore& dst) {THOR_SHA1(reinterpret_cast<Byte const*>(&src[0]), std::size(src), dst);}
void hash(std::string const& src, DigestStore& dst) {THOR_SHA1(reinterpret_cast<Byte const*>(&src[0]), std::size(src), dst);}
// Use only if you know the destination is large enough!!
void hashUnsafe(std::string_view src, DigestPtr dst) {THOR_SHA1(reinterpret_cast<Byte const*>(&src[0]), std::size(src), dst);}
};
struct Sha256
{
static constexpr std::size_t digestSize = SHA256_DIGEST_LENGTH;
using DigestStore = DigestStore<SHA256_DIGEST_LENGTH>;
void hash(DigestStore& src, DigestStore& dst) {THOR_SHA256(src, SHA256_DIGEST_LENGTH, dst);}
void hash(std::string_view src, DigestStore& dst) {THOR_SHA256(reinterpret_cast<Byte const*>(&src[0]), std::size(src), dst);}
void hash(std::string const& src, DigestStore& dst) {THOR_SHA256(reinterpret_cast<Byte const*>(&src[0]), std::size(src), dst);}
// Use only if you know the destination is large enough!
void hashUnsafe(std::string_view src, Digestptr dst) {THOR_SHA256(reinterpret_cast<Byte const*>(&src[0]), std::size(src), dst);}
};
}
#endif
คำตอบ
ตัวพิมพ์ใหญ่ที่เหมาะสมของ SHA1
ชื่ออัลกอริทึมคือ SHA1 ไม่ใช่ Sha1 ดังนั้นฉันคิดว่าควรใช้ตัวพิมพ์ใหญ่ทั้งหมดที่นี่ นั่นทำให้การจับโค้ดสำหรับอัลกอริทึมเฉพาะได้ง่ายขึ้น
คุณต้องการเพียงหนึ่งคลาสต่ออัลกอริทึมแฮช
ตามที่คุณได้กล่าวไว้ในคำตอบของคุณเองSha1ชั้นเรียนดูเหมือนจะไม่จำเป็นเนื่องจากไม่ได้เก็บสถานะใด ๆ อย่างไรก็ตามแทนที่จะสร้างฟังก์ชันคงที่ภายในSha1เนมสเปซคุณสามารถทำให้ฟังก์ชันเหล่านั้นเป็นฟังก์ชันสมาชิกของคลาสที่มีสถานะจริงได้ สิ่งนี้หลีกเลี่ยงการทำซ้ำประเภท ตัวอย่างเช่น:
Sha1::DigestStore digest;
Sha1::hash("Bob", digest)
กลายเป็น:
Sha1::DigestStore digest;
digest.hash("Bob");
Sha1::Digest เทียบกับ Digest<Sha1>
ฉันคิดว่าการมีเนมสเปซที่Sha1มีDigestStoreฟังก์ชันa และภายในเป็นตัวเลือกที่ไม่ดี คุณสามารถทำกับ SHA1 ได้มากกว่าการสร้างแฮชธรรมดาเช่นคุณอาจต้องการสร้างHMACแทนแฮชธรรมดา ดังนั้นคุณจะต้องเพิ่มฟังก์ชันเพื่อสร้าง HMAC ให้กับแต่ละเนมสเปซที่ใช้อัลกอริทึมแฮช จะดีกว่ามากที่จะมีคลาสDigestและHMACเทมเพลตบนอัลกอริทึมแฮช
อนุญาตให้อัปเดตแฮช
โค้ดที่คุณเขียนจะทำการแปลงเพียงครั้งเดียวของอินพุตบางส่วนไปยังแฮช อย่างไรก็ตามไม่ใช่เรื่องแปลกที่โปรแกรมจะไม่มีข้อมูลทั้งหมดที่ต้องการสร้างแฮชในพื้นที่หน่วยความจำเดียวที่ต่อเนื่องกัน ในกรณีดังกล่าวคุณต้องการเขียน:
std::ostream output;
Digest<SHA1> digest;
digest.add("Header");
digest.add("Data");
digest.add("Footer");
output << digest.view();
อัลกอริธึมการย่อยบางอย่างอาจต้องการให้คุณเรียกใช้ฟังก์ชันบางอย่างเพื่อคำนวณค่าแฮชสุดท้ายหลังจากเพิ่มข้อมูลทั้งหมด คุณสามารถเพิ่มfinish()ฟังก์ชันที่ชัดเจนหรือเรียกสิ่งนี้โดยปริยายเมื่อเข้าถึงผลสรุป
รับผลลัพธ์ออกมา
คุณเก็บแฮชไว้ภายในเป็นไฟล์std::array<std::byte, size>. นั่นเป็นสิ่งที่ควรทำ ฉันไม่คิดว่าจำเป็นต้องให้ฟังก์ชันสมาชิกใด ๆ นอกเหนือจากฟังก์ชันที่ทำให้คุณได้รับการconstอ้างอิงถึงอาร์เรย์นั้น ขึ้นอยู่กับผู้โทรที่จะแปลงเป็นรูปแบบใดก็ได้ A std::arrayสามารถแปลงเป็นstd::spanไฟล์. และเมื่อคุณมีการอ้างอิงถึงอาร์เรย์แล้วการเริ่มต้นและสิ้นสุดตัววนซ้ำจากอาร์เรย์นั้นเป็นเรื่องง่าย
เพิ่มตัวดำเนินการเปรียบเทียบ
เป็นเรื่องปกติที่จะต้องการตรวจสอบว่าแฮชสองอันเหมือนกันหรือไม่ดังนั้นอย่างน้อยก็จะเป็นประโยชน์ในการกำหนดoperator==()และoperator!=()คลาสที่เก็บผลสรุป
ฉันคิดว่าฉันจะเปลี่ยนอินเทอร์เฟซ
ปัจจุบันรูปแบบการใช้งานคือ:
typename Sha1::DigestStore digest;
Sha1 hasher;
hasher.hash("Bob", digest);
ดูเหมือนจะไม่จำเป็นต้องสร้างSha1วัตถุ ฉันคิดว่าอินเทอร์เฟซที่ดีกว่าอาจจะทำให้ทุกวิธีstaticการใช้งานกลายเป็น:
typename Sha1::DigestStore digest;
Sha1::hash("Bob", digest);
DigestStoreอาจต้องมีฟังก์ชั่นการเข้าถึงอื่น ๆ ปัจจุบันอนุญาตiterationแต่อาจมีกรณีการใช้งานที่เรามีไฟล์const_iterator.
string_viewยังคงพยายามที่จะเข้าใจเมื่อดีที่สุดในการใช้งาน น่าเสียดายที่มันยังเล่นได้ไม่ดีกับสายปกติ ดังนั้นเราอาจต้องเตรียมวิธีในการดึงสตริงออกจากบัฟเฟอร์ ในกรณีนี้คงจะดีไม่น้อยหากเราสามารถมีข้อมูลจาก DigestStore เป็นสตริง (ซึ่งหมายถึงไม่ได้ใช้งานstd::array) แต่จำเป็นต้องมี use case ที่ดีเพื่อให้ทำงานได้ดีขึ้น
ยังไม่แน่ใจว่าจะได้ผล โปรดระบุคำใบ้หากคุณมีความคิด