- UnswappableAllocator

- EncryptionKey uses UnswappableAllocator
This commit is contained in:
Sebastian Messmer 2018-09-11 18:53:21 -07:00
parent 1f363fce62
commit 5ad55b1d58
19 changed files with 142 additions and 127 deletions

View File

@ -2,7 +2,7 @@
#ifndef MESSMER_CPPUTILS_CRYPTO_KDF_PASSWORDBASEDKDF_H
#define MESSMER_CPPUTILS_CRYPTO_KDF_PASSWORDBASEDKDF_H
#include "../../data/FixedSizeData.h"
#include "../../crypto/symmetric/EncryptionKey.h"
#include "../../data/Data.h"
namespace cpputils {
@ -11,16 +11,16 @@ namespace cpputils {
public:
virtual ~PasswordBasedKDF() {}
template<size_t KEYSIZE> FixedSizeData<KEYSIZE> deriveKey(const std::string &password);
template<size_t KEYSIZE> EncryptionKey<KEYSIZE> deriveKey(const std::string &password);
virtual const Data &kdfParameters() const = 0;
protected:
virtual void derive(void *destination, size_t size, const std::string &password) = 0;
};
template<size_t KEYSIZE> FixedSizeData<KEYSIZE>
template<size_t KEYSIZE> EncryptionKey<KEYSIZE>
inline PasswordBasedKDF::deriveKey(const std::string &password) {
auto result = FixedSizeData<KEYSIZE>::Null();
auto result = EncryptionKey<KEYSIZE>::Null();
derive(result.data(), result.BINARY_LENGTH, password);
return result;
}

View File

@ -22,89 +22,89 @@ namespace cpputils {
template<size_t KeySize>
class EncryptionKey final {
private:
struct EncryptionKeyData final {
public:
constexpr static size_t BINARY_LENGTH = FixedSizeData<KeySize>::BINARY_LENGTH;
constexpr static size_t STRING_LENGTH = FixedSizeData<KeySize>::STRING_LENGTH;
EncryptionKeyData(const FixedSizeData<KeySize>& keyData);
~EncryptionKeyData();
// Disallow copying and moving
EncryptionKeyData(const EncryptionKeyData &rhs) = delete;
EncryptionKeyData(EncryptionKeyData &&rhs) = delete;
EncryptionKeyData &operator=(const EncryptionKeyData &rhs) = delete;
EncryptionKeyData &operator=(EncryptionKeyData &&rhs) = delete;
FixedSizeData<KeySize> key;
DontSwapMemoryRAII memoryProtector; // this makes sure that the key data isn't swapped to disk
};
explicit EncryptionKey(std::shared_ptr<Data> keyData)
: _keyData(std::move(keyData)) {
ASSERT(_keyData->size() == KeySize, "Wrong key data size");
}
template<size_t OtherKeySize> friend class EncryptionKey;
public:
constexpr static size_t BINARY_LENGTH = EncryptionKeyData::BINARY_LENGTH;
constexpr static size_t STRING_LENGTH = EncryptionKeyData::STRING_LENGTH;
EncryptionKey(const EncryptionKey& rhs) = default;
EncryptionKey(EncryptionKey&& rhs) = default;
EncryptionKey& operator=(const EncryptionKey& rhs) = default;
EncryptionKey& operator=(EncryptionKey&& rhs) = default;
EncryptionKey(const FixedSizeData<KeySize>& keyData);
static constexpr size_t BINARY_LENGTH = KeySize;
static constexpr size_t STRING_LENGTH = 2 * BINARY_LENGTH;
static EncryptionKey FromBinary(const void *source);
static EncryptionKey FromString(const std::string& keyData);
std::string ToString() const;
static EncryptionKey Null() {
auto data = std::make_shared<Data>(
KeySize,
make_unique_ref<UnswappableAllocator>()
);
data->FillWithZeroes();
return EncryptionKey(std::move(data));
}
static EncryptionKey FromString(const std::string& keyData) {
ASSERT(keyData.size() == STRING_LENGTH, "Wrong input size or EncryptionKey::FromString()");
auto data = std::make_shared<Data>(
Data::FromString(keyData, make_unique_ref<UnswappableAllocator>())
);
ASSERT(data->size() == KeySize, "Wrong input size for EncryptionKey::FromString()");
return EncryptionKey(std::move(data));
}
std::string ToString() const {
auto result = _keyData->ToString();
ASSERT(result.size() == STRING_LENGTH, "Wrong string length");
return result;
}
static EncryptionKey CreateKey(RandomGenerator &randomGenerator) {
EncryptionKey result(FixedSizeData<BINARY_LENGTH>::Null());
randomGenerator.write(result._key->key.data(), BINARY_LENGTH);
EncryptionKey result(std::make_shared<Data>(
KeySize,
make_unique_ref<UnswappableAllocator>() // the allocator makes sure key data is never swapped to disk
));
randomGenerator.write(result._keyData->data(), KeySize);
return result;
}
const void *data() const {
return _key->key.data();
return _keyData->data();
}
void *data() {
return const_cast<void*>(const_cast<const EncryptionKey*>(this)->data());
}
private:
// TODO Test take/drop
std::shared_ptr<EncryptionKeyData> _key;
template<size_t NumTaken>
EncryptionKey<NumTaken> take() const {
static_assert(NumTaken <= KeySize, "Out of bounds");
auto result = std::make_shared<Data>(NumTaken, make_unique_ref<UnswappableAllocator>());
std::memcpy(result->data(), _keyData->data(), NumTaken);
return EncryptionKey<NumTaken>(std::move(result));
}
template<size_t NumDropped>
EncryptionKey<KeySize - NumDropped> drop() const {
static_assert(NumDropped <= KeySize, "Out of bounds");
auto result = std::make_shared<Data>(KeySize - NumDropped, make_unique_ref<UnswappableAllocator>());
std::memcpy(result->data(), _keyData->dataOffset(NumDropped), KeySize - NumDropped);
return EncryptionKey<KeySize - NumDropped>(std::move(result));
}
private:
std::shared_ptr<Data> _keyData;
};
template<size_t KeySize> constexpr size_t EncryptionKey<KeySize>::BINARY_LENGTH;
template<size_t KeySize> constexpr size_t EncryptionKey<KeySize>::STRING_LENGTH;
template<size_t KeySize>
inline EncryptionKey<KeySize>::EncryptionKeyData::EncryptionKeyData(const FixedSizeData<KeySize>& keyData)
: key(keyData)
, memoryProtector(&key, sizeof(key)) {
}
template<size_t KeySize>
inline EncryptionKey<KeySize>::EncryptionKeyData::~EncryptionKeyData() {
// After destruction, the swap-protection is lifted, but we also don't need the key data anymore.
// Overwrite it with zeroes.
std::memset(key.data(), 0, KeySize);
}
template<size_t KeySize>
inline EncryptionKey<KeySize>::EncryptionKey(const FixedSizeData<KeySize>& keyData)
: _key(std::make_shared<EncryptionKeyData>(keyData)) {
}
template<size_t KeySize>
EncryptionKey<KeySize> EncryptionKey<KeySize>::FromBinary(const void *source) {
return EncryptionKey(FixedSizeData<KeySize>::FromBinary(source));
}
template<size_t KeySize>
EncryptionKey<KeySize> EncryptionKey<KeySize>::FromString(const std::string& keyData) {
return EncryptionKey(FixedSizeData<KeySize>::FromString(keyData));
}
template<size_t KeySize>
std::string EncryptionKey<KeySize>::ToString() const {
return _key->key.ToString();
}
}
#endif

View File

@ -13,15 +13,15 @@
namespace cpputils {
struct FakeKey {
static FakeKey FromBinary(const void *data) {
return FakeKey{deserialize<uint64_t>(data)};
static FakeKey FromString(const std::string& keyData) {
return FakeKey{static_cast<uint64_t>(std::atoi(keyData.c_str()))};
}
static constexpr unsigned int BINARY_LENGTH = sizeof(uint64_t);
static FakeKey CreateKey(RandomGenerator &randomGenerator) {
auto data = randomGenerator.getFixedSize<sizeof(uint64_t)>();
return FromBinary(data.data());
return FakeKey {*reinterpret_cast<uint64_t*>(data.data())};
}
uint64_t value;

View File

@ -3,19 +3,15 @@
#define MESSMER_CPPUTILS_SYSTEM_MEMORY_H
#include <cstdlib>
#include "../data/Data.h"
namespace cpputils {
// While this RAII object exists, it locks a given memory address into RAM,
// i.e. tells the operating system not to swap it.
class DontSwapMemoryRAII final {
// This allocator allocates memory that won't be swapped out to the disk, but will be kept in RAM
class UnswappableAllocator final : public Allocator {
public:
DontSwapMemoryRAII(void* addr, size_t len);
~DontSwapMemoryRAII();
private:
void* const addr_;
const size_t len_;
void* allocate(size_t size) override;
void free(void* data, size_t size) override;
};
}

View File

@ -10,19 +10,25 @@ using namespace cpputils::logging;
namespace cpputils {
DontSwapMemoryRAII::DontSwapMemoryRAII(void *addr, size_t len)
: addr_(addr), len_(len) {
const int result = ::mlock(addr_, len_);
void* UnswappableAllocator::allocate(size_t size) {
void* data = DefaultAllocator().allocate(size);
const int result = ::mlock(data, size);
if (0 != result) {
throw std::runtime_error("Error calling mlock. Errno: " + std::to_string(errno));
}
return data;
}
DontSwapMemoryRAII::~DontSwapMemoryRAII() {
const int result = ::munlock(addr_, len_);
void UnswappableAllocator::free(void* data, size_t size) {
const int result = ::munlock(data, size);
if (0 != result) {
LOG(WARN, "Error calling munlock. Errno: {}", errno);
}
// overwrite the memory with zeroes before we free it
std::memset(data, 0, size);
DefaultAllocator().free(data, size);
}
}

View File

@ -9,19 +9,25 @@ using namespace cpputils::logging;
namespace cpputils {
DontSwapMemoryRAII::DontSwapMemoryRAII(void *addr, size_t len)
: addr_(addr), len_(len) {
void* UnswappableAllocator::allocate(size_t size) {
void* data = DefaultAllocator().allocate(size);
const BOOL result = ::VirtualLock(addr_, len_);
if (!result) {
throw std::runtime_error("Error calling VirtualLock. Errno: " + std::to_string(GetLastError()));
}
return data;
}
DontSwapMemoryRAII::~DontSwapMemoryRAII() {
void UnswappableAllocator::free(void* data, size_t size) {
const BOOL result = ::VirtualUnlock(addr_, len_);
if (!result) {
LOG(WARN, "Error calling VirtualUnlock. Errno: {}", GetLastError());
}
// overwrite the memory with zeroes before we free it
std::memset(data, 0, size);
DefaultAllocator().free(data, size);
}
}

View File

@ -47,7 +47,7 @@ public:
return Cipher::EncryptionKey::CreateKey(randomGenerator).ToString();
}
unique_ref<InnerEncryptor> createInnerConfigEncryptor(const FixedSizeData<CryCiphers::MAX_KEY_SIZE> &key) const override {
unique_ref<InnerEncryptor> createInnerConfigEncryptor(const EncryptionKey<CryCiphers::MAX_KEY_SIZE>& key) const override {
return make_unique_ref<ConcreteInnerEncryptor<Cipher>>(key.take<Cipher::EncryptionKey::BINARY_LENGTH>());
}

View File

@ -8,6 +8,7 @@
#include <blockstore/interface/BlockStore2.h>
#include <cpp-utils/random/RandomGenerator.h>
#include "crypto/inner/InnerEncryptor.h"
#include <cpp-utils/crypto/symmetric/EncryptionKey.h>
namespace cryfs {
@ -40,7 +41,7 @@ public:
virtual const boost::optional<std::string> &warning() const = 0;
virtual cpputils::unique_ref<blockstore::BlockStore2> createEncryptedBlockstore(cpputils::unique_ref<blockstore::BlockStore2> baseBlockStore, const std::string &encKey) const = 0;
virtual std::string createKey(cpputils::RandomGenerator &randomGenerator) const = 0;
virtual cpputils::unique_ref<InnerEncryptor> createInnerConfigEncryptor(const cpputils::FixedSizeData<CryCiphers::MAX_KEY_SIZE> &key) const = 0;
virtual cpputils::unique_ref<InnerEncryptor> createInnerConfigEncryptor(const cpputils::EncryptionKey<CryCiphers::MAX_KEY_SIZE> &key) const = 0;
};

View File

@ -14,8 +14,8 @@ namespace cryfs {
constexpr size_t CryConfigEncryptor::OuterKeySize;
constexpr size_t CryConfigEncryptor::MaxTotalKeySize;
CryConfigEncryptor::CryConfigEncryptor(const FixedSizeData<MaxTotalKeySize>& derivedKey, cpputils::Data kdfParameters)
: _derivedKey(derivedKey), _kdfParameters(std::move(kdfParameters)) {
CryConfigEncryptor::CryConfigEncryptor(cpputils::EncryptionKey<MaxTotalKeySize> derivedKey, cpputils::Data kdfParameters)
: _derivedKey(std::move(derivedKey)), _kdfParameters(std::move(kdfParameters)) {
}
Data CryConfigEncryptor::encrypt(const Data &plaintext, const string &cipherName) const {
@ -47,11 +47,11 @@ namespace cryfs {
unique_ref<OuterEncryptor> CryConfigEncryptor::_outerEncryptor() const {
auto outerKey = _derivedKey.take<OuterKeySize>();
return make_unique_ref<OuterEncryptor>(outerKey, _kdfParameters.copy());
return make_unique_ref<OuterEncryptor>(std::move(outerKey), _kdfParameters.copy());
}
unique_ref<InnerEncryptor> CryConfigEncryptor::_innerEncryptor(const string &cipherName) const {
auto innerKey = _derivedKey.drop<OuterKeySize>();
return CryCiphers::find(cipherName).createInnerConfigEncryptor(innerKey);
return CryCiphers::find(cipherName).createInnerConfigEncryptor(std::move(innerKey));
}
}

View File

@ -23,7 +23,7 @@ namespace cryfs {
bool wasInDeprecatedConfigFormat;
};
CryConfigEncryptor(const cpputils::FixedSizeData<MaxTotalKeySize>& derivedKey, cpputils::Data _kdfParameters);
CryConfigEncryptor(cpputils::EncryptionKey<MaxTotalKeySize> derivedKey, cpputils::Data _kdfParameters);
cpputils::Data encrypt(const cpputils::Data &plaintext, const std::string &cipherName) const;
boost::optional<Decrypted> decrypt(const cpputils::Data &data) const;
@ -32,7 +32,7 @@ namespace cryfs {
cpputils::unique_ref<OuterEncryptor> _outerEncryptor() const;
cpputils::unique_ref<InnerEncryptor> _innerEncryptor(const std::string &cipherName) const;
cpputils::FixedSizeData<MaxTotalKeySize> _derivedKey;
cpputils::EncryptionKey<MaxTotalKeySize> _derivedKey;
cpputils::Data _kdfParameters;
DISALLOW_COPY_AND_ASSIGN(CryConfigEncryptor);

View File

@ -10,8 +10,8 @@ using boost::none;
using namespace cpputils::logging;
namespace cryfs {
OuterEncryptor::OuterEncryptor(const Cipher::EncryptionKey& key, cpputils::Data kdfParameters)
: _key(key), _kdfParameters(std::move(kdfParameters)) {
OuterEncryptor::OuterEncryptor(Cipher::EncryptionKey key, cpputils::Data kdfParameters)
: _key(std::move(key)), _kdfParameters(std::move(kdfParameters)) {
}
OuterConfig OuterEncryptor::encrypt(const Data &plaintext) const {

View File

@ -14,7 +14,7 @@ namespace cryfs {
using Cipher = cpputils::AES256_GCM;
static constexpr size_t CONFIG_SIZE = 1024; // Config data is grown to this size before encryption to hide its actual size
OuterEncryptor(const Cipher::EncryptionKey& key, cpputils::Data kdfParameters);
OuterEncryptor(Cipher::EncryptionKey key, cpputils::Data kdfParameters);
OuterConfig encrypt(const cpputils::Data &encryptedInnerConfig) const;
boost::optional<cpputils::Data> decrypt(const OuterConfig &outerConfig) const;

View File

@ -36,8 +36,9 @@ public:
private:
static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) {
Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed);
return Cipher::EncryptionKey::FromBinary(data.data());
return Cipher::EncryptionKey::FromString(
DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed).ToString()
);
}
};
@ -54,8 +55,9 @@ public:
private:
static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) {
Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed);
return Cipher::EncryptionKey::FromBinary(data.data());
return Cipher::EncryptionKey::FromString(
DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed).ToString()
);
}
};

View File

@ -13,36 +13,41 @@ public:
SCryptParameters result = SCryptParameters::deserialize(scrypt.kdfParameters());
return result;
}
template<size_t SIZE>
bool keyEquals(const EncryptionKey<SIZE>& lhs, const EncryptionKey<SIZE>& rhs) {
return 0 == std::memcmp(lhs.data(), rhs.data(), SIZE);
}
};
TEST_F(SCryptTest, GeneratedKeyIsReproductible_448) {
auto derivedKey = scryptForNewKey->deriveKey<56>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<56>("mypassword");
EXPECT_EQ(derivedKey, rederivedKey);
EXPECT_TRUE(keyEquals(derivedKey, rederivedKey));
}
TEST_F(SCryptTest, GeneratedKeyIsReproductible_256) {
auto derivedKey = scryptForNewKey->deriveKey<32>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<32>("mypassword");
EXPECT_EQ(derivedKey, rederivedKey);
EXPECT_TRUE(keyEquals(derivedKey, rederivedKey));
}
TEST_F(SCryptTest, GeneratedKeyIsReproductible_128) {
auto derivedKey = scryptForNewKey->deriveKey<16>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<16>("mypassword");
EXPECT_EQ(derivedKey, rederivedKey);
EXPECT_TRUE(keyEquals(derivedKey, rederivedKey));
}
TEST_F(SCryptTest, GeneratedKeyIsReproductible_DefaultSettings) {
auto derivedKey = scryptForNewKey->deriveKey<16>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<16>("mypassword");
EXPECT_EQ(derivedKey, rederivedKey);
EXPECT_TRUE(keyEquals(derivedKey, rederivedKey));
}
TEST_F(SCryptTest, DifferentPasswordResultsInDifferentKey) {
auto derivedKey = scryptForNewKey->deriveKey<16>("mypassword");
auto rederivedKey = scryptForExistingKey->deriveKey<16>("mypassword2");
EXPECT_NE(derivedKey, rederivedKey);
EXPECT_FALSE(keyEquals(derivedKey, rederivedKey));
}
TEST_F(SCryptTest, UsesCorrectSettings) {

View File

@ -19,7 +19,7 @@ public:
static typename Cipher::EncryptionKey createKeyFixture(int seed = 0) {
Data data = DataFixture::generate(Cipher::EncryptionKey::BINARY_LENGTH, seed);
return Cipher::EncryptionKey::FromBinary(data.data());
return Cipher::EncryptionKey::FromString(data.ToString());
}
void CheckEncryptThenDecryptIsIdentity(const Data &plaintext) {

View File

@ -3,24 +3,16 @@
#include <memory>
#include <cpp-utils/pointer/gcc_4_8_compatibility.h>
using cpputils::DontSwapMemoryRAII;
using cpputils::UnswappableAllocator;
TEST(MemoryTest, LockingSmallStackMemoryDoesntCrash) {
bool memory;
DontSwapMemoryRAII obj(&memory, sizeof(memory));
TEST(MemoryTest, LockingSmallMemoryDoesntCrash) {
UnswappableAllocator allocator;
void *data = allocator.allocate(5);
allocator.free(data, 5);
}
TEST(MemoryTest, LockingLargeStackMemoryDoesntCrash) {
bool memory[10*1024];
DontSwapMemoryRAII obj(memory, sizeof(memory));
}
TEST(MemoryTest, LockingSmallHeapMemoryDoesntCrash) {
auto memory = std::make_unique<bool>(false);
DontSwapMemoryRAII obj(memory.get(), sizeof(*memory));
}
TEST(MemoryTest, LockingLargeHeapMemoryDoesntCrash) {
auto memory = std::make_unique<bool[]>(10*1024);
DontSwapMemoryRAII obj(memory.get(), 10*1024);
TEST(MemoryTest, LockingLargeMemoryDoesntCrash) {
UnswappableAllocator allocator;
void *data = allocator.allocate(10240);
allocator.free(data, 10240);
}

View File

@ -8,6 +8,7 @@ using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::DataFixture;
using cpputils::Data;
using cpputils::EncryptionKey;
using cpputils::FixedSizeData;
using cpputils::AES128_CFB;
using cpputils::AES256_GCM;
@ -40,8 +41,10 @@ public:
}
private:
FixedSizeData<CryConfigEncryptor::MaxTotalKeySize> _derivedKey() {
return DataFixture::generateFixedSize<CryConfigEncryptor::MaxTotalKeySize>(3);
EncryptionKey<CryConfigEncryptor::MaxTotalKeySize> _derivedKey() {
return EncryptionKey<CryConfigEncryptor::MaxTotalKeySize>::FromString(
DataFixture::generateFixedSize<CryConfigEncryptor::MaxTotalKeySize>(3).ToString()
);
}
Data _kdfParameters() {

View File

@ -28,7 +28,9 @@ class ConcreteInnerEncryptorTest : public ::testing::Test {
public:
template<class Cipher>
unique_ref<InnerEncryptor> makeInnerEncryptor() {
auto key = DataFixture::generateFixedSize<Cipher::EncryptionKey::BINARY_LENGTH>();
auto key = Cipher::EncryptionKey::FromString(
DataFixture::generateFixedSize<Cipher::EncryptionKey::BINARY_LENGTH>().ToString()
);
return make_unique_ref<ConcreteInnerEncryptor<Cipher>>(key);
}
};

View File

@ -28,7 +28,9 @@ public:
}
unique_ref<OuterEncryptor> makeOuterEncryptor() {
auto key = DataFixture::generateFixedSize<OuterEncryptor::Cipher::EncryptionKey::BINARY_LENGTH>();
auto key = OuterEncryptor::Cipher::EncryptionKey::FromString(
DataFixture::generateFixedSize<OuterEncryptor::Cipher::EncryptionKey::BINARY_LENGTH>().ToString()
);
return make_unique_ref<OuterEncryptor>(key, kdfParameters());
}
};