Hashing functions based upon openssl created
authorTulio Cavalcanti <tulio.cavalcanti@intra2net.com>
Mon, 17 Sep 2018 12:47:26 +0000 (14:47 +0200)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Tue, 2 Oct 2018 12:15:36 +0000 (14:15 +0200)
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
src/crypto.cpp [new file with mode: 0644]
src/crypto.hxx [new file with mode: 0644]
test/CMakeLists.txt
test/test_crypto.cpp [new file with mode: 0644]

index 2ef9dc0..605b81e 100644 (file)
@@ -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 (file)
index 0000000..c7ad98b
--- /dev/null
@@ -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 <openssl/evp.h>
+#include <sstream>
+#include <iomanip>
+#include <string>
+#include <string.h>
+#include <boost/shared_ptr.hpp>
+#include <stdexcept>
+#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<char*>(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<char*>(ret.get()), olen));
+}
diff --git a/src/crypto.hxx b/src/crypto.hxx
new file mode 100644 (file)
index 0000000..8d1e26e
--- /dev/null
@@ -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 <openssl/evp.h>
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
+
+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> char_arr;
+
+typedef boost::shared_array<unsigned char> uchar_arr;
+
+typedef boost::shared_ptr<EVP_MD_CTX> EVP_MD_CTX_Ptr;
+}; // eo I2n namespace
+
+#endif /* [CRYPTO_HXX] */
index ecd259b..6694bff 100644 (file)
@@ -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 (file)
index 0000000..6f3edff
--- /dev/null
@@ -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 <boost/test/unit_test.hpp>
+#include <vector>
+#include <tmpfstream.hpp>
+#include <iostream>
+#include <fstream>
+#include <string>
+
+#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()