updated build system

This commit is contained in:
2025-08-12 18:28:15 -05:00
parent 02d59cb047
commit 3effb4c80a
50 changed files with 1915 additions and 536 deletions

View File

@@ -244,6 +244,7 @@ wextra
wfloat
wformat=2
winfsp
winfsp_drive
winhttp
wininet
winspool

View File

@@ -1,15 +1,15 @@
set(BINUTILS_VERSION 2.44)
set(BOOST_MAJOR_VERSION 1)
set(BOOST_MINOR_VERSION 88)
set(BOOST_PATCH_VERSION 0)
set(BOOST2_MAJOR_VERSION 1)
set(BOOST2_MINOR_VERSION 76)
set(BOOST2_PATCH_VERSION 0)
set(BOOST_MAJOR_VERSION 1)
set(BOOST_MINOR_VERSION 88)
set(BOOST_PATCH_VERSION 0)
set(CPP_HTTPLIB_VERSION 0.23.1)
set(CURL_VERSION 8.15.0)
set(CURL2_VERSION 8_15_0)
set(EXPAT_VERSION 2.7.1)
set(CURL_VERSION 8.15.0)
set(EXPAT2_VERSION 2_7_1)
set(EXPAT_VERSION 2.7.1)
set(GCC_VERSION 15.1.0)
set(GTEST_VERSION 1.17.0)
set(ICU_VERSION 76-1)
@@ -22,9 +22,9 @@ set(PKG_CONFIG_VERSION 0.29.2)
set(PUGIXML_VERSION 1.15)
set(ROCKSDB_VERSION 10.4.2)
set(SPDLOG_VERSION 1.15.3)
set(SQLITE_VERSION 3500300)
set(SQLITE2_VERSION 3.50.3)
set(SQLITE_VERSION 3500300)
set(STDUUID_VERSION 1.2.3)
set(WINFSP_VERSION 2.1.25156)
set(WINFSP2_VERSION 2.1)
set(WINFSP_VERSION 2.1.25156)
set(ZLIB_VERSION 1.3.1)

View File

@@ -1170,6 +1170,8 @@ ARG UID=1000
ARG GID=1000
ARG USERNAME=myuser
RUN delgroup scanner || echo "no scanner group found"
RUN addgroup -g $GID $USERNAME && \
adduser -D -u $UID -G $USERNAME -h /home/$USERNAME $USERNAME

View File

@@ -31,24 +31,22 @@ private:
static stop_type stop_requested;
public:
[[nodiscard]] static auto default_agent_name(const provider_type &prov)
[[nodiscard]] static auto default_agent_name(provider_type prov)
-> std::string;
[[nodiscard]] static auto default_api_port(const provider_type &prov)
[[nodiscard]] static auto default_api_port(provider_type prov)
-> std::uint16_t;
[[nodiscard]] static auto default_data_directory(const provider_type &prov)
[[nodiscard]] static auto default_data_directory(provider_type prov)
-> std::string;
[[nodiscard]] static auto default_remote_api_port(const provider_type &prov)
[[nodiscard]] static auto default_remote_api_port(provider_type prov)
-> std::uint16_t;
[[nodiscard]] static auto default_rpc_port() -> std::uint16_t;
[[nodiscard]] static auto get_provider_display_name(const provider_type &prov)
[[nodiscard]] static auto get_provider_display_name(provider_type prov)
-> std::string;
[[nodiscard]] static auto get_provider_name(const provider_type &prov)
[[nodiscard]] static auto get_provider_name(provider_type prov)
-> std::string;
[[nodiscard]] static auto get_root_data_directory() -> std::string;
@@ -59,7 +57,7 @@ public:
static void set_stop_requested();
public:
app_config(const provider_type &prov, std::string_view data_directory = "");
app_config(provider_type prov, std::string_view data_directory = "");
app_config() = delete;
app_config(app_config &&) = delete;
@@ -72,9 +70,9 @@ public:
private:
provider_type prov_;
atomic<std::string> api_password_;
utils::atomic<std::string> api_password_;
std::atomic<std::uint16_t> api_port_;
atomic<std::string> api_user_;
utils::atomic<std::string> api_user_;
std::atomic<bool> config_changed_;
std::atomic<database_type> db_type_{database_type::rocksdb};
std::atomic<std::uint8_t> download_timeout_secs_;
@@ -100,14 +98,14 @@ private:
private:
std::string cache_directory_;
std::string data_directory_;
atomic<encrypt_config> encrypt_config_;
atomic<host_config> host_config_;
utils::atomic<encrypt_config> encrypt_config_;
utils::atomic<host_config> host_config_;
std::string log_directory_;
mutable std::recursive_mutex read_write_mutex_;
atomic<remote::remote_config> remote_config_;
atomic<remote::remote_mount> remote_mount_;
atomic<s3_config> s3_config_;
atomic<sia_config> sia_config_;
utils::atomic<remote::remote_config> remote_config_;
utils::atomic<remote::remote_mount> remote_mount_;
utils::atomic<s3_config> s3_config_;
utils::atomic<sia_config> sia_config_;
std::unordered_map<std::string, std::function<std::string()>>
value_get_lookup_;
std::unordered_map<std::string,

View File

@@ -43,7 +43,7 @@ private:
using write_callback = size_t (*)(char *, size_t, size_t, void *);
struct read_write_info final {
data_buffer data{};
data_buffer data;
stop_type_callback stop_requested_cb;
};
@@ -93,8 +93,7 @@ public:
}
data_buffer data{};
const auto key =
utils::encryption::generate_key<utils::encryption::hash_256_t>(
const auto key = utils::encryption::generate_key<utils::hash::hash_256_t>(
request.decryption_token.value());
if (not utils::encryption::read_encrypted_range(
request.range.value(), key,

View File

@@ -28,9 +28,8 @@ namespace repertory {
class app_config;
class i_provider;
[[nodiscard]] auto
create_provider(const provider_type &prov,
app_config &config) -> std::unique_ptr<i_provider>;
[[nodiscard]] auto create_provider(provider_type prov, app_config &config)
-> std::unique_ptr<i_provider>;
} // namespace repertory
#endif // REPERTORY_INCLUDE_PROVIDERS_PROVIDER_HPP_

View File

@@ -22,6 +22,8 @@
#ifndef REPERTORY_INCLUDE_TYPES_REPERTORY_HPP_
#define REPERTORY_INCLUDE_TYPES_REPERTORY_HPP_
#include "utils/atomic.hpp"
namespace repertory {
inline constexpr auto default_api_password_size{48U};
inline constexpr auto default_download_timeout_secs{30U};
@@ -40,6 +42,7 @@ inline constexpr auto default_med_freq_interval_secs{
inline constexpr auto default_online_check_retry_secs{60U};
inline constexpr auto default_retry_read_count{6U};
inline constexpr auto default_ring_buffer_file_size{512U};
inline constexpr auto default_rpc_port{std::uint16_t(10000U)};
inline constexpr auto default_task_wait_ms{100U};
inline constexpr auto default_timeout_ms{60000U};
inline constexpr auto default_ui_mgmt_port{std::uint16_t{30000U}};
@@ -54,95 +57,6 @@ inline constexpr auto min_retry_read_count{std::uint16_t(2U)};
inline constexpr auto min_ring_buffer_file_size{std::uint16_t(64U)};
inline constexpr auto min_task_wait_ms{std::uint16_t(50U)};
template <typename data_t> class atomic final {
public:
atomic() : mtx_(std::make_shared<std::mutex>()) {}
atomic(const atomic &at_data)
: data_(at_data.load()), mtx_(std::make_shared<std::mutex>()) {}
atomic(data_t data)
: data_(std::move(data)), mtx_(std::make_shared<std::mutex>()) {}
atomic(atomic &&) = default;
~atomic() = default;
private:
data_t data_;
std::shared_ptr<std::mutex> mtx_;
public:
[[nodiscard]] auto load() const -> data_t {
mutex_lock lock(*mtx_);
return data_;
}
auto store(data_t data) -> data_t {
mutex_lock lock(*mtx_);
data_ = std::move(data);
return data_;
}
auto operator=(const atomic &at_data) -> atomic & {
if (&at_data == this) {
return *this;
}
store(at_data.load());
return *this;
}
auto operator=(atomic &&) -> atomic & = default;
auto operator=(data_t data) -> atomic & {
if (&data == &data_) {
return *this;
}
store(std::move(data));
return *this;
}
[[nodiscard]] auto operator==(const atomic &at_data) const -> bool {
if (&at_data == this) {
return true;
}
mutex_lock lock(*mtx_);
return at_data.load() == data_;
}
[[nodiscard]] auto operator==(const data_t &data) const -> bool {
if (&data == &data_) {
return true;
}
mutex_lock lock(*mtx_);
return data == data_;
}
[[nodiscard]] auto operator!=(const atomic &at_data) const -> bool {
if (&at_data == this) {
return false;
}
mutex_lock lock(*mtx_);
return at_data.load() != data_;
}
[[nodiscard]] auto operator!=(const data_t &data) const -> bool {
if (&data == &data_) {
return false;
}
mutex_lock lock(*mtx_);
return data != data_;
}
[[nodiscard]] operator data_t() const { return load(); }
};
inline constexpr auto max_time{
std::numeric_limits<std::uint64_t>::max(),
};
@@ -617,12 +531,15 @@ template <> struct adl_serializer<repertory::sia_config> {
}
};
template <typename data_t> struct adl_serializer<repertory::atomic<data_t>> {
static void to_json(json &data, const repertory::atomic<data_t> &value) {
template <typename data_t>
struct adl_serializer<repertory::utils::atomic<data_t>> {
static void to_json(json &data,
const repertory::utils::atomic<data_t> &value) {
data = value.load();
}
static void from_json(const json &data, repertory::atomic<data_t> &value) {
static void from_json(const json &data,
repertory::utils::atomic<data_t> &value) {
value.store(data.get<data_t>());
}
};

View File

@@ -92,7 +92,7 @@ inline const std::vector<option> option_list = {
// Prototypes
void get_api_authentication_data(std::string &user, std::string &password,
std::uint16_t &port, const provider_type &prov,
std::uint16_t &port, provider_type prov,
const std::string &data_directory);
[[nodiscard]] auto get_provider_type_from_args(std::vector<const char *> args)

View File

@@ -38,8 +38,7 @@ create_rocksdb(const app_config &cfg, const std::string &name,
std::vector<rocksdb::ColumnFamilyHandle *> &handles, bool clear)
-> std::unique_ptr<rocksdb::TransactionDB>;
[[nodiscard]] auto create_volume_label(const provider_type &prov)
-> std::string;
[[nodiscard]] auto create_volume_label(provider_type prov) -> std::string;
[[nodiscard]] auto get_attributes_from_meta(const api_meta_map &meta) -> DWORD;
} // namespace utils

View File

@@ -25,6 +25,7 @@
#include "events/types/event_level_changed.hpp"
#include "file_manager/cache_size_mgr.hpp"
#include "platform/platform.hpp"
#include "types/repertory.hpp"
#include "types/startup_exception.hpp"
#include "utils/common.hpp"
#include "utils/error_utils.hpp"
@@ -39,7 +40,7 @@ auto get_value(const json &data, const std::string &name, dest &dst,
REPERTORY_USES_FUNCTION_NAME();
try {
if (data.find(name) == data.end()) {
if (not data.contains(name)) {
found = false;
return false;
}
@@ -63,11 +64,11 @@ auto app_config::get_stop_requested() -> bool { return stop_requested.load(); }
void app_config::set_stop_requested() { stop_requested.store(true); }
app_config::app_config(const provider_type &prov,
app_config::app_config(provider_type prov,
std::string_view data_directory)
: prov_(prov),
api_password_(utils::generate_random_string(default_api_password_size)),
api_port_(default_rpc_port()),
api_port_(default_rpc_port),
api_user_(std::string{REPERTORY}),
config_changed_(false),
download_timeout_secs_(default_download_timeout_secs),
@@ -674,7 +675,7 @@ app_config::app_config(const provider_type &prov,
};
}
auto app_config::default_agent_name(const provider_type &prov) -> std::string {
auto app_config::default_agent_name(provider_type prov) -> std::string {
static const std::array<std::string,
static_cast<std::size_t>(provider_type::unknown)>
PROVIDER_AGENT_NAMES = {
@@ -687,7 +688,7 @@ auto app_config::default_agent_name(const provider_type &prov) -> std::string {
return PROVIDER_AGENT_NAMES.at(static_cast<std::size_t>(prov));
}
auto app_config::default_api_port(const provider_type &prov) -> std::uint16_t {
auto app_config::default_api_port(provider_type prov) -> std::uint16_t {
static const std::array<std::uint16_t,
static_cast<std::size_t>(provider_type::unknown)>
PROVIDER_API_PORTS = {
@@ -722,7 +723,7 @@ auto app_config::get_root_data_directory() -> std::string {
return data_directory;
}
auto app_config::default_data_directory(const provider_type &prov)
auto app_config::default_data_directory(provider_type prov)
-> std::string {
return utils::path::combine(app_config::get_root_data_directory(),
{
@@ -730,7 +731,7 @@ auto app_config::default_data_directory(const provider_type &prov)
});
}
auto app_config::default_remote_api_port(const provider_type &prov)
auto app_config::default_remote_api_port(provider_type prov)
-> std::uint16_t {
static const std::array<std::uint16_t,
static_cast<std::size_t>(provider_type::unknown)>
@@ -743,8 +744,6 @@ auto app_config::default_remote_api_port(const provider_type &prov)
return PROVIDER_REMOTE_PORTS.at(static_cast<std::size_t>(prov));
}
auto app_config::default_rpc_port() -> std::uint16_t { return 10000U; }
auto app_config::get_api_password() const -> std::string {
return api_password_;
}
@@ -930,7 +929,7 @@ auto app_config::get_preferred_download_type() const -> download_type {
return preferred_download_type_;
}
auto app_config::get_provider_display_name(const provider_type &prov)
auto app_config::get_provider_display_name(provider_type prov)
-> std::string {
static const std::array<std::string,
static_cast<std::size_t>(provider_type::unknown) + 1U>
@@ -940,7 +939,7 @@ auto app_config::get_provider_display_name(const provider_type &prov)
return PROVIDER_DISPLAY_NAMES.at(static_cast<std::size_t>(prov));
}
auto app_config::get_provider_name(const provider_type &prov) -> std::string {
auto app_config::get_provider_name(provider_type prov) -> std::string {
static const std::array<std::string,
static_cast<std::size_t>(provider_type::unknown) + 1U>
PROVIDER_NAMES = {

View File

@@ -19,7 +19,6 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <unistd.h>
#if !defined(_WIN32)
#include "drives/fuse/fuse_base.hpp"

View File

@@ -40,7 +40,6 @@
#include "utils/file_utils.hpp"
#include "utils/path.hpp"
#include "utils/polling.hpp"
#include <chrono>
namespace repertory {
encrypt_provider::encrypt_provider(app_config &config)
@@ -65,7 +64,7 @@ auto encrypt_provider::create_api_file(const std::string &api_path,
directory
? 0U
: utils::encryption::encrypting_reader::calculate_encrypted_size(
source_path);
source_path, false);
file.modified_date = times->get(utils::file::time_type::written);
file.source_path = source_path;
@@ -285,24 +284,26 @@ auto encrypt_provider::get_directory_items(const std::string &api_path,
(item1.api_path.compare(item2.api_path) < 0));
});
list.insert(list.begin(),
directory_item{
"..",
"",
true,
0U,
list.insert(list.begin(), directory_item{
.api_path = "..",
.api_parent = "",
.directory = true,
.size = 0U,
.meta =
{
{META_DIRECTORY, utils::string::from_bool(true)},
{META_DIRECTORY,
utils::string::from_bool(true)},
},
});
list.insert(list.begin(),
directory_item{
".",
"",
true,
0U,
list.insert(list.begin(), directory_item{
.api_path = ".",
.api_parent = "",
.directory = true,
.size = 0U,
.meta =
{
{META_DIRECTORY, utils::string::from_bool(true)},
{META_DIRECTORY,
utils::string::from_bool(true)},
},
});
return api_error::success;
@@ -387,7 +388,7 @@ auto encrypt_provider::get_file_size(const std::string &api_path,
}
file_size = utils::encryption::encrypting_reader::calculate_encrypted_size(
source_path);
source_path, false);
return api_error::success;
} catch (const std::exception &ex) {
utils::error::raise_error(function_name, ex, api_path,
@@ -425,7 +426,7 @@ auto encrypt_provider::get_filesystem_item(const std::string &api_path,
fsi.api_parent = utils::path::get_parent_api_path(fsi.api_path);
fsi.directory = false;
fsi.size = utils::encryption::encrypting_reader::calculate_encrypted_size(
source_path);
source_path, false);
fsi.source_path = source_path;
return api_error::success;
@@ -729,12 +730,12 @@ auto encrypt_provider::process_directory_entry(
api_parent + "/" + reader.get_encrypted_file_name());
file_res = db_->add_or_update_file(i_file_db::file_data{
api_path,
dynamic_cast<const utils::file::i_file *>(&dir_entry)
.api_path = api_path,
.file_size = dynamic_cast<const utils::file::i_file *>(&dir_entry)
->size()
.value_or(0U),
reader.get_iv_list(),
dir_entry.get_path(),
.iv_list = reader.get_iv_list(),
.source_path = dir_entry.get_path(),
});
if (file_res != api_error::success) {
// TODO raise error
@@ -802,8 +803,7 @@ auto encrypt_provider::read_file_bytes(const std::string &api_path,
"failed to update file");
return result;
}
} else if (reader_lookup_.find(file_data.source_path) ==
reader_lookup_.end()) {
} 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,
@@ -827,8 +827,9 @@ auto encrypt_provider::read_file_bytes(const std::string &api_path,
data.resize(size);
auto res{
info->reader->reader_function(reinterpret_cast<char *>(data.data()), 1U,
data.size(), info->reader.get()),
utils::encryption::encrypting_reader::reader_function(
reinterpret_cast<char *>(data.data()), 1U, data.size(),
info->reader.get()),
};
return res == 0 ? api_error::os_error : api_error::success;
}
@@ -941,14 +942,18 @@ auto encrypt_provider::start(api_item_added_callback /*api_item_added*/,
}
polling::instance().set_callback({
"check_deleted",
polling::frequency::low,
[this](auto &&stop_requested) { remove_deleted_files(stop_requested); },
.name = "check_deleted",
.freq = polling::frequency::low,
.action =
[this](auto &&stop_requested) {
remove_deleted_files(stop_requested);
},
});
polling::instance().set_callback({
"remove_expired",
polling::frequency::high,
.name = "remove_expired",
.freq = polling::frequency::high,
.action =
[this](auto && /* stop_requested */) { remove_expired_files(); },
});

View File

@@ -42,7 +42,7 @@ inline void create_comm(std::unique_ptr<intf_t> &comm, const config_t &config) {
} // namespace
namespace repertory {
auto create_provider(const provider_type &prov,
auto create_provider(provider_type prov,
app_config &config) -> std::unique_ptr<i_provider> {
static std::mutex mutex;
mutex_lock lock(mutex);

View File

@@ -367,7 +367,7 @@ auto s3_provider::get_directory_items_impl(const std::string &api_path,
auto size{node.select_node("Size").node().text().as_ullong()};
dir_item.size = is_encrypted ? utils::encryption::encrypting_reader::
calculate_decrypted_size(size)
calculate_decrypted_size(size, false)
: size;
}
@@ -529,7 +529,7 @@ auto s3_provider::get_file(const std::string &api_path, api_file &file) const
file.file_size =
is_encrypted
? utils::encryption::encrypting_reader::calculate_decrypted_size(
result.content_length)
result.content_length, false)
: result.content_length;
}
@@ -613,7 +613,7 @@ auto s3_provider::get_file_list(api_file_list &list, std::string &marker) const
file.file_size =
is_encrypted
? utils::encryption::encrypting_reader::calculate_decrypted_size(
size)
size, false)
: size;
file.key = is_encrypted ? utils::path::create_api_path(object_name) : "";
auto res{add_if_not_found(file, file.key)};
@@ -914,7 +914,7 @@ auto s3_provider::read_file_bytes(const std::string &api_path, std::size_t size,
auto total_size{utils::string::to_uint64(temp)};
return utils::encryption::read_encrypted_range(
{offset, offset + size - 1U},
utils::encryption::generate_key<utils::encryption::hash_256_t>(
utils::encryption::generate_key<utils::hash::hash_256_t>(
cfg.encryption_token),
[&](data_buffer &ct_buffer, std::uint64_t start_offset,
std::uint64_t end_offset) -> bool {

View File

@@ -6,6 +6,6 @@
namespace repertory::rpc {
auto create_password_hash(std::string_view password) -> std::string {
return utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_384(password));
utils::hash::create_hash_blake2b_384(password));
}
} // namespace repertory::rpc

View File

@@ -31,7 +31,7 @@
namespace repertory::utils::cli {
void get_api_authentication_data(std::string &user, std::string &password,
std::uint16_t &port, const provider_type &prov,
std::uint16_t &port, provider_type prov,
const std::string &data_directory) {
const auto cfg_file_path = utils::path::combine(
data_directory.empty() ? app_config::default_data_directory(prov)

View File

@@ -19,11 +19,11 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <utils/common.hpp>
#if !defined(_WIN32)
#include "utils/unix/unix_utils.hpp"
#include "utils/common.hpp"
#include "utils/path.hpp"
namespace repertory::utils {

View File

@@ -83,7 +83,7 @@ auto create_rocksdb(
return std::unique_ptr<rocksdb::TransactionDB>(ptr);
}
auto create_volume_label(const provider_type &prov) -> std::string {
auto create_volume_label(provider_type prov) -> std::string {
return "repertory_" + app_config::get_provider_name(prov);
}

View File

@@ -43,7 +43,7 @@
namespace repertory::cli::actions {
using action = std::function<exit_code(
std::vector<const char *>, const std::string &, const provider_type &,
std::vector<const char *>, const std::string &, provider_type,
const std::string &, std::string, std::string)>;
struct option_hasher {
@@ -78,11 +78,11 @@ inline const std::unordered_map<utils::cli::option, action, option_hasher>
[[nodiscard]] inline auto
perform_action(const utils::cli::option &opt, std::vector<const char *> args,
const std::string &data_directory, const provider_type &prov,
const std::string &data_directory, provider_type prov,
const std::string &unique_id, std::string user,
std::string password) -> exit_code {
if (utils::cli::has_option(args, opt)) {
if (option_actions.find(opt) != option_actions.end()) {
if (option_actions.contains(opt)) {
return option_actions.at(opt)(args, data_directory, prov, unique_id, user,
password);
}

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto check_version(std::vector<const char *> /* args */,
const std::string &data_directory,
const provider_type &prov,
provider_type prov,
const std::string & /*unique_id*/,
std::string /*user*/,
std::string /*password*/) -> exit_code {

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto display_config(std::vector<const char *> /* args */,
const std::string &data_directory,
const provider_type &prov,
provider_type prov,
const std::string &unique_id,
std::string user, std::string password)
-> exit_code {

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto
drive_information(std::vector<const char *> /* args */,
const std::string &data_directory, const provider_type &prov,
const std::string &data_directory, provider_type prov,
const std::string &unique_id, std::string user,
std::string password) -> exit_code {
auto ret = exit_code::success;

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto get(std::vector<const char *> args,
const std::string &data_directory,
const provider_type &prov,
provider_type prov,
const std::string &unique_id, std::string user,
std::string password) -> exit_code {
std::string data;

View File

@@ -25,10 +25,11 @@
#include "cli/common.hpp"
namespace repertory::cli::actions {
[[nodiscard]] inline auto get_directory_items(
std::vector<const char *> args, const std::string &data_directory,
const provider_type &prov, const std::string & /* unique_id */,
std::string user, std::string password) -> exit_code {
[[nodiscard]] inline auto
get_directory_items(std::vector<const char *> args,
const std::string &data_directory, provider_type prov,
const std::string & /* unique_id */, std::string user,
std::string password) -> exit_code {
std::string data;
auto ret = utils::cli::parse_string_option(
args, repertory::utils::cli::options::get_directory_items_option, data);

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto get_pinned_files(std::vector<const char *> /* args */,
const std::string &data_directory,
const provider_type &prov,
provider_type prov,
const std::string & /* unique_id */,
std::string user,
std::string password) -> exit_code {

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto
open_files(std::vector<const char *> /* args */,
const std::string &data_directory, const provider_type &prov,
const std::string &data_directory, provider_type prov,
const std::string &unique_id, std::string user, std::string password)
-> exit_code {
auto ret = exit_code::success;

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto
pin_file(std::vector<const char *> args, const std::string &data_directory,
const provider_type &prov, const std::string & /* unique_id */,
provider_type prov, const std::string & /* unique_id */,
std::string user, std::string password) -> exit_code {
std::string data;
auto ret = utils::cli::parse_string_option(

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto
pinned_status(std::vector<const char *> args, const std::string &data_directory,
const provider_type &prov, const std::string & /*unique_id*/,
provider_type prov, const std::string & /*unique_id*/,
std::string user, std::string password) -> exit_code {
std::string data;
auto ret = utils::cli::parse_string_option(

View File

@@ -27,9 +27,9 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto set(std::vector<const char *> args,
const std::string &data_directory,
const provider_type &prov,
const std::string &unique_id, std::string user,
std::string password) -> exit_code {
provider_type prov, const std::string &unique_id,
std::string user, std::string password)
-> exit_code {
auto ret = exit_code::success;
auto data = utils::cli::parse_option(args, "-set", 2U);
if (data.empty()) {

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto status(std::vector<const char *> /* args */,
const std::string & /*data_directory*/,
const provider_type &prov,
provider_type prov,
const std::string &unique_id,
std::string /* user */,
std::string /* password */) -> exit_code {

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto
test(std::vector<const char *> /* args */, const std::string &data_directory,
const provider_type &prov, const std::string & /*unique_id*/,
provider_type prov, const std::string & /*unique_id*/,
std::string /*user*/, std::string /*password*/) -> exit_code {
app_config config(prov, data_directory);

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto
unmount(std::vector<const char *> /* args */, const std::string &data_directory,
const provider_type &prov, const std::string & /* unique_id */,
provider_type prov, const std::string & /* unique_id */,
std::string user, std::string password) -> exit_code {
constexpr const std::uint8_t retry_count{30U};

View File

@@ -27,7 +27,7 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto
unpin_file(std::vector<const char *> args, const std::string &data_directory,
const provider_type &prov, const std::string & /*unique_id*/,
provider_type prov, const std::string & /*unique_id*/,
std::string user, std::string password) -> exit_code {
std::string data;
auto ret = utils::cli::parse_string_option(

View File

@@ -128,7 +128,7 @@ std::atomic<std::uint64_t> app_config_test::idx{0U};
static void defaults_tests(const json &json_data, provider_type prov) {
json json_defaults = {
{JSON_API_PORT, app_config::default_rpc_port()},
{JSON_API_PORT, default_rpc_port},
{JSON_API_USER, std::string{REPERTORY}},
{JSON_DOWNLOAD_TIMEOUT_SECS, default_download_timeout_secs},
{JSON_DATABASE_TYPE, database_type::rocksdb},

View File

@@ -24,6 +24,7 @@
#include "utils/config.hpp"
#include "utils/atomic.hpp"
#include "utils/base64.hpp"
#include "utils/collection.hpp"
#if defined(_WIN32)
@@ -49,6 +50,7 @@
#include "utils/path.hpp"
#include "utils/string.hpp"
#include "utils/time.hpp"
#include "utils/ttl_cache.hpp"
#if !defined(_WIN32)
#include "utils/unix.hpp"
#endif // !defined(_WIN32)

View File

@@ -0,0 +1,118 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef REPERTORY_INCLUDE_UTILS_ATOMIC_HPP_
#define REPERTORY_INCLUDE_UTILS_ATOMIC_HPP_
#include "utils/config.hpp"
namespace repertory::utils {
template <typename data_t> class atomic final {
public:
atomic() : mtx_(std::make_shared<std::mutex>()) {}
atomic(const atomic &at_data)
: data_(at_data.load()), mtx_(std::make_shared<std::mutex>()) {}
atomic(data_t data)
: data_(std::move(data)), mtx_(std::make_shared<std::mutex>()) {}
atomic(atomic &&) = default;
~atomic() = default;
private:
data_t data_;
std::shared_ptr<std::mutex> mtx_;
public:
[[nodiscard]] auto load() const -> data_t {
mutex_lock lock(*mtx_);
return data_;
}
auto store(data_t data) -> data_t {
mutex_lock lock(*mtx_);
data_ = std::move(data);
return data_;
}
auto operator=(const atomic &at_data) -> atomic & {
if (&at_data == this) {
return *this;
}
store(at_data.load());
return *this;
}
auto operator=(atomic &&) -> atomic & = default;
auto operator=(data_t data) -> atomic & {
if (&data == &data_) {
return *this;
}
store(std::move(data));
return *this;
}
[[nodiscard]] auto operator==(const atomic &at_data) const -> bool {
if (&at_data == this) {
return true;
}
mutex_lock lock(*mtx_);
return at_data.load() == data_;
}
[[nodiscard]] auto operator==(const data_t &data) const -> bool {
if (&data == &data_) {
return true;
}
mutex_lock lock(*mtx_);
return data == data_;
}
[[nodiscard]] auto operator!=(const atomic &at_data) const -> bool {
if (&at_data == this) {
return false;
}
mutex_lock lock(*mtx_);
return at_data.load() != data_;
}
[[nodiscard]] auto operator!=(const data_t &data) const -> bool {
if (&data == &data_) {
return false;
}
mutex_lock lock(*mtx_);
return data != data_;
}
[[nodiscard]] operator data_t() const { return load(); }
};
} // namespace repertory::utils
#endif // REPERTORY_INCLUDE_UTILS_ATOMIC_HPP_

View File

@@ -35,9 +35,10 @@ struct result final {
using retryable_action_t = std::function<bool()>;
[[nodiscard]] inline constexpr auto
calculate_read_size(std::uint64_t total_size, std::size_t read_size,
std::uint64_t offset) -> std::size_t {
[[nodiscard]] constexpr auto calculate_read_size(std::uint64_t total_size,
std::size_t read_size,
std::uint64_t offset)
-> std::size_t {
return static_cast<std::size_t>(
((offset + read_size) > total_size)
? ((offset < total_size) ? total_size - offset : 0U)

View File

@@ -25,6 +25,7 @@
#include "utils/config.hpp"
#include "utils/encryption.hpp"
#include "utils/hash.hpp"
#include "utils/types/file/i_file.hpp"
@@ -50,6 +51,27 @@ public:
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,
std::string_view token, kdf_config cfg,
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(
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::vector<std::array<unsigned char,
crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
iv_list,
std::size_t error_return = 0U);
encrypting_reader(const encrypting_reader &reader);
encrypting_reader(encrypting_reader &&) = delete;
@@ -63,18 +85,19 @@ public:
using streambuf = std::basic_streambuf<char, std::char_traits<char>>;
private:
utils::encryption::hash_256_t key_;
utils::hash::hash_256_t key_;
stop_type_callback stop_requested_cb_;
size_t error_return_;
std::unique_ptr<utils::file::i_file> source_file_;
private:
std::unordered_map<std::size_t, data_buffer> chunk_buffers_;
std::string encrypted_file_name_;
std::string encrypted_file_path_;
std::vector<
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
iv_list_;
private:
std::unordered_map<std::size_t, data_buffer> chunk_buffers_;
std::optional<std::array<std::uint8_t, kdf_config::size()>> kdf_header_;
std::size_t last_data_chunk_{};
std::size_t last_data_chunk_size_{};
std::uint64_t read_offset_{};
@@ -88,12 +111,19 @@ private:
private:
auto reader_function(char *buffer, size_t size, size_t nitems) -> size_t;
void common_initialize(bool procces_iv_list);
void create_encrypted_paths(std::string_view file_name,
std::optional<std::string> relative_parent_path);
public:
[[nodiscard]] static auto calculate_decrypted_size(std::uint64_t total_size)
[[nodiscard]] static auto calculate_decrypted_size(std::uint64_t total_size,
bool uses_kdf)
-> std::uint64_t;
[[nodiscard]] static auto
calculate_encrypted_size(std::string_view source_path) -> std::uint64_t;
calculate_encrypted_size(std::string_view source_path, bool uses_kdf)
-> std::uint64_t;
[[nodiscard]] auto create_iostream() const -> std::shared_ptr<iostream>;
@@ -127,6 +157,8 @@ public:
return iv_list_;
}
[[nodiscard]] auto get_kdf_config() const -> std::optional<kdf_config>;
[[nodiscard]] auto get_stop_requested() const -> bool {
return stop_requested_cb_();
}

View File

@@ -34,32 +34,177 @@ inline constexpr std::uint32_t encryption_header_size{
crypto_aead_xchacha20poly1305_IETF_ABYTES,
};
#if defined(PROJECT_ENABLE_BOOST)
enum class kdf_version : std::uint8_t { v1 };
enum class kdf_type : std::uint8_t { argon2id };
enum class memlimit_level : std::uint8_t {
level1, // 64MiB
level2, // 256MiB
level3, // 512MiB
level4, // 1GiB
};
enum class opslimit_level : std::uint8_t {
level1, // interactive
level2, // moderate
level3, // sensitive
};
[[nodiscard]] inline auto get_memlimit(memlimit_level memlimit) -> size_t {
constexpr const auto mib512{512ULL * 1024ULL * 1024ULL};
switch (memlimit) {
case memlimit_level::level1:
return crypto_pwhash_MEMLIMIT_INTERACTIVE;
case memlimit_level::level2:
return crypto_pwhash_MEMLIMIT_MODERATE;
case memlimit_level::level3:
return mib512;
case memlimit_level::level4:
return crypto_pwhash_MEMLIMIT_SENSITIVE;
}
return mib512;
}
[[nodiscard]] inline auto get_opslimit(opslimit_level opslimit)
-> unsigned long long {
switch (opslimit) {
case opslimit_level::level1:
return crypto_pwhash_OPSLIMIT_INTERACTIVE;
case opslimit_level::level2:
return crypto_pwhash_OPSLIMIT_MODERATE;
case opslimit_level::level3:
return crypto_pwhash_OPSLIMIT_SENSITIVE;
}
return crypto_pwhash_OPSLIMIT_MODERATE;
}
#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};
salt_t salt{};
std::uint64_t checksum{};
[[nodiscard]] static constexpr auto size() -> std::size_t {
return sizeof(kdf_config);
}
[[nodiscard]] auto generate_checksum() const -> std::uint64_t;
void generate_salt();
[[nodiscard]] static auto from_header(std::span<const unsigned char> data,
kdf_config &cfg) -> bool;
[[nodiscard]] auto to_header() -> auto {
kdf_config tmp{*this};
tmp.checksum = boost::endian::native_to_big(tmp.checksum);
tmp.magic = boost::endian::native_to_big(tmp.magic);
std::array<std::uint8_t, size()> ret{};
std::memcpy(ret.data(), &tmp, size());
return ret;
}
[[nodiscard]] auto operator==(const kdf_config &) const -> bool = default;
[[nodiscard]] auto operator!=(const kdf_config &) const -> bool = default;
};
#pragma pack(pop)
#endif // defined(PROJECT_ENABLE_BOOST)
template <typename hash_t>
inline auto generate_key(
[[nodiscard]] inline auto generate_key(
std::string_view password,
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
default_create_hash<hash_t>()) -> hash_t;
utils::hash::default_create_hash<hash_t>()) -> hash_t;
template <typename hash_t>
inline auto generate_key(
[[nodiscard]] inline auto generate_key(
std::wstring_view password,
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
default_create_hash<hash_t>()) -> hash_t;
utils::hash::default_create_hash<hash_t>()) -> hash_t;
#if defined(PROJECT_ENABLE_BOOST)
template <typename hash_t>
[[nodiscard]] inline auto generate_key(std::string_view password,
kdf_config &cfg) -> hash_t;
template <typename hash_t>
[[nodiscard]] inline auto generate_key(std::wstring_view password,
kdf_config &cfg) -> hash_t;
template <typename hash_t>
[[nodiscard]] inline auto recreate_key(std::string_view password,
const kdf_config &cfg) -> hash_t;
template <typename hash_t>
[[nodiscard]] inline auto recreate_key(std::wstring_view password,
const kdf_config &cfg) -> hash_t;
template <typename string_t>
[[nodiscard]] auto create_key_argon2id(string_t password, kdf_config &cfg,
utils::hash::hash_256_t &key) -> bool;
template <typename string_t>
[[nodiscard]] auto recreate_key_argon2id(string_t password,
const kdf_config &cfg,
utils::hash::hash_256_t &key) -> bool;
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,
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);
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);
[[nodiscard]] auto decrypt_file_name(std::string_view encryption_token,
std::string &file_name) -> bool;
[[nodiscard]] auto decrypt_file_path(std::string_view encryption_token,
std::string &file_path) -> bool;
[[nodiscard]] auto decrypt_file_name(std::string_view encryption_token,
const kdf_config &cfg,
std::string &file_name) -> bool;
[[nodiscard]] auto decrypt_file_path(std::string_view encryption_token,
const kdf_config &cfg,
std::string &file_path) -> bool;
template <typename result_t, typename arr_t, std::size_t arr_size>
[[nodiscard]] inline auto decrypt_data(const std::array<arr_t, arr_size> &key,
const unsigned char *buffer,
std::size_t buffer_size,
result_t &res) -> bool {
std::size_t buffer_size, result_t &res)
-> bool {
if (buffer_size > encryption_header_size) {
const std::uint32_t size =
std::uint32_t size =
boost::endian::native_to_big(static_cast<std::uint32_t>(buffer_size));
res.resize(buffer_size - encryption_header_size);
return crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
@@ -76,32 +221,53 @@ template <typename result_t, typename arr_t, std::size_t arr_size>
template <typename buffer_t, typename result_t, typename arr_t,
std::size_t arr_size>
[[nodiscard]] inline auto decrypt_data(const std::array<arr_t, arr_size> &key,
const buffer_t &buf,
result_t &res) -> bool {
const buffer_t &buf, result_t &res)
-> bool {
return decrypt_data<result_t>(
key, reinterpret_cast<const unsigned char *>(buf.data()), buf.size(),
res);
}
template <typename buffer_t, typename result_t, typename hash_t = hash_256_t>
template <typename buffer_t, typename result_t,
typename hash_t = utils::hash::hash_256_t>
[[nodiscard]] inline auto decrypt_data(
std::string_view password, const buffer_t &buf, result_t &res,
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
default_create_hash<hash_t>()) -> bool {
utils::hash::default_create_hash<hash_t>()) -> bool {
return decrypt_data<buffer_t, result_t>(generate_key(password, hasher), buf,
res);
}
template <typename result_t, typename hash_t = hash_256_t>
template <typename buffer_t, typename result_t,
typename hash_t = utils::hash::hash_256_t>
[[nodiscard]] inline auto decrypt_data(std::string_view password,
const kdf_config &cfg,
const buffer_t &buf, result_t &res)
-> bool {
return decrypt_data<buffer_t, result_t>(recreate_key<hash_t>(password, cfg),
buf, res);
}
template <typename result_t, typename hash_t = utils::hash::hash_256_t>
[[nodiscard]] inline auto decrypt_data(
std::string_view password, const unsigned char *buffer,
std::size_t buffer_size, result_t &res,
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
default_create_hash<hash_t>()) -> bool {
utils::hash::default_create_hash<hash_t>()) -> bool {
return decrypt_data<result_t>(generate_key(password, hasher), buffer,
buffer_size, res);
}
template <typename result_t, typename hash_t = utils::hash::hash_256_t>
[[nodiscard]] inline auto decrypt_data(std::string_view password,
const kdf_config &cfg,
const unsigned char *buffer,
std::size_t buffer_size, result_t &res)
-> bool {
return decrypt_data<result_t>(recreate_key<hash_t>(password, cfg), buffer,
buffer_size, res);
}
template <typename result_t, typename arr_t, std::size_t arr_size>
inline void
encrypt_data(const std::array<unsigned char,
@@ -144,26 +310,44 @@ inline void encrypt_data(const std::array<arr_t, arr_size> &key,
encrypt_data<result_t>(iv, key, buffer, buffer_size, res);
}
template <typename result_t, typename hash_t = hash_256_t>
template <typename result_t, typename hash_t = utils::hash::hash_256_t>
inline void encrypt_data(
std::string_view password, const unsigned char *buffer,
std::size_t buffer_size, result_t &res,
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
default_create_hash<hash_t>()) {
utils::hash::default_create_hash<hash_t>()) {
encrypt_data<result_t>(generate_key(password, hasher), buffer, buffer_size,
res);
}
template <typename buffer_t, typename result_t, typename hash_t = hash_256_t>
template <typename result_t, typename hash_t = utils::hash::hash_256_t>
inline void encrypt_data(std::string_view password, kdf_config &cfg,
const unsigned char *buffer, std::size_t buffer_size,
result_t &res) {
encrypt_data<result_t>(generate_key<hash_t>(password, cfg), buffer,
buffer_size, res);
}
template <typename buffer_t, typename result_t,
typename hash_t = utils::hash::hash_256_t>
inline void encrypt_data(
std::string_view password, const buffer_t &buf, result_t &res,
std::function<hash_t(const unsigned char *data, std::size_t size)> hasher =
default_create_hash<hash_t>()) {
utils::hash::default_create_hash<hash_t>()) {
encrypt_data<result_t>(generate_key(password, hasher),
reinterpret_cast<const unsigned char *>(buf.data()),
buf.size(), res);
}
template <typename buffer_t, typename result_t,
typename hash_t = utils::hash::hash_256_t>
inline void encrypt_data(std::string_view password, kdf_config &cfg,
const buffer_t &buf, result_t &res) {
encrypt_data<result_t>(generate_key<hash_t>(password, cfg),
reinterpret_cast<const unsigned char *>(buf.data()),
buf.size(), res);
}
template <typename buffer_t, typename result_t, typename arr_t,
std::size_t arr_size>
inline void encrypt_data(const std::array<arr_t, arr_size> &key,
@@ -189,16 +373,52 @@ using reader_func_t =
std::function<bool(data_buffer &cypher_text, std::uint64_t start_offset,
std::uint64_t end_offset)>;
[[nodiscard]] auto
read_encrypted_range(const http_range &range,
const utils::encryption::hash_256_t &key,
reader_func_t reader_func, std::uint64_t total_size,
[[nodiscard]] auto read_encrypted_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,
data_buffer &data) -> bool;
[[nodiscard]] auto read_encrypted_range(
const http_range &range, const utils::encryption::hash_256_t &key,
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;
[[nodiscard]] inline auto
read_encrypted_range(const http_range &range,
const utils::hash::hash_256_t &key,
reader_func_t reader_func, std::uint64_t total_size,
data_buffer &data) -> bool {
return read_encrypted_range(range, key, false, reader_func, total_size, data);
}
[[nodiscard]] inline auto read_encrypted_range(
const http_range &range, const utils::hash::hash_256_t &key,
reader_func_t reader_func, std::uint64_t total_size, unsigned char *data,
std::size_t size, std::size_t &bytes_read) -> bool {
return read_encrypted_range(range, key, false, reader_func, total_size, data,
size, bytes_read);
}
template <typename string_t>
auto create_key_argon2id(string_t password, kdf_config &cfg,
utils::hash::hash_256_t &key) -> bool {
cfg.generate_salt();
return recreate_key_argon2id(password, cfg, key);
}
template <typename string_t>
auto recreate_key_argon2id(string_t password, const kdf_config &cfg,
utils::hash::hash_256_t &key) -> bool {
return crypto_pwhash(
reinterpret_cast<unsigned char *>(key.data()), key.size(),
reinterpret_cast<const char *>(password.data()),
password.size() * sizeof(typename string_t::value_type),
cfg.salt.data(), get_opslimit(cfg.opslimit),
get_memlimit(cfg.memlimit), crypto_pwhash_ALG_ARGON2ID13) == 0;
}
#endif // defined(PROJECT_ENABLE_BOOST)
template <typename hash_t>
@@ -218,6 +438,142 @@ inline auto generate_key(
return hasher(reinterpret_cast<const unsigned char *>(password.data()),
password.size() * sizeof(wchar_t));
}
#if defined(PROJECT_ENABLE_BOOST)
template <typename hash_t, typename string_t>
inline auto generate_key_impl(string_t password, kdf_config &cfg) -> hash_t {
REPERTORY_USES_FUNCTION_NAME();
switch (cfg.version) {
case kdf_version::v1:
switch (cfg.kdf) {
case kdf_type::argon2id: {
hash_t key{};
if (not create_key_argon2id(password, cfg, key)) {
throw utils::error::create_exception(
function_name, {
"failed to generate argon2id key",
});
}
return key;
}
default:
throw utils::error::create_exception(
function_name, {
"unsupported kdf type",
std::to_string(static_cast<std::uint8_t>(cfg.kdf)),
});
}
default:
throw utils::error::create_exception(
function_name,
{
"unsupported kdf version",
std::to_string(static_cast<std::uint8_t>(cfg.version)),
});
}
}
template <typename hash_t, typename string_t>
inline auto recreate_key_impl(string_t password, const kdf_config &cfg)
-> hash_t {
REPERTORY_USES_FUNCTION_NAME();
switch (cfg.version) {
case kdf_version::v1:
switch (cfg.kdf) {
case kdf_type::argon2id: {
hash_t key{};
if (not recreate_key_argon2id(password, cfg, key)) {
throw utils::error::create_exception(
function_name, {
"failed to generate argon2id key",
});
}
return key;
}
default:
throw utils::error::create_exception(
function_name, {
"unsupported kdf type",
std::to_string(static_cast<std::uint8_t>(cfg.kdf)),
});
}
default:
throw utils::error::create_exception(
function_name,
{
"unsupported kdf version",
std::to_string(static_cast<std::uint8_t>(cfg.version)),
});
}
}
template <typename hash_t>
inline auto generate_key(std::string_view password, kdf_config &cfg) -> hash_t {
return generate_key_impl<hash_t, std::string_view>(password, cfg);
}
template <typename hash_t>
inline auto generate_key(std::wstring_view password, kdf_config &cfg)
-> hash_t {
return generate_key_impl<hash_t, std::wstring_view>(password, cfg);
}
template <typename hash_t>
inline auto recreate_key(std::string_view password, const kdf_config &cfg)
-> hash_t {
return recreate_key_impl<hash_t, std::string_view>(password, cfg);
}
template <typename hash_t>
inline auto recreate_key(std::wstring_view password, const kdf_config &cfg)
-> hash_t {
return recreate_key_impl<hash_t, std::wstring_view>(password, cfg);
}
template <typename hash_t, typename string_t>
inline bool detect_and_recreate_key(string_t password,
std::span<const unsigned char> header,
hash_t &key,
std::optional<kdf_config> &cfg) {
if (header.size() >= kdf_config::size()) {
kdf_config tmp{};
if (kdf_config::from_header(header.first(kdf_config::size()), tmp)) {
cfg = tmp;
key = recreate_key<hash_t>(password, *cfg);
return true;
}
}
key = generate_key<hash_t>(password);
return false;
}
template <typename hash_t>
inline bool detect_and_recreate_key(std::string_view password,
std::span<const unsigned char> header,
hash_t &key,
std::optional<kdf_config> &cfg) {
return detect_and_recreate_key<hash_t, std::string_view>(password, header,
key, cfg);
}
template <typename hash_t>
inline bool detect_and_recreate_key(std::wstring_view password,
std::span<const unsigned char> 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
#endif // defined(PROJECT_ENABLE_LIBSODIUM)

View File

@@ -27,32 +27,32 @@
#include "utils/error.hpp"
namespace repertory::utils::encryption {
namespace repertory::utils::hash {
using hash_256_t = std::array<unsigned char, 32U>;
using hash_384_t = std::array<unsigned char, 48U>;
using hash_512_t = std::array<unsigned char, 64U>;
[[nodiscard]] auto create_hash_blake2b_256(std::string_view data) -> hash_256_t;
[[nodiscard]] auto
create_hash_blake2b_256(std::wstring_view data) -> hash_256_t;
[[nodiscard]] auto create_hash_blake2b_256(std::wstring_view data)
-> hash_256_t;
[[nodiscard]] auto
create_hash_blake2b_256(const data_buffer &data) -> hash_256_t;
[[nodiscard]] auto create_hash_blake2b_256(const data_buffer &data)
-> hash_256_t;
[[nodiscard]] auto create_hash_blake2b_384(std::string_view data) -> hash_384_t;
[[nodiscard]] auto
create_hash_blake2b_384(std::wstring_view data) -> hash_384_t;
[[nodiscard]] auto create_hash_blake2b_384(std::wstring_view data)
-> hash_384_t;
[[nodiscard]] auto
create_hash_blake2b_384(const data_buffer &data) -> hash_384_t;
[[nodiscard]] auto create_hash_blake2b_384(const data_buffer &data)
-> hash_384_t;
[[nodiscard]] auto
create_hash_blake2b_512(std::wstring_view data) -> hash_512_t;
[[nodiscard]] auto create_hash_blake2b_512(std::wstring_view data)
-> hash_512_t;
[[nodiscard]] auto
create_hash_blake2b_512(const data_buffer &data) -> hash_512_t;
[[nodiscard]] auto create_hash_blake2b_512(const data_buffer &data)
-> hash_512_t;
[[nodiscard]] auto create_hash_blake2b_512(std::string_view data) -> hash_512_t;
@@ -83,8 +83,8 @@ template <typename hash_t>
std::function<hash_t(const unsigned char *data, std::size_t size)> &;
template <typename hash_t>
auto create_hash_blake2b_t(const unsigned char *data,
std::size_t data_size) -> hash_t {
auto create_hash_blake2b_t(const unsigned char *data, std::size_t data_size)
-> hash_t {
REPERTORY_USES_FUNCTION_NAME();
hash_t hash{};
@@ -175,7 +175,7 @@ template <>
std::function<hash_512_t(const unsigned char *data, std::size_t size)> & {
return blake2b_512_hasher;
}
} // namespace repertory::utils::encryption
} // namespace repertory::utils::hash
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
#endif // REPERTORY_INCLUDE_UTILS_HASH_HPP_

View File

@@ -0,0 +1,100 @@
#ifndef REPERTORY_INCLUDE_UTILS_TTL_CACHE_HPP_
#define REPERTORY_INCLUDE_UTILS_TTL_CACHE_HPP_
#include "utils/config.hpp"
namespace repertory::utils {
template <typename data_t, template <typename> class atomic_t = std::atomic>
class ttl_cache final {
public:
using clock = std::chrono::steady_clock;
using duration = std::chrono::milliseconds;
using entry_t = atomic_t<data_t>;
using entry_ptr_t = std::shared_ptr<entry_t>;
static constexpr const auto default_expiration{duration(60000U)};
private:
struct entry final {
entry_ptr_t data;
clock::time_point expires_at;
};
public:
ttl_cache(duration ttl = default_expiration) : ttl_{ttl} {}
private:
duration ttl_;
private:
mutable std::mutex mutex_;
std::unordered_map<std::string, entry> entries_;
public:
void clear() {
mutex_lock lock(mutex_);
entries_.clear();
}
void erase(const std::string &api_path) {
mutex_lock lock(mutex_);
entries_.erase(api_path);
}
[[nodiscard]] auto contains(const std::string &api_path) -> bool {
mutex_lock lock(mutex_);
return entries_.contains(api_path);
}
[[nodiscard]] auto get(const std::string &api_path) -> entry_ptr_t {
mutex_lock lock(mutex_);
auto iter = entries_.find(api_path);
if (iter == entries_.end()) {
return nullptr;
}
iter->second.expires_at = clock::now() + ttl_;
return iter->second.data;
}
void purge_expired() {
mutex_lock lock(mutex_);
auto now = clock::now();
for (auto iter = entries_.begin(); iter != entries_.end();) {
if (iter->second.expires_at <= now) {
iter = entries_.erase(iter);
continue;
}
++iter;
}
}
[[nodiscard]] auto get_ttl() const -> duration {
mutex_lock lock(mutex_);
return ttl_;
}
void set(const std::string &api_path, const data_t &data) {
mutex_lock lock(mutex_);
if (entries_.contains(api_path)) {
auto &entry = entries_.at(api_path);
entry.data->store(data);
entry.expires_at = clock::now() + ttl_;
return;
}
entries_.emplace(api_path, entry{
.data = std::make_shared<entry_t>(data),
.expires_at = clock::now() + ttl_,
});
}
void set_ttl(duration ttl) {
mutex_lock lock(mutex_);
ttl_ = ttl;
}
};
} // namespace repertory::utils
#endif // REPERTORY_INCLUDE_UTILS_TTL_CACHE_HPP_

View File

@@ -25,6 +25,7 @@
#include "utils/collection.hpp"
#include "utils/common.hpp"
#include "utils/config.hpp"
#include "utils/encryption.hpp"
#include "utils/error.hpp"
#include "utils/file.hpp"
@@ -119,7 +120,7 @@ protected:
reader_.set_read_position(reinterpret_cast<std::uintptr_t>(gptr()));
char c{};
const auto res = encrypting_reader::reader_function(&c, 1U, 1U, &reader_);
auto res = encrypting_reader::reader_function(&c, 1U, 1U, &reader_);
if (res != 1) {
return traits_type::eof();
}
@@ -180,60 +181,12 @@ 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::encryption::hash_256_t>(
token)),
: key_(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)) {
REPERTORY_USES_FUNCTION_NAME();
if (not*source_file_) {
throw utils::error::create_exception(function_name, {
"file open failed",
source_path,
});
}
data_buffer result;
utils::encryption::encrypt_data(
key_, reinterpret_cast<const unsigned char *>(file_name.data()),
file_name.size(), result);
encrypted_file_name_ = utils::collection::to_hex_string(result);
if (relative_parent_path.has_value()) {
for (auto &&part :
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()),
strnlen(part.c_str(), part.size()), result);
encrypted_file_path_ += '/' + utils::collection::to_hex_string(result);
}
encrypted_file_path_ += '/' + encrypted_file_name_;
}
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();
const 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);
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_;
iv_list_.resize(total_chunks);
for (auto &iv : iv_list_) {
randombytes_buf(iv.data(), iv.size());
}
common_initialize(true);
create_encrypted_paths(file_name, relative_parent_path);
}
encrypting_reader::encrypting_reader(std::string_view encrypted_file_path,
@@ -241,45 +194,14 @@ encrypting_reader::encrypting_reader(std::string_view encrypted_file_path,
stop_type_callback stop_requested_cb,
std::string_view token,
std::size_t error_return)
: key_(utils::encryption::generate_key<utils::encryption::hash_256_t>(
token)),
: key_(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)) {
REPERTORY_USES_FUNCTION_NAME();
if (not*source_file_) {
throw utils::error::create_exception(function_name, {
"file open failed",
source_path,
});
}
encrypted_file_path_ = encrypted_file_path;
encrypted_file_name_ = utils::path::strip_to_file_name(encrypted_file_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();
const 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);
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_;
iv_list_.resize(total_chunks);
for (auto &iv : iv_list_) {
randombytes_buf(iv.data(), iv.size());
}
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(true);
}
encrypting_reader::encrypting_reader(
@@ -289,43 +211,73 @@ encrypting_reader::encrypting_reader(
std::array<unsigned char, crypto_aead_xchacha20poly1305_IETF_NPUBBYTES>>
iv_list,
std::size_t error_return)
: key_(utils::encryption::generate_key<utils::encryption::hash_256_t>(
token)),
: key_(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),
iv_list_(std::move(iv_list)) {
common_initialize(false);
}
encrypting_reader::encrypting_reader(
std::string_view file_name, std::string_view source_path,
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)),
stop_requested_cb_(std::move(stop_requested_cb)),
error_return_(error_return),
source_file_(utils::file::file::open_or_create_file(source_path, true)) {
REPERTORY_USES_FUNCTION_NAME();
kdf_header_ = cfg.to_header();
if (not*source_file_) {
throw utils::error::create_exception(function_name, {
"file open failed",
source_path,
});
}
data_buffer result;
utils::encryption::encrypt_data(
key_, reinterpret_cast<const unsigned char *>(file_name.data()),
file_name.size(), result);
common_initialize(true);
create_encrypted_paths(file_name, relative_parent_path);
}
encrypted_file_path_ = encrypted_file_path;
encrypted_file_name_ = utils::path::strip_to_file_name(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, kdf_config cfg,
std::size_t error_return)
: key_(
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg)),
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();
common_initialize(true);
}
auto opt_size = source_file_->size();
if (not opt_size.has_value()) {
throw utils::error::create_exception(
function_name, {
"get file size failed",
std::to_string(utils::get_last_error_code()),
source_file_->get_path(),
});
}
auto file_size{opt_size.value()};
const 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);
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_;
iv_list_ = std::move(iv_list);
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,
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)),
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)) {
kdf_header_ = cfg.to_header();
common_initialize(false);
}
encrypting_reader::encrypting_reader(const encrypting_reader &reader)
@@ -334,17 +286,18 @@ encrypting_reader::encrypting_reader(const encrypting_reader &reader)
error_return_(reader.error_return_),
source_file_(
utils::file::file::open_file(reader.source_file_->get_path(), true)),
chunk_buffers_(reader.chunk_buffers_),
encrypted_file_name_(reader.encrypted_file_name_),
encrypted_file_path_(reader.encrypted_file_path_),
iv_list_(reader.iv_list_),
chunk_buffers_(reader.chunk_buffers_),
kdf_header_(reader.kdf_header_),
last_data_chunk_(reader.last_data_chunk_),
last_data_chunk_size_(reader.last_data_chunk_size_),
read_offset_(reader.read_offset_),
total_size_(reader.total_size_) {
REPERTORY_USES_FUNCTION_NAME();
if (not*source_file_) {
if (not *source_file_) {
throw utils::error::create_exception(
function_name, {
"file open failed",
@@ -354,15 +307,61 @@ encrypting_reader::encrypting_reader(const encrypting_reader &reader)
}
}
auto encrypting_reader::calculate_decrypted_size(std::uint64_t total_size)
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 {
if (uses_kdf) {
total_size -= kdf_config::size();
}
return total_size - (utils::divide_with_ceiling(
total_size, static_cast<std::uint64_t>(
get_encrypted_chunk_size())) *
encryption_header_size);
}
auto encrypting_reader::calculate_encrypted_size(std::string_view source_path)
auto encrypting_reader::calculate_encrypted_size(std::string_view source_path,
bool uses_kdf)
-> std::uint64_t {
REPERTORY_USES_FUNCTION_NAME();
@@ -377,9 +376,43 @@ auto encrypting_reader::calculate_encrypted_size(std::string_view source_path)
}
auto file_size{opt_size.value()};
const auto total_chunks = utils::divide_with_ceiling(
auto total_chunks = utils::divide_with_ceiling(
file_size, static_cast<std::uint64_t>(data_chunk_size_));
return file_size + (total_chunks * encryption_header_size);
return file_size + (total_chunks * encryption_header_size) +
(uses_kdf ? kdf_config::size() : 0U);
}
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()),
file_name.size(), result);
if (kdf_header_.has_value()) {
result.insert(result.begin(), kdf_header_->begin(), kdf_header_->end());
}
encrypted_file_name_ = utils::collection::to_hex_string(result);
if (not relative_parent_path.has_value()) {
return;
}
for (const auto &part :
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()),
strnlen(part.c_str(), part.size()), result);
if (kdf_header_.has_value()) {
result.insert(result.begin(), kdf_header_->begin(), kdf_header_->end());
}
encrypted_file_path_ += '/' + utils::collection::to_hex_string(result);
}
encrypted_file_path_ += '/' + encrypted_file_name_;
}
auto encrypting_reader::create_iostream() const
@@ -388,24 +421,73 @@ auto encrypting_reader::create_iostream() const
std::make_unique<encrypting_streambuf>(*this));
}
auto encrypting_reader::get_kdf_config() const -> std::optional<kdf_config> {
REPERTORY_USES_FUNCTION_NAME();
if (not kdf_header_.has_value()) {
return std::nullopt;
}
kdf_config cfg;
if (not kdf_config::from_header(kdf_header_.value(), cfg)) {
throw utils::error::create_exception(function_name,
{
"invalid kdf header",
});
}
return cfg;
}
auto encrypting_reader::reader_function(char *buffer, size_t size,
size_t nitems) -> size_t {
REPERTORY_USES_FUNCTION_NAME();
const auto read_size = static_cast<std::size_t>(std::min(
static_cast<std::uint64_t>(size * nitems), total_size_ - read_offset_));
auto read_size =
static_cast<std::uint64_t>(size) * static_cast<std::uint64_t>(nitems);
if (read_size == 0U) {
return 0U;
}
auto chunk = read_offset_ / encrypted_chunk_size_;
auto chunk_offset = read_offset_ % encrypted_chunk_size_;
auto read_offset{read_offset_};
std::size_t total_read{};
auto total_size{total_size_};
auto ret = false;
if (read_offset_ < total_size_) {
if (kdf_header_.has_value()) {
total_size -= kdf_header_->size();
if (read_offset < kdf_header_->size()) {
auto to_read{
utils::calculate_read_size(kdf_header_->size(), read_size,
read_offset),
};
read_offset_ += to_read;
std::memcpy(buffer, &kdf_header_->data()[read_offset], to_read);
if (read_size - to_read == 0) {
return to_read;
}
read_offset = 0U;
read_size -= to_read;
total_read += to_read;
buffer += to_read;
} else {
read_offset -= kdf_header_->size();
}
}
auto chunk = static_cast<std::size_t>(read_offset / encrypted_chunk_size_);
auto chunk_offset =
static_cast<std::size_t>(read_offset % encrypted_chunk_size_);
auto remain = utils::calculate_read_size(total_size, read_size, read_offset);
auto ret{false};
if (read_offset < total_size) {
try {
ret = true;
auto remain = read_size;
while (not get_stop_requested() && ret && (remain != 0U)) {
if (chunk_buffers_.find(chunk) == chunk_buffers_.end()) {
if (not chunk_buffers_.contains(chunk)) {
auto &chunk_buffer = chunk_buffers_[chunk];
data_buffer file_data(chunk == last_data_chunk_
? last_data_chunk_size_
@@ -413,24 +495,26 @@ auto encrypting_reader::reader_function(char *buffer, size_t size,
chunk_buffer.resize(file_data.size() + encryption_header_size);
std::size_t bytes_read{};
if ((ret = source_file_->read(file_data, chunk * data_chunk_size_,
&bytes_read))) {
ret = source_file_->read(
file_data,
static_cast<std::uint64_t>(chunk) *
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);
}
} else if (chunk) {
chunk_buffers_.erase(chunk - 1u);
} else if (chunk != 0U) {
chunk_buffers_.erase(chunk - 1U);
}
auto &chunk_buffer = chunk_buffers_[chunk];
const auto to_read = std::min(
static_cast<std::size_t>(chunk_buffer.size() - chunk_offset),
remain);
auto to_read = std::min(chunk_buffer.size() - chunk_offset, remain);
std::memcpy(buffer + total_read, &chunk_buffer[chunk_offset], to_read);
total_read += to_read;
remain -= to_read;
chunk_offset = 0u;
chunk++;
chunk_offset = 0U;
++chunk;
read_offset_ += to_read;
}
} catch (const std::exception &e) {

View File

@@ -28,6 +28,53 @@
#include "utils/path.hpp"
namespace repertory::utils::encryption {
auto kdf_config::generate_checksum() const -> std::uint64_t {
REPERTORY_USES_FUNCTION_NAME();
kdf_config tmp = *this;
tmp.checksum = 0;
auto hdr = tmp.to_header();
std::uint64_t ret{0};
if (crypto_generichash(reinterpret_cast<unsigned char *>(&ret), sizeof(ret),
hdr.data(), hdr.size(), nullptr, 0) != 0) {
throw utils::error::create_exception(function_name,
{
"failed to calculate checksum",
});
}
return ret;
}
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 {
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);
return cfg.version == kdf_version::v1 && cfg.kdf == kdf_type::argon2id &&
cfg.memlimit >= memlimit_level::level1 &&
cfg.memlimit <= memlimit_level::level4 &&
cfg.opslimit >= opslimit_level::level1 &&
cfg.opslimit <= opslimit_level::level3 &&
cfg.checksum == cfg.generate_checksum();
}
auto decrypt_file_path(std::string_view encryption_token,
std::string &file_path) -> bool {
std::vector<std::string> decrypted_parts;
@@ -49,6 +96,27 @@ auto decrypt_file_path(std::string_view encryption_token,
return true;
}
auto decrypt_file_path(std::string_view encryption_token, const kdf_config &cfg,
std::string &file_path) -> bool {
std::vector<std::string> decrypted_parts;
for (const auto &part : std::filesystem::path(file_path)) {
auto file_name = part.string();
if (file_name == "/") {
continue;
}
if (not decrypt_file_name(encryption_token, cfg, file_name)) {
return false;
}
decrypted_parts.push_back(file_name);
}
file_path =
utils::path::create_api_path(utils::string::join(decrypted_parts, '/'));
return true;
}
auto decrypt_file_name(std::string_view encryption_token,
std::string &file_name) -> bool {
data_buffer buffer;
@@ -60,10 +128,26 @@ auto decrypt_file_name(std::string_view encryption_token,
return utils::encryption::decrypt_data(encryption_token, buffer, file_name);
}
auto decrypt_file_name(std::string_view encryption_token, const kdf_config &cfg,
std::string &file_name) -> bool {
data_buffer buffer;
if (not utils::collection::from_hex_string(file_name, buffer)) {
return false;
}
file_name.clear();
return utils::encryption::decrypt_data(encryption_token, cfg, buffer,
file_name);
}
template <typename data_t>
auto read_encrypted_range(const http_range &range,
const utils::encryption::hash_256_t &key,
const utils::hash::hash_256_t &key,
reader_func_t reader_func, std::uint64_t total_size,
data_buffer &data) -> bool {
data_t &data, std::uint8_t file_header_size,
std::size_t &bytes_read) -> bool {
bytes_read = 0U;
auto encrypted_chunk_size =
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
auto data_chunk_size =
@@ -76,7 +160,7 @@ auto read_encrypted_range(const http_range &range,
for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) {
data_buffer cypher;
auto start_offset = chunk * encrypted_chunk_size;
auto start_offset = (chunk * encrypted_chunk_size) + file_header_size;
auto end_offset = std::min(
start_offset + (total_size - (chunk * data_chunk_size)) +
encryption_header_size - 1U,
@@ -98,58 +182,7 @@ auto read_encrypted_range(const http_range &range,
static_cast<std::int64_t>(source_offset)),
std::next(source_buffer.begin(),
static_cast<std::int64_t>(source_offset + data_size)),
std::back_inserter(data));
remain -= data_size;
source_offset = 0U;
}
return true;
}
auto read_encrypted_range(const http_range &range,
const utils::encryption::hash_256_t &key,
reader_func_t reader_func, std::uint64_t total_size,
unsigned char *data, std::size_t size,
std::size_t &bytes_read) -> bool {
bytes_read = 0U;
auto encrypted_chunk_size =
utils::encryption::encrypting_reader::get_encrypted_chunk_size();
auto data_chunk_size =
utils::encryption::encrypting_reader::get_data_chunk_size();
auto start_chunk = static_cast<std::size_t>(range.begin / data_chunk_size);
auto end_chunk = static_cast<std::size_t>(range.end / data_chunk_size);
auto remain = range.end - range.begin + 1U;
auto source_offset = static_cast<std::size_t>(range.begin % data_chunk_size);
std::span dest_buffer(data, size);
for (std::size_t chunk = start_chunk; chunk <= end_chunk; chunk++) {
data_buffer cypher;
auto start_offset = chunk * encrypted_chunk_size;
auto end_offset = std::min(
start_offset + (total_size - (chunk * data_chunk_size)) +
encryption_header_size - 1U,
static_cast<std::uint64_t>(start_offset + encrypted_chunk_size - 1U));
if (not reader_func(cypher, start_offset, end_offset)) {
return false;
}
data_buffer source_buffer;
if (not utils::encryption::decrypt_data(key, cypher, source_buffer)) {
return false;
}
cypher.clear();
auto data_size = static_cast<std::size_t>(std::min(
remain, static_cast<std::uint64_t>(data_chunk_size - source_offset)));
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(dest_buffer.begin(), static_cast<std::int64_t>(bytes_read)));
std::next(data.begin(), static_cast<std::int64_t>(bytes_read)));
remain -= data_size;
bytes_read += data_size;
source_offset = 0U;
@@ -157,6 +190,26 @@ auto read_encrypted_range(const http_range &range,
return true;
}
auto read_encrypted_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,
data_buffer &data) -> bool {
std::size_t bytes_read{};
return read_encrypted_range<data_buffer>(
range, key, reader_func, total_size, data,
uses_kdf ? kdf_config::size() : 0U, bytes_read);
}
[[nodiscard]] auto read_encrypted_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>>(
range, key, reader_func, total_size, dest_buffer,
uses_kdf ? kdf_config::size() : 0U, bytes_read);
}
} // namespace repertory::utils::encryption
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined (PROJECT_ENABLE_BOOST)

View File

@@ -43,7 +43,7 @@ auto enc_file::copy_to(std::string_view new_path, bool overwrite) const
return file_->copy_to(new_path, overwrite);
}
void enc_file::flush() const { return file_->flush(); }
void enc_file::flush() const { file_->flush(); }
auto enc_file::move_to(std::string_view path) -> bool {
return file_->move_to(path);
@@ -68,8 +68,8 @@ auto enc_file::read(unsigned char *data, std::size_t to_read,
std::size_t bytes_read{};
auto ret{
utils::encryption::read_encrypted_range(
{offset, offset + to_read - 1U},
utils::encryption::generate_key<utils::encryption::hash_256_t>(
{.begin = offset, .end = offset + to_read - 1U},
utils::encryption::generate_key<utils::hash::hash_256_t>(
encryption_token_),
[&](auto &&ct_buffer, auto &&start_offset,
auto &&end_offset) -> bool {
@@ -145,7 +145,7 @@ auto enc_file::truncate(std::size_t size) -> bool {
return i_file::write(data, offset);
}
auto begin_chunk{
/* auto begin_chunk{
file_size.value() /
utils::encryption::encrypting_reader::get_data_chunk_size(),
};
@@ -153,13 +153,14 @@ auto enc_file::truncate(std::size_t size) -> bool {
utils::divide_with_ceiling(
file_size.value(),
utils::encryption::encrypting_reader::get_data_chunk_size()),
};
}; */
return false;
}
auto enc_file::write(const unsigned char *data, std::size_t to_write,
std::size_t offset, std::size_t *total_written) -> bool {
auto enc_file::write(const unsigned char * /* data */, std::size_t to_write,
std::size_t offset, std::size_t * /* total_written */)
-> bool {
auto file_size{size()};
if (not file_size.has_value()) {
return false;
@@ -181,7 +182,7 @@ auto enc_file::size() const -> std::optional<std::uint64_t> {
}
return utils::encryption::encrypting_reader::calculate_decrypted_size(
file_size.value());
file_size.value(), false);
}
} // namespace repertory::utils::file

View File

@@ -25,7 +25,7 @@
#include "utils/error.hpp"
namespace repertory::utils::encryption {
namespace repertory::utils::hash {
auto create_hash_blake2b_256(std::string_view data) -> hash_256_t {
return create_hash_blake2b_t<hash_256_t>(
reinterpret_cast<const unsigned char *>(data.data()), data.size());
@@ -111,8 +111,8 @@ auto create_hash_sha512(const data_buffer &data) -> hash_512_t {
data.size() * sizeof(data_buffer::value_type));
}
auto create_hash_sha512(const unsigned char *data,
std::size_t data_size) -> hash_512_t {
auto create_hash_sha512(const unsigned char *data, std::size_t data_size)
-> hash_512_t {
REPERTORY_USES_FUNCTION_NAME();
hash_512_t hash{};
@@ -148,8 +148,8 @@ auto create_hash_sha512(const unsigned char *data,
return hash;
}
auto create_hash_sha256(const unsigned char *data,
std::size_t data_size) -> hash_256_t {
auto create_hash_sha256(const unsigned char *data, std::size_t data_size)
-> hash_256_t {
REPERTORY_USES_FUNCTION_NAME();
hash_256_t hash{};
@@ -184,6 +184,6 @@ auto create_hash_sha256(const unsigned char *data,
return hash;
}
} // namespace repertory::utils::encryption
} // namespace repertory::utils::hash
#endif // defined(PROJECT_ENABLE_LIBSODIUM)

View File

@@ -67,6 +67,54 @@ TEST(utils_encrypting_reader, read_file_data) {
}
}
TEST(utils_encrypting_reader, read_file_data_using_argon2id) {
const auto token = std::string("moose");
utils::encryption::kdf_config 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, token, cfg);
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(
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));
}
data_buffer decrypted_data;
EXPECT_TRUE(utils::encryption::decrypt_data(
token, *reader.get_kdf_config(), 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_in_multiple_chunks) {
const auto token = std::string("moose");
auto &source_file = test::create_random_file(
@@ -114,6 +162,63 @@ TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks) {
}
}
TEST(utils_encrypting_reader,
read_file_data_in_multiple_chunks_using_argon2id) {
utils::encryption::kdf_config cfg;
const auto token = std::string("moose");
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, token, cfg);
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(
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));
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(
token, *reader.get_kdf_config(),
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_as_stream) {
const auto token = std::string("moose");
auto &source_file = test::create_random_file(
@@ -166,6 +271,62 @@ TEST(utils_encrypting_reader, read_file_data_as_stream) {
}
}
TEST(utils_encrypting_reader, read_file_data_as_stream_using_argon2id) {
utils::encryption::kdf_config cfg;
const auto token = std::string("moose");
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, token, cfg);
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());
}
data_buffer decrypted_data;
EXPECT_TRUE(utils::encryption::decrypt_data(
token, *reader.get_kdf_config(), 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_in_multiple_chunks_as_stream) {
const auto token = std::string("moose");
auto &source_file = test::create_random_file(
@@ -220,6 +381,69 @@ TEST(utils_encrypting_reader, read_file_data_in_multiple_chunks_as_stream) {
}
}
}
TEST(utils_encrypting_reader,
read_file_data_in_multiple_chunks_as_stream_using_argon2id) {
utils::encryption::kdf_config cfg;
const auto token = std::string("moose");
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, token, cfg);
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());
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(
token, *reader.get_kdf_config(),
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()));
}
}
}
}
} // namespace repertory
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)

View File

@@ -0,0 +1,144 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "test.hpp"
#if defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
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.checksum);
}
TEST(utils_encryption_kdf_config, can_generate_salt) {
utils::encryption::kdf_config cfg;
cfg.generate_salt();
EXPECT_NE(utils::encryption::kdf_config::salt_t{}, cfg.salt);
auto orig_salt = cfg.salt;
cfg.generate_salt();
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());
}
TEST(utils_encryption_kdf_config, generate_salt_calculates_checksum) {
utils::encryption::kdf_config cfg;
cfg.generate_salt();
EXPECT_NE(0U, cfg.checksum);
}
TEST(utils_encryption_kdf_config, can_create_header_and_restore) {
utils::encryption::kdf_config cfg;
cfg.generate_salt();
auto hdr = cfg.to_header();
EXPECT_EQ(utils::encryption::kdf_config::size(), hdr.size());
utils::encryption::kdf_config restored_cfg;
EXPECT_TRUE(utils::encryption::kdf_config::from_header(hdr, restored_cfg));
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, 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();
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_kdf_is_invalid) {
utils::encryption::kdf_config cfg;
cfg.kdf = static_cast<utils::encryption::kdf_type>(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_memlimit_is_invalid) {
utils::encryption::kdf_config cfg;
cfg.memlimit = static_cast<utils::encryption::memlimit_level>(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_opslimit_is_invalid) {
utils::encryption::kdf_config cfg;
cfg.opslimit = static_cast<utils::encryption::opslimit_level>(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_salt_is_invalid) {
utils::encryption::kdf_config cfg;
cfg.generate_salt();
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));
}
} // namespace repertory
#endif // defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)

View File

@@ -24,6 +24,10 @@
#if defined(PROJECT_ENABLE_LIBSODIUM)
namespace {
#if defined(PROJECT_ENABLE_BOOST)
const std::string buffer = "cow moose dog chicken";
#endif // defined(PROJECT_ENABLE_BOOST)
const auto get_stop_requested = []() -> bool { return false; };
} // namespace
@@ -32,24 +36,21 @@ static constexpr std::string_view token{"moose"};
static constexpr std::wstring_view token_w{L"moose"};
TEST(utils_encryption, generate_key) {
auto key1 =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
auto key1 = utils::encryption::generate_key<utils::hash::hash_256_t>(token);
EXPECT_STREQ(
"ab4a0b004e824962913f7c0f79582b6ec7a3b8726426ca61d1a0a28ce5049e96",
utils::collection::to_hex_string(key1).c_str());
auto key2 =
utils::encryption::generate_key<utils::encryption::hash_256_t>("moose");
auto key3 =
utils::encryption::generate_key<utils::encryption::hash_256_t>("moose");
auto key2 = utils::encryption::generate_key<utils::hash::hash_256_t>("moose");
auto key3 = utils::encryption::generate_key<utils::hash::hash_256_t>("moose");
EXPECT_EQ(key2, key3);
auto key4 =
utils::encryption::generate_key<utils::encryption::hash_256_t>("moose2");
utils::encryption::generate_key<utils::hash::hash_256_t>("moose2");
EXPECT_NE(key2, key4);
auto key1_w =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token_w);
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w);
EXPECT_NE(key1, key1_w);
#if defined(_WIN32)
EXPECT_STREQ(
@@ -62,34 +63,33 @@ TEST(utils_encryption, generate_key) {
#endif
auto key2_w =
utils::encryption::generate_key<utils::encryption::hash_256_t>(L"moose");
utils::encryption::generate_key<utils::hash::hash_256_t>(L"moose");
auto key3_w =
utils::encryption::generate_key<utils::encryption::hash_256_t>(L"moose");
utils::encryption::generate_key<utils::hash::hash_256_t>(L"moose");
EXPECT_EQ(key2_w, key3_w);
EXPECT_NE(key2_w, key2);
EXPECT_NE(key3_w, key3);
auto key4_w =
utils::encryption::generate_key<utils::encryption::hash_256_t>(L"moose2");
utils::encryption::generate_key<utils::hash::hash_256_t>(L"moose2");
EXPECT_NE(key2_w, key4_w);
EXPECT_NE(key4_w, key4);
}
TEST(utils_encryption, generate_key_default_hasher_is_blake2b_256) {
auto key1 =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
auto key2 = utils::encryption::generate_key<utils::encryption::hash_256_t>(
auto key1 = utils::encryption::generate_key<utils::hash::hash_256_t>(token);
auto key2 = utils::encryption::generate_key<utils::hash::hash_256_t>(
token, [](auto &&data, auto &&size) -> auto {
return utils::encryption::create_hash_blake2b_256(
return utils::hash::create_hash_blake2b_256(
std::string_view(reinterpret_cast<const char *>(data), size));
});
EXPECT_EQ(key1, key2);
auto key1_w =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token_w);
auto key2_w = utils::encryption::generate_key<utils::encryption::hash_256_t>(
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w);
auto key2_w = utils::encryption::generate_key<utils::hash::hash_256_t>(
token_w, [](auto &&data, auto &&size) -> auto {
return utils::encryption::create_hash_blake2b_256(std::wstring_view(
return utils::hash::create_hash_blake2b_256(std::wstring_view(
reinterpret_cast<const wchar_t *>(data), size / sizeof(wchar_t)));
});
EXPECT_EQ(key1_w, key2_w);
@@ -99,22 +99,22 @@ TEST(utils_encryption, generate_key_default_hasher_is_blake2b_256) {
}
TEST(utils_encryption, generate_key_with_hasher) {
auto key1 = utils::encryption::generate_key<utils::encryption::hash_256_t>(
token, utils::encryption::blake2b_256_hasher);
auto key1 = utils::encryption::generate_key<utils::hash::hash_256_t>(
token, utils::hash::blake2b_256_hasher);
EXPECT_STREQ(
"ab4a0b004e824962913f7c0f79582b6ec7a3b8726426ca61d1a0a28ce5049e96",
utils::collection::to_hex_string(key1).c_str());
auto key2 = utils::encryption::generate_key<utils::encryption::hash_256_t>(
token, utils::encryption::sha256_hasher);
auto key2 = utils::encryption::generate_key<utils::hash::hash_256_t>(
token, utils::hash::sha256_hasher);
EXPECT_NE(key1, key2);
EXPECT_STREQ(
"182072537ada59e4d6b18034a80302ebae935f66adbdf0f271d3d36309c2d481",
utils::collection::to_hex_string(key2).c_str());
auto key1_w = utils::encryption::generate_key<utils::encryption::hash_256_t>(
token_w, utils::encryption::blake2b_256_hasher);
auto key1_w = utils::encryption::generate_key<utils::hash::hash_256_t>(
token_w, utils::hash::blake2b_256_hasher);
#if defined(_WIN32)
EXPECT_STREQ(
L"4f5eb2a2ab34e3777b230465283923080b9ba59311e74058ccd74185131d11fe",
@@ -125,8 +125,8 @@ TEST(utils_encryption, generate_key_with_hasher) {
utils::collection::to_hex_wstring(key1_w).c_str());
#endif
auto key2_w = utils::encryption::generate_key<utils::encryption::hash_256_t>(
token_w, utils::encryption::sha256_hasher);
auto key2_w = utils::encryption::generate_key<utils::hash::hash_256_t>(
token_w, utils::hash::sha256_hasher);
EXPECT_NE(key1_w, key2_w);
#if defined(_WIN32)
@@ -144,7 +144,69 @@ TEST(utils_encryption, generate_key_with_hasher) {
}
#if defined(PROJECT_ENABLE_BOOST)
static const std::string buffer = "cow moose dog chicken";
TEST(utils_encryption, generate_argon2id_key) {
utils::encryption::kdf_config cfg;
{
auto key1 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
auto key2 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
EXPECT_NE(key1, key2);
auto key3 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
EXPECT_NE(key3, key1);
auto key4 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
EXPECT_NE(key4, key2);
EXPECT_NE(key3, key4);
}
{
auto key1 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
auto key2 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
EXPECT_NE(key1, key2);
auto key3 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
EXPECT_NE(key3, key1);
auto key4 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
EXPECT_NE(key4, key2);
EXPECT_NE(key3, key4);
}
}
TEST(utils_encryption, recreate_argon2id_key) {
utils::encryption::kdf_config cfg;
{
auto key1 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token, cfg);
auto key2 =
utils::encryption::recreate_key<utils::hash::hash_256_t>(token, cfg);
EXPECT_EQ(key1, key2);
}
{
auto key1 =
utils::encryption::generate_key<utils::hash::hash_256_t>(token_w, cfg);
auto key2 =
utils::encryption::recreate_key<utils::hash::hash_256_t>(token_w, cfg);
EXPECT_EQ(key1, key2);
}
}
static void test_encrypted_result(const data_buffer &result) {
EXPECT_EQ(buffer.size() + utils::encryption::encryption_header_size,
@@ -155,6 +217,17 @@ static void test_encrypted_result(const data_buffer &result) {
EXPECT_STREQ(buffer.c_str(), data.c_str());
}
static void
test_encrypted_result_using_argon2id(const data_buffer &result,
const utils::encryption::kdf_config &cfg) {
EXPECT_EQ(buffer.size() + utils::encryption::encryption_header_size,
result.size());
std::string data;
EXPECT_TRUE(utils::encryption::decrypt_data(token, cfg, result, data));
EXPECT_EQ(buffer.size(), data.size());
EXPECT_STREQ(buffer.c_str(), data.c_str());
}
TEST(utils_encryption, encrypt_data_buffer) {
data_buffer result;
utils::encryption::encrypt_data(token, buffer, result);
@@ -163,7 +236,7 @@ TEST(utils_encryption, encrypt_data_buffer) {
TEST(utils_encryption, encrypt_data_buffer_with_key) {
const auto key =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
data_buffer result;
utils::encryption::encrypt_data(key, buffer, result);
test_encrypted_result(result);
@@ -179,7 +252,7 @@ TEST(utils_encryption, encrypt_data_pointer) {
TEST(utils_encryption, encrypt_data_pointer_with_key) {
const auto key =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
data_buffer result;
utils::encryption::encrypt_data(
key, reinterpret_cast<const unsigned char *>(buffer.data()),
@@ -189,7 +262,7 @@ TEST(utils_encryption, encrypt_data_pointer_with_key) {
TEST(utils_encryption, decrypt_data_pointer) {
const auto key =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
data_buffer result;
utils::encryption::encrypt_data(
key, reinterpret_cast<const unsigned char *>(buffer.data()),
@@ -205,7 +278,7 @@ TEST(utils_encryption, decrypt_data_pointer) {
TEST(utils_encryption, decrypt_data_buffer_with_key) {
const auto key =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
data_buffer result;
utils::encryption::encrypt_data(
key, reinterpret_cast<const unsigned char *>(buffer.data()),
@@ -220,7 +293,7 @@ TEST(utils_encryption, decrypt_data_buffer_with_key) {
TEST(utils_encryption, decrypt_data_pointer_with_key) {
const auto key =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
data_buffer result;
utils::encryption::encrypt_data(
key, reinterpret_cast<const unsigned char *>(buffer.data()),
@@ -236,7 +309,7 @@ TEST(utils_encryption, decrypt_data_pointer_with_key) {
TEST(utils_encryption, decryption_failure) {
const auto key =
utils::encryption::generate_key<utils::encryption::hash_256_t>(token);
utils::encryption::generate_key<utils::hash::hash_256_t>(token);
data_buffer result;
utils::encryption::encrypt_data(
key, reinterpret_cast<const unsigned char *>(buffer.data()),
@@ -280,6 +353,28 @@ TEST(utils_encryption, decrypt_file_path) {
EXPECT_STREQ("/moose/cow/test.dat", file_path.c_str());
}
}
TEST(utils_encryption, encrypt_data_buffer_using_argon2id) {
utils::encryption::kdf_config cfg;
data_buffer result;
utils::encryption::encrypt_data(token, cfg, buffer, result);
test_encrypted_result_using_argon2id(result, cfg);
}
TEST(utils_encryption, encrypt_data_pointer_using_argon2id) {
utils::encryption::kdf_config cfg;
data_buffer result;
utils::encryption::encrypt_data(
token, cfg, reinterpret_cast<const unsigned char *>(buffer.data()),
buffer.size(), result);
test_encrypted_result_using_argon2id(result, cfg);
}
// TEST(utils_encryption, decrypt_file_name_using_argon2id) {}
// TEST(utils_encryption, decrypt_file_path_using_argon2id) {}
#endif // defined(PROJECT_ENABLE_BOOST)
} // namespace repertory

View File

@@ -25,34 +25,31 @@
namespace repertory {
TEST(utils_hash, hash_type_sizes) {
EXPECT_EQ(32U, utils::encryption::hash_256_t{}.size());
EXPECT_EQ(48U, utils::encryption::hash_384_t{}.size());
EXPECT_EQ(64U, utils::encryption::hash_512_t{}.size());
EXPECT_EQ(32U, utils::hash::hash_256_t{}.size());
EXPECT_EQ(48U, utils::hash::hash_384_t{}.size());
EXPECT_EQ(64U, utils::hash::hash_512_t{}.size());
}
TEST(utils_hash, default_hasher_is_blake2b) {
EXPECT_EQ(
&utils::encryption::blake2b_256_hasher,
&utils::encryption::default_create_hash<utils::encryption::hash_256_t>());
EXPECT_EQ(&utils::hash::blake2b_256_hasher,
&utils::hash::default_create_hash<utils::hash::hash_256_t>());
EXPECT_EQ(
&utils::encryption::blake2b_384_hasher,
&utils::encryption::default_create_hash<utils::encryption::hash_384_t>());
EXPECT_EQ(&utils::hash::blake2b_384_hasher,
&utils::hash::default_create_hash<utils::hash::hash_384_t>());
EXPECT_EQ(
&utils::encryption::blake2b_512_hasher,
&utils::encryption::default_create_hash<utils::encryption::hash_512_t>());
EXPECT_EQ(&utils::hash::blake2b_512_hasher,
&utils::hash::default_create_hash<utils::hash::hash_512_t>());
}
TEST(utils_hash, blake2b_256) {
auto hash = utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_256("a"));
utils::hash::create_hash_blake2b_256("a"));
EXPECT_STREQ(
"8928aae63c84d87ea098564d1e03ad813f107add474e56aedd286349c0c03ea4",
hash.c_str());
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_256(L"a"));
utils::hash::create_hash_blake2b_256(L"a"));
#if defined(_WIN32)
EXPECT_STREQ(
"d2373b17cd8a8e19e39f52fa4905a274f93805fbb8bb4c7f3cb4b2cd6708ec8a",
@@ -64,7 +61,7 @@ TEST(utils_hash, blake2b_256) {
#endif
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_256({1U}));
utils::hash::create_hash_blake2b_256({1U}));
EXPECT_STREQ(
"ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25",
hash.c_str());
@@ -72,13 +69,13 @@ TEST(utils_hash, blake2b_256) {
TEST(utils_hash, blake2b_384) {
auto hash = utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_384("a"));
utils::hash::create_hash_blake2b_384("a"));
EXPECT_STREQ("7d40de16ff771d4595bf70cbda0c4ea0a066a6046fa73d34471cd4d93d827d7"
"c94c29399c50de86983af1ec61d5dcef0",
hash.c_str());
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_384(L"a"));
utils::hash::create_hash_blake2b_384(L"a"));
#if defined(_WIN32)
EXPECT_STREQ("637fe31d1e955760ef31043d525d9321826a778ddbe82fcde45a98394241380"
"96675e2f87e36b53ab223a7fd254198fd",
@@ -90,7 +87,7 @@ TEST(utils_hash, blake2b_384) {
#endif
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_384({1U}));
utils::hash::create_hash_blake2b_384({1U}));
EXPECT_STREQ("42cfe875d08d816538103b906bb0b05202e0b09c4e981680c1110684fc7845b"
"c91c178fa167afcc445490644b2bf5f5b",
hash.c_str());
@@ -98,14 +95,14 @@ TEST(utils_hash, blake2b_384) {
TEST(utils_hash, blake2b_512) {
auto hash = utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_512("a"));
utils::hash::create_hash_blake2b_512("a"));
EXPECT_STREQ(
"333fcb4ee1aa7c115355ec66ceac917c8bfd815bf7587d325aec1864edd24e34d5abe2c6"
"b1b5ee3face62fed78dbef802f2a85cb91d455a8f5249d330853cb3c",
hash.c_str());
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_512(L"a"));
utils::hash::create_hash_blake2b_512(L"a"));
#if defined(_WIN32)
EXPECT_STREQ(
"05970b95468b0b1941066ff189091493e73859ce41cde5ad08118e93ea1d81a57a144296"
@@ -119,7 +116,7 @@ TEST(utils_hash, blake2b_512) {
#endif
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_blake2b_512({1U}));
utils::hash::create_hash_blake2b_512({1U}));
EXPECT_STREQ(
"9545ba37b230d8a2e716c4707586542780815b7c4088edcb9af6a9452d50f32474d5ba9a"
"ab52a67aca864ef2696981c2eadf49020416136afd838fb048d21653",
@@ -127,14 +124,14 @@ TEST(utils_hash, blake2b_512) {
}
TEST(utils_hash, sha256) {
auto hash = utils::collection::to_hex_string(
utils::encryption::create_hash_sha256("a"));
auto hash =
utils::collection::to_hex_string(utils::hash::create_hash_sha256("a"));
EXPECT_STREQ(
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
hash.c_str());
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_sha256(L"a"));
hash =
utils::collection::to_hex_string(utils::hash::create_hash_sha256(L"a"));
#if defined(_WIN32)
EXPECT_STREQ(
"ffe9aaeaa2a2d5048174df0b80599ef0197ec024c4b051bc9860cff58ef7f9f3",
@@ -145,23 +142,23 @@ TEST(utils_hash, sha256) {
hash.c_str());
#endif
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_sha256({1U}));
hash =
utils::collection::to_hex_string(utils::hash::create_hash_sha256({1U}));
EXPECT_STREQ(
"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",
hash.c_str());
}
TEST(utils_hash, sha512) {
auto hash = utils::collection::to_hex_string(
utils::encryption::create_hash_sha512("a"));
auto hash =
utils::collection::to_hex_string(utils::hash::create_hash_sha512("a"));
EXPECT_STREQ(
"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c65"
"2bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75",
hash.c_str());
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_sha512(L"a"));
hash =
utils::collection::to_hex_string(utils::hash::create_hash_sha512(L"a"));
#if defined(_WIN32)
EXPECT_STREQ(
"5c2ca3d50f46ece6066c53bd1a490cbe5f72d2738ae9417332e91e5c3f75205c639d71a9"
@@ -174,8 +171,8 @@ TEST(utils_hash, sha512) {
hash.c_str());
#endif
hash = utils::collection::to_hex_string(
utils::encryption::create_hash_sha512({1U}));
hash =
utils::collection::to_hex_string(utils::hash::create_hash_sha512({1U}));
EXPECT_STREQ(
"7b54b66836c1fbdd13d2441d9e1434dc62ca677fb68f5fe66a464baadecdbd00576f8d6b"
"5ac3bcc80844b7d50b1cc6603444bbe7cfcf8fc0aa1ee3c636d9e339",

View File

@@ -0,0 +1,253 @@
/*
Copyright <2018-2025> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "test.hpp"
namespace repertory {
TEST(utils_ttl_cache, can_construct_cache) {
utils::ttl_cache<std::uint8_t> cache;
EXPECT_EQ(utils::ttl_cache<std::uint8_t>::default_expiration,
cache.get_ttl());
}
TEST(utils_ttl_cache, can_construct_cache_with_ttl) {
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(1000U));
EXPECT_EQ(std::chrono::milliseconds(1000U), cache.get_ttl());
}
TEST(utils_ttl_cache, can_change_ttl) {
utils::ttl_cache<std::uint8_t> cache;
cache.set_ttl(std::chrono::milliseconds(1000U));
EXPECT_EQ(std::chrono::milliseconds(1000U), cache.get_ttl());
}
TEST(utils_ttl_cache, can_set_and_get) {
utils::ttl_cache<std::uint8_t> cache;
cache.set("/test", 21U);
auto data = cache.get("/test");
ASSERT_NE(nullptr, data.get());
EXPECT_EQ(21U, data->load());
}
TEST(utils_ttl_cache, get_returns_nullptr_for_api_path_not_in_cache) {
utils::ttl_cache<std::uint8_t> cache;
auto data = cache.get("/test");
ASSERT_EQ(nullptr, data.get());
}
TEST(utils_ttl_cache, set_and_get_returns_value_and_refreshes_ttl) {
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(1000U));
cache.set("/test", 7U);
auto data = cache.get("/test");
{
EXPECT_TRUE(cache.contains("/test"));
ASSERT_NE(data, nullptr);
EXPECT_EQ(7U, data->load());
}
std::this_thread::sleep_for(std::chrono::milliseconds(200U));
{
EXPECT_TRUE(cache.contains("/test"));
auto data2 = cache.get("/test");
ASSERT_NE(data2, nullptr);
ASSERT_EQ(data.get(), data2.get());
EXPECT_EQ(7U, data2->load());
}
{
std::this_thread::sleep_for(std::chrono::milliseconds(800U));
cache.purge_expired();
auto data3 = cache.get("/test");
EXPECT_TRUE(cache.contains("/test"));
ASSERT_NE(data3, nullptr);
ASSERT_EQ(data.get(), data3.get());
EXPECT_EQ(7U, data3->load());
}
}
TEST(utils_ttl_cache, entry_expires_without_refresh) {
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(50U));
cache.set("/test", 42U);
std::this_thread::sleep_for(std::chrono::milliseconds(51U));
cache.purge_expired();
EXPECT_FALSE(cache.contains("/test"));
auto data = cache.get("/test");
EXPECT_EQ(nullptr, data.get());
}
TEST(utils_ttl_cache, can_erase) {
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(50U));
cache.set("/test", 42U);
cache.erase("/test");
EXPECT_FALSE(cache.contains("/test"));
auto data = cache.get("/test");
EXPECT_EQ(nullptr, data.get());
}
TEST(utils_ttl_cache, can_clear) {
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(50U));
cache.set("/test", 42U);
cache.set("/test2", 42U);
EXPECT_TRUE(cache.contains("/test"));
EXPECT_TRUE(cache.contains("/test2"));
cache.clear();
{
EXPECT_FALSE(cache.contains("/test"));
auto data = cache.get("/test");
EXPECT_EQ(nullptr, data.get());
}
{
EXPECT_FALSE(cache.contains("/test2"));
auto data = cache.get("/test2");
EXPECT_EQ(nullptr, data.get());
}
}
TEST(utils_ttl_cache, can_handle_concurrent_access) {
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(5000U));
std::atomic<bool> start{false};
std::thread writer([&] {
while (not start.load()) {
}
for (std::uint8_t ttl = 0U; ttl < 100U; ++ttl) {
cache.set("/key", ttl);
std::this_thread::yield();
}
});
std::thread reader([&] {
while (not start.load()) {
}
for (std::uint8_t ttl = 0U; ttl < 100U; ++ttl) {
auto data = cache.get("/key");
if (data) {
(void)data->load();
}
std::this_thread::yield();
}
});
start = true;
writer.join();
reader.join();
auto data = cache.get("/key");
ASSERT_NE(data, nullptr);
(void)data->load();
}
TEST(utils_ttl_cache, can_handle_custom_atomic) {
utils::ttl_cache<std::string, utils::atomic> cache(
std::chrono::milliseconds(5000U));
cache.set("/test", "test");
auto data = cache.get("/test");
ASSERT_NE(nullptr, data.get());
EXPECT_STREQ("test", data->load().c_str());
}
TEST(utils_ttl_cache, get_renews_after_ttl_if_purge_expired_is_not_called) {
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(50U));
cache.set("/test", 9U);
std::this_thread::sleep_for(std::chrono::milliseconds(75U));
auto data = cache.get("/test");
ASSERT_NE(nullptr, data.get());
EXPECT_EQ(9U, data->load());
cache.purge_expired();
EXPECT_TRUE(cache.contains("/test"));
}
TEST(utils_ttl_cache, can_update_data) {
utils::ttl_cache<std::uint8_t> cache;
cache.set("/test", 1U);
auto data = cache.get("/test");
ASSERT_NE(nullptr, data.get());
EXPECT_EQ(1U, data->load());
cache.set("/test", 2U);
auto data2 = cache.get("/test");
ASSERT_NE(nullptr, data2.get());
EXPECT_EQ(data.get(), data2.get());
EXPECT_EQ(2U, data2->load());
}
TEST(utils_ttl_cache, purge_expired_removes_only_expired_entries) {
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(1000U));
cache.set("/test1", 1U);
cache.set("/test2", 2U);
std::this_thread::sleep_for(std::chrono::milliseconds(500U));
auto data = cache.get("/test2");
ASSERT_NE(data, nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(501U));
cache.purge_expired();
EXPECT_FALSE(cache.contains("/test1"));
EXPECT_TRUE(cache.contains("/test2"));
}
TEST(utils_ttl_cache, can_handle_non_existing_items_without_failure) {
utils::ttl_cache<std::uint8_t> cache;
cache.set("/exists", 5U);
EXPECT_TRUE(cache.contains("/exists"));
cache.erase("/not_found");
EXPECT_TRUE(cache.contains("/exists"));
auto data = cache.get("/exists");
ASSERT_NE(nullptr, data.get());
EXPECT_EQ(5U, data->load());
}
TEST(utils_ttl_cache, changing_ttl_affects_only_future_expirations) {
utils::ttl_cache<std::uint8_t> cache(std::chrono::milliseconds(1000U));
cache.set("/test", 11U);
cache.set_ttl(std::chrono::milliseconds(100U));
std::this_thread::sleep_for(std::chrono::milliseconds(200U));
cache.purge_expired();
EXPECT_TRUE(cache.contains("/test"));
auto data = cache.get("/test");
ASSERT_NE(nullptr, data.get());
EXPECT_EQ(11U, data->load());
std::this_thread::sleep_for(std::chrono::milliseconds(200U));
cache.purge_expired();
EXPECT_FALSE(cache.contains("/test"));
}
} // namespace repertory