| 1 | /* |
| 2 | The software in this package is distributed under the GNU General |
| 3 | Public License version 2 (with a special exception described below). |
| 4 | |
| 5 | A copy of GNU General Public License (GPL) is included in this distribution, |
| 6 | in the file COPYING.GPL. |
| 7 | |
| 8 | As a special exception, if other files instantiate templates or use macros |
| 9 | or inline functions from this file, or you compile this file and link it |
| 10 | with other works to produce a work based on this file, this file |
| 11 | does not by itself cause the resulting work to be covered |
| 12 | by the GNU General Public License. |
| 13 | |
| 14 | However the source code for this file must still be made available |
| 15 | in accordance with section (3) of the GNU General Public License. |
| 16 | |
| 17 | This exception does not invalidate any other reasons why a work based |
| 18 | on this file might be covered by the GNU General Public License. |
| 19 | */ |
| 20 | /** |
| 21 | Hashing functions based upon openssl. |
| 22 | |
| 23 | @copyright Intra2net AG |
| 24 | */ |
| 25 | |
| 26 | using namespace std; |
| 27 | |
| 28 | #include <openssl/evp.h> |
| 29 | #include <sstream> |
| 30 | #include <iomanip> |
| 31 | #include <string> |
| 32 | #include <string.h> |
| 33 | #include <boost/shared_ptr.hpp> |
| 34 | #include <stdexcept> |
| 35 | #include "crypto.hxx" |
| 36 | |
| 37 | #define buf_size (size_t)65536 |
| 38 | |
| 39 | |
| 40 | using namespace I2n; |
| 41 | |
| 42 | namespace |
| 43 | { |
| 44 | const char* get_algo_name(algorithm algo) |
| 45 | { |
| 46 | switch (algo) |
| 47 | { |
| 48 | case MD5: |
| 49 | return (char*)"md5"; |
| 50 | case SHA1: |
| 51 | return (char*)"sha1"; |
| 52 | case SHA256: |
| 53 | return (char*)"sha256"; |
| 54 | case SHA384: |
| 55 | return (char*)"sha384"; |
| 56 | case SHA512: |
| 57 | return (char*)"sha512"; |
| 58 | default: |
| 59 | return NULL; |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | const string ERROR_MESSAGE = "Error while trying to hash the data"; |
| 64 | } // eo anonymous namespace |
| 65 | |
| 66 | string I2n::encode_hex(string data) |
| 67 | { |
| 68 | return encode_hex((char *)data.c_str(), data.size()); |
| 69 | } |
| 70 | |
| 71 | string I2n::encode_hex(char *data, unsigned int size) |
| 72 | { |
| 73 | ostringstream out; |
| 74 | |
| 75 | for (unsigned int i = 0; i < size; i++) |
| 76 | out << hex << uppercase << setw(2) << setfill('0') << (int)(unsigned char)data[i]; |
| 77 | |
| 78 | return out.str(); |
| 79 | } |
| 80 | |
| 81 | // output: hex encoded hash |
| 82 | string I2n::hash_data(string data, algorithm algo) |
| 83 | { |
| 84 | return encode_hex(hash_data_raw(data, algo)); |
| 85 | } |
| 86 | |
| 87 | // output: raw binary hash |
| 88 | string I2n::hash_data_raw(string data, algorithm algo) |
| 89 | { |
| 90 | unsigned int olen; |
| 91 | |
| 92 | const EVP_MD *md; |
| 93 | |
| 94 | # if (OPENSSL_VERSION_NUMBER < 0x10100000L) |
| 95 | EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy); |
| 96 | # else |
| 97 | EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); |
| 98 | # endif |
| 99 | |
| 100 | uchar_arr ret(new unsigned char[EVP_MAX_MD_SIZE]); |
| 101 | |
| 102 | OpenSSL_add_all_digests(); |
| 103 | |
| 104 | if(!(md = EVP_get_digestbyname(get_algo_name(algo))) || |
| 105 | !EVP_DigestInit_ex(ctx.get(), md, NULL) || |
| 106 | !EVP_DigestUpdate(ctx.get(), (char*)data.c_str(), data.length()) || |
| 107 | !EVP_DigestFinal_ex(ctx.get(), ret.get(), &olen)) |
| 108 | { |
| 109 | throw runtime_error(ERROR_MESSAGE); |
| 110 | } |
| 111 | |
| 112 | return string(reinterpret_cast<char*>(ret.get()), olen); |
| 113 | } |
| 114 | |
| 115 | // hash data in 64kB blocks |
| 116 | string I2n::hash_file(string filename, algorithm algo) |
| 117 | { |
| 118 | const EVP_MD *md; |
| 119 | |
| 120 | # if (OPENSSL_VERSION_NUMBER < 0x10100000L) |
| 121 | EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy); |
| 122 | # else |
| 123 | EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free); |
| 124 | # endif |
| 125 | |
| 126 | uchar_arr ret(new unsigned char[EVP_MAX_MD_SIZE]); |
| 127 | |
| 128 | unsigned int olen; |
| 129 | |
| 130 | OpenSSL_add_all_digests(); |
| 131 | |
| 132 | char_arr buf(new char[buf_size]); |
| 133 | |
| 134 | if (!buf) |
| 135 | throw bad_alloc(); |
| 136 | |
| 137 | FILE* file = fopen (filename.c_str(), "r"); |
| 138 | if (!file) |
| 139 | { |
| 140 | throw ios_base::failure("can't open file"); |
| 141 | } |
| 142 | |
| 143 | if(!(md = EVP_get_digestbyname(get_algo_name(algo))) || |
| 144 | !EVP_DigestInit_ex(ctx.get(), md, NULL)) |
| 145 | { |
| 146 | fclose(file); |
| 147 | throw runtime_error(ERROR_MESSAGE); |
| 148 | } |
| 149 | |
| 150 | int read_bytes; |
| 151 | |
| 152 | while (!feof(file)) |
| 153 | { |
| 154 | memset(buf.get(), 0, sizeof(char) * buf_size); |
| 155 | |
| 156 | read_bytes = fread (buf.get(), 1, buf_size, file); |
| 157 | |
| 158 | if (ferror(file)) |
| 159 | { |
| 160 | fclose(file); |
| 161 | throw runtime_error("Error while reading file: " + filename); |
| 162 | } |
| 163 | |
| 164 | if (read_bytes && !EVP_DigestUpdate(ctx.get(), buf.get(), read_bytes)) |
| 165 | { |
| 166 | fclose(file); |
| 167 | throw runtime_error(ERROR_MESSAGE); |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | fclose(file); |
| 172 | |
| 173 | if (!EVP_DigestFinal_ex(ctx.get(), ret.get(), &olen)) |
| 174 | { |
| 175 | throw runtime_error(ERROR_MESSAGE); |
| 176 | } |
| 177 | |
| 178 | return string(encode_hex(reinterpret_cast<char*>(ret.get()), olen)); |
| 179 | } |