web-dev-qa-db-ja.com

安全な暗号化キーのstd :: arrayにカスタムアロケーターを使用できますか?

私はstd::arrayがスタックに完全に割り当てられていることを知っていますが、この質問は次の2つを必要とするセキュリティ上の懸念から動機付けられています。

  1. std::arrayのデータは、破棄時にゼロ化またはランダム化されます
  2. std::arrayのデータは locked となり、クラッシュまたはスワップメモリ​​のどちらでもディスクに移動しません。

通常、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のパフォーマンススキームが複雑になります。

より良い解決策はありますか?

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

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を使用する方が便利です。大きなメモリチャンクを事前に割り当てて事前にロックする可能性があるため、割り当て速度はスタックの場合とほぼ同じです。

4
Stas