stop leaking FILE* objects when hashing
[libi2ncommon] / src / crypto.cpp
CommitLineData
69d568dd
TC
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on 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
26using 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
40using namespace I2n;
41
42namespace
43{
44const 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
63const string ERROR_MESSAGE = "Error while trying to hash the data";
64} // eo anonymous namespace
65
66string I2n::encode_hex(string data)
67{
68 return encode_hex((char *)data.c_str(), data.size());
69}
70
71string 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
82string I2n::hash_data(string data, algorithm algo)
83{
84 return encode_hex(hash_data_raw(data, algo));
85}
86
87// output: raw binary hash
88string I2n::hash_data_raw(string data, algorithm algo)
89{
90 unsigned int olen;
91
92 const EVP_MD *md;
93
e613cd71 94# if (OPENSSL_VERSION_NUMBER < 0x10100000L)
69d568dd 95 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy);
e613cd71
PG
96# else
97 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
98# endif
69d568dd
TC
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
116string I2n::hash_file(string filename, algorithm algo)
117{
118 const EVP_MD *md;
119
e613cd71 120# if (OPENSSL_VERSION_NUMBER < 0x10100000L)
69d568dd 121 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy);
e613cd71
PG
122# else
123 EVP_MD_CTX_Ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
124# endif
69d568dd
TC
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
4cefaf51
PG
171 fclose(file);
172
69d568dd
TC
173 if (!EVP_DigestFinal_ex(ctx.get(), ret.get(), &olen))
174 {
69d568dd
TC
175 throw runtime_error(ERROR_MESSAGE);
176 }
177
178 return string(encode_hex(reinterpret_cast<char*>(ret.get()), olen));
179}