From 69d568ddf8e19d7e4f6e65892f1a91dd5739c929 Mon Sep 17 00:00:00 2001 From: Tulio Cavalcanti Date: Mon, 17 Sep 2018 14:47:26 +0200 Subject: [PATCH] Hashing functions based upon openssl created The following functions were implemented: string hash_data(string data, algorithm algo=SHA1); // output: hex encoded hash string hash_data_raw(string data, algorithm algo=SHA1); // output: raw binary hash string hash_file(string filename, algorithm algo=SHA1); // hash data in 64kb blocks Unit test also included. --- src/CMakeLists.txt | 2 + src/crypto.cpp | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/crypto.hxx | 62 ++++++++++++++++++ test/CMakeLists.txt | 1 + test/test_crypto.cpp | 153 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 388 insertions(+), 0 deletions(-) create mode 100644 src/crypto.cpp create mode 100644 src/crypto.hxx create mode 100644 test/test_crypto.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2ef9dc0..605b81e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories( SET(cpp_sources cron.cpp + crypto.cpp daemonfunc.cpp filefunc.cpp i18n.cpp @@ -30,6 +31,7 @@ SET(cpp_sources SET(cpp_headers cron.hpp + crypto.hxx daemonfunc.hpp exception.hxx filefunc.hxx diff --git a/src/crypto.cpp b/src/crypto.cpp new file mode 100644 index 0000000..c7ad98b --- /dev/null +++ b/src/crypto.cpp @@ -0,0 +1,170 @@ +/* +The software in this package is distributed under the GNU General +Public License version 2 (with a special exception described below). + +A copy of GNU General Public License (GPL) is included in this distribution, +in the file COPYING.GPL. + +As a special exception, if other files instantiate templates or use macros +or inline functions from this file, or you compile this file and link it +with other works to produce a work based on this file, this file +does not by itself cause the resulting work to be covered +by the GNU General Public License. + +However the source code for this file must still be made available +in accordance with section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work based +on this file might be covered by the GNU General Public License. +*/ +/** + Hashing functions based upon openssl. + + @copyright Intra2net AG +*/ + +using namespace std; + +#include +#include +#include +#include +#include +#include +#include +#include "crypto.hxx" + +#define buf_size (size_t)65536 + + +using namespace I2n; + +namespace +{ +const char* get_algo_name(algorithm algo) +{ + switch (algo) + { + case MD5: + return (char*)"md5"; + case SHA1: + return (char*)"sha1"; + case SHA256: + return (char*)"sha256"; + case SHA384: + return (char*)"sha384"; + case SHA512: + return (char*)"sha512"; + default: + return NULL; + } +} + +const string ERROR_MESSAGE = "Error while trying to hash the data"; +} // eo anonymous namespace + +string I2n::encode_hex(string data) +{ + return encode_hex((char *)data.c_str(), data.size()); +} + +string I2n::encode_hex(char *data, unsigned int size) +{ + ostringstream out; + + for (unsigned int i = 0; i < size; i++) + out << hex << uppercase << setw(2) << setfill('0') << (int)(unsigned char)data[i]; + + return out.str(); +} + +// output: hex encoded hash +string I2n::hash_data(string data, algorithm algo) +{ + return encode_hex(hash_data_raw(data, algo)); +} + +// output: raw binary hash +string I2n::hash_data_raw(string data, algorithm algo) +{ + unsigned int olen; + + const EVP_MD *md; + + EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy); + + uchar_arr ret(new unsigned char[EVP_MAX_MD_SIZE]); + + OpenSSL_add_all_digests(); + + if(!(md = EVP_get_digestbyname(get_algo_name(algo))) || + !EVP_DigestInit_ex(ctx.get(), md, NULL) || + !EVP_DigestUpdate(ctx.get(), (char*)data.c_str(), data.length()) || + !EVP_DigestFinal_ex(ctx.get(), ret.get(), &olen)) + { + throw runtime_error(ERROR_MESSAGE); + } + + return string(reinterpret_cast(ret.get()), olen); +} + +// hash data in 64kB blocks +string I2n::hash_file(string filename, algorithm algo) +{ + const EVP_MD *md; + + EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy); + + uchar_arr ret(new unsigned char[EVP_MAX_MD_SIZE]); + + unsigned int olen; + + OpenSSL_add_all_digests(); + + char_arr buf(new char[buf_size]); + + if (!buf) + throw bad_alloc(); + + FILE* file = fopen (filename.c_str(), "r"); + if (!file) + { + throw ios_base::failure("can't open file"); + } + + if(!(md = EVP_get_digestbyname(get_algo_name(algo))) || + !EVP_DigestInit_ex(ctx.get(), md, NULL)) + { + fclose(file); + throw runtime_error(ERROR_MESSAGE); + } + + int read_bytes; + + while (!feof(file)) + { + memset(buf.get(), 0, sizeof(char) * buf_size); + + read_bytes = fread (buf.get(), 1, buf_size, file); + + if (ferror(file)) + { + fclose(file); + throw runtime_error("Error while reading file: " + filename); + } + + if (read_bytes && !EVP_DigestUpdate(ctx.get(), buf.get(), read_bytes)) + { + fclose(file); + throw runtime_error(ERROR_MESSAGE); + } + } + + if (!EVP_DigestFinal_ex(ctx.get(), ret.get(), &olen)) + { + fclose(file); + throw runtime_error(ERROR_MESSAGE); + } + + return string(encode_hex(reinterpret_cast(ret.get()), olen)); +} diff --git a/src/crypto.hxx b/src/crypto.hxx new file mode 100644 index 0000000..8d1e26e --- /dev/null +++ b/src/crypto.hxx @@ -0,0 +1,62 @@ +/* +The software in this package is distributed under the GNU General +Public License version 2 (with a special exception described below). + +A copy of GNU General Public License (GPL) is included in this distribution, +in the file COPYING.GPL. + +As a special exception, if other files instantiate templates or use macros +or inline functions from this file, or you compile this file and link it +with other works to produce a work based on this file, this file +does not by itself cause the resulting work to be covered +by the GNU General Public License. + +However the source code for this file must still be made available +in accordance with section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work based +on this file might be covered by the GNU General Public License. +*/ +/** + Hashing functions based upon openssl. + + @copyright Intra2net AG +*/ + +#ifndef CRYPTO_HXX +#define CRYPTO_HXX + + +#if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)) +using namespace std; +#endif + +#include +#include +#include + +namespace I2n { + +enum algorithm { NONE=0, MD5=1, SHA1=2, SHA256=8, SHA384=9, SHA512=10 }; + +// output: hex encoded hash +string hash_data(string data, algorithm algo=SHA1); + +// output: raw binary hash +string hash_data_raw(string data, algorithm algo=SHA1); + +// hash data in 64kB blocks +string hash_file(string filename, algorithm algo=SHA1); + +string encode_hex(string data); + +string encode_hex(char *data, unsigned int size); + +typedef boost::shared_array char_arr; + +typedef boost::shared_array uchar_arr; + +typedef boost::shared_ptr EVP_MD_CTX_Ptr; +}; // eo I2n namespace + +#endif /* [CRYPTO_HXX] */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ecd259b..6694bff 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -21,6 +21,7 @@ SET(cpp_sources test_timefunc.cpp test_tmpfstream.cpp test_tribool.cpp + test_crypto.cpp ) if (IMAP_UTF7_SUPPORT) SET(cpp_sources stringfunc_imaputf7.cpp ${cpp_sources}) diff --git a/test/test_crypto.cpp b/test/test_crypto.cpp new file mode 100644 index 0000000..6f3edff --- /dev/null +++ b/test/test_crypto.cpp @@ -0,0 +1,153 @@ +/* +The software in this package is distributed under the GNU General +Public License version 2 (with a special exception described below). + +A copy of GNU General Public License (GPL) is included in this distribution, +in the file COPYING.GPL. + +As a special exception, if other files instantiate templates or use macros +or inline functions from this file, or you compile this file and link it +with other works to produce a work based on this file, this file +does not by itself cause the resulting work to be covered +by the GNU General Public License. + +However the source code for this file must still be made available +in accordance with section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work based +on this file might be covered by the GNU General Public License. +*/ +/** + tests for the module "crypto" + + @copyright Intra2net AG +*/ +#define BOOST_TEST_DYN_LINK +#include +#include +#include +#include +#include +#include + +#include "crypto.hxx" + +using namespace std; +using namespace I2n; + + +class TestCryptoFixture +{ +protected: + + char* Filename; + + tmpfstream Tempfile; + + void write_tempfile() + { + string tmpfile_name = "/tmp/libi2ncommon_test_crypto_XXXXXX"; + Tempfile.open(tmpfile_name); + Tempfile << "Long Data and or Password"; + Tempfile.close(); + + Filename = (char*)Tempfile.get_tmp_filename().c_str(); + } + +public: + TestCryptoFixture() + { + write_tempfile(); + } + + ~TestCryptoFixture() + { + Tempfile.unlink(); + } +}; + +BOOST_FIXTURE_TEST_SUITE(Crypto, TestCryptoFixture) + +BOOST_AUTO_TEST_CASE(CheckHashMD5) +{ + const string hash = hash_data("Long Data and or Password", + MD5); + BOOST_CHECK_EQUAL(hash, string("E5952C81BBD04F9CB748433B9431F674")); +} + +BOOST_AUTO_TEST_CASE(CheckHashSHA1) +{ + const string hash = hash_data("Long Data and or Password", + SHA1); + BOOST_CHECK_EQUAL(hash, string("5935A22DAA087672FE2EA0E4485D58BC77E6068D")); +} + +BOOST_AUTO_TEST_CASE(CheckHashSHA256) +{ + const string hash = hash_data("Long Data and or Password", + SHA256); + BOOST_CHECK_EQUAL(hash, string("6D7F287A7E01ADF1A66C19D61C8D5A3BD68C1F" + "10565A6D37A5920B09152D62CE")); +} + +BOOST_AUTO_TEST_CASE(CheckHashSHA384) +{ + const string hash = hash_data("Long Data and or Password", + SHA384); + BOOST_CHECK_EQUAL(hash, string("1D879CCDFF4E50C32318A1F0FB8AF8A0B" + "150432CECAF41E5C7C0C75688E1F776" + "14CA70407A9BC97FDD1CEC4CF291B66B")); +} + +BOOST_AUTO_TEST_CASE(CheckHashSHA512) +{ + const string hash = hash_file(Filename, + SHA512); + BOOST_CHECK_EQUAL(hash, string("CBD561B4D1E13C08672CBCCAC855FAC3F4" + "7672548D0B61B0BB201017E4D93B239E" + "969DB3588F1466F34FB27A7D0AEFE203" + "594808E16659BEDD4B25F34EC46CDB")); +} + +BOOST_AUTO_TEST_CASE(CheckHashFileMD5) +{ + const string hash = hash_file(Filename, + MD5); + BOOST_CHECK_EQUAL(hash, string("E5952C81BBD04F9CB748433B9431F674")); +} + +BOOST_AUTO_TEST_CASE(CheckHashFileSHA1) +{ + const string hash = hash_file(Filename, + SHA1); + BOOST_CHECK_EQUAL(hash, string("5935A22DAA087672FE2EA0E4485D58BC77E6068D")); +} + +BOOST_AUTO_TEST_CASE(CheckHashFileSHA256) +{ + const string hash = hash_file(Filename, + SHA256); + BOOST_CHECK_EQUAL(hash, string("6D7F287A7E01ADF1A66C19D61C8D5A3BD68C1F" + "10565A6D37A5920B09152D62CE")); +} + +BOOST_AUTO_TEST_CASE(CheckHashFileSHA384) +{ + const string hash = hash_file(Filename, + SHA384); + BOOST_CHECK_EQUAL(hash, string("1D879CCDFF4E50C32318A1F0FB8AF8A0B" + "150432CECAF41E5C7C0C75688E1F776" + "14CA70407A9BC97FDD1CEC4CF291B66B")); +} + +BOOST_AUTO_TEST_CASE(CheckHashFileSHA512) +{ + const string hash = hash_file(Filename, + SHA512); + BOOST_CHECK_EQUAL(hash, string("CBD561B4D1E13C08672CBCCAC855FAC3F4" + "7672548D0B61B0BB201017E4D93B239E" + "969DB3588F1466F34FB27A7D0AEFE203" + "594808E16659BEDD4B25F34EC46CDB")); +} + +BOOST_AUTO_TEST_SUITE_END() -- 1.7.1