From 8ac7b06d68f2e4a48723f38a5d3b4d2706fe879c Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 3 Sep 2025 08:51:22 -0500 Subject: [PATCH] add setting to force legacy encryption --- .../include/providers/s3/s3_provider.hpp | 3 +- .../librepertory/include/types/repertory.hpp | 15 ++++++-- .../src/providers/s3/s3_provider.cpp | 35 +++++++++++++++---- .../repertory_test/src/app_config_test.cpp | 10 ++++++ 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/repertory/librepertory/include/providers/s3/s3_provider.hpp b/repertory/librepertory/include/providers/s3/s3_provider.hpp index c319f8bb..406adedb 100644 --- a/repertory/librepertory/include/providers/s3/s3_provider.hpp +++ b/repertory/librepertory/include/providers/s3/s3_provider.hpp @@ -102,7 +102,8 @@ private: return s3_config_; } - [[nodiscard]] auto initialize_crypto(const s3_config &cfg) -> bool; + [[nodiscard]] auto initialize_crypto(const s3_config &cfg, bool is_retry) + -> bool; [[nodiscard]] auto search_keys_for_master_kdf(const std::string &encryption_token) -> bool; diff --git a/repertory/librepertory/include/types/repertory.hpp b/repertory/librepertory/include/types/repertory.hpp index 1821e013..9285bbd7 100644 --- a/repertory/librepertory/include/types/repertory.hpp +++ b/repertory/librepertory/include/types/repertory.hpp @@ -336,6 +336,7 @@ struct s3_config final { std::string access_key; std::string bucket; std::string encryption_token; + bool force_legacy_encryption{false}; std::string region{"any"}; std::string secret_key; std::uint32_t timeout_ms{default_timeout_ms}; @@ -346,9 +347,11 @@ struct s3_config final { auto operator==(const s3_config &cfg) const noexcept -> bool { if (&cfg != this) { return access_key == cfg.access_key && bucket == cfg.bucket && - encryption_token == cfg.encryption_token && region == cfg.region && - secret_key == cfg.secret_key && timeout_ms == cfg.timeout_ms && - url == cfg.url && use_path_style == cfg.use_path_style && + encryption_token == cfg.encryption_token && + force_legacy_encryption == cfg.force_legacy_encryption && + region == cfg.region && secret_key == cfg.secret_key && + timeout_ms == cfg.timeout_ms && url == cfg.url && + use_path_style == cfg.use_path_style && use_region_in_url == cfg.use_region_in_url; } @@ -412,6 +415,7 @@ inline constexpr auto JSON_ENCRYPT_CONFIG{"EncryptConfig"}; inline constexpr auto JSON_EVENT_LEVEL{"EventLevel"}; inline constexpr auto JSON_EVICTION_DELAY_MINS{"EvictionDelayMinutes"}; inline constexpr auto JSON_EVICTION_USE_ACCESS_TIME{"EvictionUseAccessedTime"}; +inline constexpr auto JSON_FORCE_LEGACY_ENCRYPTION{"ForceLegacyEncryption"}; inline constexpr auto JSON_HIGH_FREQ_INTERVAL_SECS{"HighFreqIntervalSeconds"}; inline constexpr auto JSON_HOST_CONFIG{"HostConfig"}; inline constexpr auto JSON_HOST_NAME_OR_IP{"HostNameOrIp"}; @@ -530,6 +534,7 @@ template <> struct adl_serializer { data[repertory::JSON_ACCESS_KEY] = value.access_key; data[repertory::JSON_BUCKET] = value.bucket; data[repertory::JSON_ENCRYPTION_TOKEN] = value.encryption_token; + data[repertory::JSON_ENCRYPTION_TOKEN] = value.force_legacy_encryption; data[repertory::JSON_REGION] = value.region; data[repertory::JSON_SECRET_KEY] = value.secret_key; data[repertory::JSON_TIMEOUT_MS] = value.timeout_ms; @@ -548,6 +553,10 @@ template <> struct adl_serializer { data.at(repertory::JSON_URL).get_to(value.url); data.at(repertory::JSON_USE_PATH_STYLE).get_to(value.use_path_style); data.at(repertory::JSON_USE_REGION_IN_URL).get_to(value.use_region_in_url); + if (data.contains(repertory::JSON_FORCE_LEGACY_ENCRYPTION)) { + data.at(repertory::JSON_FORCE_LEGACY_ENCRYPTION) + .get_to(value.force_legacy_encryption); + } } }; diff --git a/repertory/librepertory/src/providers/s3/s3_provider.cpp b/repertory/librepertory/src/providers/s3/s3_provider.cpp index b9894708..16493547 100644 --- a/repertory/librepertory/src/providers/s3/s3_provider.cpp +++ b/repertory/librepertory/src/providers/s3/s3_provider.cpp @@ -782,7 +782,8 @@ auto s3_provider::get_total_drive_space() const -> std::uint64_t { return std::numeric_limits::max() / std::int64_t(2); } -auto s3_provider::initialize_crypto(const s3_config &cfg) -> bool { +auto s3_provider::initialize_crypto(const s3_config &cfg, bool is_retry) + -> bool { REPERTORY_USES_FUNCTION_NAME(); auto ret{true}; @@ -792,7 +793,8 @@ auto s3_provider::initialize_crypto(const s3_config &cfg) -> bool { case api_error::item_not_found: { try { if (not search_keys_for_master_kdf(cfg.encryption_token)) { - if (get_directory_item_count("/") == 0U) { + if (not cfg.force_legacy_encryption && + get_directory_item_count("/") == 0U) { legacy_bucket_ = false; master_kdf_cfg_.seal(); master_key_ = @@ -818,9 +820,23 @@ auto s3_provider::initialize_crypto(const s3_config &cfg) -> bool { 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; + if (is_retry) { + utils::error::raise_api_path_error( + function_name, "/", res, "failed to recreate master key from kdf"); + ret = false; + } else { + utils::error::raise_error(function_name, + "failed to recreate master key from kdf " + "config: re-initializing kdf configuration"); + res = set_item_meta("/", META_KDF, ""); + if (res == api_error::success) { + ret = initialize_crypto(cfg, true); + } else { + utils::error::raise_api_path_error(function_name, "/", res, + "reset kdf config in meta failed"); + ret = false; + } + } } } break; @@ -1048,8 +1064,15 @@ auto s3_provider::search_keys_for_master_kdf( {"failed to get object list"}); } + std::uint8_t count{0U}; + constexpr const max_count{3U}; + auto node_list = doc.select_nodes("/ListBucketResult/Contents"); for (const auto &node : node_list) { + if (++count > max_count) { + return false; + } + std::string object_name{ node.node().select_node("Key").node().text().as_string(), }; @@ -1151,7 +1174,7 @@ auto s3_provider::start(api_item_added_callback api_item_added, auto ret = base_provider::start(api_item_added, mgr); if (ret && not cfg.encryption_token.empty()) { - if (not initialize_crypto(cfg)) { + if (not initialize_crypto(cfg, false)) { base_provider::stop(); } } diff --git a/repertory/repertory_test/src/app_config_test.cpp b/repertory/repertory_test/src/app_config_test.cpp index d4f172e3..f491f2f8 100644 --- a/repertory/repertory_test/src/app_config_test.cpp +++ b/repertory/repertory_test/src/app_config_test.cpp @@ -623,6 +623,7 @@ static void common_tests(app_config &config, provider_type prov) { cfg1.url = "7"; cfg1.use_path_style = false; cfg1.use_region_in_url = false; + cfg1.force_legacy_encryption = false; s3_config cfg2{}; cfg2.access_key = "8"; @@ -634,6 +635,7 @@ static void common_tests(app_config &config, provider_type prov) { cfg2.url = "14"; cfg2.use_path_style = true; cfg2.use_region_in_url = true; + cfg2.force_legacy_encryption = true; ASSERT_NE(cfg1, cfg2); @@ -650,6 +652,7 @@ static void common_tests(app_config &config, provider_type prov) { cfg3.url = "14"; cfg3.use_path_style = true; cfg3.use_region_in_url = true; + cfg3.force_legacy_encryption = true; auto value = cfg.set_value_by_name( fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ACCESS_KEY), @@ -694,6 +697,13 @@ static void common_tests(app_config &config, provider_type prov) { utils::string::from_bool(cfg3.use_region_in_url)); EXPECT_STREQ(utils::string::from_bool(cfg3.use_region_in_url).c_str(), value.c_str()); + + value = cfg.set_value_by_name( + fmt::format("{}.{}", JSON_S3_CONFIG, JSON_FORCE_LEGACY_ENCRYPTION), + utils::string::from_bool(cfg3.force_legacy_encryption)); + EXPECT_STREQ( + utils::string::from_bool(cfg3.force_legacy_encryption).c_str(), + value.c_str()); }}, {JSON_SIA_CONFIG, [](app_config &cfg) {