Implement secure key via KDF for transparent data encryption/decryption #60
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good

This commit is contained in:
2025-08-31 20:00:59 -05:00
parent e680ec8664
commit 9656828700
5 changed files with 99 additions and 57 deletions

View File

@@ -740,9 +740,13 @@ auto encrypt_provider::process_directory_entry(
utils::path::get_relative_path(dir_entry.get_path(), cfg.path), utils::path::get_relative_path(dir_entry.get_path(), cfg.path),
}; };
i_file_db::file_data file_data{}; auto file_res{
auto file_res{file_db_->get_file_data(api_path, file_data)}; file_db_->get_file_api_path(dir_entry.get_path(), api_path)};
if (file_res != api_error::success) { if (file_res == api_error::success) {
return true;
}
if (file_res != api_error::item_not_found) {
// TODO raise error // TODO raise error
return false; return false;
} }
@@ -763,38 +767,34 @@ auto encrypt_provider::process_directory_entry(
do_add_directory(utils::path::get_parent_path(relative_path)); do_add_directory(utils::path::get_parent_path(relative_path));
} }
if (file_res == api_error::item_not_found) { utils::encryption::encrypting_reader reader(
utils::encryption::encrypting_reader reader( utils::path::strip_to_file_name(relative_path), dir_entry.get_path(),
utils::path::strip_to_file_name(relative_path), []() -> bool { return app_config::get_stop_requested(); },
dir_entry.get_path(), master_key_, get_encrypt_config().kdf_cfg, std::nullopt);
[]() -> bool { return app_config::get_stop_requested(); }, api_path = utils::path::create_api_path(api_parent + "/" +
master_key_, file_data.kdf_configs, reader.get_encrypted_file_name());
utils::path::get_parent_path(relative_path));
api_path = utils::path::create_api_path(
api_parent + "/" + reader.get_encrypted_file_name());
file_res = file_db_->add_or_update_file(i_file_db::file_data{ file_res = file_db_->add_or_update_file(i_file_db::file_data{
.api_path = api_path, .api_path = api_path,
.file_size = dynamic_cast<const utils::file::i_file *>(&dir_entry) .file_size = dynamic_cast<const utils::file::i_file *>(&dir_entry)
->size() ->size()
.value_or(0U), .value_or(0U),
.iv_list = reader.get_iv_list(), .iv_list = reader.get_iv_list(),
.kdf_configs = .kdf_configs =
{ {
*reader.get_kdf_config_for_data(), *reader.get_kdf_config_for_data(),
*reader.get_kdf_config_for_path(), *reader.get_kdf_config_for_path(),
}, },
.source_path = dir_entry.get_path(), .source_path = dir_entry.get_path(),
}); });
if (file_res != api_error::success) { if (file_res != api_error::success) {
// TODO raise error // TODO raise error
return false; return false;
}
event_system::instance().raise<filesystem_item_added>(
api_parent, api_path, false, function_name);
} }
event_system::instance().raise<filesystem_item_added>(
api_parent, api_path, false, function_name);
return true; return true;
} }
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
@@ -966,12 +966,25 @@ auto encrypt_provider::start(api_item_added_callback /*api_item_added*/,
if (encrypt_config_.kdf_cfg.checksum == 0U) { if (encrypt_config_.kdf_cfg.checksum == 0U) {
i_file_db::directory_data data{}; i_file_db::directory_data data{};
if (file_db_->get_directory_data("/", data) == api_error::success) { if (file_db_->get_directory_data("/", data) == api_error::success) {
encrypt_config_.kdf_cfg = data.kdf_configs.first; if (data.kdf_configs.first.checksum == 0U) {
encrypt_config_.kdf_cfg.seal();
} else {
encrypt_config_.kdf_cfg = data.kdf_configs.first;
}
} else { } else {
encrypt_config_.kdf_cfg.seal(); encrypt_config_.kdf_cfg.seal();
} }
config_.set_encrypt_config(encrypt_config_); config_.set_encrypt_config(encrypt_config_);
data.kdf_configs = {
encrypt_config_.kdf_cfg,
encrypt_config_.kdf_cfg,
};
auto res = file_db_->add_or_update_directory(data);
if (res != api_error::success) {
throw startup_exception(fmt::format("failed to update existing kdf|{}",
api_error_to_string(res)));
}
} }
if (encrypt_config_.kdf_cfg.checksum != if (encrypt_config_.kdf_cfg.checksum !=

View File

@@ -158,14 +158,17 @@ const auto create_file = [](repertory::i_provider &provider,
const auto decrypt_parts = [](const repertory::app_config &cfg, const auto decrypt_parts = [](const repertory::app_config &cfg,
std::string &path) { std::string &path) {
if (path != "/" && path != "." && path != "..") { if (path != "/" && path != "." && path != "..") {
repertory::utils::hash::hash_256_t key{};
EXPECT_TRUE(repertory::utils::encryption::recreate_key_argon2id(
cfg.get_encrypt_config().encryption_token,
cfg.get_encrypt_config().kdf_cfg, key));
auto parts = repertory::utils::string::split(path, '/', false); auto parts = repertory::utils::string::split(path, '/', false);
for (auto &part : parts) { for (auto &part : parts) {
if (part.empty()) { if (part.empty()) {
continue; continue;
} }
EXPECT_EQ(true, repertory::utils::encryption::decrypt_file_name( EXPECT_TRUE(repertory::utils::encryption::decrypt_file_name(key, part));
cfg.get_encrypt_config().encryption_token, part));
} }
path = repertory::utils::string::join(parts, '/'); path = repertory::utils::string::join(parts, '/');
} }
@@ -492,9 +495,9 @@ static void get_directory_items(const app_config &cfg, i_provider &provider) {
EXPECT_STREQ("/test.txt", file->api_path.c_str()); EXPECT_STREQ("/test.txt", file->api_path.c_str());
EXPECT_STREQ("/", file->api_parent.c_str()); EXPECT_STREQ("/", file->api_parent.c_str());
#if defined(_WIN32) #if defined(_WIN32)
EXPECT_EQ(std::size_t(47U), file->size); EXPECT_EQ(std::size_t(83U), file->size);
#else #else
EXPECT_EQ(std::size_t(46U), file->size); EXPECT_EQ(std::size_t(82U), file->size);
#endif #endif
auto source_path = auto source_path =
@@ -522,9 +525,9 @@ static void get_directory_items(const app_config &cfg, i_provider &provider) {
EXPECT_STREQ("/sub10/moose.txt", file2->api_path.c_str()); EXPECT_STREQ("/sub10/moose.txt", file2->api_path.c_str());
EXPECT_STREQ("/sub10", file2->api_parent.c_str()); EXPECT_STREQ("/sub10", file2->api_parent.c_str());
#if defined(_WIN32) #if defined(_WIN32)
EXPECT_EQ(std::size_t(46U), file2->size); EXPECT_EQ(std::size_t(82U), file2->size);
#else #else
EXPECT_EQ(std::size_t(45U), file2->size); EXPECT_EQ(std::size_t(81U), file2->size);
#endif #endif
return; return;
} }
@@ -633,9 +636,9 @@ static void get_file(const app_config &cfg, i_provider &provider) {
EXPECT_STREQ("/test.txt", file.api_path.c_str()); EXPECT_STREQ("/test.txt", file.api_path.c_str());
EXPECT_STREQ("/", file.api_parent.c_str()); EXPECT_STREQ("/", file.api_parent.c_str());
#if defined(_WIN32) #if defined(_WIN32)
EXPECT_EQ(std::size_t(47U), file.file_size); EXPECT_EQ(std::size_t(83U), file.file_size);
#else #else
EXPECT_EQ(std::size_t(46U), file.file_size); EXPECT_EQ(std::size_t(82U), file.file_size);
#endif #endif
EXPECT_STREQ(source_path.c_str(), file.source_path.c_str()); EXPECT_STREQ(source_path.c_str(), file.source_path.c_str());
return; return;

View File

@@ -281,6 +281,11 @@ encrypting_reader::encrypting_reader(
error_return_(error_return), error_return_(error_return),
source_file_(utils::file::file::open_or_create_file(source_path, true)) { source_file_(utils::file::file::open_or_create_file(source_path, true)) {
common_initialize_kdf_data(cfg, master_key); common_initialize_kdf_data(cfg, master_key);
auto [path_key, path_cfg] = cfg.create_subkey(
kdf_context::path, utils::generate_secure_random<std::uint64_t>(),
master_key);
keys_.second = std::move(path_key);
kdf_headers_->second = path_cfg.to_header();
common_initialize(true); common_initialize(true);
create_encrypted_paths(file_name, relative_parent_path); create_encrypted_paths(file_name, relative_parent_path);
} }

View File

@@ -25,6 +25,7 @@
#include "utils/base64.hpp" #include "utils/base64.hpp"
#include "utils/collection.hpp" #include "utils/collection.hpp"
#include "utils/config.hpp"
#include "utils/encrypting_reader.hpp" #include "utils/encrypting_reader.hpp"
#include "utils/hash.hpp" #include "utils/hash.hpp"
#include "utils/path.hpp" #include "utils/path.hpp"
@@ -99,29 +100,49 @@ auto decrypt_file_name(std::string_view encryption_token,
auto decrypt_file_name(std::string_view encryption_token, const kdf_config &cfg, auto decrypt_file_name(std::string_view encryption_token, const kdf_config &cfg,
std::string &file_name) -> bool { std::string &file_name) -> bool {
auto buffer = macaron::Base64::Decode(file_name); REPERTORY_USES_FUNCTION_NAME();
file_name.clear(); try {
return utils::encryption::decrypt_data(encryption_token, cfg, buffer, auto buffer = macaron::Base64::Decode(file_name);
file_name);
file_name.clear();
return utils::encryption::decrypt_data(encryption_token, cfg, buffer,
file_name);
} catch (const std::exception &e) {
utils::error::handle_exception(function_name, e);
} catch (...) {
utils::error::handle_exception(function_name);
}
return false;
} }
auto decrypt_file_name(const utils::hash::hash_256_t &master_key, auto decrypt_file_name(const utils::hash::hash_256_t &master_key,
std::string &file_name) -> bool { std::string &file_name) -> bool {
auto buffer = macaron::Base64::Decode(file_name); REPERTORY_USES_FUNCTION_NAME();
utils::encryption::kdf_config path_cfg; try {
if (not utils::encryption::kdf_config::from_header(buffer, path_cfg)) { auto buffer = macaron::Base64::Decode(file_name);
return false;
utils::encryption::kdf_config path_cfg;
if (not utils::encryption::kdf_config::from_header(buffer, path_cfg)) {
return false;
}
auto path_key = path_cfg.recreate_subkey(
utils::encryption::kdf_context::path, master_key);
file_name.clear();
return utils::encryption::decrypt_data(
path_key, &buffer[utils::encryption::kdf_config::size()],
buffer.size() - utils::encryption::kdf_config::size(), file_name);
} catch (const std::exception &e) {
utils::error::handle_exception(function_name, e);
} catch (...) {
utils::error::handle_exception(function_name);
} }
auto path_key = path_cfg.recreate_subkey(utils::encryption::kdf_context::path, return false;
master_key);
file_name.clear();
return utils::encryption::decrypt_data(
path_key, &buffer[utils::encryption::kdf_config::size()],
buffer.size() - utils::encryption::kdf_config::size(), file_name);
} }
auto decrypt_file_path(std::string_view encryption_token, auto decrypt_file_path(std::string_view encryption_token,

View File

@@ -266,7 +266,7 @@ auto file::move_to(std::string_view path) -> bool {
#if defined(_WIN32) #if defined(_WIN32)
success = ::MoveFileExA(path_.c_str(), abs_path.c_str(), success = ::MoveFileExA(path_.c_str(), abs_path.c_str(),
MOVEFILE_REPLACE_EXISTING) != 0; MOVEFILE_REPLACE_EXISTING) != 0;
#else // !// defined(_WIN32) #else // !defined(_WIN32)
std::error_code ec{}; std::error_code ec{};
std::filesystem::rename(path_, abs_path, ec); std::filesystem::rename(path_, abs_path, ec);
success = ec.value() == 0; success = ec.value() == 0;