From db5dfcaa85a4c8151700b0df4e387c076f25f255 Mon Sep 17 00:00:00 2001 From: Michele Rodolfi Date: Fri, 11 Dec 2020 12:38:02 +0100 Subject: [PATCH] Initial commit Base project structure --- .gitignore | 1 + CMakeLists.txt | 48 ++++++++++++++++++++++++++++++ CMakeLists.txt.in | 15 ++++++++++ scripts/aes256.sh | 64 ++++++++++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 10 +++++++ src/crypto.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++ src/crypto.h | 19 ++++++++++++ src/main.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++ src/utils.cpp | 41 ++++++++++++++++++++++++++ src/utils.h | 10 +++++++ test/CMakeLists.txt | 7 +++++ test/crypto-test.cpp | 43 +++++++++++++++++++++++++++ test/main.cpp | 6 ++++ test/utils-test.cpp | 45 +++++++++++++++++++++++++++++ 14 files changed, 447 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 CMakeLists.txt.in create mode 100755 scripts/aes256.sh create mode 100644 src/CMakeLists.txt create mode 100644 src/crypto.cpp create mode 100644 src/crypto.h create mode 100644 src/main.cpp create mode 100644 src/utils.cpp create mode 100644 src/utils.h create mode 100644 test/CMakeLists.txt create mode 100644 test/crypto-test.cpp create mode 100644 test/main.cpp create mode 100644 test/utils-test.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6e39d61 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.19) +project(CubbitTestProject) + +set(CMAKE_CXX_STANDARD 20) + +# Download and unpack googletest at configure time +configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) +if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") +endif() +execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) +if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") +endif() + +# Prevent overriding the parent project's compiler/linker +# settings on Windows +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +# Add googletest directly to our build. This defines +# the gtest and gtest_main targets. +add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src + ${CMAKE_CURRENT_BINARY_DIR}/googletest-build + EXCLUDE_FROM_ALL) + +# The gtest/gtest_main targets carry header search path +# dependencies automatically when using CMake 2.8.11 or +# later. Otherwise we have to add them here ourselves. +if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") +endif() + +# Now simply link against gtest or gtest_main as needed. Eg +#add_executable(example example.cpp) +#target_link_libraries(example gtest_main) +#add_test(NAME example_test COMMAND example) + +enable_testing() +include_directories(src) + +add_subdirectory(src) +add_subdirectory(test) +#add_subdirectory(lib/googletest) diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in new file mode 100644 index 0000000..c6247af --- /dev/null +++ b/CMakeLists.txt.in @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/scripts/aes256.sh b/scripts/aes256.sh new file mode 100755 index 0000000..4ad8c28 --- /dev/null +++ b/scripts/aes256.sh @@ -0,0 +1,64 @@ +#!/bin/bash +usage() { + echo "Usage: $0 [-h] [-e | -d] [-b] -k key -i iv" + echo "Reads from stdin and encrypts or decripts to stdout." + echo "key and iv must be provided as arguments." + echo " -h print this summary" + echo " -e encode mode (default)" + echo " -d decode mode" + echo " -k key specify 32 bytes encryption key" + echo " -k iv specify 16 bytes initialization vector" + exit 1 +} + + +#defaults +OPENSSL_ENC=-e + +optstring=":edbk:i:h" +while getopts ${optstring} arg; do + case ${arg} in + e) + #encode + OPENSSL_ENC=-e + ;; + d) + #decode + OPENSSL_ENC=-d + ;; + b) + #base64 + OPENSSL_BASE64=-a + ;; + k) + #encryption key + if [[ "${#OPTARG}" != 32 ]]; then + echo Please insert a 32 bytes key + exit 1 + fi + OPENSSL_KEY=$(echo -n ${OPTARG} | xxd -u -c 32 -ps) + ;; + i) + #initialization vector + if [[ "${#OPTARG}" != 16 ]]; then + echo Please insert a 16 bytes initialization vector + exit 1 + fi + OPENSSL_IV=$(echo -n ${OPTARG} | xxd -u -c 32 -ps) + ;; + h) + usage + ;; + *) + echo Invalid option: -${OPTARG}. + usage + ;; + esac +done + +[[ -z $OPENSSL_KEY ]] && echo Please insert a 32 bytes key && exit 1 +[[ -z $OPENSSL_IV ]] && echo Please insert a 16 bytes initialization vector && exit 1 + +while read line; do + echo -n $line | openssl enc -aes-256-cbc $OPENSSL_ENC $OPENSSL_BASE64 -K $OPENSSL_KEY -iv $OPENSSL_IV +done diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..92d71e3 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,10 @@ +set(SOURCES main.cpp crypto.cpp utils.cpp) + +add_executable(horcrux ${SOURCES}) +add_library(${CMAKE_PROJECT_NAME}_lib STATIC ${SOURCES}) + +find_package(OpenSSL 1.1 REQUIRED) +message("Found OpenSSL Version ${OPENSSL_VERSION}") + +target_link_libraries(horcrux OpenSSL::Crypto) +target_link_libraries(${CMAKE_PROJECT_NAME}_lib OpenSSL::Crypto) diff --git a/src/crypto.cpp b/src/crypto.cpp new file mode 100644 index 0000000..ae8f306 --- /dev/null +++ b/src/crypto.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "crypto.h" + +class EvpCipherCtx { + EVP_CIPHER_CTX *ptr; + +public: + EvpCipherCtx(){ + ptr = EVP_CIPHER_CTX_new(); + } + + ~EvpCipherCtx(){ + EVP_CIPHER_CTX_free(ptr); + } + + EVP_CIPHER_CTX* get(){ + return ptr; + } +}; + + +int AES256::encrypt(const std::vector& key, const std::vector& iv, + const std::vector& input, const size_t input_len, + std::vector& output, size_t& output_len) +{ + //auto ctx = std::unique_ptr(EVP_CIPHER_CTX_new()); + EvpCipherCtx ctx; + if (EVP_EncryptInit(ctx.get(), EVP_aes_256_cbc(), + (key.data()), (iv.data())) == 0){ + throw std::exception(); + } + + int len; + output.reserve(input.size() + block_len); + if (1 != EVP_EncryptUpdate(ctx.get(), + (output.data()), &len, (input.data()), input.size())){ + throw std::exception(); + } + output_len = len; + if (1 != EVP_EncryptFinal_ex(ctx.get(), (output.data()) + len, &len)){ + throw std::exception(); + } + output_len += len; + output.resize(output_len); + + return 0; +} + +int AES256::encrypt(const std::vector& key, const std::vector& iv, + std::istream input, const size_t input_len, + std::ostream output, size_t& output_len){ + + auto inbuf = std::vector(input_len); + auto outbuf = std::vector(input_len + 16); + input.read(reinterpret_cast(inbuf.data()), input_len); + + encrypt(key, iv, inbuf, input_len, outbuf, output_len); + + output.write(reinterpret_cast(outbuf.data()), output_len); + + return 0; +} diff --git a/src/crypto.h b/src/crypto.h new file mode 100644 index 0000000..7b6994b --- /dev/null +++ b/src/crypto.h @@ -0,0 +1,19 @@ +#include +#include +#include + +class AES256 { + + unsigned char key[256]; + unsigned char iv[128]; + const size_t block_len = 16; + +public: + int encrypt(const std::vector& key, const std::vector& iv, + const std::vector& input, const size_t input_len, + std::vector& output, size_t& output_len); + + int encrypt(const std::vector& key, const std::vector& iv, + std::istream input, const size_t input_len, + std::ostream output, size_t& output_len); +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..94553a3 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,69 @@ +#include + +enum RetErr { + kOk = 0, + kUnknownMode, +}; + +enum HorcruxMode { + kCreate, + kLoad +}; + + +struct HorcruxOptions { + HorcruxMode mode; + unsigned int count; + std::string key; + std::string input; + std::string output; +}; + +//RetErr parse_create_options(std::vector& arguments, HorcruxOptions& opt){ +// opt.mode = kCreate; +//} +//RetErr parse_load_options(std::vector& arguments, HorcruxOptions& opt){ +// opt.mode = kLoad; +//} +// +//ret_err parse_options(int argc, const char *args[]){ +// const std::vector arguments; +// for (int i = 1; i < argc, ++i){ +// arguments.push_back(args[i]); +// } +// horcrux_options options; +// if (argc < 1) +// return -EINVAL; +// const std::string mode(args[1]); +// if (mode == "create"){ +// options.horcrux_mode = horcrux_create; +// } else if (mode == "load"){ +// options.horcrux_mode = horcrux_load; +// } else { +// return kUnknownMode; +// } +//} +// +//RetErr horcrux_create(HorcruxOptions& options){ +// +// +//} + + +int main(const int argc, const char *argv[]) { + HorcruxOptions options; +// RetErr ret = parse_arguments(argc, argv); +// switch (options.mode){ +// case kCreate: +// ret = horcrux_create(options); +// break; +// case kLoad: +// ret = horcrux_load(options); +// break; +// default: +// std::cout << "Error: Unknown Mode" << std::endl; +// break; +// } + return 0; +} + diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..66d82fe --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +#include "utils.h" + +std::string utils::to_base64(const std::vector& binary) +{ + using namespace boost::archive::iterators; + using It = base64_from_binary::const_iterator, 6, 8>>; + auto base64 = std::string(It(binary.begin()), It(binary.end())); + // Add padding. + return base64.append((3 - binary.size() % 3) % 3, '='); +} + +std::vector utils::from_base64(const std::string& base64) +{ + using namespace boost::archive::iterators; + using It = transform_width, 8, 6>; + auto binary = std::vector(It(base64.begin()), It(base64.end())); + // Remove padding. + auto length = base64.size(); + if(binary.size() > 2 && base64[length - 1] == '=' && base64[length - 2] == '=') + { + binary.erase(binary.end() - 2, binary.end()); + } + else if(binary.size() > 1 && base64[length - 1] == '=') + { + binary.erase(binary.end() - 1, binary.end()); + } + return binary; +} + +std::vector utils::generate_random(const size_t size){ + std::vector res(size); + if(!RAND_bytes(res.data(), size)){ + throw std::exception(); + } + return res; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..17abed2 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,10 @@ +#include +#include + +namespace utils { + +std::string to_base64(const std::vector& binary); +std::vector from_base64(const std::string& base64); +std::vector generate_random(const size_t buf_len); + +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..3b7474f --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,7 @@ +set(BINARY ${CMAKE_PROJECT_NAME}_test) + +add_executable(${BINARY} main.cpp crypto-test.cpp utils-test.cpp) + +add_test(NAME test COMMAND ${BINARY}) + +target_link_libraries(${BINARY} PUBLIC ${CMAKE_PROJECT_NAME}_lib gtest) diff --git a/test/crypto-test.cpp b/test/crypto-test.cpp new file mode 100644 index 0000000..fd5bb7a --- /dev/null +++ b/test/crypto-test.cpp @@ -0,0 +1,43 @@ +#include +#include "gtest/gtest.h" +#include "utils.h" +#include "crypto.h" + +typedef std::basic_string ustring; + + +std::vector string_to_bytes(const std::string& str){ + std::vector out; + transform(str.begin(), str.end(), std::back_inserter(out), [](auto c){return static_cast(c);}); + return out; +} + + +/* test command: +echo "ditemi perche' se la mucca fa mu il merlo non fa me" | + scripts/aes256.sh -k 0123456789ABCDEF0123456789ABCDEF -i 0123456789ABCDEF | + xxd -i +*/ +const std::string test1_str = "ditemi perche' se la mucca fa mu il merlo non fa me"; +const std::string test1_key = "0123456789ABCDEF0123456789ABCDEF"; +const std::string test1_iv = "0123456789ABCDEF"; +const std::vector test1_enc { + 0x4c, 0x17, 0x6e, 0x6d, 0xd2, 0x83, 0x51, 0x52, 0xfc, 0x5d, 0xbe, 0x0f, + 0x1b, 0xcf, 0x86, 0xef, 0x73, 0x91, 0x58, 0xc4, 0xdd, 0x1b, 0x09, 0x3d, + 0x77, 0xe0, 0x78, 0x5d, 0x21, 0xfe, 0x59, 0x9c, 0xb2, 0x12, 0xa6, 0x81, + 0x12, 0x96, 0x50, 0xd6, 0x5c, 0xe2, 0xc1, 0x99, 0xe3, 0x38, 0x39, 0x8e, + 0x55, 0xd2, 0x04, 0x73, 0x16, 0x39, 0xc7, 0x6a, 0xd3, 0x61, 0x2c, 0x22, + 0x59, 0x25, 0xa6, 0x20 }; + +TEST(encryptTest, test1){ + const std::vector input(test1_str.begin(), test1_str.end()); + size_t output_len = input.size() + 16; + std::vector output(output_len); + const std::vector key(test1_key.begin(), test1_key.end()); + const std::vector iv(test1_iv.begin(), test1_iv.end()); + + AES256 a; + a.encrypt(key, iv, input, input.size(), output, output_len); + + EXPECT_EQ(test1_enc, output); +} diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..72bd115 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,6 @@ +#include "gtest/gtest.h" + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/utils-test.cpp b/test/utils-test.cpp new file mode 100644 index 0000000..7db1df1 --- /dev/null +++ b/test/utils-test.cpp @@ -0,0 +1,45 @@ +#include + +#include "gtest/gtest.h" +#include "utils.h" + +const std::string test1_str = "sei proprio un sandrone"; +const std::string test1_base64 = "c2VpIHByb3ByaW8gdW4gc2FuZHJvbmU="; + +const std::string test2_str = "catopleba io ti dono le mie tepa"; +const std::string test2_base64 = "Y2F0b3BsZWJhIGlvIHRpIGRvbm8gbGUgbWllIHRlcGE="; + +const std::string test3_str = "il pene mi da' si' la moto, ma la moto non da' pene"; +const std::string test3_base64 = "aWwgcGVuZSBtaSBkYScgc2knIGxhIG1vdG8sIG1hIGxhIG1vdG8gbm9uIGRhJyBwZW5l"; + +const std::string test4_str = "mi e' entrata una bruschetta nell'occhio"; +const std::string test4_base64 = "bWkgZScgZW50cmF0YSB1bmEgYnJ1c2NoZXR0YSBuZWxsJ29jY2hpbw=="; + +TEST(utilTests, to_base64Test){ + EXPECT_EQ(test1_base64, utils::to_base64(std::vector(test1_str.begin(), test1_str.end()))); + EXPECT_EQ(test2_base64, utils::to_base64(std::vector(test2_str.begin(), test2_str.end()))); + EXPECT_EQ(test3_base64, utils::to_base64(std::vector(test3_str.begin(), test3_str.end()))); + EXPECT_EQ(test4_base64, utils::to_base64(std::vector(test4_str.begin(), test4_str.end()))); +} + +TEST(utilTests, from_base64Test){ + auto result = utils::from_base64(test1_base64); + EXPECT_EQ(test1_str, std::string(result.begin(), result.end())); + + result = utils::from_base64(test2_base64); + EXPECT_EQ(test2_str, std::string(result.begin(), result.end())); + + result = utils::from_base64(test3_base64); + EXPECT_EQ(test3_str, std::string(result.begin(), result.end())); + + result = utils::from_base64(test4_base64); + EXPECT_EQ(test4_str, std::string(result.begin(), result.end())); +} + +TEST(utilTests, generate_random){ + EXPECT_NE(utils::generate_random(32), utils::generate_random(32)); + EXPECT_NE(utils::generate_random(16), utils::generate_random(16)); + EXPECT_NE(utils::generate_random(8), utils::generate_random(8)); + EXPECT_NE(utils::generate_random(1), utils::generate_random(1)); + EXPECT_EQ(utils::generate_random(0), utils::generate_random(0)); +}