updated build system
This commit is contained in:
		| @@ -806,11 +806,11 @@ auto encrypt_provider::read_file_bytes(const std::string &api_path, | ||||
|   } else if (not reader_lookup_.contains(file_data.source_path)) { | ||||
|     auto info{std::make_shared<reader_info>()}; | ||||
|     info->reader = std::make_unique<utils::encryption::encrypting_reader>( | ||||
|         api_path, file_data.source_path, | ||||
|         [&stop_requested]() -> bool { | ||||
|           return stop_requested || app_config::get_stop_requested(); | ||||
|         }, | ||||
|         cfg.encryption_token, std::move(file_data.iv_list)); | ||||
|         api_path, file_data.source_path, cfg.encryption_token, | ||||
|         std::move(file_data.iv_list)); | ||||
|     reader_lookup_[file_data.source_path] = info; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -425,6 +425,8 @@ using vlc_string_t = std::unique_ptr<char, vlc_string_deleter>; | ||||
|  | ||||
| namespace repertory { | ||||
| using data_buffer = std::vector<unsigned char>; | ||||
| using data_span = std::span<unsigned char>; | ||||
| using data_cspan = std::span<const unsigned char>; | ||||
| using mutex_lock = std::lock_guard<std::mutex>; | ||||
| using recur_mutex_lock = std::lock_guard<std::recursive_mutex>; | ||||
| using stop_type = std::atomic_bool; | ||||
|   | ||||
| @@ -38,14 +38,15 @@ public: | ||||
|                     std::optional<std::string> relative_parent_path, | ||||
|                     std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader(std::string_view encrypted_file_path, | ||||
|                     std::string_view source_path, | ||||
|                     stop_type_callback stop_requested_cb, | ||||
|                     std::string_view token, std::size_t error_return = 0U); | ||||
|   encrypting_reader(stop_type_callback stop_requested_cb, | ||||
|                     std::string_view encrypted_file_path, | ||||
|                     std::string_view source_path, std::string_view token, | ||||
|                     std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader( | ||||
|       stop_type_callback stop_requested_cb, | ||||
|       std::string_view encrypted_file_path, std::string_view source_path, | ||||
|       stop_type_callback stop_requested_cb, std::string_view token, | ||||
|       std::string_view token, | ||||
|       std::vector<std::array<unsigned char, | ||||
|                              crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> | ||||
|           iv_list, | ||||
| @@ -57,16 +58,37 @@ public: | ||||
|                     std::optional<std::string> relative_parent_path, | ||||
|                     std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader(std::string_view encrypted_file_path, | ||||
|                     std::string_view source_path, | ||||
|                     stop_type_callback stop_requested_cb, | ||||
|                     std::string_view token, kdf_config cfg, | ||||
|                     std::size_t error_return = 0U); | ||||
|   encrypting_reader(stop_type_callback stop_requested_cb, | ||||
|                     std::string_view encrypted_file_path, | ||||
|                     std::string_view source_path, std::string_view token, | ||||
|                     kdf_config cfg, std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader( | ||||
|       stop_type_callback stop_requested_cb, | ||||
|       std::string_view encrypted_file_path, std::string_view source_path, | ||||
|       stop_type_callback stop_requested_cb, std::string_view token, | ||||
|       kdf_config cfg, | ||||
|       std::string_view token, kdf_config cfg, | ||||
|       std::vector<std::array<unsigned char, | ||||
|                              crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> | ||||
|           iv_list, | ||||
|       std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader(std::string_view file_name, std::string_view source_path, | ||||
|                     stop_type_callback stop_requested_cb, | ||||
|                     const utils::hash::hash_256_t &master_key, | ||||
|                     const kdf_config &cfg, | ||||
|                     std::optional<std::string> relative_parent_path, | ||||
|                     std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader(stop_type_callback stop_requested_cb, | ||||
|                     std::string_view encrypted_file_path, | ||||
|                     std::string_view source_path, | ||||
|                     const utils::hash::hash_256_t &master_key, | ||||
|                     const kdf_config &cfg, std::size_t error_return = 0U); | ||||
|  | ||||
|   encrypting_reader( | ||||
|       stop_type_callback stop_requested_cb, | ||||
|       std::string_view encrypted_file_path, std::string_view source_path, | ||||
|       const utils::hash::hash_256_t &master_key, const kdf_config &cfg, | ||||
|       std::vector<std::array<unsigned char, | ||||
|                              crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> | ||||
|           iv_list, | ||||
| @@ -82,10 +104,13 @@ public: | ||||
|  | ||||
| public: | ||||
|   using iostream = std::basic_iostream<char, std::char_traits<char>>; | ||||
|   using kdf_pair_t = std::pair<data_buffer, data_buffer>; | ||||
|   using key_pair_t = | ||||
|       std::pair<utils::hash::hash_256_t, utils::hash::hash_256_t>; | ||||
|   using streambuf = std::basic_streambuf<char, std::char_traits<char>>; | ||||
|  | ||||
| private: | ||||
|   utils::hash::hash_256_t key_; | ||||
|   key_pair_t keys_; | ||||
|   stop_type_callback stop_requested_cb_; | ||||
|   size_t error_return_; | ||||
|   std::unique_ptr<utils::file::i_file> source_file_; | ||||
| @@ -97,7 +122,7 @@ private: | ||||
|  | ||||
| private: | ||||
|   std::unordered_map<std::size_t, data_buffer> chunk_buffers_; | ||||
|   std::optional<data_buffer> kdf_header_; | ||||
|   std::optional<kdf_pair_t> kdf_headers_; | ||||
|   std::size_t last_data_chunk_{}; | ||||
|   std::size_t last_data_chunk_size_{}; | ||||
|   std::uint64_t read_offset_{}; | ||||
| @@ -113,6 +138,11 @@ private: | ||||
|  | ||||
|   void common_initialize(bool procces_iv_list); | ||||
|  | ||||
|   void common_initialize_kdf_data(const kdf_config &cfg, | ||||
|                                   const utils::hash::hash_256_t &master_key); | ||||
|  | ||||
|   void common_initialize_kdf_path(const utils::hash::hash_256_t &master_key); | ||||
|  | ||||
|   void create_encrypted_paths(std::string_view file_name, | ||||
|                               std::optional<std::string> relative_parent_path); | ||||
|  | ||||
| @@ -157,7 +187,11 @@ public: | ||||
|     return iv_list_; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] auto get_kdf_config() const -> std::optional<kdf_config>; | ||||
|   [[nodiscard]] auto get_kdf_config_for_data() const | ||||
|       -> std::optional<kdf_config>; | ||||
|  | ||||
|   [[nodiscard]] auto get_kdf_config_for_path() const | ||||
|       -> std::optional<kdf_config>; | ||||
|  | ||||
|   [[nodiscard]] auto get_stop_requested() const -> bool { | ||||
|     return stop_requested_cb_(); | ||||
|   | ||||
| @@ -25,6 +25,9 @@ | ||||
|  | ||||
| #include "utils/config.hpp" | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_BOOST) && defined(PROJECT_ENABLE_JSON) | ||||
| #include "utils/collection.hpp" | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) && defined(PROJECT_ENABLE_JSON) | ||||
| #include "utils/error.hpp" | ||||
| #include "utils/hash.hpp" | ||||
|  | ||||
| @@ -53,7 +56,7 @@ enum class opslimit_level : std::uint8_t { | ||||
| }; | ||||
|  | ||||
| [[nodiscard]] inline auto get_memlimit(memlimit_level memlimit) -> size_t { | ||||
|   constexpr const auto mib512{512ULL * 1024ULL * 1024ULL}; | ||||
|   constexpr auto mib512{512ULL * 1024ULL * 1024ULL}; | ||||
|  | ||||
|   switch (memlimit) { | ||||
|   case memlimit_level::level1: | ||||
| @@ -88,25 +91,64 @@ enum class opslimit_level : std::uint8_t { | ||||
|   return crypto_pwhash_OPSLIMIT_MODERATE; | ||||
| } | ||||
|  | ||||
| enum class kdf_context : std::uint8_t { | ||||
|   data, | ||||
|   path, | ||||
|   undefined, | ||||
| }; | ||||
| using kdf_ctx_t = std::array<char, crypto_kdf_CONTEXTBYTES>; | ||||
|  | ||||
| namespace kdf { | ||||
| constexpr inline std::array< | ||||
|     kdf_ctx_t, static_cast<std::size_t>(kdf_context::undefined) + 1U> | ||||
|     KDF_CTXS{ | ||||
|         { | ||||
|             {'D', 'A', 'T', 'A', '_', 'C', 'T', 'X'}, | ||||
|             {'F', 'I', 'L', 'E', '_', 'C', 'T', 'X'}, | ||||
|             {'D', 'E', 'F', 'L', '_', 'C', 'T', 'X'}, | ||||
|         }, | ||||
|     }; | ||||
| } // namespace kdf | ||||
|  | ||||
| [[nodiscard]] constexpr inline auto get_kdf_context_name(kdf_context ctx) | ||||
|     -> kdf_ctx_t { | ||||
|   const auto idx = static_cast<std::size_t>(ctx); | ||||
|   return idx < kdf::KDF_CTXS.size() ? kdf::KDF_CTXS.at(idx) | ||||
|                                     : kdf::KDF_CTXS.back(); | ||||
| } | ||||
|  | ||||
| #pragma pack(push, 1) | ||||
| struct kdf_config final { | ||||
|   static constexpr std::uint32_t repertory_magic{0x52505432}; | ||||
|   using salt_t = std::array<std::uint8_t, crypto_pwhash_SALTBYTES>; | ||||
|  | ||||
|   std::uint32_t magic{repertory_magic}; | ||||
|   kdf_version version{kdf_version::v1}; | ||||
|   kdf_type kdf{kdf_type::argon2id}; | ||||
|   memlimit_level memlimit{memlimit_level::level3}; | ||||
|   opslimit_level opslimit{opslimit_level::level2}; | ||||
|   std::uint64_t unique_id{}; | ||||
|   salt_t salt{}; | ||||
|   std::uint64_t checksum{}; | ||||
|  | ||||
|   [[nodiscard]] static auto from_header(std::span<const unsigned char> data, | ||||
|                                         kdf_config &cfg) -> bool; | ||||
|   template <typename hash_t> | ||||
|   [[nodiscard]] auto create_subkey(kdf_context ctx, std::size_t unique_id_, | ||||
|                                    const hash_t &master_key) const | ||||
|       -> std::pair<hash_t, kdf_config> { | ||||
|     hash_t sub_key; | ||||
|     crypto_kdf_derive_from_key(sub_key.data(), sub_key.size(), unique_id_, | ||||
|                                get_kdf_context_name(ctx).data(), | ||||
|                                master_key.data()); | ||||
|     auto cfg = *this; | ||||
|     cfg.unique_id = unique_id_; | ||||
|     cfg.checksum = cfg.generate_checksum(); | ||||
|     return {sub_key, cfg}; | ||||
|   } | ||||
|  | ||||
|   [[nodiscard]] static auto from_header(data_cspan data, kdf_config &cfg) | ||||
|       -> bool; | ||||
|  | ||||
|   [[nodiscard]] auto generate_checksum() const -> std::uint64_t; | ||||
|  | ||||
|   void generate_salt(); | ||||
|   void seal(); | ||||
|  | ||||
|   [[nodiscard]] static constexpr auto size() -> std::size_t { | ||||
|     return sizeof(kdf_config); | ||||
| @@ -160,21 +202,18 @@ template <typename string_t> | ||||
|  | ||||
| template <typename hash_t, typename string_t> | ||||
| [[nodiscard]] inline bool | ||||
| detect_and_recreate_key(string_t password, | ||||
|                         std::span<const unsigned char> header, hash_t &key, | ||||
| detect_and_recreate_key(string_t password, data_cspan header, hash_t &key, | ||||
|                         std::optional<kdf_config> &cfg); | ||||
|  | ||||
| template <typename hash_t> | ||||
| [[nodiscard]] inline bool | ||||
| detect_and_recreate_key(std::string_view password, | ||||
|                         std::span<const unsigned char> header, hash_t &key, | ||||
|                         std::optional<kdf_config> &cfg); | ||||
| detect_and_recreate_key(std::string_view password, data_cspan header, | ||||
|                         hash_t &key, std::optional<kdf_config> &cfg); | ||||
|  | ||||
| template <typename hash_t> | ||||
| [[nodiscard]] inline bool | ||||
| detect_and_recreate_key(std::wstring_view password, | ||||
|                         std::span<const unsigned char> header, hash_t &key, | ||||
|                         std::optional<kdf_config> &cfg); | ||||
| detect_and_recreate_key(std::wstring_view password, data_cspan header, | ||||
|                         hash_t &key, std::optional<kdf_config> &cfg); | ||||
|  | ||||
| [[nodiscard]] auto decrypt_file_name(std::string_view encryption_token, | ||||
|                                      std::string &file_name) -> bool; | ||||
| @@ -396,7 +435,7 @@ read_encrypted_range(const http_range &range, | ||||
| template <typename string_t> | ||||
| auto create_key_argon2id(string_t password, kdf_config &cfg, | ||||
|                          utils::hash::hash_256_t &key) -> bool { | ||||
|   cfg.generate_salt(); | ||||
|   cfg.seal(); | ||||
|  | ||||
|   return recreate_key_argon2id(password, cfg, key); | ||||
| } | ||||
| @@ -531,8 +570,7 @@ inline auto recreate_key(std::wstring_view password, const kdf_config &cfg) | ||||
| } | ||||
|  | ||||
| template <typename hash_t, typename string_t> | ||||
| inline bool detect_and_recreate_key(string_t password, | ||||
|                                     std::span<const unsigned char> header, | ||||
| inline bool detect_and_recreate_key(string_t password, data_cspan header, | ||||
|                                     hash_t &key, | ||||
|                                     std::optional<kdf_config> &cfg) { | ||||
|   if (header.size() >= kdf_config::size()) { | ||||
| @@ -550,8 +588,7 @@ inline bool detect_and_recreate_key(string_t password, | ||||
|  | ||||
| template <typename hash_t> | ||||
| inline bool detect_and_recreate_key(std::string_view password, | ||||
|                                     std::span<const unsigned char> header, | ||||
|                                     hash_t &key, | ||||
|                                     data_cspan header, hash_t &key, | ||||
|                                     std::optional<kdf_config> &cfg) { | ||||
|   return detect_and_recreate_key<hash_t, std::string_view>(password, header, | ||||
|                                                            key, cfg); | ||||
| @@ -559,14 +596,96 @@ inline bool detect_and_recreate_key(std::string_view password, | ||||
|  | ||||
| template <typename hash_t> | ||||
| inline bool detect_and_recreate_key(std::wstring_view password, | ||||
|                                     std::span<const unsigned char> header, | ||||
|                                     hash_t &key, | ||||
|                                     data_cspan header, hash_t &key, | ||||
|                                     std::optional<kdf_config> &cfg) { | ||||
|   return detect_and_recreate_key<hash_t, std::wstring_view>(password, header, | ||||
|                                                             key, cfg); | ||||
| } | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) | ||||
| } // namespace repertory::utils::encryption | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_BOOST) && defined(PROJECT_ENABLE_JSON) | ||||
| NLOHMANN_JSON_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace kdf { | ||||
| inline constexpr std::string_view JSON_CHECKSUM{"checksum"}; | ||||
| inline constexpr std::string_view JSON_KDF{"kdf"}; | ||||
| inline constexpr std::string_view JSON_MEMLIMIT{"memlimit"}; | ||||
| inline constexpr std::string_view JSON_OPSLIMIT{"opslimit"}; | ||||
| inline constexpr std::string_view JSON_SALT{"salt"}; | ||||
| inline constexpr std::string_view JSON_UNIQUE_ID{"unique_id"}; | ||||
| inline constexpr std::string_view JSON_VERSION{"version"}; | ||||
| } // namespace kdf | ||||
|  | ||||
| template <> | ||||
| struct adl_serializer<repertory::utils::encryption::kdf_config::salt_t> { | ||||
|   static void | ||||
|   to_json(json &data, | ||||
|           const repertory::utils::encryption::kdf_config::salt_t &value) { | ||||
|     data = repertory::utils::collection::to_hex_string(value); | ||||
|   } | ||||
|  | ||||
|   static void | ||||
|   from_json(const json &data, | ||||
|             repertory::utils::encryption::kdf_config::salt_t &value) { | ||||
|     REPERTORY_USES_FUNCTION_NAME(); | ||||
|  | ||||
|     repertory::data_buffer buffer{}; | ||||
|     if (not repertory::utils::collection::from_hex_string( | ||||
|             data.get<std::string>(), buffer)) { | ||||
|       throw repertory::utils::error::create_exception( | ||||
|           function_name, { | ||||
|                              "failed to convert hex string to salt", | ||||
|                              data.get<std::string>(), | ||||
|                          }); | ||||
|     } | ||||
|  | ||||
|     if (buffer.size() != value.size()) { | ||||
|       throw repertory::utils::error::create_exception( | ||||
|           function_name, { | ||||
|                              "unexpected length for salt after hex conversion", | ||||
|                              "expected", | ||||
|                              std::to_string(value.size()), | ||||
|                              "actual", | ||||
|                              std::to_string(buffer.size()), | ||||
|                          }); | ||||
|     } | ||||
|  | ||||
|     std::copy_n(buffer.begin(), value.size(), value.begin()); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <> struct adl_serializer<repertory::utils::encryption::kdf_config> { | ||||
|   static void to_json(json &data, | ||||
|                       const repertory::utils::encryption::kdf_config &value) { | ||||
|     data[kdf::JSON_CHECKSUM] = value.checksum; | ||||
|     data[kdf::JSON_KDF] = value.kdf; | ||||
|     data[kdf::JSON_MEMLIMIT] = value.memlimit; | ||||
|     data[kdf::JSON_OPSLIMIT] = value.opslimit; | ||||
|     data[kdf::JSON_SALT] = value.salt; | ||||
|     data[kdf::JSON_UNIQUE_ID] = value.unique_id; | ||||
|     data[kdf::JSON_VERSION] = value.version; | ||||
|   } | ||||
|  | ||||
|   static void from_json(const json &data, | ||||
|                         repertory::utils::encryption::kdf_config &value) { | ||||
|     data.at(kdf::JSON_CHECKSUM).get_to<std::uint64_t>(value.checksum); | ||||
|     data.at(kdf::JSON_KDF) | ||||
|         .get_to<repertory::utils::encryption::kdf_type>(value.kdf); | ||||
|     data.at(kdf::JSON_MEMLIMIT) | ||||
|         .get_to<repertory::utils::encryption::memlimit_level>(value.memlimit); | ||||
|     data.at(kdf::JSON_OPSLIMIT) | ||||
|         .get_to<repertory::utils::encryption::opslimit_level>(value.opslimit); | ||||
|     data.at(kdf::JSON_SALT) | ||||
|         .get_to<repertory::utils::encryption::kdf_config::salt_t>(value.salt); | ||||
|     data.at(kdf::JSON_UNIQUE_ID).get_to<std::uint64_t>(value.unique_id); | ||||
|     data.at(kdf::JSON_VERSION) | ||||
|         .get_to<repertory::utils::encryption::kdf_version>(value.version); | ||||
|   } | ||||
| }; | ||||
| NLOHMANN_JSON_NAMESPACE_END | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) && defined(PROJECT_ENABLE_JSON) | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) | ||||
| #endif // REPERTORY_INCLUDE_UTILS_ENCRYPTION_HPP_ | ||||
|   | ||||
| @@ -12,7 +12,7 @@ public: | ||||
|   using entry_t = atomic_t<data_t>; | ||||
|   using entry_ptr_t = std::shared_ptr<entry_t>; | ||||
|  | ||||
|   static constexpr const auto default_expiration{duration(60000U)}; | ||||
|   static constexpr auto default_expiration{duration(60000U)}; | ||||
|  | ||||
| private: | ||||
|   struct entry final { | ||||
|   | ||||
| @@ -181,7 +181,8 @@ encrypting_reader::encrypting_reader( | ||||
|     std::string_view file_name, std::string_view source_path, | ||||
|     stop_type_callback stop_requested_cb, std::string_view token, | ||||
|     std::optional<std::string> relative_parent_path, std::size_t error_return) | ||||
|     : key_(utils::encryption::generate_key<utils::hash::hash_256_t>(token)), | ||||
|     : keys_(utils::encryption::generate_key<utils::hash::hash_256_t>(token), | ||||
|             utils::encryption::generate_key<utils::hash::hash_256_t>(token)), | ||||
|       stop_requested_cb_(std::move(stop_requested_cb)), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)) { | ||||
| @@ -189,12 +190,13 @@ encrypting_reader::encrypting_reader( | ||||
|   create_encrypted_paths(file_name, relative_parent_path); | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader(std::string_view encrypted_file_path, | ||||
| encrypting_reader::encrypting_reader(stop_type_callback stop_requested_cb, | ||||
|                                      std::string_view encrypted_file_path, | ||||
|                                      std::string_view source_path, | ||||
|                                      stop_type_callback stop_requested_cb, | ||||
|                                      std::string_view token, | ||||
|                                      std::size_t error_return) | ||||
|     : key_(utils::encryption::generate_key<utils::hash::hash_256_t>(token)), | ||||
|     : keys_(utils::encryption::generate_key<utils::hash::hash_256_t>(token), | ||||
|             utils::encryption::generate_key<utils::hash::hash_256_t>(token)), | ||||
|       stop_requested_cb_(std::move(stop_requested_cb)), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)), | ||||
| @@ -205,13 +207,14 @@ encrypting_reader::encrypting_reader(std::string_view encrypted_file_path, | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader( | ||||
|     std::string_view encrypted_file_path, std::string_view source_path, | ||||
|     stop_type_callback stop_requested_cb, std::string_view token, | ||||
|     stop_type_callback stop_requested_cb, std::string_view encrypted_file_path, | ||||
|     std::string_view source_path, std::string_view token, | ||||
|     std::vector< | ||||
|         std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> | ||||
|         iv_list, | ||||
|     std::size_t error_return) | ||||
|     : key_(utils::encryption::generate_key<utils::hash::hash_256_t>(token)), | ||||
|     : keys_(utils::encryption::generate_key<utils::hash::hash_256_t>(token), | ||||
|             utils::encryption::generate_key<utils::hash::hash_256_t>(token)), | ||||
|       stop_requested_cb_(std::move(stop_requested_cb)), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)), | ||||
| @@ -227,48 +230,50 @@ encrypting_reader::encrypting_reader( | ||||
|     stop_type_callback stop_requested_cb, std::string_view token, | ||||
|     kdf_config cfg, std::optional<std::string> relative_parent_path, | ||||
|     std::size_t error_return) | ||||
|     : key_( | ||||
|           utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg)), | ||||
|     : keys_( | ||||
|           utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg), | ||||
|           utils::encryption::generate_key<utils::hash::hash_256_t>(token)), | ||||
|       stop_requested_cb_(std::move(stop_requested_cb)), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)) { | ||||
|   kdf_header_ = cfg.to_header(); | ||||
|   kdf_headers_ = {cfg.to_header(), cfg.to_header()}; | ||||
|  | ||||
|   data_buffer result; | ||||
|   utils::encryption::encrypt_data( | ||||
|       key_, reinterpret_cast<const unsigned char *>(file_name.data()), | ||||
|       keys_.second, reinterpret_cast<const unsigned char *>(file_name.data()), | ||||
|       file_name.size(), result); | ||||
|   common_initialize(true); | ||||
|   create_encrypted_paths(file_name, relative_parent_path); | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader(std::string_view encrypted_file_path, | ||||
| encrypting_reader::encrypting_reader(stop_type_callback stop_requested_cb, | ||||
|                                      std::string_view encrypted_file_path, | ||||
|                                      std::string_view source_path, | ||||
|                                      stop_type_callback stop_requested_cb, | ||||
|                                      std::string_view token, kdf_config cfg, | ||||
|                                      std::size_t error_return) | ||||
|     : key_( | ||||
|           utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg)), | ||||
|     : keys_( | ||||
|           utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg), | ||||
|           utils::encryption::generate_key<utils::hash::hash_256_t>(token)), | ||||
|       stop_requested_cb_(std::move(stop_requested_cb)), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)), | ||||
|       encrypted_file_name_( | ||||
|           utils::path::strip_to_file_name(std::string{encrypted_file_path})), | ||||
|       encrypted_file_path_(encrypted_file_path) { | ||||
|   kdf_header_ = cfg.to_header(); | ||||
|   kdf_headers_ = {cfg.to_header(), cfg.to_header()}; | ||||
|   common_initialize(true); | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader( | ||||
|     std::string_view encrypted_file_path, std::string_view source_path, | ||||
|     stop_type_callback stop_requested_cb, std::string_view token, | ||||
|     kdf_config cfg, | ||||
|     stop_type_callback stop_requested_cb, std::string_view encrypted_file_path, | ||||
|     std::string_view source_path, std::string_view token, kdf_config cfg, | ||||
|     std::vector< | ||||
|         std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> | ||||
|         iv_list, | ||||
|     std::size_t error_return) | ||||
|     : key_( | ||||
|           utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg)), | ||||
|     : keys_( | ||||
|           utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg), | ||||
|           utils::encryption::generate_key<utils::hash::hash_256_t>(token)), | ||||
|       stop_requested_cb_(std::move(stop_requested_cb)), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)), | ||||
| @@ -276,12 +281,74 @@ encrypting_reader::encrypting_reader( | ||||
|           utils::path::strip_to_file_name(std::string{encrypted_file_path})), | ||||
|       encrypted_file_path_(encrypted_file_path), | ||||
|       iv_list_(std::move(iv_list)) { | ||||
|   kdf_header_ = cfg.to_header(); | ||||
|   kdf_headers_ = {cfg.to_header(), cfg.to_header()}; | ||||
|   common_initialize(false); | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader( | ||||
|     std::string_view file_name, std::string_view source_path, | ||||
|     stop_type_callback stop_requested_cb, | ||||
|     const utils::hash::hash_256_t &master_key, const kdf_config &cfg, | ||||
|     std::optional<std::string> relative_parent_path, std::size_t error_return) | ||||
|     : stop_requested_cb_(std::move(stop_requested_cb)), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)) { | ||||
|   common_initialize_kdf_data(cfg, master_key); | ||||
|  | ||||
|   kdf_config path_cfg; | ||||
|   std::tie(keys_.second, path_cfg) = cfg.create_subkey( | ||||
|       kdf_context::path, utils::generate_secure_random<std::uint64_t>(), | ||||
|       master_key); | ||||
|   kdf_headers_->second = path_cfg.to_header(); | ||||
|  | ||||
|   data_buffer result; | ||||
|   utils::encryption::encrypt_data( | ||||
|       keys_.second, reinterpret_cast<const unsigned char *>(file_name.data()), | ||||
|       file_name.size(), result); | ||||
|   common_initialize(true); | ||||
|   create_encrypted_paths(file_name, relative_parent_path); | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader(stop_type_callback stop_requested_cb, | ||||
|                                      std::string_view encrypted_file_path, | ||||
|                                      std::string_view source_path, | ||||
|                                      const utils::hash::hash_256_t &master_key, | ||||
|                                      const kdf_config &cfg, | ||||
|                                      std::size_t error_return) | ||||
|     : keys_(), | ||||
|       stop_requested_cb_(std::move(stop_requested_cb)), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)), | ||||
|       encrypted_file_name_( | ||||
|           utils::path::strip_to_file_name(std::string{encrypted_file_path})), | ||||
|       encrypted_file_path_(encrypted_file_path) { | ||||
|   common_initialize_kdf_data(cfg, master_key); | ||||
|   common_initialize_kdf_path(master_key); | ||||
|   common_initialize(true); | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader( | ||||
|     stop_type_callback stop_requested_cb, std::string_view encrypted_file_path, | ||||
|     std::string_view source_path, const utils::hash::hash_256_t &master_key, | ||||
|     const kdf_config &cfg, | ||||
|     std::vector< | ||||
|         std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>> | ||||
|         iv_list, | ||||
|     std::size_t error_return) | ||||
|     : stop_requested_cb_(std::move(stop_requested_cb)), | ||||
|       error_return_(error_return), | ||||
|       source_file_(utils::file::file::open_or_create_file(source_path, true)), | ||||
|       encrypted_file_name_( | ||||
|           utils::path::strip_to_file_name(std::string{encrypted_file_path})), | ||||
|       encrypted_file_path_(encrypted_file_path), | ||||
|       iv_list_(std::move(iv_list)) { | ||||
|   common_initialize_kdf_data(cfg, master_key); | ||||
|   common_initialize_kdf_path(master_key); | ||||
|   common_initialize(false); | ||||
| } | ||||
|  | ||||
| encrypting_reader::encrypting_reader(const encrypting_reader &reader) | ||||
|     : key_(reader.key_), | ||||
|     : keys_(reader.keys_), | ||||
|       stop_requested_cb_(reader.stop_requested_cb_), | ||||
|       error_return_(reader.error_return_), | ||||
|       source_file_( | ||||
| @@ -290,7 +357,7 @@ encrypting_reader::encrypting_reader(const encrypting_reader &reader) | ||||
|       encrypted_file_path_(reader.encrypted_file_path_), | ||||
|       iv_list_(reader.iv_list_), | ||||
|       chunk_buffers_(reader.chunk_buffers_), | ||||
|       kdf_header_(reader.kdf_header_), | ||||
|       kdf_headers_(reader.kdf_headers_), | ||||
|       last_data_chunk_(reader.last_data_chunk_), | ||||
|       last_data_chunk_size_(reader.last_data_chunk_size_), | ||||
|       read_offset_(reader.read_offset_), | ||||
| @@ -307,46 +374,6 @@ encrypting_reader::encrypting_reader(const encrypting_reader &reader) | ||||
|   } | ||||
| } | ||||
|  | ||||
| void encrypting_reader::common_initialize(bool procces_iv_list) { | ||||
|   REPERTORY_USES_FUNCTION_NAME(); | ||||
|  | ||||
|   if (not *source_file_) { | ||||
|     throw utils::error::create_exception(function_name, | ||||
|                                          { | ||||
|                                              "file open failed", | ||||
|                                              source_file_->get_path(), | ||||
|                                          }); | ||||
|   } | ||||
|  | ||||
|   auto opt_size = source_file_->size(); | ||||
|   if (not opt_size.has_value()) { | ||||
|     throw utils::error::create_exception(function_name, | ||||
|                                          { | ||||
|                                              "failed to get file size", | ||||
|                                              source_file_->get_path(), | ||||
|                                          }); | ||||
|   } | ||||
|   auto file_size = opt_size.value(); | ||||
|  | ||||
|   auto total_chunks = utils::divide_with_ceiling( | ||||
|       file_size, static_cast<std::uint64_t>(data_chunk_size_)); | ||||
|   total_size_ = file_size + (total_chunks * encryption_header_size) + | ||||
|                 (kdf_header_.has_value() ? kdf_header_->size() : 0U); | ||||
|   last_data_chunk_ = total_chunks - 1U; | ||||
|   last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size | ||||
|                           : (file_size % data_chunk_size_) == 0U | ||||
|                               ? data_chunk_size_ | ||||
|                               : file_size % data_chunk_size_; | ||||
|   if (not procces_iv_list) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   iv_list_.resize(total_chunks); | ||||
|   for (auto &data : iv_list_) { | ||||
|     randombytes_buf(data.data(), data.size()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| auto encrypting_reader::calculate_decrypted_size(std::uint64_t total_size, | ||||
|                                                  bool uses_kdf) | ||||
|     -> std::uint64_t { | ||||
| @@ -382,15 +409,88 @@ auto encrypting_reader::calculate_encrypted_size(std::string_view source_path, | ||||
|          (uses_kdf ? kdf_config::size() : 0U); | ||||
| } | ||||
|  | ||||
| void encrypting_reader::common_initialize(bool procces_iv_list) { | ||||
|   REPERTORY_USES_FUNCTION_NAME(); | ||||
|  | ||||
|   if (not *source_file_) { | ||||
|     throw utils::error::create_exception(function_name, | ||||
|                                          { | ||||
|                                              "file open failed", | ||||
|                                              source_file_->get_path(), | ||||
|                                          }); | ||||
|   } | ||||
|  | ||||
|   auto opt_size = source_file_->size(); | ||||
|   if (not opt_size.has_value()) { | ||||
|     throw utils::error::create_exception(function_name, | ||||
|                                          { | ||||
|                                              "failed to get file size", | ||||
|                                              source_file_->get_path(), | ||||
|                                          }); | ||||
|   } | ||||
|   auto file_size = opt_size.value(); | ||||
|  | ||||
|   auto total_chunks = utils::divide_with_ceiling( | ||||
|       file_size, static_cast<std::uint64_t>(data_chunk_size_)); | ||||
|   total_size_ = file_size + (total_chunks * encryption_header_size) + | ||||
|                 (kdf_headers_.has_value() ? kdf_headers_->first.size() : 0U); | ||||
|   last_data_chunk_ = total_chunks - 1U; | ||||
|   last_data_chunk_size_ = (file_size <= data_chunk_size_) ? file_size | ||||
|                           : (file_size % data_chunk_size_) == 0U | ||||
|                               ? data_chunk_size_ | ||||
|                               : file_size % data_chunk_size_; | ||||
|   if (not procces_iv_list) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   iv_list_.resize(total_chunks); | ||||
|   for (auto &data : iv_list_) { | ||||
|     randombytes_buf(data.data(), data.size()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void encrypting_reader::common_initialize_kdf_data( | ||||
|     const kdf_config &cfg, const utils::hash::hash_256_t &master_key) { | ||||
|   auto [data_key, data_cfg] = cfg.create_subkey( | ||||
|       kdf_context::data, utils::generate_secure_random<std::uint64_t>(), | ||||
|       master_key); | ||||
|   keys_.first = std::move(data_key); | ||||
|   kdf_headers_ = {data_cfg.to_header(), {}}; | ||||
| } | ||||
|  | ||||
| void encrypting_reader::common_initialize_kdf_path( | ||||
|     const utils::hash::hash_256_t &master_key) { | ||||
|   REPERTORY_USES_FUNCTION_NAME(); | ||||
|  | ||||
|   data_buffer buffer; | ||||
|   if (not utils::collection::from_hex_string(encrypted_file_path_, buffer)) { | ||||
|     throw utils::error::create_exception( | ||||
|         function_name, {"failed to convert encrypted path from hex to bytes"}); | ||||
|   } | ||||
|  | ||||
|   kdf_config path_cfg; | ||||
|   if (not kdf_config::from_header(buffer, path_cfg)) { | ||||
|     throw utils::error::create_exception( | ||||
|         function_name, {"failed to create path kdf config from header"}); | ||||
|   } | ||||
|  | ||||
|   utils::hash::hash_256_t path_key; | ||||
|   std::tie(path_key, std::ignore) = | ||||
|       path_cfg.create_subkey(kdf_context::path, path_cfg.unique_id, master_key); | ||||
|  | ||||
|   kdf_headers_->second = path_cfg.to_header(); | ||||
| } | ||||
|  | ||||
| void encrypting_reader::create_encrypted_paths( | ||||
|     std::string_view file_name, | ||||
|     std::optional<std::string> relative_parent_path) { | ||||
|   data_buffer result; | ||||
|   utils::encryption::encrypt_data( | ||||
|       key_, reinterpret_cast<const unsigned char *>(file_name.data()), | ||||
|       keys_.second, reinterpret_cast<const unsigned char *>(file_name.data()), | ||||
|       file_name.size(), result); | ||||
|   if (kdf_header_.has_value()) { | ||||
|     result.insert(result.begin(), kdf_header_->begin(), kdf_header_->end()); | ||||
|   if (kdf_headers_.has_value()) { | ||||
|     result.insert(result.begin(), kdf_headers_->second.begin(), | ||||
|                   kdf_headers_->second.end()); | ||||
|   } | ||||
|  | ||||
|   encrypted_file_name_ = utils::collection::to_hex_string(result); | ||||
| @@ -403,10 +503,11 @@ void encrypting_reader::create_encrypted_paths( | ||||
|        utils::string::split(relative_parent_path.value(), | ||||
|                             utils::path::directory_seperator, false)) { | ||||
|     utils::encryption::encrypt_data( | ||||
|         key_, reinterpret_cast<const unsigned char *>(part.c_str()), | ||||
|         keys_.second, reinterpret_cast<const unsigned char *>(part.c_str()), | ||||
|         strnlen(part.c_str(), part.size()), result); | ||||
|     if (kdf_header_.has_value()) { | ||||
|       result.insert(result.begin(), kdf_header_->begin(), kdf_header_->end()); | ||||
|     if (kdf_headers_.has_value()) { | ||||
|       result.insert(result.begin(), kdf_headers_->second.begin(), | ||||
|                     kdf_headers_->second.end()); | ||||
|     } | ||||
|  | ||||
|     encrypted_file_path_ += '/' + utils::collection::to_hex_string(result); | ||||
| @@ -421,15 +522,35 @@ auto encrypting_reader::create_iostream() const | ||||
|       std::make_unique<encrypting_streambuf>(*this)); | ||||
| } | ||||
|  | ||||
| auto encrypting_reader::get_kdf_config() const -> std::optional<kdf_config> { | ||||
| auto encrypting_reader::get_kdf_config_for_data() const | ||||
|     -> std::optional<kdf_config> { | ||||
|   REPERTORY_USES_FUNCTION_NAME(); | ||||
|  | ||||
|   if (not kdf_header_.has_value()) { | ||||
|   if (not kdf_headers_.has_value()) { | ||||
|     return std::nullopt; | ||||
|   } | ||||
|  | ||||
|   kdf_config cfg; | ||||
|   if (not kdf_config::from_header(kdf_header_.value(), cfg)) { | ||||
|   if (not kdf_config::from_header(kdf_headers_->first, cfg)) { | ||||
|     throw utils::error::create_exception(function_name, | ||||
|                                          { | ||||
|                                              "invalid kdf header", | ||||
|                                          }); | ||||
|   } | ||||
|  | ||||
|   return cfg; | ||||
| } | ||||
|  | ||||
| auto encrypting_reader::get_kdf_config_for_path() const | ||||
|     -> std::optional<kdf_config> { | ||||
|   REPERTORY_USES_FUNCTION_NAME(); | ||||
|  | ||||
|   if (not kdf_headers_.has_value()) { | ||||
|     return std::nullopt; | ||||
|   } | ||||
|  | ||||
|   kdf_config cfg; | ||||
|   if (not kdf_config::from_header(kdf_headers_->second, cfg)) { | ||||
|     throw utils::error::create_exception(function_name, | ||||
|                                          { | ||||
|                                              "invalid kdf header", | ||||
| @@ -443,6 +564,8 @@ auto encrypting_reader::reader_function(char *buffer, size_t size, | ||||
|                                         size_t nitems) -> size_t { | ||||
|   REPERTORY_USES_FUNCTION_NAME(); | ||||
|  | ||||
|   std::span<char> dest(buffer, size); | ||||
|  | ||||
|   auto read_size = | ||||
|       static_cast<std::uint64_t>(size) * static_cast<std::uint64_t>(nitems); | ||||
|   if (read_size == 0U) { | ||||
| @@ -453,17 +576,17 @@ auto encrypting_reader::reader_function(char *buffer, size_t size, | ||||
|   std::size_t total_read{}; | ||||
|   auto total_size{total_size_}; | ||||
|  | ||||
|   if (kdf_header_.has_value()) { | ||||
|     total_size -= kdf_header_->size(); | ||||
|   if (kdf_headers_.has_value()) { | ||||
|     auto &hdr = kdf_headers_->first; | ||||
|     total_size -= hdr.size(); | ||||
|  | ||||
|     if (read_offset < kdf_header_->size()) { | ||||
|     if (read_offset < hdr.size()) { | ||||
|       auto to_read{ | ||||
|           utils::calculate_read_size(kdf_header_->size(), read_size, | ||||
|                                      read_offset), | ||||
|           utils::calculate_read_size(hdr.size(), read_size, read_offset), | ||||
|       }; | ||||
|       read_offset_ += to_read; | ||||
|  | ||||
|       std::memcpy(buffer, &kdf_header_->data()[read_offset], to_read); | ||||
|       std::memcpy(&dest[total_read], &hdr.at(read_offset), to_read); | ||||
|       if (read_size - to_read == 0) { | ||||
|         return to_read; | ||||
|       } | ||||
| @@ -471,9 +594,8 @@ auto encrypting_reader::reader_function(char *buffer, size_t size, | ||||
|       read_offset = 0U; | ||||
|       read_size -= to_read; | ||||
|       total_read += to_read; | ||||
|       buffer += to_read; | ||||
|     } else { | ||||
|       read_offset -= kdf_header_->size(); | ||||
|       read_offset -= hdr.size(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -501,8 +623,8 @@ auto encrypting_reader::reader_function(char *buffer, size_t size, | ||||
|                   static_cast<std::uint64_t>(data_chunk_size_), | ||||
|               &bytes_read); | ||||
|           if (ret) { | ||||
|             utils::encryption::encrypt_data(iv_list_.at(chunk), key_, file_data, | ||||
|                                             chunk_buffer); | ||||
|             utils::encryption::encrypt_data(iv_list_.at(chunk), keys_.first, | ||||
|                                             file_data, chunk_buffer); | ||||
|           } | ||||
|         } else if (chunk != 0U) { | ||||
|           chunk_buffers_.erase(chunk - 1U); | ||||
| @@ -510,7 +632,7 @@ auto encrypting_reader::reader_function(char *buffer, size_t size, | ||||
|  | ||||
|         auto &chunk_buffer = chunk_buffers_[chunk]; | ||||
|         auto to_read = std::min(chunk_buffer.size() - chunk_offset, remain); | ||||
|         std::memcpy(buffer + total_read, &chunk_buffer[chunk_offset], to_read); | ||||
|         std::memcpy(&dest[total_read], &chunk_buffer[chunk_offset], to_read); | ||||
|         total_read += to_read; | ||||
|         remain -= to_read; | ||||
|         chunk_offset = 0U; | ||||
|   | ||||
| @@ -28,11 +28,24 @@ | ||||
| #include "utils/hash.hpp" | ||||
| #include "utils/path.hpp" | ||||
|  | ||||
| namespace { | ||||
| constexpr auto resize_by(repertory::data_span &data, std::size_t /* size */) | ||||
|     -> repertory::data_span & { | ||||
|   return data; | ||||
| } | ||||
|  | ||||
| auto resize_by(repertory::data_buffer &data, std::size_t size) | ||||
|     -> repertory::data_buffer & { | ||||
|   data.resize(data.size() + size); | ||||
|   return data; | ||||
| } | ||||
| } // namespace | ||||
|  | ||||
| namespace repertory::utils::encryption { | ||||
| auto kdf_config::to_header() const -> data_buffer { | ||||
|   kdf_config tmp{*this}; | ||||
|   tmp.checksum = boost::endian::native_to_big(tmp.checksum); | ||||
|   tmp.magic = boost::endian::native_to_big(tmp.magic); | ||||
|   tmp.unique_id = boost::endian::native_to_big(tmp.unique_id); | ||||
|  | ||||
|   data_buffer ret(size()); | ||||
|   std::memcpy(ret.data(), &tmp, ret.size()); | ||||
| @@ -49,24 +62,15 @@ auto kdf_config::generate_checksum() const -> std::uint64_t { | ||||
|   return *reinterpret_cast<std::uint64_t *>(hash.data()); | ||||
| } | ||||
|  | ||||
| void kdf_config::generate_salt() { | ||||
|   randombytes_buf(salt.data(), salt.size()); | ||||
|   checksum = generate_checksum(); | ||||
| } | ||||
|  | ||||
| auto kdf_config::from_header(std::span<const unsigned char> data, | ||||
|                              kdf_config &cfg) -> bool { | ||||
| auto kdf_config::from_header(data_cspan data, kdf_config &cfg) -> bool { | ||||
|   if (data.size() < kdf_config::size()) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   std::memcpy(&cfg, data.data(), kdf_config::size()); | ||||
|   cfg.magic = boost::endian::big_to_native(cfg.magic); | ||||
|   if (cfg.magic != repertory_magic) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   cfg.checksum = boost::endian::big_to_native(cfg.checksum); | ||||
|   cfg.unique_id = boost::endian::big_to_native(cfg.unique_id); | ||||
|   return cfg.version == kdf_version::v1 && cfg.kdf == kdf_type::argon2id && | ||||
|          cfg.memlimit >= memlimit_level::level1 && | ||||
|          cfg.memlimit <= memlimit_level::level4 && | ||||
| @@ -75,6 +79,11 @@ auto kdf_config::from_header(std::span<const unsigned char> data, | ||||
|          cfg.checksum == cfg.generate_checksum(); | ||||
| } | ||||
|  | ||||
| void kdf_config::seal() { | ||||
|   randombytes_buf(salt.data(), salt.size()); | ||||
|   checksum = generate_checksum(); | ||||
| } | ||||
|  | ||||
| auto decrypt_file_path(std::string_view encryption_token, | ||||
|                        std::string &file_path) -> bool { | ||||
|   std::vector<std::string> decrypted_parts; | ||||
| @@ -140,16 +149,6 @@ auto decrypt_file_name(std::string_view encryption_token, const kdf_config &cfg, | ||||
|                                          file_name); | ||||
| } | ||||
|  | ||||
| constexpr auto resize_by(std::span<unsigned char> &data, std::size_t size) | ||||
|     -> std::span<unsigned char> & { | ||||
|   return data; | ||||
| } | ||||
|  | ||||
| static auto resize_by(data_buffer &data, std::size_t size) -> data_buffer & { | ||||
|   data.resize(data.size() + size); | ||||
|   return data; | ||||
| } | ||||
|  | ||||
| template <typename data_t> | ||||
| [[nodiscard]] auto | ||||
| read_encrypted_range(http_range range, const utils::hash::hash_256_t &key, | ||||
| @@ -215,12 +214,12 @@ read_encrypted_range(http_range range, const utils::hash::hash_256_t &key, | ||||
|  | ||||
|     auto data_size = static_cast<std::size_t>(std::min( | ||||
|         remain, static_cast<std::uint64_t>(data_chunk_size - source_offset))); | ||||
|     resize_by(data, data_size); | ||||
|     std::copy(std::next(source_buffer.begin(), | ||||
|                         static_cast<std::int64_t>(source_offset)), | ||||
|               std::next(source_buffer.begin(), | ||||
|                         static_cast<std::int64_t>(source_offset + data_size)), | ||||
|               std::next(data.begin(), static_cast<std::int64_t>(bytes_read))); | ||||
|               std::next(resize_by(data, data_size).begin(), | ||||
|                         static_cast<std::int64_t>(bytes_read))); | ||||
|     remain -= data_size; | ||||
|     bytes_read += data_size; | ||||
|     source_offset = 0U; | ||||
| @@ -243,8 +242,8 @@ auto read_encrypted_range(const http_range &range, | ||||
|     const http_range &range, const utils::hash::hash_256_t &key, bool uses_kdf, | ||||
|     reader_func_t reader_func, std::uint64_t total_size, unsigned char *data, | ||||
|     std::size_t size, std::size_t &bytes_read) -> bool { | ||||
|   std::span dest_buffer(data, size); | ||||
|   return read_encrypted_range<std::span<unsigned char>>( | ||||
|   data_span dest_buffer(data, size); | ||||
|   return read_encrypted_range<data_span>( | ||||
|       range, key, reader_func, total_size, dest_buffer, | ||||
|       uses_kdf ? kdf_config::size() : 0U, bytes_read); | ||||
| } | ||||
|   | ||||
| @@ -51,7 +51,7 @@ auto from_utf8(std::string_view str) -> std::wstring { | ||||
|   while (idx < len) { | ||||
|     UChar32 uni_ch{}; | ||||
|     U8_NEXT(str_ptr, idx, len, uni_ch); | ||||
|     if (uni_ch < 0 || !U_IS_UNICODE_CHAR(uni_ch)) { | ||||
|     if (uni_ch < 0 || not U_IS_UNICODE_CHAR(uni_ch)) { | ||||
|       throw std::runtime_error("from_utf8: invalid UTF-8 sequence"); | ||||
|     } | ||||
|     std::array<UChar, 2U> units{}; | ||||
| @@ -71,7 +71,7 @@ auto from_utf8(std::string_view str) -> std::wstring { | ||||
|   while (idx < len) { | ||||
|     UChar32 uni_ch{}; | ||||
|     U8_NEXT(str_ptr, idx, len, uni_ch); | ||||
|     if (uni_ch < 0 || !U_IS_UNICODE_CHAR(uni_ch)) { | ||||
|     if (uni_ch < 0 || not U_IS_UNICODE_CHAR(uni_ch)) { | ||||
|       throw std::runtime_error("from_utf8: invalid UTF-8 sequence"); | ||||
|     } | ||||
|     out.push_back(static_cast<wchar_t>(uni_ch)); | ||||
| @@ -183,7 +183,7 @@ auto to_utf8(std::wstring_view str) -> std::string { | ||||
|   while (idx < len) { | ||||
|     UChar32 uni_ch{}; | ||||
|     U16_NEXT(u16, idx, len, uni_ch); | ||||
|     if (uni_ch < 0 || !U_IS_UNICODE_CHAR(uni_ch)) { | ||||
|     if (uni_ch < 0 || not U_IS_UNICODE_CHAR(uni_ch)) { | ||||
|       throw std::runtime_error("to_utf8: invalid UTF-16 sequence"); | ||||
|     } | ||||
|     std::array<std::uint8_t, U8_MAX_LENGTH> buf{}; | ||||
| @@ -197,9 +197,9 @@ auto to_utf8(std::wstring_view str) -> std::string { | ||||
|                static_cast<std::size_t>(off)); | ||||
|   } | ||||
| #else  // WCHAR_MAX > 0xFFFF | ||||
|   for (auto cur_ch : str) { | ||||
|   for (const auto &cur_ch : str) { | ||||
|     auto uni_char{static_cast<UChar32>(cur_ch)}; | ||||
|     if (!U_IS_UNICODE_CHAR(uni_char)) { | ||||
|     if (not U_IS_UNICODE_CHAR(uni_char)) { | ||||
|       throw std::runtime_error("to_utf8: invalid Unicode scalar value"); | ||||
|     } | ||||
|     std::array<std::uint8_t, U8_MAX_LENGTH> buf{}; | ||||
|   | ||||
| @@ -32,8 +32,8 @@ namespace { | ||||
|  | ||||
|   std::vector<gid_t> groups{}; | ||||
| #if defined(__APPLE__) | ||||
|   constexpr const int buffer_count{8}; | ||||
|   constexpr const int max_group_count{1024}; | ||||
|   constexpr int buffer_count{8}; | ||||
|   constexpr int max_group_count{1024}; | ||||
|   groups.resize(buffer_count); | ||||
|  | ||||
|   std::size_t orig_count{0U}; | ||||
|   | ||||
| @@ -35,7 +35,8 @@ TEST(utils_encrypting_reader, read_file_data) { | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token); | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, | ||||
|         std::nullopt); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i++) { | ||||
|       data_buffer buffer( | ||||
| @@ -76,14 +77,14 @@ TEST(utils_encrypting_reader, read_file_data_using_argon2id) { | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, cfg); | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, cfg, | ||||
|         std::nullopt); | ||||
|  | ||||
|     std::array<std::uint8_t, utils::encryption::kdf_config::size()> hdr; | ||||
|     EXPECT_EQ(hdr.size(), utils::encryption::encrypting_reader::reader_function( | ||||
|                               reinterpret_cast<char *>(hdr.data()), hdr.size(), | ||||
|                               1U, &reader)); | ||||
|     EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, cfg)); | ||||
|     // EXPECT_EQ(cfg, reader.get_kdf_config().value()); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i++) { | ||||
|       data_buffer buffer( | ||||
| @@ -98,7 +99,62 @@ TEST(utils_encrypting_reader, read_file_data_using_argon2id) { | ||||
|  | ||||
|       data_buffer decrypted_data; | ||||
|       EXPECT_TRUE(utils::encryption::decrypt_data( | ||||
|           token, *reader.get_kdf_config(), buffer, decrypted_data)); | ||||
|           token, *reader.get_kdf_config_for_data(), buffer, decrypted_data)); | ||||
|  | ||||
|       EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                 decrypted_data.size()); | ||||
|  | ||||
|       std::size_t bytes_read{}; | ||||
|       data_buffer file_data(decrypted_data.size()); | ||||
|       EXPECT_TRUE(source_file.read( | ||||
|           file_data, | ||||
|           utils::encryption::encrypting_reader::get_data_chunk_size() * i, | ||||
|           &bytes_read)); | ||||
|       EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(), | ||||
|                                file_data.size())); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_encrypting_reader, read_file_data_using_argon2id_master_key) { | ||||
|   const auto token = std::string("moose"); | ||||
|   utils::encryption::kdf_config cfg; | ||||
|  | ||||
|   auto master_key = | ||||
|       utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg); | ||||
|  | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8U * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, master_key, cfg, | ||||
|         std::nullopt); | ||||
|  | ||||
|     std::array<std::uint8_t, utils::encryption::kdf_config::size()> hdr; | ||||
|     EXPECT_EQ(hdr.size(), utils::encryption::encrypting_reader::reader_function( | ||||
|                               reinterpret_cast<char *>(hdr.data()), hdr.size(), | ||||
|                               1U, &reader)); | ||||
|     EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, cfg)); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i++) { | ||||
|       data_buffer buffer( | ||||
|           utils::encryption::encrypting_reader::get_encrypted_chunk_size()); | ||||
|       for (std::uint8_t j = 0U; j < 2U; j++) { | ||||
|         ASSERT_EQ( | ||||
|             buffer.size() / 2U, | ||||
|             utils::encryption::encrypting_reader::reader_function( | ||||
|                 reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]), | ||||
|                 buffer.size() / 2U, 1U, &reader)); | ||||
|       } | ||||
|  | ||||
|       auto data_cfg = *reader.get_kdf_config_for_data(); | ||||
|       utils::hash::hash_256_t data_key; | ||||
|       std::tie(data_key, std::ignore) = cfg.create_subkey( | ||||
|           utils::encryption::kdf_context::data, data_cfg.unique_id, master_key); | ||||
|       data_buffer decrypted_data; | ||||
|       EXPECT_TRUE( | ||||
|           utils::encryption::decrypt_data(data_key, buffer, decrypted_data)); | ||||
|  | ||||
|       EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                 decrypted_data.size()); | ||||
| @@ -122,7 +178,8 @@ TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks) { | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token); | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, | ||||
|         std::nullopt); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i += 2U) { | ||||
|       data_buffer buffer( | ||||
| @@ -172,14 +229,14 @@ TEST(utils_encrypting_reader, | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, cfg); | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, cfg, | ||||
|         std::nullopt); | ||||
|  | ||||
|     std::array<std::uint8_t, utils::encryption::kdf_config::size()> hdr; | ||||
|     EXPECT_EQ(hdr.size(), utils::encryption::encrypting_reader::reader_function( | ||||
|                               reinterpret_cast<char *>(hdr.data()), hdr.size(), | ||||
|                               1U, &reader)); | ||||
|     EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, cfg)); | ||||
|     // EXPECT_EQ(cfg, reader.get_kdf_config().value()); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i += 2U) { | ||||
|       data_buffer buffer( | ||||
| @@ -194,7 +251,72 @@ TEST(utils_encrypting_reader, | ||||
|         data_buffer decrypted_data; | ||||
|         const auto offset = (j * (buffer.size() / 2U)); | ||||
|         EXPECT_TRUE(utils::encryption::decrypt_data( | ||||
|             token, *reader.get_kdf_config(), | ||||
|             token, *reader.get_kdf_config_for_data(), | ||||
|             data_buffer( | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>(offset)), | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>( | ||||
|                                               offset + (buffer.size() / 2U)))), | ||||
|             decrypted_data)); | ||||
|  | ||||
|         EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                   decrypted_data.size()); | ||||
|  | ||||
|         std::size_t bytes_read{}; | ||||
|         data_buffer file_data(decrypted_data.size()); | ||||
|         EXPECT_TRUE(source_file.read( | ||||
|             file_data, | ||||
|             (utils::encryption::encrypting_reader::get_data_chunk_size() * i) + | ||||
|                 (j * | ||||
|                  utils::encryption::encrypting_reader::get_data_chunk_size()), | ||||
|             &bytes_read)); | ||||
|         EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(), | ||||
|                                  file_data.size())); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_encrypting_reader, | ||||
|      read_file_data_in_multiple_chunks_using_argon2id_master_key) { | ||||
|   const auto token = std::string("moose"); | ||||
|   utils::encryption::kdf_config cfg; | ||||
|  | ||||
|   auto master_key = | ||||
|       utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg); | ||||
|  | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8U * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, master_key, cfg, | ||||
|         std::nullopt); | ||||
|  | ||||
|     std::array<std::uint8_t, utils::encryption::kdf_config::size()> hdr; | ||||
|     EXPECT_EQ(hdr.size(), utils::encryption::encrypting_reader::reader_function( | ||||
|                               reinterpret_cast<char *>(hdr.data()), hdr.size(), | ||||
|                               1U, &reader)); | ||||
|     EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, cfg)); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i += 2U) { | ||||
|       data_buffer buffer( | ||||
|           utils::encryption::encrypting_reader::get_encrypted_chunk_size() * | ||||
|           2U); | ||||
|       EXPECT_EQ(buffer.size(), | ||||
|                 utils::encryption::encrypting_reader::reader_function( | ||||
|                     reinterpret_cast<char *>(buffer.data()), buffer.size(), 1U, | ||||
|                     &reader)); | ||||
|  | ||||
|       auto data_cfg = *reader.get_kdf_config_for_data(); | ||||
|       utils::hash::hash_256_t data_key; | ||||
|       std::tie(data_key, std::ignore) = cfg.create_subkey( | ||||
|           utils::encryption::kdf_context::data, data_cfg.unique_id, master_key); | ||||
|  | ||||
|       for (std::uint8_t j = 0U; j < 2U; j++) { | ||||
|         data_buffer decrypted_data; | ||||
|         const auto offset = (j * (buffer.size() / 2U)); | ||||
|         EXPECT_TRUE(utils::encryption::decrypt_data( | ||||
|             data_key, | ||||
|             data_buffer( | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>(offset)), | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>( | ||||
| @@ -226,7 +348,8 @@ TEST(utils_encrypting_reader, read_file_data_as_stream) { | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token); | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, | ||||
|         std::nullopt); | ||||
|     auto io_stream = reader.create_iostream(); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
| @@ -280,7 +403,8 @@ TEST(utils_encrypting_reader, read_file_data_as_stream_using_argon2id) { | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, cfg); | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, cfg, | ||||
|         std::nullopt); | ||||
|     auto io_stream = reader.create_iostream(); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
| @@ -310,7 +434,73 @@ TEST(utils_encrypting_reader, read_file_data_as_stream_using_argon2id) { | ||||
|  | ||||
|       data_buffer decrypted_data; | ||||
|       EXPECT_TRUE(utils::encryption::decrypt_data( | ||||
|           token, *reader.get_kdf_config(), buffer, decrypted_data)); | ||||
|           token, *reader.get_kdf_config_for_data(), buffer, decrypted_data)); | ||||
|  | ||||
|       EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                 decrypted_data.size()); | ||||
|  | ||||
|       std::size_t bytes_read{}; | ||||
|       data_buffer file_data(decrypted_data.size()); | ||||
|       EXPECT_TRUE(source_file.read( | ||||
|           file_data, | ||||
|           utils::encryption::encrypting_reader::get_data_chunk_size() * i, | ||||
|           &bytes_read)); | ||||
|       EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(), | ||||
|                                file_data.size())); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_encrypting_reader, | ||||
|      read_file_data_as_stream_using_argon2id_master_key) { | ||||
|   const auto token = std::string("moose"); | ||||
|   utils::encryption::kdf_config cfg; | ||||
|  | ||||
|   auto master_key = | ||||
|       utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg); | ||||
|  | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8U * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, master_key, cfg, | ||||
|         std::nullopt); | ||||
|     auto io_stream = reader.create_iostream(); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
|     EXPECT_EQ(reader.get_total_size(), | ||||
|               static_cast<std::uint64_t>(io_stream->tellg())); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i++) { | ||||
|       data_buffer buffer( | ||||
|           utils::encryption::encrypting_reader::get_encrypted_chunk_size()); | ||||
|       EXPECT_FALSE( | ||||
|           io_stream | ||||
|               ->seekg(static_cast<std::streamoff>( | ||||
|                   i * buffer.size() + utils::encryption::kdf_config::size())) | ||||
|               .fail()); | ||||
|       EXPECT_TRUE(io_stream->good()); | ||||
|       for (std::uint8_t j = 0U; j < 2U; j++) { | ||||
|         EXPECT_FALSE( | ||||
|             io_stream | ||||
|                 ->read( | ||||
|                     reinterpret_cast<char *>(&buffer[(buffer.size() / 2U) * j]), | ||||
|                     static_cast<std::streamsize>(buffer.size()) / 2U) | ||||
|                 .fail()); | ||||
|         EXPECT_TRUE(io_stream->good()); | ||||
|       } | ||||
|  | ||||
|       auto data_cfg = *reader.get_kdf_config_for_data(); | ||||
|       utils::hash::hash_256_t data_key; | ||||
|       std::tie(data_key, std::ignore) = cfg.create_subkey( | ||||
|           utils::encryption::kdf_context::data, data_cfg.unique_id, master_key); | ||||
|  | ||||
|       data_buffer decrypted_data; | ||||
|       EXPECT_TRUE( | ||||
|           utils::encryption::decrypt_data(data_key, buffer, decrypted_data)); | ||||
|  | ||||
|       EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                 decrypted_data.size()); | ||||
| @@ -334,7 +524,8 @@ TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks_as_stream) { | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token); | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, | ||||
|         std::nullopt); | ||||
|     auto io_stream = reader.create_iostream(); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
| @@ -392,7 +583,8 @@ TEST(utils_encrypting_reader, | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, cfg); | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, token, cfg, | ||||
|         std::nullopt); | ||||
|     auto io_stream = reader.create_iostream(); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
| @@ -420,7 +612,79 @@ TEST(utils_encrypting_reader, | ||||
|         data_buffer decrypted_data; | ||||
|         const auto offset = (j * (buffer.size() / 2U)); | ||||
|         EXPECT_TRUE(utils::encryption::decrypt_data( | ||||
|             token, *reader.get_kdf_config(), | ||||
|             token, *reader.get_kdf_config_for_data(), | ||||
|             data_buffer( | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>(offset)), | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>( | ||||
|                                               offset + (buffer.size() / 2U)))), | ||||
|             decrypted_data)); | ||||
|  | ||||
|         EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), | ||||
|                   decrypted_data.size()); | ||||
|  | ||||
|         std::size_t bytes_read{}; | ||||
|         data_buffer file_data(decrypted_data.size()); | ||||
|         EXPECT_TRUE(source_file.read( | ||||
|             file_data, | ||||
|             (utils::encryption::encrypting_reader::get_data_chunk_size() * i) + | ||||
|                 (j * | ||||
|                  utils::encryption::encrypting_reader::get_data_chunk_size()), | ||||
|             &bytes_read)); | ||||
|         EXPECT_EQ(0, std::memcmp(file_data.data(), decrypted_data.data(), | ||||
|                                  file_data.size())); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_encrypting_reader, | ||||
|      read_file_data_in_multiple_chunks_as_stream_using_argon2id_master_key) { | ||||
|   const auto token = std::string("moose"); | ||||
|   utils::encryption::kdf_config cfg; | ||||
|  | ||||
|   auto master_key = | ||||
|       utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg); | ||||
|  | ||||
|   auto &source_file = test::create_random_file( | ||||
|       8u * utils::encryption::encrypting_reader::get_data_chunk_size()); | ||||
|   EXPECT_TRUE(source_file); | ||||
|   if (source_file) { | ||||
|     utils::encryption::encrypting_reader reader( | ||||
|         "test.dat", source_file.get_path(), get_stop_requested, master_key, cfg, | ||||
|         std::nullopt); | ||||
|     auto io_stream = reader.create_iostream(); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::end).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
|     EXPECT_EQ(reader.get_total_size(), | ||||
|               static_cast<std::uint64_t>(io_stream->tellg())); | ||||
|     EXPECT_FALSE(io_stream->seekg(0, std::ios_base::beg).fail()); | ||||
|     EXPECT_TRUE(io_stream->good()); | ||||
|  | ||||
|     EXPECT_FALSE(io_stream | ||||
|                      ->seekg(static_cast<std::streamoff>( | ||||
|                          utils::encryption::kdf_config::size())) | ||||
|                      .fail()); | ||||
|  | ||||
|     for (std::uint8_t i = 0U; i < 8U; i += 2U) { | ||||
|       data_buffer buffer( | ||||
|           utils::encryption::encrypting_reader::get_encrypted_chunk_size() * | ||||
|           2U); | ||||
|       EXPECT_FALSE(io_stream | ||||
|                        ->read(reinterpret_cast<char *>(buffer.data()), | ||||
|                               static_cast<std::streamsize>(buffer.size())) | ||||
|                        .fail()); | ||||
|       EXPECT_TRUE(io_stream->good()); | ||||
|  | ||||
|       auto data_cfg = *reader.get_kdf_config_for_data(); | ||||
|       utils::hash::hash_256_t data_key; | ||||
|       std::tie(data_key, std::ignore) = cfg.create_subkey( | ||||
|           utils::encryption::kdf_context::data, data_cfg.unique_id, master_key); | ||||
|  | ||||
|       for (std::uint8_t j = 0U; j < 2U; j++) { | ||||
|         data_buffer decrypted_data; | ||||
|         const auto offset = (j * (buffer.size() / 2U)); | ||||
|         EXPECT_TRUE(utils::encryption::decrypt_data( | ||||
|             data_key, | ||||
|             data_buffer( | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>(offset)), | ||||
|                 std::next(buffer.begin(), static_cast<std::int64_t>( | ||||
|   | ||||
| @@ -27,40 +27,41 @@ namespace repertory { | ||||
| TEST(utils_encryption_kdf_config, can_construct_using_default_constructor) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|  | ||||
|   EXPECT_EQ(utils::encryption::kdf_config::repertory_magic, cfg.magic); | ||||
|   EXPECT_EQ(utils::encryption::kdf_version::v1, cfg.version); | ||||
|   EXPECT_EQ(utils::encryption::kdf_type::argon2id, cfg.kdf); | ||||
|   EXPECT_EQ(utils::encryption::memlimit_level::level3, cfg.memlimit); | ||||
|   EXPECT_EQ(utils::encryption::opslimit_level::level2, cfg.opslimit); | ||||
|   EXPECT_EQ(utils::encryption::kdf_config::salt_t{}, cfg.salt); | ||||
|   EXPECT_EQ(0U, cfg.unique_id); | ||||
|   EXPECT_EQ(0U, cfg.checksum); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, can_generate_salt) { | ||||
| TEST(utils_encryption_kdf_config, can_seal) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.generate_salt(); | ||||
|   cfg.seal(); | ||||
|   EXPECT_NE(utils::encryption::kdf_config::salt_t{}, cfg.salt); | ||||
|  | ||||
|   auto orig_salt = cfg.salt; | ||||
|   cfg.generate_salt(); | ||||
|   cfg.seal(); | ||||
|   EXPECT_NE(orig_salt, cfg.salt); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, can_generate_checksum) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   EXPECT_EQ(8853559678329530327ULL, cfg.generate_checksum()); | ||||
|   EXPECT_EQ(13087047540462255120ULL, cfg.generate_checksum()); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, generate_salt_calculates_checksum) { | ||||
| TEST(utils_encryption_kdf_config, seal_calculates_checksum) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.generate_salt(); | ||||
|   cfg.seal(); | ||||
|  | ||||
|   EXPECT_NE(0U, cfg.checksum); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, can_create_header_and_restore) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.generate_salt(); | ||||
|   cfg.unique_id = 2U; | ||||
|   cfg.seal(); | ||||
|   auto hdr = cfg.to_header(); | ||||
|  | ||||
|   EXPECT_EQ(utils::encryption::kdf_config::size(), hdr.size()); | ||||
| @@ -70,30 +71,20 @@ TEST(utils_encryption_kdf_config, can_create_header_and_restore) { | ||||
|   auto restored_hdr = restored_cfg.to_header(); | ||||
|  | ||||
|   EXPECT_EQ(hdr, restored_hdr); | ||||
|   EXPECT_EQ(cfg.magic, restored_cfg.magic); | ||||
|   EXPECT_EQ(cfg.version, restored_cfg.version); | ||||
|   EXPECT_EQ(cfg.kdf, restored_cfg.kdf); | ||||
|   EXPECT_EQ(cfg.memlimit, restored_cfg.memlimit); | ||||
|   EXPECT_EQ(cfg.opslimit, restored_cfg.opslimit); | ||||
|   EXPECT_EQ(cfg.salt, restored_cfg.salt); | ||||
|   EXPECT_EQ(cfg.checksum, restored_cfg.checksum); | ||||
|   EXPECT_EQ(cfg.unique_id, restored_cfg.unique_id); | ||||
|   EXPECT_EQ(cfg, restored_cfg); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, header_restore_fails_if_magic_is_invalid) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.magic = 0x11; | ||||
|   cfg.generate_salt(); | ||||
|  | ||||
|   auto hdr = cfg.to_header(); | ||||
|   utils::encryption::kdf_config restored_cfg; | ||||
|   EXPECT_FALSE(utils::encryption::kdf_config::from_header(hdr, restored_cfg)); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, header_restore_fails_if_version_is_invalid) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.version = static_cast<utils::encryption::kdf_version>(0x11); | ||||
|   cfg.generate_salt(); | ||||
|   cfg.seal(); | ||||
|  | ||||
|   auto hdr = cfg.to_header(); | ||||
|   utils::encryption::kdf_config restored_cfg; | ||||
| @@ -103,7 +94,7 @@ TEST(utils_encryption_kdf_config, header_restore_fails_if_version_is_invalid) { | ||||
| TEST(utils_encryption_kdf_config, header_restore_fails_if_kdf_is_invalid) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.kdf = static_cast<utils::encryption::kdf_type>(0x11); | ||||
|   cfg.generate_salt(); | ||||
|   cfg.seal(); | ||||
|  | ||||
|   auto hdr = cfg.to_header(); | ||||
|   utils::encryption::kdf_config restored_cfg; | ||||
| @@ -113,7 +104,7 @@ TEST(utils_encryption_kdf_config, header_restore_fails_if_kdf_is_invalid) { | ||||
| TEST(utils_encryption_kdf_config, header_restore_fails_if_memlimit_is_invalid) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.memlimit = static_cast<utils::encryption::memlimit_level>(0x11); | ||||
|   cfg.generate_salt(); | ||||
|   cfg.seal(); | ||||
|  | ||||
|   auto hdr = cfg.to_header(); | ||||
|   utils::encryption::kdf_config restored_cfg; | ||||
| @@ -123,7 +114,7 @@ TEST(utils_encryption_kdf_config, header_restore_fails_if_memlimit_is_invalid) { | ||||
| TEST(utils_encryption_kdf_config, header_restore_fails_if_opslimit_is_invalid) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.opslimit = static_cast<utils::encryption::opslimit_level>(0x11); | ||||
|   cfg.generate_salt(); | ||||
|   cfg.seal(); | ||||
|  | ||||
|   auto hdr = cfg.to_header(); | ||||
|   utils::encryption::kdf_config restored_cfg; | ||||
| @@ -132,13 +123,280 @@ TEST(utils_encryption_kdf_config, header_restore_fails_if_opslimit_is_invalid) { | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, header_restore_fails_if_salt_is_invalid) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.generate_salt(); | ||||
|   cfg.seal(); | ||||
|   cfg.salt = utils::encryption::kdf_config::salt_t{}; | ||||
|  | ||||
|   auto hdr = cfg.to_header(); | ||||
|   utils::encryption::kdf_config restored_cfg; | ||||
|   EXPECT_FALSE(utils::encryption::kdf_config::from_header(hdr, restored_cfg)); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, header_restore_fails_if_id_is_invalid) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.seal(); | ||||
|   cfg.unique_id = 22U; | ||||
|  | ||||
|   auto hdr = cfg.to_header(); | ||||
|   utils::encryption::kdf_config restored_cfg; | ||||
|   EXPECT_FALSE(utils::encryption::kdf_config::from_header(hdr, restored_cfg)); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, create_subkey_sets_id_and_updates_checksum) { | ||||
|   using hash_t = utils::hash::hash_256_t; | ||||
|  | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.seal(); | ||||
|  | ||||
|   hash_t master_key = | ||||
|       utils::encryption::generate_key<hash_t>("root-master-key"); | ||||
|  | ||||
|   constexpr std::size_t sub_id = 42; | ||||
|   auto [subkey, out_cfg] = cfg.create_subkey<hash_t>( | ||||
|       utils::encryption::kdf_context::path, sub_id, master_key); | ||||
|  | ||||
|   EXPECT_NE(subkey, hash_t{}); | ||||
|   EXPECT_NE(subkey, master_key); | ||||
|  | ||||
|   EXPECT_EQ(out_cfg.unique_id, static_cast<std::uint64_t>(sub_id)); | ||||
|   EXPECT_EQ(out_cfg.checksum, out_cfg.generate_checksum()); | ||||
|  | ||||
|   EXPECT_EQ(out_cfg.version, cfg.version); | ||||
|   EXPECT_EQ(out_cfg.kdf, cfg.kdf); | ||||
|   EXPECT_EQ(out_cfg.memlimit, cfg.memlimit); | ||||
|   EXPECT_EQ(out_cfg.opslimit, cfg.opslimit); | ||||
|   EXPECT_EQ(out_cfg.salt, cfg.salt); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, | ||||
|      create_subkey_is_deterministic_for_same_inputs) { | ||||
|   using hash_t = utils::hash::hash_256_t; | ||||
|  | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.seal(); | ||||
|  | ||||
|   hash_t master_key = | ||||
|       utils::encryption::generate_key<hash_t>("root-master-key"); | ||||
|  | ||||
|   constexpr auto ctx = utils::encryption::kdf_context::data; | ||||
|   constexpr std::size_t sub_id = 7; | ||||
|  | ||||
|   auto [k1, c1] = cfg.create_subkey<hash_t>(ctx, sub_id, master_key); | ||||
|   auto [k2, c2] = cfg.create_subkey<hash_t>(ctx, sub_id, master_key); | ||||
|  | ||||
|   EXPECT_EQ(k1, k2); | ||||
|   EXPECT_EQ(c1.unique_id, c2.unique_id); | ||||
|   EXPECT_EQ(c1.checksum, c2.checksum); | ||||
|   EXPECT_EQ(c1, c2); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, create_subkey_varies_with_different_id) { | ||||
|   using hash_t = utils::hash::hash_256_t; | ||||
|  | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.seal(); | ||||
|  | ||||
|   hash_t master_key = | ||||
|       utils::encryption::generate_key<hash_t>("root-master-key"); | ||||
|  | ||||
|   constexpr auto ctx = utils::encryption::kdf_context::data; | ||||
|  | ||||
|   auto [k1, c1] = cfg.create_subkey<hash_t>(ctx, 1, master_key); | ||||
|   auto [k2, c2] = cfg.create_subkey<hash_t>(ctx, 2, master_key); | ||||
|  | ||||
|   EXPECT_NE(k1, k2); | ||||
|   EXPECT_NE(c1.unique_id, c2.unique_id); | ||||
|   EXPECT_NE(c1.checksum, c2.checksum); | ||||
|  | ||||
|   EXPECT_EQ(c1.version, c2.version); | ||||
|   EXPECT_EQ(c1.kdf, c2.kdf); | ||||
|   EXPECT_EQ(c1.memlimit, c2.memlimit); | ||||
|   EXPECT_EQ(c1.opslimit, c2.opslimit); | ||||
|   EXPECT_EQ(c1.salt, c2.salt); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, create_subkey_varies_with_different_context) { | ||||
|   using hash_t = utils::hash::hash_256_t; | ||||
|  | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.seal(); | ||||
|  | ||||
|   hash_t master_key = | ||||
|       utils::encryption::generate_key<hash_t>("root-master-key"); | ||||
|  | ||||
|   constexpr std::size_t sub_id = 123; | ||||
|  | ||||
|   auto [ka, ca] = cfg.create_subkey<hash_t>( | ||||
|       utils::encryption::kdf_context::data, sub_id, master_key); | ||||
|   auto [kb, cb] = cfg.create_subkey<hash_t>( | ||||
|       utils::encryption::kdf_context::path, sub_id, master_key); | ||||
|  | ||||
|   EXPECT_NE(ka, kb); | ||||
|   EXPECT_EQ(ca.unique_id, cb.unique_id); | ||||
|   EXPECT_EQ(ca.checksum, cb.checksum); | ||||
|   EXPECT_EQ(ca, cb); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, | ||||
|      create_subkey_with_undefined_context_uses_fallback) { | ||||
|   using hash_t = utils::hash::hash_256_t; | ||||
|  | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.seal(); | ||||
|  | ||||
|   hash_t master_key = | ||||
|       utils::encryption::generate_key<hash_t>("root-master-key"); | ||||
|  | ||||
|   constexpr std::size_t sub_id = 55; | ||||
|  | ||||
|   auto [k_def, c_def] = cfg.create_subkey<hash_t>( | ||||
|       utils::encryption::kdf_context::undefined, sub_id, master_key); | ||||
|   auto [k_dat, c_dat] = cfg.create_subkey<hash_t>( | ||||
|       utils::encryption::kdf_context::data, sub_id, master_key); | ||||
|  | ||||
|   EXPECT_NE(k_def, hash_t{}); | ||||
|   EXPECT_NE(k_dat, hash_t{}); | ||||
|   EXPECT_NE(k_def, k_dat); | ||||
|  | ||||
|   EXPECT_EQ(c_def, c_dat); | ||||
| } | ||||
|  | ||||
| #if defined(PROJECT_ENABLE_JSON) | ||||
| TEST(utils_encryption_kdf_config, can_convert_kdf_config_to_and_from_json) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.unique_id = 2U; | ||||
|   cfg.seal(); | ||||
|  | ||||
|   nlohmann::json json_kdf(cfg); | ||||
|  | ||||
|   auto cfg2 = json_kdf.get<utils::encryption::kdf_config>(); | ||||
|   EXPECT_EQ(cfg, cfg2); | ||||
| } | ||||
| #endif // defined(PROJECT_ENABLE_JSON) | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, equality) { | ||||
|   { | ||||
|     utils::encryption::kdf_config cfg; | ||||
|     utils::encryption::kdf_config cfg2; | ||||
|  | ||||
|     EXPECT_EQ(cfg, cfg2); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     utils::encryption::kdf_config cfg; | ||||
|     utils::encryption::kdf_config cfg2{cfg}; | ||||
|  | ||||
|     EXPECT_EQ(cfg, cfg2); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     utils::encryption::kdf_config cfg; | ||||
|     cfg.seal(); | ||||
|  | ||||
|     utils::encryption::kdf_config cfg2{cfg}; | ||||
|  | ||||
|     EXPECT_EQ(cfg, cfg2); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     utils::encryption::kdf_config cfg; | ||||
|     utils::encryption::kdf_config cfg2; | ||||
|     cfg2 = cfg; | ||||
|  | ||||
|     EXPECT_EQ(cfg, cfg2); | ||||
|   } | ||||
|  | ||||
|   { | ||||
|     utils::encryption::kdf_config cfg; | ||||
|     cfg.seal(); | ||||
|  | ||||
|     utils::encryption::kdf_config cfg2; | ||||
|     cfg2 = cfg; | ||||
|  | ||||
|     EXPECT_EQ(cfg, cfg2); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, sealed_is_not_equal_to_unsealed) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.seal(); | ||||
|  | ||||
|   utils::encryption::kdf_config cfg2; | ||||
|  | ||||
|   EXPECT_NE(cfg, cfg2); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, sealed_is_not_equal_to_sealed) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.seal(); | ||||
|  | ||||
|   utils::encryption::kdf_config cfg2; | ||||
|   cfg2.seal(); | ||||
|  | ||||
|   EXPECT_NE(cfg, cfg2); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, is_not_equal_to_different_id) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.unique_id = 2UL; | ||||
|  | ||||
|   utils::encryption::kdf_config cfg2; | ||||
|  | ||||
|   EXPECT_NE(cfg, cfg2); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, is_not_equal_to_different_version) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.version = static_cast<utils::encryption::kdf_version>(0x11); | ||||
|  | ||||
|   utils::encryption::kdf_config cfg2; | ||||
|  | ||||
|   EXPECT_NE(cfg, cfg2); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, is_not_equal_to_different_kdf) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.kdf = static_cast<utils::encryption::kdf_type>(0x11); | ||||
|  | ||||
|   utils::encryption::kdf_config cfg2; | ||||
|  | ||||
|   EXPECT_NE(cfg, cfg2); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, is_not_equal_to_different_memlimit) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.memlimit = static_cast<utils::encryption::memlimit_level>(0x11); | ||||
|  | ||||
|   utils::encryption::kdf_config cfg2; | ||||
|  | ||||
|   EXPECT_NE(cfg, cfg2); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, is_not_equal_to_different_opslimit) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.opslimit = static_cast<utils::encryption::opslimit_level>(0x11); | ||||
|  | ||||
|   utils::encryption::kdf_config cfg2; | ||||
|  | ||||
|   EXPECT_NE(cfg, cfg2); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, is_not_equal_to_different_salt) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.salt[0U] = 1U; | ||||
|  | ||||
|   utils::encryption::kdf_config cfg2; | ||||
|  | ||||
|   EXPECT_NE(cfg, cfg2); | ||||
| } | ||||
|  | ||||
| TEST(utils_encryption_kdf_config, is_not_equal_to_different_checksum) { | ||||
|   utils::encryption::kdf_config cfg; | ||||
|   cfg.checksum = 2U; | ||||
|  | ||||
|   utils::encryption::kdf_config cfg2; | ||||
|  | ||||
|   EXPECT_NE(cfg, cfg2); | ||||
| } | ||||
| } // namespace repertory | ||||
|  | ||||
| #endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) | ||||
|   | ||||
| @@ -375,6 +375,10 @@ TEST(utils_encryption, encrypt_data_pointer_using_argon2id) { | ||||
| // TEST(utils_encryption, decrypt_file_name_using_argon2id) {} | ||||
|  | ||||
| // TEST(utils_encryption, decrypt_file_path_using_argon2id) {} | ||||
| // | ||||
| // TEST(utils_encryption, decrypt_file_name_using_argon2id_master_key) {} | ||||
|  | ||||
| // TEST(utils_encryption, decrypt_file_path_using_argon2id_master_key) {} | ||||
| #endif // defined(PROJECT_ENABLE_BOOST) | ||||
| } // namespace repertory | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user