c7ad98b99a76a4a244d14467687f6665339c92bb
[libi2ncommon] / src / crypto.cpp
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     EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy);
95
96     uchar_arr ret(new unsigned char[EVP_MAX_MD_SIZE]);
97
98     OpenSSL_add_all_digests();
99
100     if(!(md = EVP_get_digestbyname(get_algo_name(algo))) ||
101        !EVP_DigestInit_ex(ctx.get(), md, NULL) ||
102        !EVP_DigestUpdate(ctx.get(), (char*)data.c_str(), data.length()) ||
103        !EVP_DigestFinal_ex(ctx.get(), ret.get(), &olen))
104     {
105         throw runtime_error(ERROR_MESSAGE);
106     }
107
108     return string(reinterpret_cast<char*>(ret.get()), olen);
109 }
110
111 // hash data in 64kB blocks
112 string I2n::hash_file(string filename, algorithm algo)
113 {
114     const EVP_MD *md;
115
116     EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy);
117
118     uchar_arr ret(new unsigned char[EVP_MAX_MD_SIZE]);
119
120     unsigned int olen;
121
122     OpenSSL_add_all_digests();
123
124     char_arr buf(new char[buf_size]);
125
126     if (!buf)
127         throw bad_alloc();
128
129     FILE* file = fopen (filename.c_str(), "r");
130     if (!file)
131     {
132         throw ios_base::failure("can't open file");
133     }
134
135     if(!(md = EVP_get_digestbyname(get_algo_name(algo))) ||
136        !EVP_DigestInit_ex(ctx.get(), md, NULL))
137     {
138         fclose(file);
139         throw runtime_error(ERROR_MESSAGE);
140     }
141
142     int read_bytes;
143
144     while (!feof(file))
145     {
146         memset(buf.get(), 0, sizeof(char) * buf_size);
147
148         read_bytes = fread (buf.get(), 1, buf_size, file);
149     
150         if (ferror(file))
151         {
152             fclose(file);
153             throw runtime_error("Error while reading file: " + filename);
154         }
155
156         if (read_bytes && !EVP_DigestUpdate(ctx.get(), buf.get(), read_bytes))
157         {
158             fclose(file);
159             throw runtime_error(ERROR_MESSAGE);
160         }
161     }
162
163     if (!EVP_DigestFinal_ex(ctx.get(), ret.get(), &olen))
164     {
165         fclose(file);
166         throw runtime_error(ERROR_MESSAGE);
167     }
168
169     return string(encode_hex(reinterpret_cast<char*>(ret.get()), olen));
170 }