CryFS tells the operating system to not swap the encryption key to the disk (note: this is best-effort and cannot be guaranteed. Hibernation, for example, will still write the encryption key to the disk)

This commit is contained in:
Sebastian Messmer 2017-09-05 00:43:43 +01:00
parent 06e8fc15d4
commit 7e56c46bb0
17 changed files with 209 additions and 25 deletions

View File

@ -1,14 +1,13 @@
Version 0.10.0 (unreleased) Version 0.10.0 (unreleased)
--------------- ---------------
New Features: New Features & Improvements:
* Integrity checks ensure you notice when someone modifies your file system. * Integrity checks ensure you notice when someone modifies your file system.
* File system nodes (files, directories, symlinks) store a parent pointer to the directory that contains them. This information can be used in later versions to resolve some synchronization conflicts. * File system nodes (files, directories, symlinks) store a parent pointer to the directory that contains them. This information can be used in later versions to resolve some synchronization conflicts.
* Allow mounting using system mount tool and /etc/fstab (e.g. mount -t fuse.cryfs basedir mountdir) * Allow mounting using system mount tool and /etc/fstab (e.g. mount -t fuse.cryfs basedir mountdir)
Improvements:
* Performance improvements * Performance improvements
* Use relatime instead of strictatime (further performance improvement) * Use relatime instead of strictatime (further performance improvement)
* Pass fuse options directly to cryfs (i.e. 'cryfs basedir mountdir -o allow_other' instead of 'cryfs basedir mountdir -- -o allow_other') * Pass fuse options directly to cryfs (i.e. 'cryfs basedir mountdir -o allow_other' instead of 'cryfs basedir mountdir -- -o allow_other')
* CryFS tells the operating system to not swap the encryption key to the disk (note: this is best-effort and cannot be guaranteed. Hibernation, for example, will still write the encryption key to the disk)
Fixed bugs: Fixed bugs:
* `du` shows correct file system size on Mac OS X. * `du` shows correct file system size on Mac OS X.

View File

@ -6,6 +6,7 @@ set(SOURCES
crypto/kdf/SCryptParameters.cpp crypto/kdf/SCryptParameters.cpp
crypto/kdf/PasswordBasedKDF.cpp crypto/kdf/PasswordBasedKDF.cpp
crypto/RandomPadding.cpp crypto/RandomPadding.cpp
crypto/symmetric/EncryptionKey.cpp
process/daemonize.cpp process/daemonize.cpp
process/subprocess.cpp process/subprocess.cpp
tempfile/TempFile.cpp tempfile/TempFile.cpp
@ -37,6 +38,7 @@ set(SOURCES
assert/AssertFailed.cpp assert/AssertFailed.cpp
system/get_total_memory.cpp system/get_total_memory.cpp
system/homedir.cpp system/homedir.cpp
system/memory.cpp
) )
add_library(${PROJECT_NAME} STATIC ${SOURCES}) add_library(${PROJECT_NAME} STATIC ${SOURCES})

View File

@ -9,17 +9,14 @@
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <cryptopp/modes.h> #include <cryptopp/modes.h>
#include "Cipher.h" #include "Cipher.h"
#include "EncryptionKey.h"
namespace cpputils { namespace cpputils {
template<typename BlockCipher, unsigned int KeySize> template<typename BlockCipher, unsigned int KeySize>
class CFB_Cipher { class CFB_Cipher {
public: public:
using EncryptionKey = FixedSizeData<KeySize>; using EncryptionKey = cpputils::EncryptionKey<KeySize>;
static EncryptionKey CreateKey(RandomGenerator &randomGenerator) {
return randomGenerator.getFixedSize<EncryptionKey::BINARY_LENGTH>();
}
static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) { static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) {
return plaintextBlockSize + IV_SIZE; return plaintextBlockSize + IV_SIZE;
@ -39,7 +36,7 @@ private:
template<typename BlockCipher, unsigned int KeySize> template<typename BlockCipher, unsigned int KeySize>
Data CFB_Cipher<BlockCipher, KeySize>::encrypt(const CryptoPP::byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) { Data CFB_Cipher<BlockCipher, KeySize>::encrypt(const CryptoPP::byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) {
FixedSizeData<IV_SIZE> iv = Random::PseudoRandom().getFixedSize<IV_SIZE>(); FixedSizeData<IV_SIZE> iv = Random::PseudoRandom().getFixedSize<IV_SIZE>();
auto encryption = typename CryptoPP::CFB_Mode<BlockCipher>::Encryption(encKey.data(), encKey.BINARY_LENGTH, iv.data()); auto encryption = typename CryptoPP::CFB_Mode<BlockCipher>::Encryption((CryptoPP::byte*)encKey.data(), encKey.BINARY_LENGTH, iv.data());
Data ciphertext(ciphertextSize(plaintextSize)); Data ciphertext(ciphertextSize(plaintextSize));
std::memcpy(ciphertext.data(), iv.data(), IV_SIZE); std::memcpy(ciphertext.data(), iv.data(), IV_SIZE);
encryption.ProcessData((CryptoPP::byte*)ciphertext.data() + IV_SIZE, plaintext, plaintextSize); encryption.ProcessData((CryptoPP::byte*)ciphertext.data() + IV_SIZE, plaintext, plaintextSize);

View File

@ -17,7 +17,7 @@ public:
BOOST_CONCEPT_USAGE(CipherConcept) { BOOST_CONCEPT_USAGE(CipherConcept) {
same_type(UINT32_C(0), X::ciphertextSize(UINT32_C(5))); same_type(UINT32_C(0), X::ciphertextSize(UINT32_C(5)));
same_type(UINT32_C(0), X::plaintextSize(UINT32_C(5))); same_type(UINT32_C(0), X::plaintextSize(UINT32_C(5)));
typename X::EncryptionKey key = X::CreateKey(Random::OSRandom()); typename X::EncryptionKey key = X::EncryptionKey::CreateKey(Random::OSRandom());
same_type(Data(0), X::encrypt((uint8_t*)nullptr, UINT32_C(0), key)); same_type(Data(0), X::encrypt((uint8_t*)nullptr, UINT32_C(0), key));
same_type(boost::optional<Data>(Data(0)), X::decrypt((uint8_t*)nullptr, UINT32_C(0), key)); same_type(boost::optional<Data>(Data(0)), X::decrypt((uint8_t*)nullptr, UINT32_C(0), key));
string name = X::NAME; string name = X::NAME;

View File

@ -0,0 +1 @@
#include "EncryptionKey.h"

View File

@ -0,0 +1,107 @@
#pragma once
#ifndef MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_ENCRYPTIONKEY_H_
#define MESSMER_CPPUTILS_CRYPTO_SYMMETRIC_ENCRYPTIONKEY_H_
#include <cpp-utils/data/FixedSizeData.h>
#include <memory>
#include <cpp-utils/system/memory.h>
#include "../cryptopp_byte.h"
#include <cpp-utils/random/RandomGenerator.h>
namespace cpputils {
/**
* Use this to store an encryption key and keep it safe in memory.
* It will only keep the key in one memory location, even if the EncryptionKey object is copied or moved.
* This one memory location will be prevented from swapping to disk.
* Note: This is a best-effort, but not a guarantee. System hibernation might still write the encryption key to the disk.
* Also, when (de)serializing the config file or calling a crypto library with the encryption key, it isn't guaranteed
* that there aren't any copies made to different memory regions. However, these other memory regions should be short-lived
* and therefore much less likely to swap.
*/
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
};
public:
constexpr static size_t BINARY_LENGTH = EncryptionKeyData::BINARY_LENGTH;
constexpr static size_t STRING_LENGTH = EncryptionKeyData::STRING_LENGTH;
EncryptionKey(const FixedSizeData<KeySize>& keyData);
static EncryptionKey FromBinary(const void *source);
static EncryptionKey FromString(const std::string& keyData);
std::string ToString() const;
static EncryptionKey CreateKey(RandomGenerator &randomGenerator) {
EncryptionKey result(FixedSizeData<BINARY_LENGTH>::Null());
randomGenerator.write(result._key->key.data(), BINARY_LENGTH);
return result;
}
const void *data() const {
return _key->key.data();
}
void *data() {
return const_cast<void*>(const_cast<const EncryptionKey*>(this)->data());
}
private:
std::shared_ptr<EncryptionKeyData> _key;
};
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

@ -8,17 +8,14 @@
#include "../../random/Random.h" #include "../../random/Random.h"
#include <cryptopp/gcm.h> #include <cryptopp/gcm.h>
#include "Cipher.h" #include "Cipher.h"
#include "EncryptionKey.h"
namespace cpputils { namespace cpputils {
template<typename BlockCipher, unsigned int KeySize> template<typename BlockCipher, unsigned int KeySize>
class GCM_Cipher { class GCM_Cipher {
public: public:
using EncryptionKey = FixedSizeData<KeySize>; using EncryptionKey = cpputils::EncryptionKey<KeySize>;
static EncryptionKey CreateKey(RandomGenerator &randomGenerator) {
return randomGenerator.getFixedSize<EncryptionKey::BINARY_LENGTH>();
}
static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) { static constexpr unsigned int ciphertextSize(unsigned int plaintextBlockSize) {
return plaintextBlockSize + IV_SIZE + TAG_SIZE; return plaintextBlockSize + IV_SIZE + TAG_SIZE;
@ -40,7 +37,7 @@ template<typename BlockCipher, unsigned int KeySize>
Data GCM_Cipher<BlockCipher, KeySize>::encrypt(const CryptoPP::byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) { Data GCM_Cipher<BlockCipher, KeySize>::encrypt(const CryptoPP::byte *plaintext, unsigned int plaintextSize, const EncryptionKey &encKey) {
FixedSizeData<IV_SIZE> iv = Random::PseudoRandom().getFixedSize<IV_SIZE>(); FixedSizeData<IV_SIZE> iv = Random::PseudoRandom().getFixedSize<IV_SIZE>();
typename CryptoPP::GCM<BlockCipher, CryptoPP::GCM_64K_Tables>::Encryption encryption; typename CryptoPP::GCM<BlockCipher, CryptoPP::GCM_64K_Tables>::Encryption encryption;
encryption.SetKeyWithIV(encKey.data(), encKey.BINARY_LENGTH, iv.data(), IV_SIZE); encryption.SetKeyWithIV((CryptoPP::byte*)encKey.data(), encKey.BINARY_LENGTH, iv.data(), IV_SIZE);
Data ciphertext(ciphertextSize(plaintextSize)); Data ciphertext(ciphertextSize(plaintextSize));
std::memcpy(ciphertext.data(), iv.data(), IV_SIZE); std::memcpy(ciphertext.data(), iv.data(), IV_SIZE);

View File

@ -12,6 +12,8 @@ namespace cpputils {
template<size_t SIZE> FixedSizeData<SIZE> getFixedSize(); template<size_t SIZE> FixedSizeData<SIZE> getFixedSize();
Data get(size_t size); Data get(size_t size);
void write(void *target, size_t size);
protected: protected:
virtual void _get(void *target, size_t bytes) = 0; virtual void _get(void *target, size_t bytes) = 0;
private: private:
@ -23,6 +25,10 @@ namespace cpputils {
inline RandomGenerator::RandomGenerator() { inline RandomGenerator::RandomGenerator() {
} }
inline void RandomGenerator::write(void *target, size_t size) {
_get(target, size);
}
template<size_t SIZE> inline FixedSizeData<SIZE> RandomGenerator::getFixedSize() { template<size_t SIZE> inline FixedSizeData<SIZE> RandomGenerator::getFixedSize() {
FixedSizeData<SIZE> result = FixedSizeData<SIZE>::Null(); FixedSizeData<SIZE> result = FixedSizeData<SIZE>::Null();
_get(result.data(), SIZE); _get(result.data(), SIZE);

View File

@ -0,0 +1,26 @@
#include "memory.h"
#include <sys/mman.h>
#include <errno.h>
#include <stdexcept>
#include <cpp-utils/logging/logging.h>
using namespace cpputils::logging;
namespace cpputils {
DontSwapMemoryRAII::DontSwapMemoryRAII(const void *addr, size_t len)
: addr_(addr), len_(len) {
const int result = ::mlock(addr_, len_);
if (0 != result) {
throw std::runtime_error("Error calling mlock. Errno: " + std::to_string(errno));
}
}
DontSwapMemoryRAII::~DontSwapMemoryRAII() {
const int result = ::munlock(addr_, len_);
if (0 != result) {
LOG(WARN, "Error calling munlock. Errno: {}", errno);
}
}
}

View File

@ -0,0 +1,23 @@
#pragma once
#ifndef MESSMER_CPPUTILS_SYSTEM_MEMORY_H
#define MESSMER_CPPUTILS_SYSTEM_MEMORY_H
#include <cstdlib>
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 {
public:
DontSwapMemoryRAII(const void* addr, size_t len);
~DontSwapMemoryRAII();
private:
const void* addr_;
const size_t len_;
};
}
#endif

View File

@ -44,7 +44,7 @@ public:
} }
string createKey(cpputils::RandomGenerator &randomGenerator) const override { string createKey(cpputils::RandomGenerator &randomGenerator) const override {
return Cipher::CreateKey(randomGenerator).ToString(); return Cipher::EncryptionKey::CreateKey(randomGenerator).ToString();
} }
unique_ref<InnerEncryptor> createInnerConfigEncryptor(const FixedSizeData<CryCiphers::MAX_KEY_SIZE> &key) const override { unique_ref<InnerEncryptor> createInnerConfigEncryptor(const FixedSizeData<CryCiphers::MAX_KEY_SIZE> &key) const override {

View File

@ -58,7 +58,7 @@ set<Key> _getBlockstoreUnaccountedBlocks(const CryConfig &config) {
i = 0; i = 0;
cout << "\nRemove blocks that have a parent" << endl; cout << "\nRemove blocks that have a parent" << endl;
//Remove root block from unaccountedBlocks //Remove root block from unaccountedBlocks
unaccountedBlocks.erase(Key::FromString(config.RootBlob())); unaccountedBlocks.erase(Key::FrComString(config.RootBlob()));
//Remove all blocks that have a parent node from unaccountedBlocks //Remove all blocks that have a parent node from unaccountedBlocks
for (auto file = directory_iterator("/home/heinzi/basedir"); file != directory_iterator(); ++file) { for (auto file = directory_iterator("/home/heinzi/basedir"); file != directory_iterator(); ++file) {
cout << "\r" << (++i) << "/" << numBlocks << flush; cout << "\r" << (++i) << "/" << numBlocks << flush;

View File

@ -46,6 +46,7 @@ set(SOURCES
assert/backtrace_include_test.cpp assert/backtrace_include_test.cpp
assert/assert_include_test.cpp assert/assert_include_test.cpp
assert/assert_debug_test.cpp assert/assert_debug_test.cpp
system/MemoryTest.cpp
) )
add_executable(${PROJECT_NAME} ${SOURCES}) add_executable(${PROJECT_NAME} ${SOURCES})

View File

@ -17,6 +17,11 @@ namespace cpputils {
static constexpr unsigned int BINARY_LENGTH = 1; static constexpr unsigned int BINARY_LENGTH = 1;
static FakeKey CreateKey(RandomGenerator &randomGenerator) {
auto data = randomGenerator.getFixedSize<1>();
return FakeKey{*((uint8_t *) data.data())};
}
uint8_t value; uint8_t value;
}; };
@ -27,11 +32,6 @@ namespace cpputils {
using EncryptionKey = FakeKey; using EncryptionKey = FakeKey;
static EncryptionKey CreateKey(RandomGenerator &randomGenerator) {
auto data = randomGenerator.getFixedSize<1>();
return FakeKey{*((uint8_t *) data.data())};
}
static EncryptionKey Key1() { static EncryptionKey Key1() {
return FakeKey{5}; return FakeKey{5};
} }

View File

@ -0,0 +1,25 @@
#include <gtest/gtest.h>
#include <cpp-utils/system/memory.h>
#include <memory>
using cpputils::DontSwapMemoryRAII;
TEST(MemoryTest, LockingSmallStackMemoryDoesntCrash) {
bool memory;
DontSwapMemoryRAII obj(&memory, sizeof(memory));
}
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);
}

View File

@ -37,7 +37,7 @@ public:
void EXPECT_CREATES_CORRECT_ENCRYPTED_BLOCKSTORE(const string &cipherName) { void EXPECT_CREATES_CORRECT_ENCRYPTED_BLOCKSTORE(const string &cipherName) {
const auto &actualCipher = CryCiphers::find(cipherName); const auto &actualCipher = CryCiphers::find(cipherName);
Data dataFixture = DataFixture::generate(1024); Data dataFixture = DataFixture::generate(1024);
string encKey = ExpectedCipher::CreateKey(Random::PseudoRandom()).ToString(); string encKey = ExpectedCipher::EncryptionKey::CreateKey(Random::PseudoRandom()).ToString();
_EXPECT_ENCRYPTS_WITH_ACTUAL_BLOCKSTORE_DECRYPTS_CORRECTLY_WITH_EXPECTED_BLOCKSTORE<ExpectedCipher>(actualCipher, encKey, std::move(dataFixture)); _EXPECT_ENCRYPTS_WITH_ACTUAL_BLOCKSTORE_DECRYPTS_CORRECTLY_WITH_EXPECTED_BLOCKSTORE<ExpectedCipher>(actualCipher, encKey, std::move(dataFixture));
} }

View File

@ -17,7 +17,7 @@ public:
cryfs::CryConfigFile configFile() { cryfs::CryConfigFile configFile() {
cryfs::CryConfig config; cryfs::CryConfig config;
config.SetCipher("aes-256-gcm"); config.SetCipher("aes-256-gcm");
config.SetEncryptionKey(cpputils::AES256_GCM::CreateKey(cpputils::Random::PseudoRandom()).ToString()); config.SetEncryptionKey(cpputils::AES256_GCM::EncryptionKey::CreateKey(cpputils::Random::PseudoRandom()).ToString());
config.SetBlocksizeBytes(10240); config.SetBlocksizeBytes(10240);
return cryfs::CryConfigFile::create(_configFile.path(), std::move(config), "mypassword", cpputils::SCrypt::TestSettings); return cryfs::CryConfigFile::create(_configFile.path(), std::move(config), "mypassword", cpputils::SCrypt::TestSettings);
} }