¿Puedo usar un asignador personalizado para std :: array para claves criptográficas seguras?

9

Sé que std::arrayestá completamente asignado en la pila, pero esta pregunta está motivada por preocupaciones de seguridad que requieren dos cosas:

  1. Los datos std::arrayserán zerod o aleatorios en la destrucción.
  2. Los datos std::arrayse bloquearán , de modo que nunca irán al disco ni en caso de bloqueo ni de memoria de intercambio

Generalmente, con std::vector, la solución es crear un asignador personalizado que haga estas cosas . Sin embargo, para std::array, no veo cómo hacer esto, y de ahí esta pregunta.

Lo mejor que puedo hacer es esto:

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

Pero esto obviamente carece de bloqueo de memoria y complica el esquema de rendimiento std::arrayque se debe obtener al usar std::arrayen primer lugar.

¿Hay alguna solución mejor?

El físico cuántico
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Samuel Liew
Lo siento, eso fue por las modificaciones; el otro fue el mod que rechazó la bandera, no tú. Por supuesto, lo único que puede hacer es indicar si las respuestas son correctas o no, de modo que pueda asignar la recompensa a la mejor. Puedo evaluarme a mí mismo, por supuesto, pero no soy un gran experto. El motivo de la recompensa desaparece de todos modos una vez que se asigna.
Maarten Bodewes
@ Maarten-reinstateMonica Lamentablemente, ninguna de las respuestas resuelve el problema de manera limpia.
The Quantum Physicist
@TheQuantumPhysicist ¿Qué se necesitaría para ser considerado una forma limpia? ¿Podrías tratar de hacer esos requisitos explícitos? Eso ayuda a pensar en una posible solución también. Creo que podría saber a qué te refieres, pero también creo que probablemente puedas ser más preciso.
Maarten Bodewes
@ Maarten-reinstateMonica Usando un asignador que ya tenemos de alguna manera. Reescribir cosas desde cero es una mala idea y requerirá muchísimas pruebas. Ese debería ser el último recurso. Las respuestas a continuación sugieren soluciones obvias que ya mencioné que estoy evitando en los comentarios (antes de moverlas al chat).
The Quantum Physicist

Respuestas:

5

std::arrayno puede usar un asignador; sin embargo, parece que su clase SecureArray puede lograr lo que desea a través de un constructor / deconstructor personalizado.

Algo como esto:

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

Sé que std::arrayestá completamente asignado en la pila

Esto no es completamente cierto. std::arrayno asigna ninguna memoria, por lo que depende de dónde la asigne.

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

Pero esto obviamente carece de bloqueo de memoria y complica el esquema de rendimiento std::arrayque se debe obtener al usar std::arrayen primer lugar.

En primer lugar, no es un problema bloquear la memoria en la pila. Ver ejemplo 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;
    }
}

Por lo tanto, puede llamar mlocko cualquier análogo portátil en el SecureArrayconstructor.

En segundo lugar, ¿qué aumento de rendimiento espera obtener? La velocidad de lectura / escritura de la memoria no depende de dónde asigne su matriz, en el montón o en la pila. Entonces, se trata de qué tan rápido puede asignar y bloquear la memoria. Si el rendimiento es crítico, el bloqueo de memoria puede ser demasiado lento (o no, ¿quién sabe?) Para llamarlo cada vez en el SecureArrayconstructor, incluso si la memoria está asignada en la pila.

Por lo tanto, es más útil de usar std::vectorcon un asignador personalizado. Puede preasignar y prebloquear fragmentos de memoria grandes, por lo que la velocidad de asignación será casi tan rápida como en la pila.

Stas
fuente
No se trata de velocidad en absoluto, se trata de seguridad y de asegurarse de que las cosas no se muevan, ya que mover generalmente significa copiar.
Maarten Bodewes
2
@ Maarten-reinstateMonica hmm ... parece que no tuve la intención de usar en std::arraylugar de std::vectoren primer lugar. Pensé que se trataba de la velocidad de asignación.
Fecha