Kann ich einen benutzerdefinierten Allokator für std :: array für sichere kryptografische Schlüssel verwenden?

9

Ich weiß, dass std::arrayder Stapel vollständig zugeordnet ist, aber diese Frage ist durch Sicherheitsbedenken motiviert, die zwei Dinge erfordern:

  1. Die Daten in std::arraywerden bei Zerstörung auf Null gesetzt oder zufällig ausgewählt
  2. Die eingehenden Daten std::arraywerden gesperrt , sodass sie weder beim Absturz noch im Swap-Speicher auf die Festplatte übertragen werden

Normalerweise besteht std::vectordie Lösung darin, einen benutzerdefinierten Allokator zu erstellen , der diese Aufgaben ausführt . Doch für die std::array, ich sehe nicht , wie dies zu tun , und deshalb wird diese Frage.

Das Beste, was ich tun kann, ist Folgendes:

template <typename T, std::size_t Size>
struct SecureArray : public std::array<T, Size>
{
    static_assert(std::is_pod<T>::value, "Only POD types allowed")
    static_assert(sizeof(T) == 1, "Only 1-byte types allowed")
    virtual ~SecureArray()
    {
        std::vector<uint8_t> d = RandomBytes(Size); // generates Size random bytes
        std::memcpy(this->data(), d.data(), Size);
    }
}

Dies fehlt jedoch offensichtlich die Speichersperre und verkompliziert das Leistungsschema, std::arraydas durch std::arraydie erstmalige Verwendung erreicht werden soll.

Gibt es eine bessere Lösung?

Der Quantenphysiker
quelle
Kommentare sind nicht für eine ausführliche Diskussion gedacht. Dieses Gespräch wurde in den Chat verschoben .
Samuel Liew
Sorry, das war für die Mods; Der andere war der Mod, der die Flagge abgelehnt hat, nicht du. Das einzige, was Sie tun können, ist natürlich anzugeben, ob die Antworten richtig sind oder nicht, damit ich das Kopfgeld dem besten zuweisen kann. Ich kann mich natürlich selbst bewerten, aber ich bin kein so großer Experte. Der Grund für das Kopfgeld verschwindet ohnehin, sobald es zugewiesen wurde.
Maarten Bodewes
@ Maarten-reinstateMonica Leider löst keine der Antworten das Problem auf saubere Weise.
Der Quantenphysiker
@TheQuantumPhysicist Was würde es brauchen, um als sauberer Weg angesehen zu werden? Könnten Sie versuchen, diese Anforderungen explizit zu machen? Das hilft auch, über eine mögliche Lösung nachzudenken. Ich denke, ich weiß vielleicht, was du meinst, aber ich denke auch, dass du wahrscheinlich genauer sein kannst.
Maarten Bodewes
@ Maarten-reinstateMonica Mit einem Allokator, den wir schon irgendwie haben. Das Umschreiben von Grund auf ist eine schlechte Idee und erfordert eine Menge Tests. Das sollte der letzte Ausweg sein. Die folgenden Antworten schlagen offensichtliche Lösungen vor, die ich bereits erwähnt habe und die ich in den Kommentaren vermeide (bevor ich sie in den Chat verschiebe).
Der Quantenphysiker

Antworten:

5

std::arraykann keinen Allokator verwenden; Es scheint jedoch, dass Ihre SecureArray-Klasse durch einen benutzerdefinierten Konstruktor / Dekonstruktor das erreichen kann, was Sie wollen.

Etwas wie das:

#include <sys/mman.h>

template<class T, std::size_t Size>
struct SecureArray : public std::array<T, Size>
{
    // Your static_asserts...

    SecureArray(void) {
        mlock(std::array<T, Size>::data(), sizeof(T) * Size);
    }

    ~SecureArray(void) {
        char *bytes = reinterpret_cast<char *>(std::array<T, Size>::data());
        for (std::size_t i = 0; i < sizeof(T) * Size; i++)
            bytes[i] = 0;
        munlock(bytes, sizeof(T) * N);
    }
};
Clyne
quelle
4

Ich weiß, std::arrayist vollständig im Stapel zugeordnet

Das ist nicht ganz richtig. std::arrayweist keinen Speicher zu, daher hängt es davon ab, wo Sie ihn zuweisen.

auto* arr = new std::array<int, 100>(); // BUM! it is allocated on the heap

Dies fehlt jedoch offensichtlich die Speichersperre und verkompliziert das Leistungsschema, std::arraydas durch std::arraydie erstmalige Verwendung erreicht werden soll.

Erstens ist es kein Problem, den Speicher auf dem Stapel zu sperren. Siehe POSIX-Beispiel:

#include <iostream>
#include <sys/mman.h>
#include <array>

int main()
{
    std::array<int, 3> a = {1, 2, 3};        // std::array allocated on the stack
    if (mlock(a.data(), sizeof(a)) == 0)
    {
        std::cout << "LOCKED" << std::endl;
    }
}

Sie können also einfach ein mlockbeliebiges tragbares Analogon im SecureArrayKonstruktor aufrufen .

Zweitens, welchen Leistungsgewinn erwarten Sie? Die Lese- / Schreibgeschwindigkeit des Speichers hängt nicht davon ab, wo Sie Ihr Array, den Heap oder den Stapel zuweisen. Es geht also darum, wie schnell Sie den Speicher zuweisen und sperren können. Wenn die Leistung kritisch ist, ist die Speichersperre möglicherweise zu langsam (oder nicht, wer weiß?), Um sie jedes Mal im SecureArrayKonstruktor aufzurufen, selbst wenn Speicher auf dem Stapel zugewiesen ist.

Daher ist die Verwendung std::vectormit einem benutzerdefinierten Allokator praktischer . Es kann große Speicherblöcke vorab zuweisen und vorab sperren, sodass die Zuweisungsgeschwindigkeit fast so hoch ist wie auf dem Stapel.

Stas
quelle
Hier geht es überhaupt nicht um Geschwindigkeit, sondern um Sicherheit und darum sicherzustellen, dass Dinge nicht bewegt werden, da das Bewegen im Allgemeinen das Kopieren bedeutet.
Maarten Bodewes
2
@ Maarten-reinstateMonica hmm ... es scheint, ich hatte nicht die Absicht, std::arrayanstatt std::vectoran erster Stelle zu verwenden. Ich dachte, es geht um die Zuweisungsgeschwindigkeit.
Stas