Posso usar um alocador personalizado para std :: array para chaves criptográficas seguras?

9

Eu sei que std::arrayestá completamente alocado na pilha, mas essa pergunta é motivada por preocupações de segurança que exigem duas coisas:

  1. Os dados std::arrayserão zerod ou randomizados na destruição
  2. Os dados std::arrayserão bloqueados , de forma que nunca entrem no disco nem na falha ou na memória de troca

Geralmente, com std::vector, a solução é criar um alocador personalizado que faça essas coisas . No entanto, para std::array, eu não vejo como fazer isso e, portanto, esta pergunta.

O melhor que pude fazer é o seguinte:

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

Mas isso obviamente não possui bloqueio de memória e complica o esquema de desempenho std::arraya ser obtido usando-o std::arrayem primeiro lugar.

Existe alguma solução melhor?

O físico quântico
fonte
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Samuel Liew
Desculpe, isso foi para os mods; o outro foi o mod que rejeitou a bandeira, não você. É claro que a única coisa que você pode fazer é indicar se as respostas estão corretas ou não, para que eu possa atribuir a recompensa à melhor. É claro que posso me avaliar, mas não sou um especialista tão bom assim. O motivo da recompensa desaparece assim mesmo quando é atribuído.
Maarten Bodewes
@ Maarten-reinstateMonica Infelizmente, nenhuma das respostas resolve o problema de maneira limpa.
The Quantum Physicist
@TheQuantumPhysicist O que seria necessário para ser considerado uma maneira limpa? Você poderia tentar explicitar esses requisitos? Isso ajuda a pensar em uma possível solução também. Acho que sei o que você quer dizer, mas também acho que você pode ser mais preciso.
Maarten Bodewes
@ Maarten-reinstateMonica Usando um alocador que já temos de alguma forma. Reescrever as coisas do zero é uma má idéia e exigirá muito teste. Esse deve ser o último recurso. As respostas abaixo estão sugerindo soluções óbvias que eu já mencionei que estou evitando nos comentários (antes de movê-las para o bate-papo).
The Quantum Physicist

Respostas:

5

std::arraynão pode usar um alocador; no entanto, parece que sua classe SecureArray pode alcançar o que você deseja através de um construtor / desconstrutor personalizado.

Algo assim:

#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
fonte
4

Eu sei que std::arrayestá completamente alocado na pilha

Isso não é bem verdade. std::arraynão aloca nenhuma memória, portanto depende de onde você a aloca.

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

Mas isso obviamente não possui bloqueio de memória e complica o esquema de desempenho std::arraya ser obtido usando-o std::arrayem primeiro lugar.

Em primeiro lugar, não é um problema bloquear a memória na pilha. Veja o exemplo 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;
    }
}

Então, você pode simplesmente chamar mlockou qualquer analógico portátil no SecureArrayconstrutor.

Em segundo lugar, que ganho de desempenho você espera obter? A velocidade de leitura / gravação na memória não depende de onde você aloca sua matriz, na pilha ou na pilha. Portanto, é tudo sobre a rapidez com que você pode alocar e bloquear a memória. Se o desempenho for crítico, o bloqueio da memória pode ser muito lento (ou não, quem sabe?) Para chamá-lo sempre no SecureArrayconstrutor, mesmo que a memória esteja alocada na pilha.

Portanto, é mais útil usar std::vectorcom alocador personalizado. Ele pode pré-alocar e pré-bloquear grandes blocos de memória; portanto, a velocidade de alocação será quase tão rápida quanto na pilha.

Stas
fonte
Não se trata de velocidade, trata-se de segurança e de garantir que as coisas não sejam movidas, pois mover geralmente significa copiar.
Maarten Bodewes
2
@ Maarten-reinstateMonica hmm ... parece que não tive a intenção de usar em std::arrayvez de std::vectorem primeiro lugar. Eu pensei que era sobre velocidade de alocação.
Stas