Implement secure key via KDF for transparent data encryption/decryption #60

This commit is contained in:
2025-08-30 14:43:24 -05:00
parent f04d4d531a
commit d13ab3106e
2 changed files with 90 additions and 65 deletions

View File

@@ -102,8 +102,10 @@ private:
return s3_config_; return s3_config_;
} }
[[nodiscard]] auto initialize_crypto(const s3_config &cfg) -> bool;
[[nodiscard]] auto [[nodiscard]] auto
search_keys_for_master_kdf(std::string_view encryption_token) -> bool; search_keys_for_master_kdf(const std::string &encryption_token) -> bool;
[[nodiscard]] auto set_meta_key(const std::string &api_path, [[nodiscard]] auto set_meta_key(const std::string &api_path,
api_meta_map &meta) -> api_error; api_meta_map &meta) -> api_error;
@@ -177,8 +179,8 @@ public:
const std::string &to_api_path) const std::string &to_api_path)
-> api_error override; -> api_error override;
[[nodiscard]] auto start(api_item_added_callback api_item_added, auto start(api_item_added_callback api_item_added, i_file_manager *mgr)
i_file_manager *mgr) -> bool override; -> bool override;
void stop() override; void stop() override;
}; };

View File

@@ -771,6 +771,69 @@ auto s3_provider::get_total_drive_space() const -> std::uint64_t {
return std::numeric_limits<std::int64_t>::max() / std::int64_t(2); return std::numeric_limits<std::int64_t>::max() / std::int64_t(2);
} }
auto s3_provider::initialize_crypto(const s3_config &cfg) -> bool {
REPERTORY_USES_FUNCTION_NAME();
auto ret{true};
auto res = get_kdf_config_from_meta("/", master_kdf_cfg_);
switch (res) {
case api_error::item_not_found: {
try {
event_system::instance().raise<debug_log>(
function_name, "searching for master kdf config");
if (not search_keys_for_master_kdf(cfg.encryption_token)) {
if (get_directory_item_count("/") == 0U) {
event_system::instance().raise<debug_log>(
function_name, "creating master kdf config for empty bucket");
legacy_bucket_ = false;
master_kdf_cfg_.seal();
master_key_ =
utils::encryption::generate_key<utils::hash::hash_256_t>(
cfg.encryption_token, master_kdf_cfg_);
res = set_item_meta("/", META_KDF,
nlohmann::json(master_kdf_cfg_).dump());
event_system::instance().raise<debug_log>(
function_name,
fmt::format("master_kdf|{}",
nlohmann::json(master_kdf_cfg_).dump(2)));
if (res != api_error::success) {
utils::error::raise_api_path_error(function_name, "/", res,
"set kdf config in meta failed");
ret = false;
}
}
}
} catch (const std::exception &e) {
utils::error::raise_error(function_name, e, "exception occurred");
ret = false;
}
} break;
case api_error::success: {
event_system::instance().raise<debug_log>(
function_name, "recreating master kdf config for existing bucket");
legacy_bucket_ = false;
if (not utils::encryption::recreate_key_argon2id(
cfg.encryption_token, master_kdf_cfg_, master_key_)) {
utils::error::raise_error(
function_name, "failed to recreate master key from kdf config");
ret = false;
}
} break;
default: {
utils::error::raise_api_path_error(function_name, "/", res,
"get kdf config from meta failed");
ret = false;
}
}
return ret;
}
auto s3_provider::is_directory(const std::string &api_path, bool &exists) const auto s3_provider::is_directory(const std::string &api_path, bool &exists) const
-> api_error { -> api_error {
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();
@@ -955,8 +1018,8 @@ auto s3_provider::rename_file(const std::string & /* from_api_path */,
return api_error::not_implemented; return api_error::not_implemented;
} }
auto s3_provider::search_keys_for_master_kdf(std::string_view encryption_token) auto s3_provider::search_keys_for_master_kdf(
-> bool { const std::string &encryption_token) -> bool {
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();
std::string token{}; std::string token{};
@@ -998,30 +1061,34 @@ auto s3_provider::search_keys_for_master_kdf(std::string_view encryption_token)
continue; continue;
} }
utils::encryption::kdf_config cfg; if (not utils::encryption::kdf_config::from_header(buffer,
if (not utils::encryption::kdf_config::from_header(buffer, cfg)) { master_kdf_cfg_)) {
continue; continue;
} }
cfg.unique_id = 0U; master_kdf_cfg_.unique_id = 0U;
cfg.seal(); master_kdf_cfg_.seal();
master_kdf_cfg_ = cfg;
if (not utils::encryption::recreate_key_argon2id( if (not utils::encryption::recreate_key_argon2id(
encryption_token, master_kdf_cfg_, master_key_)) { encryption_token, master_kdf_cfg_, master_key_)) {
throw utils::error::create_exception( throw utils::error::create_exception(
function_name, {"failed to recreate master key from kdf"}); function_name, {"failed to recreate master key from kdf config"});
} }
auto res = auto res =
set_item_meta("/", META_KDF, nlohmann::json(master_kdf_cfg_).dump()); set_item_meta("/", META_KDF, nlohmann::json(master_kdf_cfg_).dump());
event_system::instance().raise<debug_log>(
function_name,
fmt::format("master_kdf|{}", nlohmann::json(master_kdf_cfg_).dump(2)));
if (res == api_error::success) { if (res == api_error::success) {
legacy_bucket_ = false; legacy_bucket_ = false;
event_system::instance().raise<debug_log>(function_name,
"found master kdf config");
return true; return true;
} }
throw utils::error::create_exception(function_name, throw utils::error::create_exception(function_name,
{"failed to set meta kdf"}); {"failed to set meta kdf config"});
} }
return false; return false;
@@ -1081,61 +1148,11 @@ auto s3_provider::start(api_item_added_callback api_item_added,
event_system::instance().raise<service_start_begin>(function_name, event_system::instance().raise<service_start_begin>(function_name,
"s3_provider"); "s3_provider");
auto ret = base_provider::start(api_item_added, mgr);
const auto &cfg{get_s3_config()}; const auto &cfg{get_s3_config()};
auto ret = base_provider::start(api_item_added, mgr);
if (ret && not cfg.encryption_token.empty()) { if (ret && not cfg.encryption_token.empty()) {
auto res = get_kdf_config_from_meta("/", master_kdf_cfg_); if (not initialize_crypto(cfg)) {
switch (res) {
case api_error::item_not_found: {
try {
event_system::instance().raise<debug_log>(function_name,
"searching for master kdf");
if (not search_keys_for_master_kdf(cfg.encryption_token)) {
if (get_directory_item_count("/") == 0U) {
event_system::instance().raise<debug_log>(
function_name, "creating master kdf for empty bucket");
legacy_bucket_ = false;
master_kdf_cfg_.seal();
master_key_ =
utils::encryption::generate_key<utils::hash::hash_256_t>(
cfg.encryption_token, master_kdf_cfg_);
res = set_item_meta("/", META_KDF,
nlohmann::json(master_kdf_cfg_).dump());
if (res != api_error::success) {
utils::error::raise_api_path_error(function_name, "/", res,
"set kdf in meta failed");
ret = false;
}
}
}
} catch (const std::exception &e) {
utils::error::raise_error(function_name, e, "exception occurred");
ret = false;
}
} break;
case api_error::success: {
event_system::instance().raise<debug_log>(
function_name, "recreating master kdf for existing bucket");
legacy_bucket_ = false;
if (not utils::encryption::recreate_key_argon2id(
cfg.encryption_token, master_kdf_cfg_, master_key_)) {
utils::error::raise_error(function_name,
"failed to recreate master key from kdf");
ret = false;
}
} break;
default: {
utils::error::raise_api_path_error(function_name, "/", res,
"get kdf from meta failed");
ret = false;
}
}
if (not ret) {
base_provider::stop(); base_provider::stop();
} }
} }
@@ -1218,6 +1235,12 @@ auto s3_provider::upload_file_impl(const std::string &api_path,
res = set_item_meta( res = set_item_meta(
api_path, META_KDF, api_path, META_KDF,
nlohmann::json(*put_file.reader->get_kdf_config_for_data()).dump()); nlohmann::json(*put_file.reader->get_kdf_config_for_data()).dump());
event_system::instance().raise<debug_log>(
function_name,
fmt::format(
"file_kdf|{}",
nlohmann::json(*put_file.reader->get_kdf_config_for_data())
.dump(2)));
if (res != api_error::success) { if (res != api_error::success) {
return res; return res;
} }