diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index ab7244a97..5f51e0c53 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -4,12 +4,12 @@ cmake_minimum_required (VERSION 3.17.0) # we want to place them in the binary folder in /proto/common add_subdirectory(proto/common) -if( BUILD_COMMON ) +if( BUILD_COMMON ) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/include/common/Version.hpp.in" "${CMAKE_CURRENT_BINARY_DIR}/include/common/Version.hpp" @ONLY) - file( GLOB Sources "source/*.cpp" + file( GLOB Sources "source/*.cpp" "source/credentials/*cpp" "source/communicators/*.cpp" "source/messages/*.cpp" @@ -19,12 +19,11 @@ if( BUILD_COMMON ) "source/sockets/*.cpp") if(BUILD_SHARED_LIBS) add_library( common SHARED ${Sources}) - target_link_libraries( common PUBLIC ${DATAFED_BOOST_DATE_TIME_LIBRARY_PATH} protobuf::libprotobuf libzmq datafed-protobuf) + target_link_libraries( common PUBLIC ${DATAFED_BOOST_DATE_TIME_LIBRARY_PATH} protobuf::libprotobuf libzmq datafed-protobuf ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}) else() add_library( common STATIC ${Sources}) - target_link_libraries( common PUBLIC ${DATAFED_BOOST_DATE_TIME_LIBRARY_PATH} protobuf::libprotobuf libzmq-static datafed-protobuf) + target_link_libraries( common PUBLIC ${DATAFED_BOOST_DATE_TIME_LIBRARY_PATH} protobuf::libprotobuf libzmq-static datafed-protobuf ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}) endif() - set_target_properties(common PROPERTIES POSITION_INDEPENDENT_CODE ON SOVERSION ${DATAFED_COMMON_LIB_MAJOR} VERSION ${DATAFED_COMMON_LIB_MAJOR}.${DATAFED_COMMON_LIB_MINOR}.${DATAFED_COMMON_LIB_PATCH} ) target_include_directories( common PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ) target_include_directories( common PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ) diff --git a/common/include/common/CipherEngine.hpp b/common/include/common/CipherEngine.hpp new file mode 100644 index 000000000..57ff8b136 --- /dev/null +++ b/common/include/common/CipherEngine.hpp @@ -0,0 +1,164 @@ +#ifndef CIPHER_ENGINE_HPP +#define CIPHER_ENGINE_HPP +#pragma once + +//Local public includes +#include "DynaLog.hpp" +#include "libjson.hpp" + +// Standard includes +#include +#include + +namespace SDMS{ + +/** + * @class CipherEngine + * @brief Provides symmetric encryption and decryption functionalities using a 256-bit key. + */ +class CipherEngine +{ + protected: + /** + * @brief Base64 encodes an input of a specified length and returns the output + * @param input Pointer to input bytes to encode + * @param length Number of bytes in input + * @param log_context Context for logging + * @return Base64 encoded null-terminated char array + */ + std::unique_ptr encode64(const unsigned char* input, const int length, LogContext log_context) const; + + /** + * @brief Base64 decodes an input of a specified length and returns the output + * @param input Pointer to Base64 encoded input chars + * @param length Number of bytes in input + * @param log_context Context for logging + * @return Decoded bytes + */ + std::unique_ptr decode64(const char* input, const int length, LogContext log_context) const; + + public: + /** Base64 encoded block size (always 4) */ + static const int BASE64_ENCODED_BLOCK_SIZE = 4; + + /** Base64 input block size (always 3) */ + static const int BASE64_INPUT_BLOCK_SIZE = 3; + + /** Null terminator size */ + static const int NULL_TERMINATOR_SIZE = 1; + + /** Initialization vector length in bytes */ + static const int IV_LENGTH = 16; + + /** Encryption key length in bytes (256-bit) */ + static const int KEY_LENGTH = 32; + + /** Maximum message length in bytes */ + static const int MAX_MSG_LENGTH = 96; + + /** Base64 encoded IV length */ + static const int ENCODED_IV_LENGTH = 24; + + /** Base64 encoded encrypted message length */ + static const int ENCODED_MSG_LENGTH = 128; + + /** + * @brief Generates a random encryption key. + * @param token_key Buffer to store the generated key (must be KEY_LENGTH bytes). + */ + static void generateEncryptionKey(unsigned char token_key[KEY_LENGTH]); + + /** + * @brief Generates a random initialization vector (IV). + * @param iv Buffer to store the generated IV (must be IV_LENGTH bytes). + */ + static void generateIV(unsigned char iv[IV_LENGTH]); + + /** + * @brief Constructs the CipherEngine with the specified key. + * @param inputKey Pointer to encryption key bytes (KEY_LENGTH bytes). + */ + explicit CipherEngine(const unsigned char* inputKey); + + /** + * @brief Constructs the empty CipherEngine. + */ + CipherEngine(){}; + + + /** + * @struct CipherBytes + * @brief Holds encrypted message bytes, IV, and message length. + */ + struct CipherBytes + { + unsigned char encrypted_msg[ENCODED_MSG_LENGTH]; /**< Encrypted message bytes */ + unsigned char iv[IV_LENGTH]; /**< Initialization vector bytes */ + int encrypted_msg_len; /**< Length of encrypted message */ + }; + + /** + * @struct CipherString + * @brief Holds Base64 encoded encrypted message, IV, and message length. + */ + struct CipherString + { + std::unique_ptr encrypted_msg; /**< Base64 encoded encrypted message */ + std::unique_ptr iv; /**< Base64 encoded IV */ + int encrypted_msg_len; /**< Length of encrypted message */ + }; + + /** + * @brief Encrypts the message using the given IV. + * @param iv Initialization vector to use for encryption. + * @param msg Plaintext message to encrypt. + * @param log_context Context for logging. + * @return CipherBytes struct with raw encrypted bytes. + */ + CipherBytes encryptAlgorithm(unsigned char* iv, const std::string& msg, LogContext log_context); + + /** + * @brief Encodes CipherBytes into Base64 strings. + * @param unencoded_bytes CipherBytes to encode. + * @param log_context Context for logging. + * @return CipherString with Base64 encoded encrypted message and IV. + */ + CipherString encodeBytes(CipherBytes unencoded_bytes, LogContext log_context); + + /** + * @brief Encrypts a message using the specified IV and returns Base64 encoded output. + * @param iv Initialization vector to use. + * @param msg Plaintext message. + * @param log_context Context for logging. + * @return CipherString with Base64 encoded encrypted data. + */ + CipherString encrypt(unsigned char *iv, const std::string& msg, LogContext log_context); + + /** + * @brief Encrypts a message, generating a random IV automatically. + * @param msg Plaintext message. + * @param log_context Context for logging. + * @return CipherString with Base64 encoded encrypted data. + */ + CipherString encrypt(const std::string& msg, LogContext log_context); + + /** + * @brief Decrypts a Base64 encoded encrypted string. + * @param encrypted_string CipherString with encrypted message and IV. + * @param log_context Context for logging. + * @return Decrypted plaintext string. + */ + std::string decrypt(const CipherString& encrypted_string, LogContext log_context); + + static bool tokenNeedsUpdate(const libjson::Value::Object &obj); + private: + unsigned char key[KEY_LENGTH]; /**< Encryption key bytes */ + + /** + * @brief Handles errors during encryption/decryption. + */ + static void handleErrors(void); + +}; +} // namespace SDMS +#endif diff --git a/common/include/common/Util.hpp b/common/include/common/Util.hpp index a309892cd..6c4dd4f11 100644 --- a/common/include/common/Util.hpp +++ b/common/include/common/Util.hpp @@ -21,11 +21,6 @@ void hexDump(const char *a_buffer, const char *a_buffer_end, std::string escapeCSV(const std::string &a_value); std::string escapeJSON(const std::string &a_value); bool to_uint32(const char *a_str, uint32_t &a_out); - -// std::vector smartTokenize( const std::string & a_text, const -// std::string & a_delim ); - -// std::string parseQuery( const std::string & a_query, bool & use_client, bool -// & use_shared_users, bool & use_shared_projects ); +void readFile(const std::string &fileName,const int arraySize, unsigned char* array); #endif diff --git a/common/source/CipherEngine.cpp b/common/source/CipherEngine.cpp new file mode 100644 index 000000000..ae65b5200 --- /dev/null +++ b/common/source/CipherEngine.cpp @@ -0,0 +1,264 @@ +//Local Include +#include "common/CipherEngine.hpp" + +// Local Public includes +#include "common/TraceException.hpp" +#include "common/DynaLog.hpp" +#include "common/libjson.hpp" + +// Third party includes +#include +#include +#include +#include + +// Standard includes +#include +#include +#include +#include +#include +#include +#include +#include +using namespace libjson; +namespace SDMS{ + + void CipherEngine::handleErrors(void) + { + unsigned long err = ERR_get_error(); + char err_buf[256]; + ERR_error_string_n(err, err_buf, sizeof(err_buf)); + throw TraceException(__FILE__, __LINE__, 0, std::string("OpenSSL Error: ") + err_buf); + } + + std::unique_ptr CipherEngine::encode64(const unsigned char* input,const int length, LogContext log_context) const + { + // Calculate the padded length based on the input length: + // (length + 2) / 3 gives the number of 3-byte blocks (rounded up), multiplied by 4 gives the number of base64 characters required. + // Allocate memory for the output buffer; std::make_unique will throw std::bad_alloc if allocation fails. + const int paddedLength = SDMS::CipherEngine::BASE64_ENCODED_BLOCK_SIZE*((length + (SDMS::CipherEngine::BASE64_INPUT_BLOCK_SIZE-1))/SDMS::CipherEngine::BASE64_INPUT_BLOCK_SIZE); + auto output = std::make_unique(paddedLength+SDMS::CipherEngine::NULL_TERMINATOR_SIZE); + std::fill_n(output.get(), paddedLength + SDMS::CipherEngine::NULL_TERMINATOR_SIZE, 0); // manually zero-initialize + const int outputLength = EVP_EncodeBlock(reinterpret_cast(output.get()), input, length); + if (paddedLength != outputLength) + { + std::ostringstream oss; + oss << "Output Length (" << outputLength + << ") and Predicted Padded Length (" << paddedLength + << ") of encoded bytes not equal!"; + + DL_ERROR(log_context, oss.str()); + EXCEPT_PARAM(1, oss.str()); + } + return output; + } + + std::unique_ptr CipherEngine::decode64(const char* input,const int length, LogContext log_context) const { + // Calculate the padded length, the number of original decoded bytes + // (length / 4) gives the number of 4-byte blocks of base64 data, multiplied by 3 gives the decoded byte length + // Allocate memory for the output buffer; std::make_unique will throw std::bad_alloc if allocation fails. + const int paddedLength = ((length/SDMS::CipherEngine::BASE64_ENCODED_BLOCK_SIZE)*SDMS::CipherEngine::BASE64_INPUT_BLOCK_SIZE); + auto output = std::make_unique(paddedLength+SDMS::CipherEngine::NULL_TERMINATOR_SIZE); + std::fill_n(output.get(), paddedLength+SDMS::CipherEngine::NULL_TERMINATOR_SIZE, 0); + const int outputLength = EVP_DecodeBlock(output.get(), reinterpret_cast(input), length); + if (paddedLength != outputLength) { + std::ostringstream oss; + oss << "Output Length (" << outputLength + << ") and Predicted Padded Length (" << paddedLength + << ") of decoded bytes not equal!"; + + DL_ERROR(log_context, oss.str()); + EXCEPT_PARAM(1, oss.str()); + } + return output; + } + void CipherEngine::generateIV(unsigned char *iv) + { + if (RAND_bytes(iv, SDMS::CipherEngine::IV_LENGTH) != 1) + { + handleErrors(); + } + } + + // Constructor to set the encryption key + CipherEngine::CipherEngine(const unsigned char* inputKey) + { + memcpy(key, inputKey, SDMS::CipherEngine::KEY_LENGTH); + } + + void CipherEngine::generateEncryptionKey(unsigned char token_key[SDMS::CipherEngine::KEY_LENGTH]) + { + if (RAND_bytes(token_key, SDMS::CipherEngine::KEY_LENGTH) != 1) + { + handleErrors(); + } + } + +bool CipherEngine::tokenNeedsUpdate(const Value::Object &obj) + { + //checking for existance + if(!obj.has("access_iv") || + !obj.has("access_len") || + !obj.has("refresh_iv") || + !obj.has("refresh_len")) + { + return true; + } + + //Checking if it does exist that it isnt empty + if(obj.getValue("refresh").asString().length() == 0 || + obj.getValue("access").asString().length() == 0 || + obj.getValue("access_iv").asString().length() == 0 || + obj.getNumber("access_len") == 0 || + obj.getValue("refresh_iv").asString().length() == 0 || + obj.getNumber("refresh_len") == 0) + { + return true; + } + + return false; + } + CipherEngine::CipherBytes CipherEngine::encryptAlgorithm(unsigned char *iv, const std::string& msg, LogContext log_context) + { + if (msg.length() > MAX_MSG_LENGTH) { + EXCEPT_PARAM(0, std::string("Message too long for encryption")); + } + EVP_CIPHER_CTX *ctx = nullptr; + CipherBytes bytes_result = {}; + + //setting IV for the resulting obj: + for(int i = 0; i < SDMS::CipherEngine::IV_LENGTH; i++) + { + bytes_result.iv[i] = iv[i]; + } + + std::vector msg_unsigned(msg.begin(), msg.end()); + + /* Create and initialise the context */ + if(!(ctx = EVP_CIPHER_CTX_new())) + handleErrors(); + + /* + * Initialise the encryption operation. + * IMPORTANT - ensure you use a key + * and IV size appropriate for your cipher + * In this example we are using 256 bit AES + * (i.e. a 256 bit key). The + * IV size for *most* modes is the same + * as the block size. For AES this + * is 128 bits + */ + if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) + handleErrors(); + + int len; + /* + * Provide the message to be encrypted, and obtain + * the encrypted output. + * EVP_EncryptUpdate can be called multiple times if necessary + */ + if(1 != EVP_EncryptUpdate(ctx, bytes_result.encrypted_msg, &len, msg_unsigned.data(), msg_unsigned.size())) + handleErrors(); + bytes_result.encrypted_msg_len = len; + + /* + * Finalise the encryption. Further ciphertext bytes + * may be written at + * this stage. + */ + if(1 != EVP_EncryptFinal_ex(ctx, bytes_result.encrypted_msg + len, &len)) + handleErrors(); + bytes_result.encrypted_msg_len += len; + + /* Clean up */ + EVP_CIPHER_CTX_free(ctx); + + return bytes_result; + } + + CipherEngine::CipherString CipherEngine::encodeBytes(CipherEngine::CipherBytes unencoded_bytes, LogContext log_context) + { + CipherString encoded_string_result; + + //Assigning values to encoded_string_result + encoded_string_result.encrypted_msg = encode64(unencoded_bytes.encrypted_msg, unencoded_bytes.encrypted_msg_len, log_context); + encoded_string_result.iv = encode64(unencoded_bytes.iv, SDMS::CipherEngine::IV_LENGTH, log_context); + encoded_string_result.encrypted_msg_len = unencoded_bytes.encrypted_msg_len; + + return encoded_string_result; + } + + CipherEngine::CipherString CipherEngine::encrypt(unsigned char *iv,const std::string& msg, LogContext log_context) + { + CipherEngine::CipherBytes unencoded_bytes; + unencoded_bytes = encryptAlgorithm(iv, msg, log_context); + return encodeBytes(unencoded_bytes, log_context); + } + + CipherEngine::CipherString CipherEngine::encrypt(const std::string& msg, LogContext log_context) + { + unsigned char iv[SDMS::CipherEngine::IV_LENGTH] = {}; + generateIV(iv); + CipherEngine::CipherBytes unencoded_bytes; + unencoded_bytes = encryptAlgorithm(iv, msg, log_context); + return encodeBytes(unencoded_bytes, log_context); + + } + + std::string CipherEngine::decrypt(const CipherString& encoded_encrypted_string, LogContext log_context) + { + EVP_CIPHER_CTX *ctx = nullptr; + + //converts the cipherstring back to a unsigned char + std::unique_ptr ciphertext = decode64(encoded_encrypted_string.encrypted_msg.get(), static_cast(strlen(encoded_encrypted_string.encrypted_msg.get())), log_context); + std::unique_ptr iv = decode64(encoded_encrypted_string.iv.get(), static_cast(strlen(encoded_encrypted_string.iv.get())),log_context); + /* Create and initialise the context */ + if(!(ctx = EVP_CIPHER_CTX_new())) + { + handleErrors(); + } + /* Initialise the decryption operation. IMPORTANT - ensure you use a key + * and IV size appropriate for your cipher + * In this example we are using 256 bit AES (i.e. a 256 bit key). The + * IV size for *most* modes is the same as the block size. For AES this + * is 128 bits + */ + + if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv.get())) + { + handleErrors(); + } + int plaintext_len = 0; + unsigned char plaintext[encoded_encrypted_string.encrypted_msg_len + EVP_MAX_BLOCK_LENGTH] = {}; + const int ciphertext_len = encoded_encrypted_string.encrypted_msg_len; + int len = 0; + /* + * Provide the message to be decrypted, and obtain the plaintext output. + * EVP_DecryptUpdate can be called multiple times if necessary. + */ + + if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext.get(), ciphertext_len)) + { + handleErrors(); + } + plaintext_len = len; + + /* + * Finalise the decryption. Further plaintext bytes may be written at + * this stage. + */ + + if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) + { + handleErrors(); + } + plaintext_len += len; + + /* Clean up */ + EVP_CIPHER_CTX_free(ctx); + + //Convert the plaintext back to string + return std::string(reinterpret_cast(plaintext), plaintext_len); + } +} diff --git a/common/source/Util.cpp b/common/source/Util.cpp index 1170e265d..62c97f0e9 100644 --- a/common/source/Util.cpp +++ b/common/source/Util.cpp @@ -5,8 +5,13 @@ // Third party includes #include +#include +#include +#include +#include // Standard includes +#include #include #include #include @@ -15,6 +20,7 @@ #include #include #include +#include using namespace std; @@ -34,6 +40,22 @@ std::string exec(const char *cmd) { return result; } +void readFile(const std::string &fileName,const int arraySize, unsigned char* array) +{ + //Converting Key for encryption funct + unsigned char keyChar[arraySize] = {}; + //Grabbing key + std::ifstream keyFile(fileName, std::ios::binary); + if(!keyFile.is_open()) + { + throw TraceException(__FILE__, __LINE__, 0, std::string("Cannot find token key file: ") + fileName); + return; + } + + keyFile.read(reinterpret_cast(keyChar),arraySize); + + std::copy(keyChar, keyChar + arraySize, array); +} size_t curlResponseWriteCB(char *ptr, size_t size, size_t nmemb, void *userdata) { if (!userdata) diff --git a/common/tests/unit/CMakeLists.txt b/common/tests/unit/CMakeLists.txt index 60f169546..49fe21612 100644 --- a/common/tests/unit/CMakeLists.txt +++ b/common/tests/unit/CMakeLists.txt @@ -13,6 +13,7 @@ foreach(PROG test_ProxyBasicZMQ test_SocketFactory test_SocketOptions + test_CipherEngine ) include_directories(${PROJECT_SOURCE_DIR}/common/source) @@ -20,11 +21,13 @@ foreach(PROG add_executable(unit_${PROG} ${${PROG}_SOURCES}) if(BUILD_SHARED_LIBS) target_link_libraries(unit_${PROG} PRIVATE ${DATAFED_BOOST_LIBRARIES} - common libzmq protobuf::libprotobuf Threads::Threads) + common libzmq protobuf::libprotobuf Threads::Threads + "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}" ) target_compile_definitions(unit_${PROG} PRIVATE BOOST_TEST_DYN_LINK) else() target_link_libraries(unit_${PROG} PRIVATE ${DATAFED_BOOST_LIBRARIES} - common libzmq-static protobuf::libprotobuf Threads::Threads) + common libzmq-static protobuf::libprotobuf Threads::Threads + "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}" ) endif() # Only want this if using shared boost libraries if ( ENABLE_UNIT_TESTS ) diff --git a/common/tests/unit/test_CipherEngine.cpp b/common/tests/unit/test_CipherEngine.cpp new file mode 100644 index 000000000..b6b179fac --- /dev/null +++ b/common/tests/unit/test_CipherEngine.cpp @@ -0,0 +1,191 @@ +#define BOOST_TEST_MAIN + +#define BOOST_TEST_MODULE keyEncryptionDecryption +#include +#include +//Local private includes +#include "../../include/common/CipherEngine.hpp" +#include "../../include/common/Util.hpp" +#include "../../include/common/DynaLog.hpp" + +#include "common/libjson.hpp" + +//Standard includes +#include +#include +#include +#include +using namespace std; +using namespace SDMS; + +BOOST_AUTO_TEST_SUITE(KeyEncryptionDecryptionTest) + +BOOST_AUTO_TEST_CASE(testing_KeyGeneration) +{ + unsigned char token_key[SDMS::CipherEngine::KEY_LENGTH]; + unsigned char keyArray[SDMS::CipherEngine::KEY_LENGTH]; + unsigned char finalArray[SDMS::CipherEngine::KEY_LENGTH]; + CipherEngine::generateEncryptionKey(token_key); + std::string fname = "datafed-token-key.txt"; + + std::ofstream outf(fname, std::ios::binary); + outf.write(reinterpret_cast(token_key), SDMS::CipherEngine::KEY_LENGTH); + outf.close(); + + //Grabbing key + std::ifstream keyFile("datafed-token-key.txt", std::ios::binary); + + keyFile.read(reinterpret_cast(keyArray),SDMS::CipherEngine::KEY_LENGTH); + for (int lv = 0; lv < SDMS::CipherEngine::KEY_LENGTH; lv++) + { + finalArray[lv] = keyArray[lv]; + } + BOOST_CHECK(sizeof(finalArray)==SDMS::CipherEngine::KEY_LENGTH); + + // Assert that the generated key is not all zeros + bool all_zeros = true; + for (int i = 0; i < SDMS::CipherEngine::KEY_LENGTH; ++i) { + if (token_key[i] != 0) { + all_zeros = false; + break; + } + } + BOOST_CHECK(!all_zeros); + // Generate a second key and check that it is different from the first + unsigned char second_key[SDMS::CipherEngine::KEY_LENGTH]; + CipherEngine::generateEncryptionKey(second_key); + bool keys_are_different = false; + for (int i = 0; i < SDMS::CipherEngine::KEY_LENGTH; ++i) { + if (token_key[i] != second_key[i]) { + keys_are_different = true; + break; + } + } + BOOST_CHECK(keys_are_different); +} + +BOOST_AUTO_TEST_CASE(test_EncryptionDecryption) +{ + LogContext log_context; + unsigned char key[SDMS::CipherEngine::KEY_LENGTH]; + + readFile("datafed-token-key.txt", SDMS::CipherEngine::KEY_LENGTH, key); + + //Instance only used for encrypting + CipherEngine encryptCipher(key); + + //Sets struct CipherString: which contains cipherText, cipherIV, cipherPaddedLen + //Mimicing a globus token + const string msg = "1234567890yoDa56Bx5yobvJYEjdGr2YpGYJybE7x4Bq42pQ3zuXCb8YQyn0EqEB7vjPx3GlNlKwkEsMn1234567890"; + + CipherEngine::CipherString encoded_encrypted_packet = encryptCipher.encrypt(msg,log_context); + + //Instance only used for decrypting (we dont want to use the encrypt instance as this is how the code is oriented from going in and coming out the database) + CipherEngine decryptCipher(key); + + //START OF ENCRYPTION + std::string unencrypted_msg; + unencrypted_msg = decryptCipher.decrypt(encoded_encrypted_packet, log_context); + BOOST_CHECK(msg.compare(unencrypted_msg) == 0); +} + +BOOST_AUTO_TEST_CASE(test_EncryptionDecryption_KeyGen) +{ + + LogContext log_context; + unsigned char key[SDMS::CipherEngine::KEY_LENGTH]; + CipherEngine::generateEncryptionKey(key); + + //Instance only used for encrypting + CipherEngine encryptCipher(key); + //Sets struct CipherString: which contains cipherText, cipherIV, cipherPaddedLen + //Mimicing a globus token + const string msg = "1234567890yoDa56Bx5yobvJYEjdGr2YpGYJybE7x4Bq42pQ3zuXCb8YQyn0EqEB7vjPx3GlNlKwkEsMn1234567890"; + + //Start of Encryption + CipherEngine::CipherString encoded_encrypted_packet = encryptCipher.encrypt(msg,log_context); + + //Instance only used for decrypting (we dont want to use the encrypt instance as this is how the code is oriented from going in and coming out the database) + CipherEngine decryptCipher(key); + + //START OF DECRYPTION + std::string unencrypted_msg; + unencrypted_msg = decryptCipher.decrypt(encoded_encrypted_packet, log_context); + BOOST_CHECK(msg.compare(unencrypted_msg) == 0); +} + +BOOST_AUTO_TEST_CASE(test_EncryptionDecryption_IVGen) +{ + LogContext log_context; + unsigned char key[SDMS::CipherEngine::KEY_LENGTH]; + readFile("datafed-token-key.txt", SDMS::CipherEngine::KEY_LENGTH, key); + + //Instance only used for encrypting + CipherEngine encryptCipher(key); + + //Sets struct CipherString: which contains cipherText, cipherIV, cipherPaddedLen + //Mimicing a globus token + const string msg = "1234567890yoDa56Bx5yobvJYEjdGr2YpGYJybE7x4Bq42pQ3zuXCb8YQyn0EqEB7vjPx3GlNlKwkEsMn1234567890"; + + unsigned char iv[SDMS::CipherEngine::IV_LENGTH] = {}; + CipherEngine::generateIV(iv); + + //Start of Encryption + CipherEngine::CipherString encoded_encrypted_packet = encryptCipher.encrypt(iv, msg, log_context); + + //Instance only used for decrypting (we dont want to use the encrypt instance as this is how the code is oriented from going in and coming out the database) + CipherEngine decryptCipher(key); + + //START OF DECRYPTION + std::string unencrypted_msg; + unencrypted_msg = decryptCipher.decrypt(encoded_encrypted_packet, log_context); + BOOST_CHECK(msg.compare(unencrypted_msg) == 0); +} + + + +BOOST_AUTO_TEST_CASE(test_EncryptionDecryptionJSONValue) +{ + + LogContext log_context; + unsigned char key[SDMS::CipherEngine::KEY_LENGTH]; + readFile("datafed-token-key.txt", SDMS::CipherEngine::KEY_LENGTH, key); + + //Instance only used for encrypting + CipherEngine testCipher(key); + + //Sets struct CipherString: which contains cipherText, cipherIV, cipherPaddedLen + //Mimicing a globus token + const string msg = "1234567890yoDa56Bx5yobvJYEjdGr2YpGYJybE7x4Bq42pQ3zuXCb8YQyn0EqEB7vjPx3GlNlKwkEsMn1234567890"; + + //Start of Encryption + CipherEngine::CipherString encoded_encrypted_packet = testCipher.encrypt(msg, log_context); + + libjson::Value value; + std::string json_document = std::string("{ \"access\": \"") + std::string(encoded_encrypted_packet.encrypted_msg.get()) + "\", \"access_len\": 96, \"access_iv\": \"" + std::string(encoded_encrypted_packet.iv.get()) + "\"}"; + + value.fromString(json_document); + libjson::Value::Object &obj = value.asObject(); + + //Start of Decryption + CipherEngine testCipher2(key); + CipherEngine::CipherString encoded_access_obj; + encoded_access_obj.encrypted_msg_len = obj.getNumber("access_len"); + encoded_access_obj.encrypted_msg = std::make_unique(SDMS::CipherEngine::ENCODED_MSG_LENGTH + 1); // add 1 for null terminator + + std::string access = obj.getString("access"); + memcpy(encoded_access_obj.encrypted_msg.get(), access.c_str(), SDMS::CipherEngine::ENCODED_MSG_LENGTH); + encoded_access_obj.encrypted_msg[SDMS::CipherEngine::ENCODED_MSG_LENGTH] = '\0'; // null terminate + + // Do the same for IV + std::string access_iv = obj.getString("access_iv"); + encoded_access_obj.iv = std::make_unique(SDMS::CipherEngine::ENCODED_IV_LENGTH + 1); + memcpy(encoded_access_obj.iv.get(), access_iv.c_str(), SDMS::CipherEngine::ENCODED_IV_LENGTH); + encoded_access_obj.iv[SDMS::CipherEngine::ENCODED_IV_LENGTH] = '\0'; + + std::string unencrypted_msg; + unencrypted_msg = testCipher.decrypt(encoded_access_obj, log_context); + BOOST_CHECK(msg.compare(unencrypted_msg) == 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/core/server/CMakeLists.txt b/core/server/CMakeLists.txt index 22e4c1b5d..14c7db647 100644 --- a/core/server/CMakeLists.txt +++ b/core/server/CMakeLists.txt @@ -13,18 +13,18 @@ list(REMOVE_ITEM Sources files ${Main}) if(BUILD_SHARED_LIBS) add_library( datafed-core-lib SHARED ${Sources} ) target_link_libraries( datafed-core-lib PRIVATE protobuf::libprotobuf Threads::Threads - "${DATAFED_CURL_LIBRARIES}" "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}" + "${DATAFED_CURL_LIBRARIES}" "${DATAFED_ZLIB_LIBRARIES}" ${DATAFED_BOOST_LIBRARIES} libzmq nlohmann_json_schema_validator -ldl ) else() add_library( datafed-core-lib STATIC ${Sources} ) target_link_libraries( datafed-core-lib PRIVATE protobuf::libprotobuf Threads::Threads - "${DATAFED_CURL_LIBRARIES}" "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}" + "${DATAFED_CURL_LIBRARIES}" "${DATAFED_ZLIB_LIBRARIES}" "${DATAFED_BOOST_LIBRARIES}" libzmq-static "${DATAFED_JSON_SCHEMA_LIBRARY_PATH}" -ldl ) endif() target_include_directories( datafed-core-lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) set_target_properties(datafed-core-lib PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_link_libraries( datafed-core-lib PUBLIC common datafed-protobuf ) +target_link_libraries( datafed-core-lib PUBLIC common datafed-protobuf "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}") add_executable( datafed-core ${Main} ) target_link_libraries( datafed-core datafed-core-lib )