Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use a custom allocator for std::array for secure cryptographic keys?

I know std::array is completely allocated in the stack, but this question is motivated by security concerns that require two things:

  1. The data in std::array will be zerod or randomized on destruction
  2. The data in std::array will be locked, such that it never goes to disk neither on crash or on swap memory

Usually, with std::vector, the solution is to create a custom allocator that does these things. However, for std::array, I don't see how to do this, and hence this question.

The best I could do is this:

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

But this obviously lacks memory locking and complicates the performance scheme of std::array that is to be gained by using std::array in the first place.

Is there any better solution?

like image 965
The Quantum Physicist Avatar asked Dec 07 '19 12:12

The Quantum Physicist


People also ask

What does STD allocator do?

std::allocator is used when you want to separate allocation and do construction in two steps. It is also used when separate destruction and deallocation is done in two steps. All the STL containers in C++ have a type parameter Allocator that is by default std::allocator.

What is container allocator?

Allocators handle all the requests for allocation and deallocation of memory for a given container. The C++ Standard Library provides general-purpose allocators that are used by default, however, custom allocators may also be supplied by the programmer.


2 Answers

std::array cannot use an allocator; however, it seems like your SecureArray class can achieve what you want through a custom constructor/deconstructor.

Something like this:

#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);
    }
};
like image 73
clyne Avatar answered Sep 29 '22 16:09

clyne


I know std::array is completely allocated in the stack

This is not quite true. std::array doesn't allocate any memory, so it depends on where you allocate it.

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

But this obviously lacks memory locking and complicates the performance scheme of std::array that is to be gained by using std::array in the first place.

Firstly, it is not a problem to lock memory on the stack. See POSIX example:

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

So, you can just call mlock or any portable analog in SecureArray constructor.

Secondly, what performance gain do you expect to get? Memory reading/writing speed doesn't depend on where you allocate your array, on the heap or on the stack. So, it is all about how fast you can allocate and lock the memory. If performance is critical, memory locking may be too slow (or not, who knows?) to call it every time in SecureArray constructor even if memory is allocated on the stack.

So, it is more handy to use std::vector with custom allocator. It may preallocate and prelock big memory chunks, so allocation speed will be almost as fast as on the stack.

like image 23
Stas Avatar answered Sep 29 '22 14:09

Stas