Skip to content
Snippets Groups Projects
Verified Commit 1d66cf16 authored by Daniel Lyubomirov's avatar Daniel Lyubomirov Committed by Daniel Lyubomirov
Browse files

[17] crypto storage windows implementation

parent 61aa3d49
Branches
Tags
1 merge request!97Crypto Storage and LoginWithNewDevice and LoginWithPreviouslyAddedDevice APIs
......@@ -101,7 +101,7 @@ elseif (WIN32)
vereign/ncrypt/errors.cc
vereign/ncrypt/unique_ptr.cc
vereign/ncrypt/rsa.cc
vereign/kvstore/detail/linux_crypto_storage.cc
vereign/kvstore/detail/win_crypto_storage.cc
)
endif()
......
#include <vereign/kvstore/crypto_storage.hh>
#if defined(_WIN32)
// # include <vereign/kvstore/detail/win_crypto_storage.hh>
# include <vereign/kvstore/detail/linux_crypto_storage.hh>
# include <vereign/kvstore/detail/win_crypto_storage.hh>
#else
# include <vereign/kvstore/detail/linux_crypto_storage.hh>
#endif
namespace vereign::kvstore {
CryptoStorage::CryptoStorage(kvstore::Storage& storage)
: impl_{std::make_unique<detail::CryptoStorageImpl>(storage)}
CryptoStorage::CryptoStorage(Storage& storage, bool disable_key_protection)
: impl_{std::make_unique<detail::CryptoStorageImpl>(storage, disable_key_protection)}
{}
CryptoStorage::~CryptoStorage() = default;
......
......@@ -14,7 +14,7 @@ class CryptoStorageImpl;
class CryptoStorage {
public:
CryptoStorage(Storage& storage);
CryptoStorage(Storage& storage, bool disable_key_protection = false);
~CryptoStorage();
void Reset(const std::string& pin);
......
......@@ -17,6 +17,7 @@ namespace {
// FIXME: should these be injected and provided by the integrator
constexpr int iterations = 1 << 18;
constexpr int saltSizeBytes = 64;
constexpr int tagSizeBytes = 64;
constexpr int aesKeySizeBytes = 32;
constexpr int lockRetryCount = 10;
......@@ -91,7 +92,7 @@ void CryptoStorageImpl::Reset(const std::string& pin) {
key.IncSize(aesKeySizeBytes);
key_ = std::move(key);
bytes::Buffer tag{saltSizeBytes};
bytes::Buffer tag{tagSizeBytes};
crypto::Rand(tag);
{
......
#include <vereign/kvstore/detail/win_crypto_storage.hh>
#include <vereign/kvstore/detail/value_encoder.hh>
#include <vereign/kvstore/errors.hh>
#include <vereign/kvstore/lock.hh>
#include <vereign/crypto/digest.hh>
#include <vereign/crypto/errors.hh>
#include <vereign/crypto/rand.hh>
#include <vereign/crypto/rsa.hh>
#include <vereign/crypto/aes.hh>
#include <vereign/crypto/bio.hh>
#include <boost/core/ignore_unused.hpp>
#include <vereign/ncrypt/rsa.hh>
#include <chrono>
namespace {
// FIXME: should these be injected and provided by the integrator
constexpr int keySizeBits = 2048;
constexpr int tagSizeBytes = 64;
constexpr int aesKeySizeBytes = 32;
constexpr int lockRetryCount = 10;
constexpr auto lockRetrySleep = std::chrono::milliseconds{1000};
// FIXME: ask business for these values
constexpr const auto vereignKey = std::string_view{"vereign_key"};
constexpr const auto vereignKeyCreationTitle = std::string_view{"Vereign Client"};
constexpr const auto vereignKeyDescription = std::string_view{
"Vereign Client will use this key to authenticate with the Vereign Services"
};
constexpr const auto vereignKeyFriendlyName = std::string_view{"Vereign Client Identity Key"};
}
namespace vereign::kvstore::detail {
CryptoStorageImpl::CryptoStorageImpl(kvstore::Storage& storage, bool disable_key_protection)
: storage_{storage},
disable_key_protection_{disable_key_protection}
{}
void CryptoStorageImpl::Open(const std::string& pin) {
boost::ignore_unused(pin);
kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
auto provider = ncrypt::rsa::OpenStorageProvider();
auto rsa_key = ncrypt::rsa::LoadKey(provider.Get(), std::string(vereignKey));
if (!rsa_key) {
throw IdentityChanged{};
}
bytes::Buffer encrypted_key;
storage_.GetBytes("__master_key", encrypted_key);
bytes::Buffer key;
ncrypt::rsa::PrivateKeyDecrypt(rsa_key.Get(), encrypted_key.View(), key);
key_ = std::move(key);
// FIXME: write tests for tampering and regular identity change
try {
bytes::Buffer tag;
GetBytes("__tag", tag);
tag_ = std::move(tag);
} catch (const crypto::Error&) {
throw IdentityChanged{};
}
}
void CryptoStorageImpl::Reset(const std::string& pin) {
boost::ignore_unused(pin);
kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
auto provider = ncrypt::rsa::OpenStorageProvider();
auto old_key = ncrypt::rsa::LoadKey(provider.Get(), std::string(vereignKey));
if (old_key) {
ncrypt::rsa::DeleteKey(old_key.Get());
}
std::optional<ncrypt::rsa::KeyUIPolicy> ui_policy;
if (!disable_key_protection_) {
ui_policy = ncrypt::rsa::KeyUIPolicy{
std::string(vereignKeyCreationTitle),
std::string(vereignKeyDescription),
std::string(vereignKeyFriendlyName)
};
}
auto rsa_key = ncrypt::rsa::CreateKey(
provider.Get(),
keySizeBits,
std::string(vereignKey),
ui_policy
);
storage_.DeleteAll();
bytes::Buffer key{aesKeySizeBytes};
crypto::Rand(key);
bytes::Buffer encrypted_key;
ncrypt::rsa::PublicKeyEncrypt(rsa_key.Get(), key.View(), encrypted_key);
storage_.PutBytes("__master_key", encrypted_key.View());
key_ = std::move(key);
bytes::Buffer tag{tagSizeBytes};
crypto::Rand(tag);
encryptBytes("__tag", tag.View());
tag_ = std::move(tag);
}
void CryptoStorageImpl::encryptBytes(const std::string& key, bytes::View value) {
if (key_.Size() == 0) {
throw Error("crypto storage is not initialized");
}
bytes::Buffer iv;
bytes::Buffer tag;
bytes::Buffer encrypted;
crypto::aes::GCM256Encrypt(value, key_.View(), iv, encrypted, tag);
bytes::Buffer encoded_value;
EncodeEncryptedValue(encoded_value, iv.View(), tag.View(), encrypted.View());
storage_.PutBytes(key, encoded_value.View());
}
void CryptoStorageImpl::PutBytes(const std::string& key, bytes::View value) {
kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
bytes::Buffer tag;
try {
GetBytes("__tag", tag);
} catch (const crypto::Error&) {
throw IdentityChanged{};
}
if (tag.View() != tag_.View()) {
throw IdentityChanged{};
}
encryptBytes(key, value);
}
void CryptoStorageImpl::GetBytes(const std::string& key, bytes::Buffer& value) {
kvstore::Lock l{storage_, lockRetryCount, lockRetrySleep};
if (key_.Size() == 0) {
throw Error("crypto storage is not initialized");
}
bytes::Buffer encoded;
storage_.GetBytes(key, encoded);
bytes::View iv;
bytes::View tag;
bytes::View encrypted;
DecodeEncryptedValue(encoded.View(), iv, tag, encrypted);
crypto::aes::GCM256Decrypt(encrypted, key_.View(), iv, tag, value);
}
}
#ifndef __VEREIGN_KVSTORE_DETAIL_WIN_CRYPTO_STORAGE_HH
#define __VEREIGN_KVSTORE_DETAIL_WIN_CRYPTO_STORAGE_HH
#include <vereign/kvstore/storage.hh>
namespace vereign::kvstore::detail {
class CryptoStorageImpl {
public:
CryptoStorageImpl(kvstore::Storage& storage, bool disable_key_protection);
// disable copying
CryptoStorageImpl(const CryptoStorageImpl&) = delete;
auto operator=(const CryptoStorageImpl&) -> CryptoStorageImpl& = delete;
void Reset(const std::string& pin);
void Open(const std::string& pin);
void PutBytes(const std::string& key, bytes::View value);
void GetBytes(const std::string& key, bytes::Buffer& value);
private:
void encryptBytes(const std::string& key, bytes::View value);
private:
kvstore::Storage& storage_;
bool disable_key_protection_ = false;
bytes::Buffer key_;
bytes::Buffer tag_;
};
} // namespace vereign::kvstore::detail
#endif // __VEREIGN_KVSTORE_DETAIL_WIN_CRYPTO_STORAGE_HH
......@@ -26,7 +26,12 @@ auto LoadKey(NCRYPT_PROV_HANDLE provider, const std::string& key_name) -> Unique
return key;
}
auto CreateKey(NCRYPT_PROV_HANDLE provider, int bits, const std::string& key_name) -> UniquePtr {
auto CreateKey(
NCRYPT_PROV_HANDLE provider,
int bits,
const std::string& key_name,
std::optional<KeyUIPolicy> ui_policy
) -> UniquePtr {
UniquePtr key{};
auto wkey_name = string::widen(key_name);
auto status = NCryptCreatePersistedKey(
......@@ -53,25 +58,29 @@ auto CreateKey(NCRYPT_PROV_HANDLE provider, int bits, const std::string& key_nam
throw Error{status, "setup rsa key length failed"};
}
// FIXME: add the ui policy
// NCRYPT_UI_POLICY ui_policy{};
// ui_policy.dwVersion = 1;
// ui_policy.dwFlags = NCRYPT_UI_PROTECT_KEY_FLAG;
// ui_policy.pszCreationTitle = L"Vereign Client";
// ui_policy.pszDescription =
// L"Vereign Client will use this key to authenticate with the Vereign Services";
// ui_policy.pszFriendlyName = L"Vereign Client Identity Key";
// status = NCryptSetProperty(
// key.Get(),
// NCRYPT_UI_POLICY_PROPERTY,
// (PBYTE)&ui_policy,
// sizeof(ui_policy),
// NCRYPT_PERSIST_FLAG
// );
// if (status != ERROR_SUCCESS) {
// throw Error{status, "configure key ui policy failed"};
// }
if (ui_policy) {
auto creation_title = vereign::string::widen(ui_policy->CreationTitle);
auto description = vereign::string::widen(ui_policy->Description);
auto friendly_name = vereign::string::widen(ui_policy->FriendlyName);
NCRYPT_UI_POLICY ui_policy_prop{};
ui_policy_prop.dwVersion = 1;
ui_policy_prop.dwFlags = NCRYPT_UI_PROTECT_KEY_FLAG;
ui_policy_prop.pszCreationTitle = creation_title.data();
ui_policy_prop.pszDescription = description.data();
ui_policy_prop.pszFriendlyName = friendly_name.data();
status = NCryptSetProperty(
key.Get(),
NCRYPT_UI_POLICY_PROPERTY,
(PBYTE)&ui_policy_prop,
sizeof(ui_policy_prop),
NCRYPT_PERSIST_FLAG
);
if (status != ERROR_SUCCESS) {
throw Error{status, "configure key ui policy failed"};
}
}
status = NCryptFinalizeKey(key.Get(), 0);
if (status != ERROR_SUCCESS) {
......
......@@ -7,12 +7,24 @@
#include <windows.h>
#include <ncrypt.h>
#include <string>
#include <optional>
namespace vereign::ncrypt::rsa {
struct KeyUIPolicy {
std::string CreationTitle;
std::string Description;
std::string FriendlyName;
};
auto OpenStorageProvider() -> UniquePtr;
auto LoadKey(NCRYPT_PROV_HANDLE provider, const std::string& key_name) -> UniquePtr;
auto CreateKey(NCRYPT_PROV_HANDLE provider, int bits, const std::string& key_name) -> UniquePtr;
auto CreateKey(
NCRYPT_PROV_HANDLE provider,
int bits,
const std::string& key_name,
std::optional<KeyUIPolicy> ui_policy
) -> UniquePtr;
void DeleteKey(NCRYPT_KEY_HANDLE key);
void PublicKeyEncrypt(NCRYPT_KEY_HANDLE key, bytes::View src, bytes::Buffer& encrypted);
......
......@@ -20,7 +20,7 @@ TEST_CASE("CryptoStorage::Reset", "[vereign/kvstore]") {
// put value
{
auto kvstorage = kvstore::SqliteStorage(storage_path.string());
kvstore::CryptoStorage storage{kvstorage};
kvstore::CryptoStorage storage{kvstorage, true};
storage.Reset("foo");
std::string v{"test value"};
......@@ -30,7 +30,7 @@ TEST_CASE("CryptoStorage::Reset", "[vereign/kvstore]") {
// with another storage instance get the value
{
auto kvstorage = kvstore::SqliteStorage(storage_path.string());
kvstore::CryptoStorage storage{kvstorage};
kvstore::CryptoStorage storage{kvstorage, true};
bytes::Buffer v;
storage.Open("foo");
......@@ -47,7 +47,7 @@ TEST_CASE("CryptoStorage::PutBytes", "[vereign/kvstore]") {
bytes::Buffer big_value{100000};
crypto::Rand(big_value);
auto kvstorage = kvstore::SqliteStorage(storage_path.string());
kvstore::CryptoStorage storage{kvstorage};
kvstore::CryptoStorage storage{kvstorage, true};
storage.Reset("foo");
storage.PutBytes("test", big_value.View());
......
......@@ -27,7 +27,7 @@ TEST_CASE("ncrypt::CreateKey", "[vereign/ncrypt/rsa][vereign/ncrypt]") {
ncrypt::rsa::DeleteKey(key.Get());
}
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key);
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key, {});
CHECK(new_key.Get() != 0);
key = ncrypt::rsa::LoadKey(provider.Get(), test_key);
......@@ -46,11 +46,11 @@ TEST_CASE("ncrypt::CreateKey", "[vereign/ncrypt/rsa][vereign/ncrypt]") {
ncrypt::rsa::DeleteKey(key.Get());
}
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key);
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key, {});
CHECK(new_key.Get() != 0);
CHECK_THROWS_WITH(
ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key),
ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key, {}),
"creating rsa key failed: NTE_EXISTS"
);
......@@ -74,7 +74,7 @@ TEST_CASE("ncrypt::rsa::LoadKey", "[vereign/ncrypt/rsa][vereign/ncrypt]") {
ncrypt::rsa::DeleteKey(key.Get());
}
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key);
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key, {});
CHECK(new_key.Get() != 0);
key = ncrypt::rsa::LoadKey(provider.Get(), test_key);
......@@ -110,7 +110,7 @@ TEST_CASE("ncrypt::rsa::DeleteKey", "[vereign/ncrypt/rsa][vereign/ncrypt]") {
ncrypt::rsa::DeleteKey(key.Get());
}
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key);
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key, {});
CHECK(new_key.Get() != 0);
ncrypt::rsa::DeleteKey(new_key.Get());
......@@ -128,7 +128,7 @@ TEST_CASE("ncrypt::rsa::DeleteKey", "[vereign/ncrypt/rsa][vereign/ncrypt]") {
ncrypt::rsa::DeleteKey(key.Get());
}
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key);
auto new_key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key, {});
CHECK(new_key.Get() != 0);
key = ncrypt::rsa::LoadKey(provider.Get(), test_key);
......@@ -151,7 +151,7 @@ TEST_CASE("ncrypt::rsa PublicKeyEncrypt/PrivateKeyDecrypt", "[vereign/ncrypt/rsa
ncrypt::rsa::DeleteKey(key.Get());
}
key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key);
key = ncrypt::rsa::CreateKey(provider.Get(), 2048, test_key, {});
REQUIRE(key.Get() != 0);
auto delete_key = core::ScopeGuard([&key] { ncrypt::rsa::DeleteKey(key.Get()); });
......
......@@ -23,7 +23,7 @@ ServiceContext::ServiceContext(
client_session_{std::make_unique<restapi::ClientSession>(*client_)},
storage_path_{std::move(storage_path)},
sqlite_storage_{std::make_unique<kvstore::SqliteStorage>(storage_path_.string())},
storage_{std::make_unique<kvstore::CryptoStorage>(*sqlite_storage_)},
storage_{std::make_unique<kvstore::CryptoStorage>(*sqlite_storage_, true)},
identity_provider_{std::make_unique<identity::Provider>(*storage_)}
{
service_thread_ = std::thread([this]() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment