Могу ли я использовать собственный распределитель для std :: array для безопасных криптографических ключей?

9

Я знаю, std::arrayчто полностью распределен в стеке, но этот вопрос мотивирован проблемами безопасности, которые требуют двух вещей:

  1. Данные в std::arrayнуле или будут рандомизированы при уничтожении
  2. Данные в std::arrayбудут заблокированы , так что они никогда не попадут на диск ни в случае сбоя, ни в разделе подкачки

Обычно std::vectorрешение заключается в создании собственного распределителя, который делает эти вещи . Тем не менее, std::arrayя не вижу, как это сделать, и, следовательно, этот вопрос.

Лучшее, что я мог сделать, это:

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

Но это, очевидно, не std::arrayтребует блокировки памяти и усложняет схему производительности, которую нужно получить, используя std::arrayв первую очередь.

Есть ли лучшее решение?

Квантовый физик
источник
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Самуэль Лью
Извините, это было для модов; другой был мод, который отклонил флаг, а не вы. Единственное, что вы можете сделать, это, конечно, указать, правильные ответы или нет, поэтому я могу назначить награду лучшему. Конечно, я могу оценить себя, но я не такой уж и хороший специалист. Причина награды исчезает в любом случае, как только она назначена.
Maarten Bodewes
@ Maarten-reinstateMonica К сожалению, ни один из ответов не решает проблему чисто.
Квантовый физик
@TheQuantumPhysicist Что нужно, чтобы считаться чистым способом? Не могли бы вы попытаться сделать эти требования явными? Это также помогает подумать о возможном решении. Я думаю, я мог бы знать, что вы имеете в виду, но я также думаю, что вы, возможно, можете быть более точным.
Maarten Bodewes
@ Maarten-reinstateMonica Использование распределителя, который у нас уже есть. Переписывать материал с нуля - плохая идея, и он требует адских испытаний. Это должно быть последним средством. Ответы ниже предлагают очевидные решения, о которых я уже упоминал, которых я избегаю в комментариях (перед тем, как переместить их в чат).
Квантовый физик

Ответы:

5

std::arrayне может использовать распределитель; однако кажется, что ваш класс SecureArray может достичь того, чего вы хотите, с помощью собственного конструктора / деконструктора.

Что-то вроде этого:

#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);
    }
};
Клайн
источник
4

Я знаю std::array, полностью выделен в стеке

Это не совсем так. std::arrayне выделяет никакой памяти, поэтому это зависит от того, где вы ее выделите.

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

Но это, очевидно, не std::arrayтребует блокировки памяти и усложняет схему производительности, которую нужно получить, используя std::arrayв первую очередь.

Во-первых, это не проблема для блокировки памяти в стеке. Смотрите пример 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;
    }
}

Итак, вы можете просто позвонить mlockили любой портативный аналог в SecureArrayконструкторе.

Во-вторых, какой прирост производительности вы ожидаете получить? Скорость чтения / записи в память не зависит от того, где вы разместите свой массив, в куче или в стеке. Итак, все дело в том, как быстро вы можете выделить и заблокировать память. Если производительность критична, блокировка памяти может быть слишком медленной (или нет, кто знает?), Чтобы вызывать ее каждый раз в SecureArrayконструкторе, даже если память выделена в стеке.

Таким образом, это более удобно для использования std::vectorс пользовательским распределителем. Он может предварительно выделять и предварительно блокировать большие фрагменты памяти, поэтому скорость выделения будет почти такой же высокой, как в стеке.

Стас
источник
Речь идет не о скорости, а о безопасности и обеспечении того, чтобы все не перемещалось, поскольку перемещение обычно означает копирование.
Мартен Бодьюс
2
@ Maarten-reinstateMonica хм ... кажется, я не собирался использовать std::arrayвместо того, чтобы std::vectorв первую очередь. Я думал, что это о скорости распределения.
Стас