C ++ Crypto: Bagian 1- Hash

Aug 26 2020

Mencari perpustakaan Crypto modern.
Tidak dapat menemukan sesuatu yang bagus.

Saya tahu saya mungkin melakukan ini semua salah jadi bekerjalah dengan saya di sini. Akan ada empat ulasan berbeda untuk empat struktur yang dibangun di atas satu sama lain:

  1. Hashing
  2. Kunci Hashed
  3. Kunci Sandi
  4. Respon Tantangan Asin

Ini adalah kode hashing dan menyediakan pembungkus sederhana di sekitar SHA-1 dan SHA-256 tetapi polanya cukup sederhana sehingga kami dapat mengembangkannya untuk mekanisme hashing lainnya.

Struktur data dan implementasi yang disajikan dalam pertanyaan-pertanyaan ini didasarkan pada RFC2104 dan posting ini pada codeproject .

Contoh Penggunaan:

DigestStore<Sha1>    hash;    // <- destination of hash
Sha1                 hasher;

hasher.hash("This string can be hashsed", hash);

hash.h

#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

Jawaban

3 G.Sliepen Aug 27 2020 at 03:58

Kapitalisasi SHA1 yang tepat

Nama algoritmanya adalah SHA1, bukan Sha1, jadi menurut saya lebih baik menggunakan huruf besar semua di sini. Itu membuat grepping kode untuk algoritma tertentu lebih mudah.

Anda hanya perlu satu kelas per algoritme hash

Memang, seperti yang Anda sebutkan dalam jawaban Anda sendiri, Sha1kelas tersebut tampaknya berlebihan, karena tidak menyimpan status apa pun. Namun, alih-alih membuat fungsi statis di dalam Sha1namespace, Anda bisa membuat fungsi tersebut menjadi fungsi anggota kelas yang memiliki status sebenarnya. Ini untuk menghindari pengulangan tipe; sebagai contoh:

Sha1::DigestStore digest;
Sha1::hash("Bob", digest)

Menjadi:

Sha1::DigestStore digest;
digest.hash("Bob");

Sha1::Digest vs. Digest<Sha1>

Saya pikir memiliki namespace Sha1dengan DigestStorefungsi dan di dalamnya adalah pilihan yang buruk. Ada banyak hal yang dapat Anda lakukan dengan SHA1 selain hanya membuat hash biasa, misalnya Anda mungkin ingin membuat HMAC alih-alih hash biasa. Jadi, Anda harus menambahkan fungsi untuk membuat HMAC ke setiap namespace yang menerapkan algoritme hash. Jauh lebih baik untuk memiliki kelas Digestdan HMACitu memiliki template pada algoritma hash.

Izinkan hash diperbarui

Kode yang Anda tulis hanya melakukan konversi sekali pakai dari beberapa input ke hash. Namun, tidak jarang program tidak memiliki semua data yang ingin mereka buat hashnya dalam satu wilayah memori yang berdekatan. Dalam kasus tersebut, Anda ingin menulis:

std::ostream output;
Digest<SHA1> digest;

digest.add("Header");
digest.add("Data");
digest.add("Footer");

output << digest.view();

Beberapa algoritme intisari mungkin mengharuskan Anda memanggil beberapa fungsi untuk menghitung nilai hash akhir setelah menambahkan semua data. Anda dapat menambahkan finish()fungsi eksplisit atau memanggilnya secara implisit saat mengakses hasil intisari.

Mendapatkan hasilnya

Anda menyimpan hash secara internal sebagai file std::array<std::byte, size>. Itu hal yang tepat untuk dilakukan. Saya rasa tidak perlu menyediakan fungsi anggota selain yang memberi Anda constreferensi ke array itu. Terserah penelepon untuk mengubahnya menjadi bentuk apa pun yang mereka inginkan. A std::arraysudah secara implisit dapat diubah menjadi a std::span. Dan begitu Anda memiliki referensi ke larik, mudah untuk memulai dan mengakhiri iterator darinya.

Tambahkan operator perbandingan

Sangat umum untuk ingin memeriksa apakah dua hash itu identik, jadi akan sangat membantu untuk setidaknya mendefinisikan operator==()dan operator!=()ke kelas yang menyimpan hasil intisari.

1 MartinYork Aug 26 2020 at 05:27

Saya pikir saya akan mengubah antarmuka.

Saat ini pola penggunaan adalah:

typename Sha1::DigestStore      digest;
Sha1                            hasher;

hasher.hash("Bob", digest);

Sepertinya tidak ada kebutuhan untuk membuat Sha1objek. Saya pikir antarmuka yang lebih baik mungkin membuat semua metode staticsehingga penggunaannya menjadi:

typename Sha1::DigestStore      digest;

Sha1::hash("Bob", digest);

The DigestStoremungkin perlu beberapa fungsi akses lainnya. Saat ini memungkinkan iterationtetapi mungkin ada kasus penggunaan di mana kami memiliki file const_iterator.

Masih mencoba memahami kapan sebaiknya digunakan string_view. Sayangnya itu masih tidak bermain bagus dengan senar normal. Jadi kita mungkin harus menyediakan cara untuk juga mengekstrak string dari buffer. Dalam hal ini, alangkah baiknya jika kita dapat memiliki data dari DigestStore menjadi string (yang berarti tidak menggunakan std::array) tetapi perlu memiliki kasus penggunaan yang baik untuk membuatnya berfungsi lebih baik.

Belum yakin bagaimana itu akan berhasil. Tolong berikan petunjuk jika Anda punya ide.