Implement secure key via KDF for transparent data encryption/decryption #60
This commit is contained in:
		| @@ -102,8 +102,8 @@ private: | ||||
|     return s3_config_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto search_keys_for_kdf(std::string_view encryption_token) | ||||
|       -> bool; | ||||
|   [[nodiscard]] auto | ||||
|   search_keys_for_master_kdf(std::string_view encryption_token) -> bool; | ||||
|  | ||||
|   [[nodiscard]] auto set_meta_key(const std::string &api_path, | ||||
|                                   api_meta_map &meta) -> api_error; | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include "app_config.hpp" | ||||
| #include "comm/i_http_comm.hpp" | ||||
| #include "events/event_system.hpp" | ||||
| #include "events/types/debug_log.hpp" | ||||
| #include "events/types/service_start_begin.hpp" | ||||
| #include "events/types/service_start_end.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; | ||||
| } | ||||
|  | ||||
| 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 { | ||||
|   REPERTORY_USES_FUNCTION_NAME(); | ||||
|  | ||||
|   auto grab_more{true}; | ||||
|   std::string token{}; | ||||
|   while (grab_more) { | ||||
|     std::string response_data{}; | ||||
|     long response_code{}; | ||||
|     if (not get_object_list(response_data, response_code, "/", "", token)) { | ||||
|       throw utils::error::create_exception(function_name, | ||||
|                                            {"failed to get object list"}); | ||||
|   std::string response_data{}; | ||||
|   long response_code{}; | ||||
|   if (not get_object_list(response_data, response_code, "/", "", token)) { | ||||
|     throw utils::error::create_exception(function_name, | ||||
|                                          {"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) { | ||||
|       throw utils::error::create_exception(function_name, | ||||
|                                            {"failed to get object list"}); | ||||
|     data_buffer buffer; | ||||
|     if (not utils::collection::from_hex_string(object_name, buffer)) { | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     if (response_code != http_error_codes::ok) { | ||||
|       throw utils::error::create_exception(function_name, | ||||
|                                            {"failed to get object list"}); | ||||
|     utils::encryption::kdf_config cfg; | ||||
|     if (not utils::encryption::kdf_config::from_header(buffer, cfg)) { | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     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"}); | ||||
|     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"}); | ||||
|     } | ||||
|  | ||||
|     grab_more = doc.select_node("/ListBucketResult/IsTruncated") | ||||
|                     .node() | ||||
|                     .text() | ||||
|                     .as_bool(); | ||||
|     if (grab_more) { | ||||
|       token = doc.select_node("/ListBucketResult/NextContinuationToken") | ||||
|                   .node() | ||||
|                   .text() | ||||
|                   .as_string(); | ||||
|     auto res = | ||||
|         set_item_meta("/", META_KDF, nlohmann::json(master_kdf_cfg_).dump()); | ||||
|     if (res == api_error::success) { | ||||
|       legacy_bucket_ = false; | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|       } | ||||
|  | ||||
|       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"}); | ||||
|     } | ||||
|     throw utils::error::create_exception(function_name, | ||||
|                                          {"failed to set meta kdf"}); | ||||
|   } | ||||
|  | ||||
|   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, | ||||
|                                                       "s3_provider"); | ||||
|   const auto &cfg{get_s3_config()}; | ||||
|  | ||||
|   auto ret = base_provider::start(api_item_added, mgr); | ||||
|   const auto &cfg{get_s3_config()}; | ||||
|   if (ret && not cfg.encryption_token.empty()) { | ||||
|     auto res = get_kdf_config_from_meta("/", master_kdf_cfg_); | ||||
|     switch (res) { | ||||
|     case api_error::item_not_found: { | ||||
|       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) { | ||||
|             event_system::instance().raise<debug_log>( | ||||
|                 function_name, "creating master kdf for empty bucket"); | ||||
|             legacy_bucket_ = false; | ||||
|             master_kdf_cfg_.seal(); | ||||
|             master_key_ = | ||||
| @@ -1123,6 +1114,10 @@ auto s3_provider::start(api_item_added_callback api_item_added, | ||||
|     } 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, | ||||
| @@ -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, | ||||
|                                                     "s3_provider"); | ||||
|   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); | ||||
|  | ||||
|       res = set_item_meta( | ||||
|           "/", META_KDF, | ||||
|           api_path, META_KDF, | ||||
|           nlohmann::json(*put_file.reader->get_kdf_config_for_data()).dump()); | ||||
|       if (res == api_error::success) { | ||||
|         return res; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user