Czy mogę użyć niestandardowego alokatora dla std :: array dla bezpiecznych kluczy kryptograficznych?

9

Wiem, że std::arrayjest całkowicie przydzielony na stosie, ale to pytanie jest uzasadnione względami bezpieczeństwa, które wymagają dwóch rzeczy:

  1. Dane std::arrayzostaną zerowane lub losowo po zniszczeniu
  2. Dane std::arrayzostaną zablokowane , tak aby nigdy nie trafiały na dysk ani w przypadku awarii, ani w pamięci wymiany

Zwykle std::vectorrozwiązaniem jest utworzenie niestandardowego programu przydzielającego, który wykonuje te czynności . Jednak std::arraynie widzę, jak to zrobić, i stąd to pytanie.

Najlepsze, co mogłem zrobić, to:

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);
    }
}

Ale to oczywiście nie blokuje pamięci i komplikuje schemat wydajności, std::arrayktóry należy uzyskać, używając std::arrayw pierwszej kolejności.

Czy jest jakieś lepsze rozwiązanie?

Fizyk kwantowy
źródło
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Samuel Liew
Przepraszam, to dotyczyło modów; drugim był mod, który odrzucił flagę, nie ty. Jedyne, co możesz zrobić, to oczywiście wskazać, czy odpowiedzi są prawidłowe, czy nie, więc mogę przypisać nagrodę najlepszemu. Oczywiście mogę się ewaluować, ale nie jestem tak wielkim ekspertem. Powód nagrody znika i tak po jej przypisaniu.
Maarten Bodewes,
@ Maarten-reinstateMonica Niestety żadna z odpowiedzi nie rozwiązuje problemu w czysty sposób.
The Quantum Physicist,
@TheQuantumPhysicist Co by można było uznać za czysty sposób? Czy możesz spróbować sprecyzować te wymagania? Pomaga to również myśleć o możliwym rozwiązaniu. Myślę, że mogę wiedzieć, co masz na myśli, ale myślę też, że możesz być bardziej precyzyjny.
Maarten Bodewes,
@ Maarten-reinstateMonica Korzystanie z podzielnika, który już w jakiś sposób mamy. Przepisywanie rzeczy od zera jest złym pomysłem i będzie wymagało piekła testów. To powinno być ostateczność. Poniższe odpowiedzi sugerują oczywiste rozwiązania, o których już wspomniałem, których unikam w komentarzach (przed przeniesieniem ich na czat).
The Quantum Physicist,

Odpowiedzi:

5

std::arraynie może użyć alokatora; Wydaje się jednak, że klasa SecureArray może osiągnąć to, co chcesz, za pomocą niestandardowego konstruktora / dekonstratora.

Coś takiego:

#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
źródło
4

Wiem, że std::arrayjest całkowicie przydzielony na stosie

To nie do końca prawda. std::arraynie przydziela żadnej pamięci, więc zależy to od tego, gdzie ją przydzielisz.

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

Ale to oczywiście nie blokuje pamięci i komplikuje schemat wydajności, std::arrayktóry należy uzyskać, używając std::arrayw pierwszej kolejności.

Po pierwsze, zablokowanie pamięci na stosie nie stanowi problemu. Zobacz przykład POSIX:

#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;
    }
}

Możesz więc po prostu wywołać mlocklub dowolny przenośny analog w SecureArraykonstruktorze.

Po drugie, jakiego wzrostu wydajności oczekujesz? Szybkość odczytu / zapisu pamięci nie zależy od tego, gdzie przydzielisz tablicę, na stercie lub na stosie. Chodzi więc o to, jak szybko można przydzielić i zablokować pamięć. Jeśli wydajność jest krytyczna, blokowanie pamięci może być zbyt wolne (czy nie, kto wie?), Aby wywoływać ją za każdym razem w SecureArraykonstruktorze, nawet jeśli pamięć jest przydzielona na stosie.

Dlatego bardziej przydatne jest użycie std::vectorniestandardowego programu przydzielającego. Może wstępnie alokować i blokować duże porcje pamięci, więc szybkość alokacji będzie prawie tak duża jak na stosie.

Staś
źródło
W ogóle nie chodzi o szybkość, chodzi o bezpieczeństwo i upewnienie się, że rzeczy nie są przenoszone, ponieważ przenoszenie zwykle oznacza kopiowanie.
Maarten Bodewes,
2
@ Maarten-reinstateMonica hmm ... wygląda na to, że nie miałem zamiaru używać std::arrayzamiast std::vectorna pierwszym miejscu. Myślałem, że chodzi o szybkość przydziału.
Stas,