diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 92d71e3..aa42c45 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -set(SOURCES main.cpp crypto.cpp utils.cpp) +set(SOURCES main.cpp crypto.cpp io.cpp utils.cpp) add_executable(horcrux ${SOURCES}) add_library(${CMAKE_PROJECT_NAME}_lib STATIC ${SOURCES}) diff --git a/src/io.cpp b/src/io.cpp new file mode 100644 index 0000000..2792955 --- /dev/null +++ b/src/io.cpp @@ -0,0 +1,97 @@ +#include +#include "io.h" + +namespace horcrux { +namespace fs = std::filesystem; + +FsPlainInput::FsPlainInput(const std::string& path) : file_path(path) +{ + if (fs::status(path).type() != fs::file_type::regular){ + throw std::invalid_argument("Input is not a regular file"); + } + file_size = fs::file_size(file_path); + if (file_size == 0){ + throw std::invalid_argument("Input is empty, nothing to encrypt"); + } + file_stream = std::ifstream(file_path, std::ios::binary); +} + +std::vector FsPlainInput::read(){ + std::vector result(file_size); + file_stream.read(reinterpret_cast(result.data()), file_size); + return result; +} + +FsCryptoInput::FsCryptoInput(const std::vector& filenames) +{ + if (filenames.empty()){ + throw std::invalid_argument("No files to decrypt"); + } + for (auto name : filenames){ + auto path = fs::path{name}; + if (fs::status(path).type() != fs::file_type::regular){ + throw std::invalid_argument("Input is not a regular file"); + } + file_paths.emplace_back(path, fs::file_size(path)); + total_size += file_paths.back().second; + } + if (total_size == 0){ + throw std::invalid_argument("No data to decrypt"); + } +} + +std::vector FsCryptoInput::read(){ + std::vector result(total_size); + size_t data_read{0}; + for (auto& f : file_paths){ + std::ifstream ifstream(f.first); + ifstream.read(reinterpret_cast(result.data()) + data_read, f.second); + data_read += f.second; + } + return result; +} + +FsPlainOutput::FsPlainOutput(const std::string& filename) + : file_path(filename) +{ + if (fs::exists(file_path) && fs::status(file_path).type() != fs::file_type::regular){ + throw std::invalid_argument("Output file is not a regular file"); + } +} +size_t FsPlainOutput::write(const std::vector& to_write){ + std::ofstream out(file_path, std::ios::binary); + out.write(reinterpret_cast(to_write.data()), to_write.size()); + return to_write.size(); +} + +FsCryptoOutput::FsCryptoOutput(const std::string& folder, const int horcrux_num, const size_t horcrux_size = 0, + const std::string& filename = "horcrux") + : folder_path(folder), base_name(filename), + num(horcrux_num), size(horcrux_size) +{ + if (fs::status(folder_path).type() != fs::file_type::directory){ + throw std::invalid_argument("Output is not a directory"); + } + + if (horcrux_num <= 0){ + throw std::invalid_argument("Invalid horcrux num"); + } +} + +size_t FsCryptoOutput::write(const std::vector& to_write){ + size = to_write.size() / num; + std::ofstream f; + created_files.clear(); + size_t data_written{0}; + for (int i = 0; i < num; ++i){ + std::string name = base_name + '_' + std::to_string(i) + ".enc"; + created_files.emplace_back(folder_path / name); + f = std::ofstream(created_files.back(), + std::ios::binary | std::ios::trunc); + auto chunk_size = i + 1 != num ? size : size + to_write.size() % size; + f.write(reinterpret_cast(to_write.data()) + data_written, chunk_size); + data_written += chunk_size; + } + return data_written; +} +}; diff --git a/src/io.h b/src/io.h new file mode 100644 index 0000000..e92ea35 --- /dev/null +++ b/src/io.h @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +namespace horcrux { + +class FsPlainInput { + std::filesystem::path file_path; + size_t file_size; + std::ifstream file_stream; + +public: + FsPlainInput(const std::string& path); + std::vector read(); +}; + +class FsCryptoInput { + std::vector> file_paths; + size_t total_size{0}; + +public: + FsCryptoInput(const std::vector& filenames); + std::vector read(); +}; + +class FsPlainOutput { + std::filesystem::path file_path; + +public: + FsPlainOutput(const std::string& filename); + size_t write(const std::vector& to_write); +}; + +class FsCryptoOutput { + std::filesystem::path folder_path; + std::string base_name; + int num; + size_t size; + +public: + std::vector created_files; + + FsCryptoOutput(const std::string& folder, const int horcrux_num, const size_t horcrux_size, const std::string& filename); + size_t write(const std::vector& to_write); +}; +}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3b7474f..b954fe4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,11 @@ set(BINARY ${CMAKE_PROJECT_NAME}_test) -add_executable(${BINARY} main.cpp crypto-test.cpp utils-test.cpp) +add_executable(${BINARY} main.cpp crypto-test.cpp io-test.cpp utils-test.cpp) add_test(NAME test COMMAND ${BINARY}) target_link_libraries(${BINARY} PUBLIC ${CMAKE_PROJECT_NAME}_lib gtest) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/test_work_dir + DESTINATION ${CMAKE_BINARY_DIR}) +add_definitions(-DTEST_WORK_DIR="${CMAKE_BINARY_DIR}/test_work_dir") diff --git a/test/io-test.cpp b/test/io-test.cpp new file mode 100644 index 0000000..1d20bf0 --- /dev/null +++ b/test/io-test.cpp @@ -0,0 +1,126 @@ +#include "gtest/gtest.h" +#include "io.cpp" + +#ifndef TEST_WORK_DIR +#error Please define TEST_WORK_DIR +#endif + +const std::string folder{TEST_WORK_DIR}; +const std::string noexist{TEST_WORK_DIR "/nope"}; +const std::string empty{TEST_WORK_DIR "/empty"}; +const std::string text{TEST_WORK_DIR "/test.txt"}; +const std::string image{TEST_WORK_DIR "/mangoni.jpg"}; + +static std::vector generic_read_file(const std::string& filename){ + auto ifstream = std::ifstream{filename, std::ios::binary}; + auto path = std::filesystem::path{filename}; + auto size = std::filesystem::file_size(path); + std::vector buf(size); + ifstream.read(reinterpret_cast(buf.data()), size); + return buf; +} + +TEST(IoTests, FsPlainInput) { + EXPECT_THROW(horcrux::FsPlainInput input{noexist}, std::invalid_argument); + EXPECT_THROW(horcrux::FsPlainInput input{folder}, std::invalid_argument); + EXPECT_THROW(horcrux::FsPlainInput input{empty}, std::invalid_argument); + EXPECT_NO_THROW(horcrux::FsPlainInput input{text}); + EXPECT_NO_THROW(horcrux::FsPlainInput input{image}); +} + +TEST(IoTests, FsPlainInputReadText) { + auto input = horcrux::FsPlainInput{text}; + auto buf = generic_read_file(text); + EXPECT_EQ(buf, input.read()); +} + +TEST(IoTests, FsPlainInputReadImg) { + auto input = horcrux::FsPlainInput{image}; + auto buf = generic_read_file(image); + EXPECT_EQ(buf, input.read()); +} + +TEST(IoTests, FsCryptoOutput) { + EXPECT_THROW(horcrux::FsCryptoOutput output(noexist, 1), std::invalid_argument); + EXPECT_NO_THROW(horcrux::FsCryptoOutput output(folder, 1)); + EXPECT_THROW(horcrux::FsCryptoOutput output(folder, 0), std::invalid_argument); + EXPECT_THROW(horcrux::FsCryptoOutput output(empty, 1), std::invalid_argument); + EXPECT_THROW(horcrux::FsCryptoOutput output(text, 1), std::invalid_argument); + EXPECT_THROW(horcrux::FsCryptoOutput output(image, 1), std::invalid_argument); +} + +TEST(IoTests, FsCryptoOutputWriteImage){ + auto ifstream = std::ifstream{image, std::ios::binary}; + auto buf = generic_read_file(image); + + horcrux::FsCryptoOutput out(folder, 10); + auto written = out.write(buf); + EXPECT_EQ(written, buf.size()); + + EXPECT_EQ(10, out.created_files.size()); + for_each(out.created_files.begin(), out.created_files.end(), + [](auto f){std::filesystem::remove(f);}); +} + +TEST(IoTests, FsCryptoOutputWriteText){ + auto buf = generic_read_file(text); + + horcrux::FsCryptoOutput out(folder, 2); + auto written = out.write(buf); + EXPECT_EQ(written, buf.size()); + + EXPECT_EQ(2, out.created_files.size()); + for_each(out.created_files.begin(), out.created_files.end(), + [](auto f){std::filesystem::remove(f);}); +} + +TEST(IoTest, FsCryptoInput){ + EXPECT_THROW(horcrux::FsCryptoInput output({noexist, text, image}), std::invalid_argument); + EXPECT_THROW(horcrux::FsCryptoInput output({folder, text, image}), std::invalid_argument); + EXPECT_THROW(horcrux::FsCryptoInput output({}), std::invalid_argument); + EXPECT_THROW(horcrux::FsCryptoInput output({empty}), std::invalid_argument); + EXPECT_THROW(horcrux::FsCryptoInput output({empty, empty}), std::invalid_argument); + EXPECT_NO_THROW(horcrux::FsCryptoInput output({empty, empty, text})); + EXPECT_NO_THROW(horcrux::FsCryptoInput output({image, empty, text})); +} + +TEST(IoTest, FsCryptoInputImage){ + auto buf = generic_read_file(image); + //split image using FsCryptoOutput + auto out = horcrux::FsCryptoOutput(folder, 7); + auto written = out.write(buf); + EXPECT_EQ(written, buf.size()); + + std::vector files; + transform(out.created_files.begin(), out.created_files.end(),std::back_inserter(files), [](auto p){return p.string();}); + auto in = horcrux::FsCryptoInput(files); + auto read = in.read(); + EXPECT_EQ(read, buf); + + files.push_back(text); + in = horcrux::FsCryptoInput(files); + read = in.read(); + EXPECT_NE(read, buf); + + for_each(out.created_files.begin(), out.created_files.end(), + [](auto f){std::filesystem::remove(f);}); +} + +TEST(IoTest, FsPlainOuput){ + + EXPECT_NO_THROW(horcrux::FsPlainOutput output{noexist}); + EXPECT_THROW(horcrux::FsPlainOutput output{folder}, std::invalid_argument); + EXPECT_NO_THROW(horcrux::FsPlainOutput output{empty}); + EXPECT_NO_THROW(horcrux::FsPlainOutput output{text}); + EXPECT_NO_THROW(horcrux::FsPlainOutput output{image}); +} + +TEST(IoTest, FsPlainOutputWrite){ + auto buf = generic_read_file(image); + auto out = horcrux::FsPlainOutput(noexist); + auto written = out.write(buf); + EXPECT_EQ(written, buf.size()); + auto buf2 = generic_read_file(noexist); + EXPECT_EQ(buf, buf2); + std::filesystem::remove(noexist); +} diff --git a/test/test_work_dir/empty b/test/test_work_dir/empty new file mode 100644 index 0000000..e69de29 diff --git a/test/test_work_dir/mangoni.jpg b/test/test_work_dir/mangoni.jpg new file mode 100644 index 0000000..5aebcec Binary files /dev/null and b/test/test_work_dir/mangoni.jpg differ diff --git a/test/test_work_dir/test.txt b/test/test_work_dir/test.txt new file mode 100644 index 0000000..57528e3 --- /dev/null +++ b/test/test_work_dir/test.txt @@ -0,0 +1,28 @@ +What's your name Catalogna +What's your name Catalogna +What's your name Catalogna +What's your name Catalogna +Cara non so perché ma quando io ti vedo mi vien da vomitare +Forse sara' perché tu mangi troppa catalogna, catalogna +Catalogna catalogna cata cata cata cata catalogna +Catalogna catalogna e nessun altro tipo di verdura cotta. +Sono andato in un centro di allevamento di cinghiali in Lunigiana +E ti ho comprato un cinghialino, l'ho arrostito e l'ho riempito +Con un tacchino di terra cotta ripieno di un piccione vivo con il verme +In bocca, ho arrostito tutto insieme te l'ho portato, oh, non l'hai +Mangiato eri immersa in una vasca di catalogna. Non si fa cosi'! +Catalogna catalogna cata cata cata cata catalogna +Catalogna catalogna cata cata cata cata catalogna +Non puoi mangiare solo catalogna +E' una dieta costituita da un elemento solo +Non mi puoi far credere che questo sia sufficiente +Alla tua corretta crescita, perdi i capelli, +Non puoi vivere mangiando solo catalogna, +Non mi puoi convincere di questo fatto. +Ehi... ma... no... amore ma... solo ora invece capisco +Che la catalogna effettivamente ci permette di condurre +Una vita psichicamente corretta e anche sessualmente +Fantastica! Si', ho capito, infilami la catalogna nel culo, +Voglio fare all'amore con te, infilamela nei capezzoli, +Voglio godere tantissimo!... +Siamo cresciuti mangiando verdura voglio morire con te