Skip to content

Commit

Permalink
wip: implement encryption for logfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolaDucak committed Aug 23, 2023
1 parent eec51a1 commit 0333a67
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 18 deletions.
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

include(FetchContent)

# --------------------------------- ftxui ------------------------------- #
# -------------------------- Fetech ftxui ------------------------------- #

set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
FetchContent_Declare(ftxui
Expand All @@ -31,6 +31,11 @@ if(NOT Boost_FOUND)
message(FATAL_ERROR "Boost Not found")
endif()

# ------------------------------- OpenSSL -------------------------------- #

set(OPENSSL_USE_STATIC_LIBS TRUE)
find_package(OpenSSL REQUIRED)

# ------------------------------- Fetch FMT ------------------------------ #

FetchContent_Declare(fmt
Expand Down
4 changes: 4 additions & 0 deletions source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ add_executable(
./date/date.cpp
./date/date.hpp

./utils/crypto.cpp
./utils/crypto.hpp
./utils/string.hpp
./utils/map.hpp
)
Expand All @@ -42,6 +44,8 @@ target_link_libraries(clog
ftxui::component
fmt::fmt
Boost::program_options
OpenSSL::Crypto
OpenSSL::SSL
)

install(TARGETS clog DESTINATION "bin")
8 changes: 6 additions & 2 deletions source/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Config applyCommandlineOverrides(const Config &config, const ArgParser &commandL
.sundayStart = commandLineArgs.has("--sunday-start") ? true : config.sundayStart,
.ignoreFirstLineWhenParsingSections = commandLineArgs.has("--first-line-section")
? true
: config.ignoreFirstLineWhenParsingSections};
: config.ignoreFirstLineWhenParsingSections,
.password = commandLineArgs.getIfHas("--password").value_or(config.password)};
}

Config applyConfigFileOverrides(const Config &config, std::istream &istream) {
Expand All @@ -39,8 +40,10 @@ Config applyConfigFileOverrides(const Config &config, std::istream &istream) {
.value_or(config.logFilenameFormat),
.sundayStart = ptree.get_optional<bool>("sunday-start").value_or(config.sundayStart),
.ignoreFirstLineWhenParsingSections =
ptree.get_optional<bool>("--first-line-section")
ptree.get_optional<bool>("first-line-section")
.value_or(config.ignoreFirstLineWhenParsingSections),
.password =
ptree.get_optional<std::string>("password").value_or(config.password),
};
}

Expand All @@ -57,6 +60,7 @@ Config Config::make(const FileReader &fileReader, const ArgParser &cmdLineArgs)
.sundayStart = Config::DEFAULT_SATURDAY_START,
.ignoreFirstLineWhenParsingSections =
Config::DEFAULT_IGNORE_FIRST_LINE_WHEN_PARSING_SECTIONS,
.password = "",
};

auto config = configFile ? applyConfigFileOverrides(defaultConfig, *configFile) : defaultConfig;
Expand Down
1 change: 1 addition & 0 deletions source/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct Config {
const std::string logFilenameFormat;
const bool sundayStart;
const bool ignoreFirstLineWhenParsingSections;
const std::string password;
// TODO: const bool useOldTaskSyntaxAsTags;
// TODO: colors?

Expand Down
60 changes: 55 additions & 5 deletions source/main.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <optional>
#include <sstream>
#include <string>

#include "app.hpp"
Expand All @@ -12,32 +14,80 @@
#include "editor/disabled_editor.hpp"
#include "editor/env_based_editor.hpp"
#include "model/local_log_repository.hpp"
#include "utils/crypto.hpp"

#include <filesystem>

auto makeClog(const clog::Config &conf) {
using namespace clog;

auto pathProvider = model::LocalFSLogFilePathProvider{conf.logDirPath, conf.logFilenameFormat};
auto repo = std::make_shared<model::LocalLogRepository>(pathProvider);
auto repo = std::make_shared<model::LocalLogRepository>(pathProvider, conf.password);
auto view = std::make_shared<view::YearView>(date::Date::getToday(), conf.sundayStart);
auto editor = std::make_shared<editor::EnvBasedEditor>(pathProvider);
return clog::App{std::move(view), std::move(repo), std::move(editor),
conf.ignoreFirstLineWhenParsingSections};
}


enum class Crypto{
Encryption, Dectryption
};

void applyCryptograpyToLogFiles(const clog::Config &conf, Crypto c) {
auto dir = std::filesystem::directory_iterator{conf.logDirPath};
auto logsProcessed = 0u;

for (const auto& entry : std::filesystem::directory_iterator{conf.logDirPath}) {
std::tm tm;
std::istringstream iss{entry.path().filename()};
const auto s = std::get_time(&tm, conf.logFilenameFormat.c_str());

if (entry.is_regular_file() && !iss.fail()) {
std::string fileContentsAfterCrypto;

{
std::ifstream ifs{entry};
if (c == Crypto::Encryption) {
fileContentsAfterCrypto = clog::utils::encryptFile(conf.password, ifs);
} else {
fileContentsAfterCrypto = clog::utils::decryptFile(conf.password, ifs);
}
}

std::ofstream{entry} << fileContentsAfterCrypto;
logsProcessed++;
}
}
std::cout << "Finished! "
<< ((c == Crypto::Encryption) ? "Encrypted " :"Decrypted ")
<< logsProcessed
<< " log files in: " << conf.logDirPath << "." << std::endl;
}

int main(int argc, const char **argv) try {
const auto commandLineArgs = clog::ArgParser{argc, argv};


if (commandLineArgs.has("-h", "--help")) {
std::cout << clog::helpString() << std::endl;
return 0;
}

const auto config = clog::Config::make(
[](const auto &path) { return std::make_unique<std::ifstream>(path); },
commandLineArgs);

if (commandLineArgs.has("--encrypt")) {
applyCryptograpyToLogFiles(config, Crypto::Encryption);
} else if (commandLineArgs.has("--decrypt")) {
applyCryptograpyToLogFiles(config, Crypto::Dectryption);
} else {
auto config = clog::Config::make(
[](const auto &path) { return std::make_unique<std::ifstream>(path); },
commandLineArgs);
makeClog(config).run();
}

return 0;
} catch (std::exception &e) {
std::cout << "clog encountered an error: \n " << e.what() << std::endl;
std::cerr << "clog encountered an error: \n " << e.what() << std::endl;
return 1;
}
36 changes: 27 additions & 9 deletions source/model/local_log_repository.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,51 @@
#include "utils/string.hpp"
#include <fstream>
#include <iostream>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <regex>
#include <sstream>
#include <utility>
#include <utils/crypto.hpp>

namespace clog::model {

using namespace date;

LocalLogRepository::LocalLogRepository(LocalFSLogFilePathProvider pathProvider)
: m_pathProvider(std::move(pathProvider)) {

LocalLogRepository::LocalLogRepository(LocalFSLogFilePathProvider pathProvider, std::string password)
: m_pathProvider(std::move(pathProvider)), m_password{std::move(password)} {
std::filesystem::create_directories(m_pathProvider.getLogDirPath());
}

std::optional<LogFile> LocalLogRepository::read(const Date &date) const {
if (std::ifstream t{m_pathProvider.path(date)}; t.is_open()) {
std::stringstream buffer;
buffer << t.rdbuf();
return LogFile{date, std::string{buffer.str()}};
std::ifstream t{m_pathProvider.path(date)};

if (not t.is_open()) {
return std::nullopt;
}

if (not m_password.empty()) {
return LogFile{date, utils::decryptFile(m_password, t)};
}
return {};

std::stringstream buffer;
buffer << t.rdbuf();
return LogFile{date, std::string{buffer.str()}};
}

void LocalLogRepository::write(const LogFile &log) {
std::ofstream{m_pathProvider.path(log.getDate())} << log.getContent();
if (not m_password.empty()) {
std::istringstream iss{log.getContent()};
std::ofstream{m_pathProvider.path(log.getDate())} << utils::encryptFile(m_password, iss);
} else {
std::ofstream{m_pathProvider.path(log.getDate())} << log.getContent();
}
}

void LocalLogRepository::remove(const Date &date) {
(void)std::remove(m_pathProvider.path(date).c_str());
std::ignore = std::remove(m_pathProvider.path(date).c_str());
}


} // namespace clog::model
3 changes: 2 additions & 1 deletion source/model/local_log_repository.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ class LocalLogRepository : public LogRepositoryBase {
* @param logFilenameFormat Format used to generate names for log entries,
* that format is used with Date::formatToString that.
*/
LocalLogRepository(LocalFSLogFilePathProvider pathProvider);
LocalLogRepository(LocalFSLogFilePathProvider pathProvider, std::string password = "");

std::optional<LogFile> read(const date::Date &date) const override;
void remove(const date::Date &date) override;
void write(const LogFile &log) override;

private:
LocalFSLogFilePathProvider m_pathProvider;
std::string m_password;
};

} // namespace clog::model
109 changes: 109 additions & 0 deletions source/utils/crypto.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "crypto.hpp"

#include <openssl/evp.h>
#include <openssl/rand.h>

namespace clog::utils {

std::string encryptFile(const std::string& password, std::istream& file) {
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
throw std::runtime_error{"Encryption failed!"};
}

const EVP_CIPHER* cipher = EVP_aes_128_cfb(); // You can change the cipher if needed
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];

if (EVP_BytesToKey(cipher, EVP_md5(), NULL, reinterpret_cast<const unsigned char*>(password.c_str()), password.size(), 1, key, iv) != 16) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error{"Encryption failed!"};
}

if (EVP_EncryptInit_ex(ctx, cipher, nullptr, key, iv) != 1) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error{"Encryption failed!"};
}

unsigned char inBuffer[1024];
unsigned char outBuffer[1024 + EVP_MAX_BLOCK_LENGTH];
int bytesRead = 0;
int outLength = 0;

std::string output;

while (file.good()) {
file.read(reinterpret_cast<char*>(inBuffer), sizeof(inBuffer));
bytesRead = static_cast<int>(file.gcount());

if (EVP_EncryptUpdate(ctx, outBuffer, &outLength, inBuffer, bytesRead) != 1) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error{"Encryption failed!"};
}

output += std::string{outBuffer, outBuffer + outLength};
}

if (EVP_EncryptFinal_ex(ctx, outBuffer, &outLength) != 1) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error{"Encryption failed!"};
}
output += std::string{outBuffer, outBuffer + outLength};
EVP_CIPHER_CTX_free(ctx);

return output;
}


std::string decryptFile(const std::string& password, std::istream& file) {
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error{"Encryption failed!"};
}

const EVP_CIPHER* cipher = EVP_aes_128_cfb();
unsigned char key[EVP_MAX_KEY_LENGTH];
unsigned char iv[EVP_MAX_IV_LENGTH];

if (EVP_BytesToKey(cipher, EVP_md5(), NULL, reinterpret_cast<const unsigned char*>(password.c_str()), password.size(), 1, key, iv) != 16) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error{"Encryption failed!"};
}

if (EVP_DecryptInit_ex(ctx, cipher, nullptr, key, iv) != 1) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error{"Encryption failed!"};
}

unsigned char inBuffer[1024 + EVP_MAX_BLOCK_LENGTH];
unsigned char outBuffer[1024];
int bytesRead = 0;
int outLength = 0;
std::string output;

while (file.good()) {
file.read(reinterpret_cast<char*>(inBuffer), sizeof(inBuffer));
bytesRead = static_cast<int>(file.gcount());

if (EVP_DecryptUpdate(ctx, outBuffer, &outLength, inBuffer, bytesRead) != 1) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error{"Encryption failed!"};
}

output += std::string{outBuffer, outBuffer + outLength};
}

if (EVP_DecryptFinal_ex(ctx, outBuffer, &outLength) != 1) {
EVP_CIPHER_CTX_free(ctx);
throw std::runtime_error{"Encryption failed!"};
}

output += std::string{outBuffer, outBuffer + outLength};

EVP_CIPHER_CTX_free(ctx);

return output;
}
}

12 changes: 12 additions & 0 deletions source/utils/crypto.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <string>
#include <istream>

namespace clog::utils {

std::string encryptFile(const std::string& password, std::istream& file);
std::string decryptFile(const std::string& password, std::istream& file);

}

3 changes: 3 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ add_executable(
./../source/model/log_file.cpp
./../source/model/local_log_repository.cpp
./../source/model/year_overview_data.cpp
./../source/utils/crypto.cpp
)

target_link_libraries(
Expand All @@ -42,6 +43,8 @@ target_link_libraries(
ftxui::component
fmt::fmt
Boost::program_options
OpenSSL::Crypto
OpenSSL::SSL
)

target_include_directories(
Expand Down
Loading

0 comments on commit 0333a67

Please sign in to comment.