d08f5d6a709ef6fedc11669e7dc4bda27979eee6
[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 #   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     if (!EVP_DigestFinal_ex(ctx.get(), ret.get(), &olen))
172     {
173         fclose(file);
174         throw runtime_error(ERROR_MESSAGE);
175     }
176
177     return string(encode_hex(reinterpret_cast<char*>(ret.get()), olen));
178 }