Imported Upstream version 17.22.1
[platform/upstream/libzypp.git] / zypp / Digest.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/Digest.cc
10  *
11  * \todo replace by Blocxx
12  *
13 */
14
15 #include <cstdio> // snprintf
16 #include <openssl/evp.h>
17 #include <openssl/conf.h>
18 #include <openssl/engine.h>
19 #include <string>
20 #include <string.h>
21
22 #include <iostream>
23 #include <sstream>
24
25 #ifdef DIGEST_TESTSUITE
26 #include <fstream>
27 #endif
28
29 #include "zypp/Digest.h"
30 #include "zypp/base/PtrTypes.h"
31
32 namespace zypp {
33
34     bool DigestReport::askUserToAcceptNoDigest( const zypp::Pathname &file )
35     { return false; }
36
37     bool DigestReport::askUserToAccepUnknownDigest( const Pathname &file, const std::string &name )
38     { return false; }
39
40     bool DigestReport::askUserToAcceptWrongDigest( const Pathname &file, const std::string &requested, const std::string &found )
41     { return false; }
42
43
44     const std::string & Digest::md5()
45     { static std::string _type( "md5" ); return _type; }
46
47     const std::string & Digest::sha1()
48     { static std::string _type( "sha1" ); return _type; }
49
50     const std::string & Digest::sha224()
51     { static std::string _type( "sha224" ); return _type; }
52
53     const std::string & Digest::sha256()
54     { static std::string _type( "sha256" ); return _type; }
55
56     const std::string & Digest::sha384()
57     { static std::string _type( "sha384" ); return _type; }
58
59     const std::string & Digest::sha512()
60     { static std::string _type( "sha512" ); return _type; }
61
62     // private data
63     class Digest::P
64     {
65       P(const P& p);
66       const P& operator=(const P& p);
67
68       public:
69         typedef zypp::shared_ptr<EVP_MD_CTX> EvpDataPtr;
70         P();
71         ~P();
72
73         EvpDataPtr mdctx;
74
75         const EVP_MD *md;
76         unsigned char md_value[EVP_MAX_MD_SIZE];
77         unsigned md_len;
78
79         bool finalized : 1;
80         static bool openssl_digests_added;
81
82         std::string name;
83
84         inline bool maybeInit();
85         inline void cleanup();
86     };
87
88
89     using namespace std;
90
91     bool Digest::P::openssl_digests_added = false;
92
93     Digest::P::P() :
94       md(NULL),
95       finalized(false)
96     {
97     }
98
99     Digest::P::~P()
100     {
101       cleanup();
102     }
103
104     bool Digest::P::maybeInit()
105     {
106       if(!openssl_digests_added)
107       {
108         OPENSSL_config(NULL);
109         ENGINE_load_builtin_engines();
110         ENGINE_register_all_complete();
111         OpenSSL_add_all_digests();
112         openssl_digests_added = true;
113       }
114
115       if(!mdctx)
116       {
117         md = EVP_get_digestbyname(name.c_str());
118         if(!md)
119           return false;
120
121 #if OPENSSL_VERSION_NUMBER < 0x10100000L
122         EvpDataPtr tmp_mdctx(EVP_MD_CTX_create(), EVP_MD_CTX_destroy);
123 #else
124         EvpDataPtr tmp_mdctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
125 #endif
126         if (!tmp_mdctx)
127           return false;
128
129         if (!EVP_DigestInit_ex(tmp_mdctx.get(), md, NULL)) {
130           return false;
131         }
132
133         md_len = 0;
134         ::memset(md_value, 0, sizeof(md_value));
135
136         mdctx.swap(tmp_mdctx);
137       }
138       return true;
139     }
140
141     void Digest::P::cleanup()
142     {
143       mdctx.reset();
144       finalized = false;
145     }
146
147     Digest::Digest() : _dp(new P())
148     {
149     }
150
151     Digest::~Digest()
152     {
153       delete _dp;
154     }
155
156     bool Digest::create(const std::string& name)
157     {
158       if(name.empty()) return false;
159
160       if(_dp->mdctx)
161         _dp->cleanup();
162
163       _dp->name = name;
164
165       return _dp->maybeInit();
166     }
167
168     const std::string& Digest::name()
169     {
170       return _dp->name;
171     }
172
173     bool Digest::reset()
174     {
175       if (!_dp->mdctx)
176         return false;
177       if(!_dp->finalized)
178       {
179         (void)EVP_DigestFinal_ex(_dp->mdctx.get(), _dp->md_value, &_dp->md_len);
180         _dp->finalized = true;
181       }
182       if(!EVP_DigestInit_ex(_dp->mdctx.get(), _dp->md, NULL))
183         return false;
184       _dp->finalized = false;
185       return true;
186     }
187
188     std::string Digest::digest()
189     {
190       return digestVectorToString( digestVector() );
191     }
192
193     string Digest::digestVectorToString(const std::vector<unsigned char> &vec)
194     {
195       if ( vec.empty() )
196         return std::string();
197
198       std::vector<char> resData ( vec.size()*2 + 1, '\0' );
199       char *mdtxt = &resData[0];
200       for(unsigned i = 0; i < vec.size(); ++i)
201       {
202         ::snprintf( mdtxt+(i*2), 3, "%02hhx", vec[i]);
203       }
204       return std::string( resData.data() );
205     }
206
207     std::vector<unsigned char> Digest::digestVector()
208     {
209       std::vector<unsigned char> r;
210       if(!_dp->maybeInit())
211         return r;
212
213       if(!_dp->finalized)
214       {
215         if(!EVP_DigestFinal_ex(_dp->mdctx.get(), _dp->md_value, &_dp->md_len))
216             return r;
217         _dp->finalized = true;
218       }
219       r.reserve(_dp->md_len);
220       for(unsigned i = 0; i < _dp->md_len; ++i)
221         r.push_back(_dp->md_value[i]);
222       return r;
223     }
224
225     bool Digest::update(const char* bytes, size_t len)
226     {
227       if(!bytes)
228       {
229         return false;
230       }
231
232       if(!_dp->maybeInit())
233         return false;
234
235       if(_dp->finalized)
236       {
237         _dp->cleanup();
238         if(!_dp->maybeInit())
239             return false;
240
241       }
242       if(!EVP_DigestUpdate(_dp->mdctx.get(), reinterpret_cast<const unsigned char*>(bytes), len))
243         return false;
244
245       return true;
246     }
247
248     bool Digest::update(istream &is, size_t bufsize)
249     {
250       if( !is )
251         return false;
252
253       char buf[bufsize];
254
255       while(is.good())
256       {
257         size_t readed;
258         is.read(buf, bufsize);
259         readed = is.gcount();
260         if(readed && !update(buf, readed))
261           return false;
262       }
263
264       return true;
265     }
266
267     std::string Digest::digest(const std::string& name, std::istream& is, size_t bufsize)
268     {
269       if(name.empty() || !is)
270         return string();
271
272       Digest digest;
273       if(!digest.create(name))
274         return string();
275
276       if ( !digest.update( is, bufsize ))
277         return string();
278
279       return digest.digest();
280     }
281
282     std::string Digest::digest( const std::string & name, const std::string & input, size_t bufsize )
283     {
284       istringstream is( input );
285       return digest( name, is, bufsize );
286     }
287
288 #ifdef DIGEST_TESTSUITE
289     int main(int argc, char *argv[])
290     {
291       bool openssl = false;
292       unsigned argpos = 1;
293
294       if(argc > 1 && string(argv[argpos]) == "--openssl")
295       {
296         openssl = true;
297         ++argpos;
298       }
299
300       if(argc - argpos < 2)
301       {
302         cerr << "Usage: " << argv[0] << " <DIGESTNAME> <FILE>" << endl;
303         return 1;
304       }
305
306       const char* digestname = argv[argpos++];
307       const char* fn = argv[argpos++];
308
309       ifstream file(fn);
310
311       string digest = Digest::digest(digestname, file);
312
313       if(openssl)
314         cout << digestname << "(" << fn << ")= " << digest << endl;
315       else
316         cout << digest << "  " << fn << endl;
317
318       return 0;
319     }
320 #endif
321
322 } // namespace zypp