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

This commit is contained in:
2025-08-30 11:38:06 -05:00
parent 8979e6e2a4
commit 5a033e7f12
2 changed files with 72 additions and 72 deletions

View File

@@ -102,8 +102,8 @@ private:
return s3_config_; return s3_config_;
} }
[[nodiscard]] auto search_keys_for_kdf(std::string_view encryption_token) [[nodiscard]] auto
-> bool; search_keys_for_master_kdf(std::string_view 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;

View File

@@ -24,6 +24,7 @@
#include "app_config.hpp" #include "app_config.hpp"
#include "comm/i_http_comm.hpp" #include "comm/i_http_comm.hpp"
#include "events/event_system.hpp" #include "events/event_system.hpp"
#include "events/types/debug_log.hpp"
#include "events/types/service_start_begin.hpp" #include "events/types/service_start_begin.hpp"
#include "events/types/service_start_end.hpp" #include "events/types/service_start_end.hpp"
#include "events/types/service_stop_begin.hpp" #include "events/types/service_stop_begin.hpp"
@@ -952,87 +953,73 @@ 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_kdf(std::string_view encryption_token) auto s3_provider::search_keys_for_master_kdf(std::string_view encryption_token)
-> bool { -> bool {
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();
auto grab_more{true};
std::string token{}; std::string token{};
while (grab_more) { std::string response_data{};
std::string response_data{}; long response_code{};
long response_code{}; if (not get_object_list(response_data, response_code, "/", "", token)) {
if (not get_object_list(response_data, response_code, "/", "", token)) { throw utils::error::create_exception(function_name,
throw utils::error::create_exception(function_name, {"failed to get object list"});
{"failed to get object list"}); }
if (response_code == http_error_codes::not_found) {
throw utils::error::create_exception(function_name,
{"failed to get object list"});
}
if (response_code != http_error_codes::ok) {
throw utils::error::create_exception(function_name,
{"failed to get object list"});
}
pugi::xml_document doc;
auto parse_res{doc.load_string(response_data.c_str())};
if (parse_res.status != pugi::xml_parse_status::status_ok) {
throw utils::error::create_exception(function_name,
{"failed to get object list"});
}
auto node_list = doc.select_nodes("/ListBucketResult/Contents");
for (const auto &node : node_list) {
std::string object_name{
node.node().select_node("Key").node().text().as_string(),
};
if (object_name == "/") {
continue;
} }
if (response_code == http_error_codes::not_found) { data_buffer buffer;
throw utils::error::create_exception(function_name, if (not utils::collection::from_hex_string(object_name, buffer)) {
{"failed to get object list"}); continue;
} }
if (response_code != http_error_codes::ok) { utils::encryption::kdf_config cfg;
throw utils::error::create_exception(function_name, if (not utils::encryption::kdf_config::from_header(buffer, cfg)) {
{"failed to get object list"}); continue;
} }
pugi::xml_document doc; cfg.unique_id = 0U;
auto parse_res{doc.load_string(response_data.c_str())}; cfg.seal();
if (parse_res.status != pugi::xml_parse_status::status_ok) {
throw utils::error::create_exception(function_name, master_kdf_cfg_ = cfg;
{"failed to get object list"}); if (not utils::encryption::recreate_key_argon2id(
encryption_token, master_kdf_cfg_, master_key_)) {
throw utils::error::create_exception(
function_name, {"failed to recreate master key from kdf"});
} }
grab_more = doc.select_node("/ListBucketResult/IsTruncated") auto res =
.node() set_item_meta("/", META_KDF, nlohmann::json(master_kdf_cfg_).dump());
.text() if (res == api_error::success) {
.as_bool(); legacy_bucket_ = false;
if (grab_more) { return true;
token = doc.select_node("/ListBucketResult/NextContinuationToken")
.node()
.text()
.as_string();
} }
auto node_list = doc.select_nodes("/ListBucketResult/Contents"); throw utils::error::create_exception(function_name,
for (const auto &node : node_list) { {"failed to set meta kdf"});
std::string object_name{
node.node().select_node("Key").node().text().as_string(),
};
if (object_name == "/") {
continue;
}
data_buffer buffer;
if (not utils::collection::from_hex_string(object_name, buffer)) {
continue;
}
utils::encryption::kdf_config cfg;
if (not utils::encryption::kdf_config::from_header(buffer, cfg)) {
continue;
}
cfg.unique_id = 0U;
cfg.seal();
master_kdf_cfg_ = cfg;
if (not utils::encryption::recreate_key_argon2id(
encryption_token, master_kdf_cfg_, master_key_)) {
throw utils::error::create_exception(
function_name, {"failed to recreate master key from kdf"});
}
auto res =
set_item_meta("/", META_KDF, nlohmann::json(master_kdf_cfg_).dump());
if (res == api_error::success) {
legacy_bucket_ = false;
return true;
}
throw utils::error::create_exception(function_name,
{"failed to set meta kdf"});
}
} }
return false; return false;
@@ -1091,16 +1078,20 @@ 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");
const auto &cfg{get_s3_config()};
auto ret = base_provider::start(api_item_added, mgr); auto ret = base_provider::start(api_item_added, mgr);
const auto &cfg{get_s3_config()};
if (ret && not cfg.encryption_token.empty()) { if (ret && not cfg.encryption_token.empty()) {
auto res = get_kdf_config_from_meta("/", master_kdf_cfg_); auto res = get_kdf_config_from_meta("/", master_kdf_cfg_);
switch (res) { switch (res) {
case api_error::item_not_found: { case api_error::item_not_found: {
try { try {
if (not search_keys_for_kdf(cfg.encryption_token)) { 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) { if (get_directory_item_count("/") == 0U) {
event_system::instance().raise<debug_log>(
function_name, "creating master kdf for empty bucket");
legacy_bucket_ = false; legacy_bucket_ = false;
master_kdf_cfg_.seal(); master_kdf_cfg_.seal();
master_key_ = master_key_ =
@@ -1123,6 +1114,10 @@ auto s3_provider::start(api_item_added_callback api_item_added,
} break; } break;
case api_error::success: { 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( if (not utils::encryption::recreate_key_argon2id(
cfg.encryption_token, master_kdf_cfg_, master_key_)) { cfg.encryption_token, master_kdf_cfg_, master_key_)) {
utils::error::raise_error(function_name, utils::error::raise_error(function_name,
@@ -1143,6 +1138,11 @@ auto s3_provider::start(api_item_added_callback api_item_added,
} }
} }
event_system::instance().raise<debug_log>(
function_name,
fmt::format("encrypted|{}|legacy_bucket|{}|ret|{}",
not cfg.encryption_token.empty(), legacy_bucket_, ret));
event_system::instance().raise<service_start_end>(function_name, event_system::instance().raise<service_start_end>(function_name,
"s3_provider"); "s3_provider");
return ret; return ret;
@@ -1214,7 +1214,7 @@ auto s3_provider::upload_file_impl(const std::string &api_path,
master_key_, master_kdf_cfg_, std::nullopt, -1); master_key_, master_kdf_cfg_, std::nullopt, -1);
res = set_item_meta( res = set_item_meta(
"/", 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());
if (res == api_error::success) { if (res == api_error::success) {
return res; return res;