C ++ Crypto: Teil 1 - Hash

Aug 26 2020

Suchen Sie nach modernen Crypto-Bibliotheken.
Konnte nichts Gutes finden.

Ich weiß, dass ich das wahrscheinlich alles falsch gemacht habe, also arbeite hier mit mir. Es gibt vier verschiedene Überprüfungen für vier Strukturen, die aufeinander aufbauen:

  1. Hashing
  2. Hashed Key
  3. Passwortschlüssel
  4. Antwort auf gesalzene Herausforderung

Dies ist der Hashing-Code und bietet einen einfachen Wrapper um SHA-1 und SHA-256, aber das Muster ist so einfach, dass wir es für andere Hashing-Mechanismen erweitern können.

Die in diesen Fragen vorgestellten Datenstrukturen und Implementierungen basieren auf RFC2104 und diesem Beitrag zum Codeprojekt .

Anwendungsbeispiel:

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

Antworten

3 G.Sliepen Aug 27 2020 at 03:58

Richtige Kapitalisierung von SHA1

Der Name des Algorithmus ist SHA1, nicht Sha1, daher denke ich, dass es besser ist, hier alle Großbuchstaben zu verwenden. Das erleichtert das Durchsuchen des Codes für einen bestimmten Algorithmus.

Sie benötigen nur eine Klasse pro Hash-Algorithmus

Wie Sie in Ihrer eigenen Antwort erwähnt haben, erscheint die Sha1Klasse in der Tat überflüssig, da sie keinen Status speichert. Anstatt jedoch statische Funktionen in einem Sha1Namespace zu erstellen , können Sie diese Funktionen zu Mitgliedsfunktionen der Klasse machen, die den tatsächlichen Status enthält. Dadurch wird vermieden, dass der Typ wiederholt wird. zum Beispiel:

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

Wird:

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

Sha1::Digest vs. Digest<Sha1>

Ich denke, einen Namespace Sha1mit a DigestStoreund Funktionen darin zu haben, ist eine schlechte Wahl. Mit SHA1 können Sie mehr tun, als nur einfache Hashes zu erstellen. Beispielsweise möchten Sie möglicherweise eine HMAC anstelle eines einfachen Hashs erstellen . Sie müssten also Funktionen hinzufügen, um jedem Namespace, der einen Hash-Algorithmus implementiert, einen HMAC zu erstellen. Es ist viel besser Klassen zu haben , Digestund HMACdass auf dem Hash - Algorithmus als Templat.

Ermöglichen, dass Hashes aktualisiert werden

Der von Ihnen geschriebene Code führt nur einmalige Konvertierungen einiger Eingaben in einen Hash durch. Es ist jedoch nicht ungewöhnlich, dass Programme nicht alle Daten, für die sie einen Hash erstellen möchten, in einem einzigen zusammenhängenden Speicherbereich haben. In diesen Fällen möchten Sie schreiben:

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

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

output << digest.view();

Bei einigen Digest-Algorithmen müssen Sie möglicherweise eine Funktion aufrufen, um den endgültigen Hash-Wert nach dem Hinzufügen aller Daten zu berechnen. Sie können eine explizite finish()Funktion hinzufügen oder diese implizit aufrufen, wenn Sie auf das Digest-Ergebnis zugreifen.

Das Ergebnis herausholen

Sie speichern den Hash intern als std::array<std::byte, size>. Das ist das Richtige. Ich denke nicht, dass es notwendig ist, andere Mitgliedsfunktionen als eine bereitzustellen, mit denen Sie einen constVerweis auf dieses Array erhalten. Es liegt an dem Anrufer, es in eine beliebige Form umzuwandeln. A std::arrayist bereits implizit in a konvertierbar std::span. Und sobald Sie einen Verweis auf das Array haben, ist es einfach, die Start- und Enditeratoren daraus abzurufen.

Vergleichsoperatoren hinzufügen

Es ist durchaus üblich zu wollen , um zu überprüfen , ob zwei Hashes identisch sind, so dass es zu hilfreich wäre zumindest definieren operator==()und operator!=()zu der Klasse, die das verdauen Ergebnis hält.

1 MartinYork Aug 26 2020 at 05:27

Ich denke, ich werde die Schnittstelle ändern.

Derzeit lautet das Verwendungsmuster:

typename Sha1::DigestStore      digest;
Sha1                            hasher;

hasher.hash("Bob", digest);

Es scheint nicht notwendig zu sein, ein Sha1Objekt zu erstellen . Ich denke, eine bessere Schnittstelle könnte darin bestehen, alle Methoden staticso zu gestalten, dass die Verwendung wie folgt wird:

typename Sha1::DigestStore      digest;

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

Das DigestStorekann einige andere Zugriffe Funktionen benötigen. Es erlaubt derzeit, iterationaber es kann einen Anwendungsfall geben, in dem wir eine haben const_iterator.

Ich versuche immer noch zu verstehen, wann ich es am besten benutze string_view. Leider spielt es mit normalen Saiten immer noch nicht gut. Daher müssen wir möglicherweise eine Möglichkeit bereitstellen, um auch eine Zeichenfolge aus dem Puffer zu extrahieren. In diesem Fall wäre es schön, wenn wir die Daten aus dem DigestStore in eine Zeichenfolge umwandeln könnten (was bedeutet, dass sie nicht verwendet werden std::array), aber einen guten Anwendungsfall benötigen, damit dies besser funktioniert.

Ich bin mir noch nicht sicher, wie das funktionieren wird. Bitte geben Sie einen Hinweis, wenn Sie eine Idee haben.