1 /*-
2  * Copyright (c) 2017-2019 Ribose Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 /*-
28  * Copyright (c) 2009 The NetBSD Foundation, Inc.
29  * All rights reserved.
30  *
31  * This code is derived from software contributed to The NetBSD Foundation
32  * by Alistair Crooks (agc@NetBSD.org)
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions
36  * are met:
37  * 1. Redistributions of source code must retain the above copyright
38  *    notice, this list of conditions and the following disclaimer.
39  * 2. Redistributions in binary form must reproduce the above copyright
40  *    notice, this list of conditions and the following disclaimer in the
41  *    documentation and/or other materials provided with the distribution.
42  *
43  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
44  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
45  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
46  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
47  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
48  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
49  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
50  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
51  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
52  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
53  * POSSIBILITY OF SUCH DAMAGE.
54  */
55 /*
56  * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
57  * All rights reserved.
58  * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
59  * their moral rights under the UK Copyright Design and Patents Act 1988 to
60  * be recorded as the authors of this copyright work.
61  *
62  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
63  * use this file except in compliance with the License.
64  *
65  * You may obtain a copy of the License at
66  *     http://www.apache.org/licenses/LICENSE-2.0
67  *
68  * Unless required by applicable law or agreed to in writing, software
69  * distributed under the License is distributed on an "AS IS" BASIS,
70  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
71  *
72  * See the License for the specific language governing permissions and
73  * limitations under the License.
74  */
75 
76 #include <stdio.h>
77 #include <memory>
78 #include <botan/hash.h>
79 #include "hash.h"
80 #include "types.h"
81 #include "utils.h"
82 #include "defaults.h"
83 
84 static const struct hash_alg_map_t {
85     pgp_hash_alg_t type;
86     const char *   name;
87     const char *   botan_name;
88     size_t         digest_size;
89 } hash_alg_map[] = {{PGP_HASH_MD5, "MD5", "MD5", 16},
90                     {PGP_HASH_SHA1, "SHA1", "SHA-1", 20},
91                     {PGP_HASH_RIPEMD, "RIPEMD160", "RIPEMD-160", 20},
92                     {PGP_HASH_SHA256, "SHA256", "SHA-256", 32},
93                     {PGP_HASH_SHA384, "SHA384", "SHA-384", 48},
94                     {PGP_HASH_SHA512, "SHA512", "SHA-512", 64},
95                     {PGP_HASH_SHA224, "SHA224", "SHA-224", 28},
96                     {PGP_HASH_SM3, "SM3", "SM3", 32},
97                     {PGP_HASH_SHA3_256, "SHA3-256", "SHA-3(256)", 32},
98                     {PGP_HASH_SHA3_512, "SHA3-512", "SHA-3(512)", 64}};
99 /**
100  * \ingroup Core_Print
101  *
102  * returns description of the Hash Algorithm type
103  * \param hash Hash Algorithm type
104  * \return string or "Unknown"
105  */
106 const char *
pgp_show_hash_alg(uint8_t hash)107 pgp_show_hash_alg(uint8_t hash)
108 {
109     const char *ret = NULL;
110     ARRAY_LOOKUP_BY_ID(hash_alg_map, type, name, hash, ret);
111     return ret;
112 }
113 
114 const char *
pgp_hash_name_botan(pgp_hash_alg_t hash)115 pgp_hash_name_botan(pgp_hash_alg_t hash)
116 {
117     const char *ret = NULL;
118     ARRAY_LOOKUP_BY_ID(hash_alg_map, type, botan_name, hash, ret);
119     return ret;
120 }
121 
122 const char *
pgp_hash_name(const pgp_hash_t * hash)123 pgp_hash_name(const pgp_hash_t *hash)
124 {
125     return pgp_show_hash_alg(hash->_alg);
126 }
127 
128 /**
129 \ingroup Core_Hashes
130 \brief Returns hash enum corresponding to given string
131 \param hash Text name of hash algorithm i.e. "SHA1"
132 \returns Corresponding enum i.e. PGP_HASH_SHA1
133 */
134 pgp_hash_alg_t
pgp_str_to_hash_alg(const char * hash)135 pgp_str_to_hash_alg(const char *hash)
136 {
137     if (hash == NULL) {
138         return DEFAULT_PGP_HASH_ALG;
139     }
140     for (size_t i = 0; i < ARRAY_SIZE(hash_alg_map); i++) {
141         if (!rnp_strcasecmp(hash, hash_alg_map[i].name)) {
142             return hash_alg_map[i].type;
143         }
144     }
145     return PGP_HASH_UNKNOWN;
146 }
147 
148 static bool
botan_hash_create(pgp_hash_t * hash,const char * hash_name)149 botan_hash_create(pgp_hash_t *hash, const char *hash_name)
150 {
151     if (!hash_name) {
152         return false;
153     }
154 
155     std::unique_ptr<Botan::HashFunction> hash_fn;
156     try {
157         hash_fn = Botan::HashFunction::create(hash_name);
158     } catch (std::exception &ex) {
159         RNP_LOG("Error creating HashFunction ('%s')", ex.what());
160     }
161     if (!hash_fn) {
162         RNP_LOG("Error creating hash object for '%s'", hash_name);
163         return false;
164     }
165 
166     hash->_output_len = hash_fn->output_length();
167     if (hash->_output_len == 0) {
168         RNP_LOG("In pgp_hash_create, botan_hash_output_length failed");
169         return false;
170     }
171 
172     hash->handle = hash_fn.release();
173     return true;
174 }
175 
176 /**
177 \ingroup Core_Hashes
178 \brief Setup hash for given hash algorithm
179 \param hash Hash to set up
180 \param alg Hash algorithm to use
181 */
182 bool
pgp_hash_create(pgp_hash_t * hash,pgp_hash_alg_t alg)183 pgp_hash_create(pgp_hash_t *hash, pgp_hash_alg_t alg)
184 {
185     if (!botan_hash_create(hash, pgp_hash_name_botan(alg))) {
186         return false;
187     }
188 
189     hash->_alg = alg;
190     return true;
191 }
192 
193 bool
pgp_hash_create_crc24(pgp_hash_t * hash)194 pgp_hash_create_crc24(pgp_hash_t *hash)
195 {
196     if (!botan_hash_create(hash, "CRC24")) {
197         return false;
198     }
199 
200     hash->_alg = PGP_HASH_UNKNOWN;
201     return true;
202 }
203 
204 bool
pgp_hash_copy(pgp_hash_t * dst,const pgp_hash_t * src)205 pgp_hash_copy(pgp_hash_t *dst, const pgp_hash_t *src)
206 {
207     if (!src || !dst) {
208         return false;
209     }
210 
211     Botan::HashFunction *hash_fn = static_cast<Botan::HashFunction *>(src->handle);
212     if (!hash_fn) {
213         return false;
214     }
215 
216     std::unique_ptr<Botan::HashFunction> handle;
217     try {
218         handle = hash_fn->copy_state();
219     } catch (std::exception &ex) {
220         RNP_LOG("Error copying HashFunction ('%s')", ex.what());
221     }
222     if (!handle) {
223         return false;
224     }
225 
226     dst->_output_len = src->_output_len;
227     dst->_alg = src->_alg;
228     dst->handle = handle.release();
229     return true;
230 }
231 
232 int
pgp_hash_add(pgp_hash_t * hash,const void * buf,size_t len)233 pgp_hash_add(pgp_hash_t *hash, const void *buf, size_t len)
234 {
235     if (!hash->handle) {
236         return -1;
237     }
238 
239     try {
240         static_cast<Botan::HashFunction *>(hash->handle)
241           ->update(static_cast<const uint8_t *>(buf), len);
242     } catch (std::exception &ex) {
243         RNP_LOG("Error adding to HashFunction ('%s')", ex.what());
244         return -2;
245     }
246     return 0;
247 }
248 
249 size_t
pgp_hash_finish(pgp_hash_t * hash,uint8_t * out)250 pgp_hash_finish(pgp_hash_t *hash, uint8_t *out)
251 {
252     if (!hash || !hash->handle) {
253         return 0;
254     }
255 
256     Botan::HashFunction *hash_fn = static_cast<Botan::HashFunction *>(hash->handle);
257     if (!hash_fn) {
258         RNP_LOG("Hash finalization failed");
259         return 0;
260     }
261 
262     size_t outlen = hash->_output_len;
263     hash->handle = NULL;
264     try {
265         if (out) {
266             hash_fn->final(out);
267         }
268         delete hash_fn;
269     } catch (std::exception &ex) {
270         RNP_LOG("Error finishing HashFunction ('%s')", ex.what());
271         outlen = 0;
272     }
273     hash->_output_len = 0;
274     return outlen;
275 }
276 
277 pgp_hash_alg_t
pgp_hash_alg_type(const pgp_hash_t * hash)278 pgp_hash_alg_type(const pgp_hash_t *hash)
279 {
280     return hash->_alg;
281 }
282 
283 size_t
pgp_digest_length(pgp_hash_alg_t alg)284 pgp_digest_length(pgp_hash_alg_t alg)
285 {
286     size_t val = 0;
287     ARRAY_LOOKUP_BY_ID(hash_alg_map, type, digest_size, alg, val);
288     return val;
289 }
290 
291 bool
pgp_hash_list_add(std::vector<pgp_hash_t> & hashes,pgp_hash_alg_t alg)292 pgp_hash_list_add(std::vector<pgp_hash_t> &hashes, pgp_hash_alg_t alg)
293 {
294     pgp_hash_t hash = {0};
295     if (!pgp_hash_list_get(hashes, alg)) {
296         if (!pgp_hash_create(&hash, alg)) {
297             RNP_LOG("failed to initialize hash algorithm %d", (int) alg);
298             return false;
299         }
300         try {
301             hashes.push_back(hash);
302         } catch (const std::exception &e) {
303             RNP_LOG("%s", e.what());
304             pgp_hash_finish(&hash, NULL);
305             return false;
306         }
307     }
308     return true;
309 }
310 
311 const pgp_hash_t *
pgp_hash_list_get(std::vector<pgp_hash_t> & hashes,pgp_hash_alg_t alg)312 pgp_hash_list_get(std::vector<pgp_hash_t> &hashes, pgp_hash_alg_t alg)
313 {
314     for (auto &hash : hashes) {
315         if (pgp_hash_alg_type(&hash) == alg) {
316             return &hash;
317         }
318     }
319     return NULL;
320 }
321 
322 void
pgp_hash_list_update(std::vector<pgp_hash_t> & hashes,const void * buf,size_t len)323 pgp_hash_list_update(std::vector<pgp_hash_t> &hashes, const void *buf, size_t len)
324 {
325     for (auto &hash : hashes) {
326         pgp_hash_add(&hash, buf, len);
327     }
328 }
329 
330 bool
pgp_hash_uint32(pgp_hash_t * hash,uint32_t n)331 pgp_hash_uint32(pgp_hash_t *hash, uint32_t n)
332 {
333     uint8_t ibuf[4];
334     STORE32BE(ibuf, n);
335     return !pgp_hash_add(hash, ibuf, sizeof(ibuf));
336 }
337