File Coverage

_rhash.c
Criterion Covered Total %
statement 252 479 52.6
branch 123 306 40.2
condition n/a
subroutine n/a
pod n/a
total 375 785 47.7


line stmt bran cond sub pod time code
1             /* rhash.c - implementation of LibRHash library calls
2             *
3             * Copyright (c) 2008, Aleksey Kravchenko
4             *
5             * Permission to use, copy, modify, and/or distribute this software for any
6             * purpose with or without fee is hereby granted.
7             *
8             * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9             * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10             * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11             * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12             * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13             * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14             * PERFORMANCE OF THIS SOFTWARE.
15             */
16              
17             /* modifier for Windows DLL */
18             #if (defined(_WIN32) || defined(__CYGWIN__)) && defined(RHASH_EXPORTS)
19             # define RHASH_API __declspec(dllexport)
20             #endif
21              
22             /* macros for large file support, must be defined before any include file */
23             #define _LARGEFILE_SOURCE
24             #define _LARGEFILE64_SOURCE
25             #define _FILE_OFFSET_BITS 64
26              
27             #include "rhash.h"
28             #include "algorithms.h"
29             #include "byte_order.h"
30             #include "hex.h"
31             #include "plug_openssl.h"
32             #include "torrent.h"
33             #include "util.h"
34             #include
35             #include
36             #include
37             #include
38             #include
39              
40             #define STATE_ACTIVE 0xb01dbabe
41             #define STATE_STOPPED 0xdeadbeef
42             #define STATE_DELETED 0xdecea5ed
43             #define IS_BAD_STATE(s) ((s) != STATE_ACTIVE && (s) != STATE_STOPPED)
44             #define RCTX_AUTO_FINAL 0x1
45             #define RCTX_FINALIZED 0x2
46             #define RCTX_FINALIZED_MASK (RCTX_AUTO_FINAL | RCTX_FINALIZED)
47             #define RHPR_FORMAT (RHPR_RAW | RHPR_HEX | RHPR_BASE32 | RHPR_BASE64)
48             #define RHPR_MODIFIER (RHPR_UPPERCASE | RHPR_URLENCODE | RHPR_REVERSE)
49              
50             #define HAS_ZERO_OR_ONE_BIT(id) (((id) & ((id) - 1)) == 0)
51             #define IS_VALID_HASH_MASK(bitmask) ((bitmask) != 0 && ((bitmask) & ~RHASH_ALL_HASHES) == 0)
52             #define IS_VALID_HASH_ID(id) (IS_VALID_HASH_MASK(id) && HAS_ZERO_OR_ONE_BIT(id))
53              
54             /* each hash function context must be aligned to DEFAULT_ALIGNMENT bytes */
55             #define GET_CTX_ALIGNED(size) ALIGN_SIZE_BY((size), DEFAULT_ALIGNMENT)
56             #define GET_EXPORT_ALIGNED(size) ALIGN_SIZE_BY((size), 8)
57              
58 3           RHASH_API void rhash_library_init(void)
59             {
60 3           rhash_init_algorithms(RHASH_ALL_HASHES);
61             #ifdef USE_OPENSSL
62             rhash_plug_openssl();
63             #endif
64 3           }
65              
66 1           RHASH_API int rhash_count(void)
67             {
68 1           return rhash_info_size;
69             }
70              
71             /* LOW-LEVEL LIBRHASH INTERFACE */
72              
73             /**
74             * Allocate and initialize RHash context for calculating a single or multiple hash functions.
75             * The context after usage must be freed by calling rhash_free().
76             *
77             * @param count the size of the hash_ids array, the count must be greater than zero
78             * @param hash_ids array of identifiers of hash functions. Each element must
79             * be an identifier of one hash function
80             * @param need_init initialize context for each hash function
81             * @return initialized rhash context, NULL on fail with error code stored in errno
82             */
83 6           static rhash_context_ext* rhash_alloc_multi(size_t count, const unsigned hash_ids[], int need_init)
84             {
85             struct rhash_hash_info* info; /* hash algorithm information */
86 6           rhash_context_ext* rctx = NULL; /* allocated rhash context */
87 6           const size_t header_size = GET_CTX_ALIGNED(sizeof(rhash_context_ext) + sizeof(rhash_vector_item) * count);
88 6           size_t ctx_size_sum = 0; /* size of hash contexts to store in rctx */
89             size_t i;
90             char* phash_ctx;
91 6           unsigned hash_bitmask = 0;
92              
93 6 50         if (count < 1) {
94 0           errno = EINVAL;
95 0           return NULL;
96             }
97 6 100         if (count == 1 && hash_ids[0] == RHASH_ALL_HASHES)
    50          
98 0           hash_ids = rhash_get_all_hash_ids(&count);
99 74 100         for (i = 0; i < count; i++) {
100             unsigned hash_index;
101 68 50         if (!IS_VALID_HASH_ID(hash_ids[i])) {
    50          
    50          
102 0           errno = EINVAL;
103 0           return NULL;
104             }
105 68           hash_bitmask |= hash_ids[i];
106 68           hash_index = rhash_ctz(hash_ids[i]);
107 68 50         assert(hash_index < RHASH_HASH_COUNT); /* correct until extended hash_ids are supported */
108 68           info = &rhash_info_table[hash_index];
109              
110             /* align context sizes and sum up */
111 68           ctx_size_sum += GET_CTX_ALIGNED(info->context_size);
112             }
113              
114             /* allocate rhash context with enough memory to store contexts of all selected hash functions */
115 6           rctx = (rhash_context_ext*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, header_size + ctx_size_sum);
116 6 50         if (rctx == NULL)
117 0           return NULL;
118              
119             /* initialize common fields of the rhash context */
120 6           memset(rctx, 0, header_size);
121 6           rctx->rc.hash_id = hash_bitmask;
122 6           rctx->flags = RCTX_AUTO_FINAL; /* turn on auto-final by default */
123 6           rctx->state = STATE_ACTIVE;
124 6           rctx->hash_vector_size = count;
125              
126             /* calculate aligned pointer >= (&rctx->vector[count]) */
127 6           phash_ctx = (char*)rctx + header_size;
128 6 50         assert(phash_ctx >= (char*)&rctx->vector[count]);
129 6 50         assert(phash_ctx < ((char*)&rctx->vector[count] + DEFAULT_ALIGNMENT));
130              
131 74 100         for (i = 0; i < count; i++) {
132 68           unsigned hash_index = rhash_ctz(hash_ids[i]);
133 68           info = &rhash_info_table[hash_index];
134 68 50         assert(info->context_size > 0);
135 68 50         assert(info->init != NULL);
136 68 50         assert(IS_PTR_ALIGNED_BY(phash_ctx, DEFAULT_ALIGNMENT)); /* hash context is aligned */
137              
138 68           rctx->vector[i].hash_info = info;
139 68           rctx->vector[i].context = phash_ctx;
140              
141             /* BTIH initialization is a bit complicated, so store the context pointer for later usage */
142 68 100         if ((hash_ids[i] & RHASH_BTIH) != 0)
143 2           rctx->bt_ctx = phash_ctx;
144 68           phash_ctx += GET_CTX_ALIGNED(info->context_size);
145              
146             /* initialize the i-th hash context */
147 68 50         if (need_init)
148 68           info->init(rctx->vector[i].context);
149             }
150 6           return rctx;
151             }
152              
153 6           RHASH_API rhash rhash_init_multi(size_t count, const unsigned hash_ids[])
154             {
155 6           rhash_context_ext* ectx = rhash_alloc_multi(count, hash_ids, 1);
156 6           return &ectx->rc; /* return initialized rhash context */
157             }
158              
159 1           RHASH_API rhash rhash_init(unsigned hash_id)
160             {
161 1 50         if (hash_id == RHASH_ALL_HASHES) {
162             size_t count;
163 0           const unsigned* hash_ids = rhash_get_all_hash_ids(&count);
164 0           return rhash_init_multi(count, hash_ids);
165             }
166 1 50         if (!IS_VALID_HASH_MASK(hash_id)) {
    50          
167 0           errno = EINVAL;
168 0           return NULL;
169             }
170 1 50         if (HAS_ZERO_OR_ONE_BIT(hash_id)) {
171 1           return rhash_init_multi(1, &hash_id);
172             } else {
173             /* handle the deprecated case, when hash_id is a bitwise union of several hash function identifiers */
174             size_t count;
175             unsigned hash_ids[32];
176 0           unsigned id = hash_id & -hash_id; /* get the trailing bit */
177 0 0         for (count = 0; id <= hash_id; id = id << 1) {
178 0 0         assert(id != 0);
179 0 0         if (hash_id & id)
180 0           hash_ids[count++] = id;
181             }
182 0 0         assert(count > 1);
183 0           return rhash_init_multi(count, hash_ids);
184             }
185             }
186              
187 4           void rhash_free(rhash ctx)
188             {
189 4           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
190             unsigned i;
191              
192 4 50         if (ctx == 0) return;
193 4           ectx->state = STATE_DELETED; /* mark memory block as being removed */
194              
195             /* clean the hash functions, which require additional clean up */
196 39 100         for (i = 0; i < ectx->hash_vector_size; i++) {
197 35           struct rhash_hash_info* info = ectx->vector[i].hash_info;
198 35 100         if (info->cleanup != 0) {
199 2           info->cleanup(ectx->vector[i].context);
200             }
201             }
202 4           rhash_aligned_free(ectx);
203             }
204              
205 3           RHASH_API void rhash_reset(rhash ctx)
206             {
207 3           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
208             unsigned i;
209              
210 3 50         assert(ectx->hash_vector_size > 0);
211 3 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
212              
213             /* re-initialize every hash in a loop */
214 7 100         for (i = 0; i < ectx->hash_vector_size; i++) {
215 4           struct rhash_hash_info* info = ectx->vector[i].hash_info;
216 4 50         if (info->cleanup != 0) {
217 0           info->cleanup(ectx->vector[i].context);
218             }
219              
220 4 50         assert(info->init != NULL);
221 4           info->init(ectx->vector[i].context);
222             }
223 3           ectx->flags &= ~RCTX_FINALIZED; /* clear finalized state */
224 3           ectx->state = STATE_ACTIVE; /* re-activate the structure */
225 3           ctx->msg_size = 0;
226 3           }
227              
228 8           RHASH_API int rhash_update(rhash ctx, const void* message, size_t length)
229             {
230 8           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
231             unsigned i;
232              
233 8 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
234 8 50         if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */
235              
236 8           ctx->msg_size += length;
237              
238             /* call update method for every algorithm */
239 78 100         for (i = 0; i < ectx->hash_vector_size; i++) {
240 70           struct rhash_hash_info* info = ectx->vector[i].hash_info;
241 70 50         assert(info->update != 0);
242 70           info->update(ectx->vector[i].context, message, length);
243             }
244 8           return 0; /* no error processing at the moment */
245             }
246              
247 9           RHASH_API int rhash_final(rhash ctx, unsigned char* first_result)
248             {
249 9           unsigned i = 0;
250             unsigned char buffer[130];
251 9 100         unsigned char* out = (first_result ? first_result : buffer);
252 9           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
253 9 50         assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
254              
255             /* skip final call if already finalized and auto-final is on */
256 9 50         if ((ectx->flags & RCTX_FINALIZED_MASK) ==
257 0           (RCTX_AUTO_FINAL | RCTX_FINALIZED)) return 0;
258              
259             /* call final method for every algorithm */
260 81 100         for (i = 0; i < ectx->hash_vector_size; i++) {
261 72           struct rhash_hash_info* info = ectx->vector[i].hash_info;
262 72 50         assert(info->final != 0);
263 72 50         assert(info->info->digest_size < sizeof(buffer));
264 72           info->final(ectx->vector[i].context, out);
265 72           out = buffer;
266             }
267 9           ectx->flags |= RCTX_FINALIZED;
268 9           return 0; /* no error processing at the moment */
269             }
270              
271             /**
272             * Header block for rhash context import/export.
273             */
274             typedef struct export_header
275             {
276             uint32_t state;
277             uint16_t hash_vector_size;
278             uint16_t flags;
279             uint64_t msg_size;
280             } export_header;
281              
282             /**
283             * Process export error. Returns 0 and set errno to EINVAL.
284             *
285             * @return NULL
286             */
287 0           static size_t export_error_einval(void)
288             {
289 0           errno = EINVAL;
290 0           return 0;
291             }
292              
293             /**
294             * Process import error. Returns NULL and set errno to EINVAL.
295             *
296             * @return NULL
297             */
298 0           static rhash import_error_einval(void)
299             {
300 0           errno = EINVAL;
301 0           return NULL;
302             }
303              
304 0           RHASH_API size_t rhash_export(rhash ctx, void* out, size_t size)
305             {
306             #if !defined(NO_IMPORT_EXPORT)
307             size_t export_size;
308             size_t i;
309 0           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
310 0           export_header* header = (export_header*)out;
311 0           unsigned* hash_ids = NULL;
312 0 0         if (!ctx || (out && size < sizeof(export_header)) || IS_BAD_STATE(ectx->state))
    0          
    0          
    0          
    0          
313 0           return export_error_einval();
314 0           export_size = sizeof(export_header) + sizeof(unsigned) * ectx->hash_vector_size;
315 0 0         if (out != NULL) {
316 0           memset(out, 0, size);
317 0           header->state = ectx->state;
318 0           header->hash_vector_size = (uint16_t)(ectx->hash_vector_size);
319 0           header->flags = (uint16_t)(ectx->flags);
320 0           header->msg_size = ctx->msg_size;
321 0           hash_ids = (unsigned*)(void*)(header + 1);
322             }
323 0 0         for (i = 0; i < ectx->hash_vector_size; i++) {
324 0           void* src_context = ectx->vector[i].context;
325 0           struct rhash_hash_info* hash_info = ectx->vector[i].hash_info;
326 0           unsigned is_special = (hash_info->info->flags & F_SPCEXP);
327             size_t item_size;
328 0 0         if (out != NULL) {
329 0 0         if (size <= export_size)
330 0           return export_error_einval();
331 0           hash_ids[i] = hash_info->info->hash_id;
332 0 0         if (is_special) {
333             char* dst_item;
334             size_t left_size;
335 0           export_size = GET_EXPORT_ALIGNED(export_size);
336 0           dst_item = (char*)out + export_size;
337 0           left_size = size - export_size;
338 0           item_size = rhash_export_alg(hash_info->info->hash_id,
339             src_context, dst_item, left_size);
340 0 0         if (!item_size)
341 0           return export_error_einval();
342             } else {
343 0           char* dst_item = (char*)out + export_size;
344 0           item_size = hash_info->context_size;
345 0 0         if (size < (export_size + item_size))
346 0           return export_error_einval();
347 0           memcpy(dst_item, src_context, item_size);
348             }
349             } else {
350 0 0         if (is_special) {
351 0           export_size = GET_EXPORT_ALIGNED(export_size);
352 0           item_size = rhash_export_alg(
353 0           hash_info->info->hash_id, src_context, NULL, 0);
354             } else
355 0           item_size = hash_info->context_size;
356             }
357 0           export_size += item_size;
358             }
359 0 0         if (export_size < size)
360 0           return export_error_einval();
361 0           return export_size;
362             #else
363             return export_error_einval();
364             #endif /* !defined(NO_IMPORT_EXPORT) */
365             }
366              
367 0           RHASH_API rhash rhash_import(const void* in, size_t size)
368             {
369             #if !defined(NO_IMPORT_EXPORT)
370 0           const export_header* header = (const export_header*)in;
371             size_t i;
372             size_t imported_size;
373             const unsigned* hash_ids;
374             const char* src_item;
375             rhash_context_ext* ectx;
376 0 0         if (!header || IS_BAD_STATE(header->state) || size < sizeof(export_header))
    0          
    0          
    0          
377 0           return import_error_einval();
378 0           imported_size = sizeof(export_header) + sizeof(unsigned) * header->hash_vector_size;
379 0 0         if (!header->hash_vector_size || size < imported_size)
    0          
380 0           return import_error_einval();
381 0           hash_ids = (const unsigned*)(const void*)(header + 1);
382 0           ectx = (rhash_context_ext*)rhash_alloc_multi(header->hash_vector_size, hash_ids, 0);
383 0 0         if (!ectx)
384 0           return NULL; /* errno must be set by the previous function */
385 0           ectx->state = header->state;
386 0           ectx->hash_vector_size = header->hash_vector_size;
387 0           ectx->flags = header->flags;
388 0           ectx->rc.msg_size = header->msg_size;
389 0 0         for (i = 0; i < ectx->hash_vector_size; i++) {
390 0           void* dst_context = ectx->vector[i].context;
391 0           struct rhash_hash_info* hash_info = ectx->vector[i].hash_info;
392 0           unsigned is_special = (hash_info->info->flags & F_SPCEXP);
393             size_t item_size;
394              
395 0 0         if (is_special) {
396             size_t left_size;
397 0           imported_size = GET_EXPORT_ALIGNED(imported_size);
398 0           src_item = (const char*)in + imported_size;
399 0           left_size = size - imported_size;
400 0 0         assert(size >= imported_size);
401 0           item_size = rhash_import_alg(hash_ids[i], dst_context, src_item, left_size);
402 0           imported_size += item_size;
403 0 0         if (!item_size || size < imported_size) {
    0          
404 0           ectx->hash_vector_size = i + 1; /* clean only initialized contextes */
405 0           rhash_free(&ectx->rc);
406 0           return import_error_einval();
407             }
408             } else {
409 0           src_item = (const char*)in + imported_size;
410 0           item_size = hash_info->context_size;
411 0           imported_size += item_size;
412 0 0         if (size < imported_size) {
413 0           ectx->hash_vector_size = i + 1;
414 0           rhash_free(&ectx->rc);
415 0           return import_error_einval();
416             }
417 0           memcpy(dst_context, src_item, item_size);
418             }
419             }
420 0           return &ectx->rc;
421             #else
422             return import_error_einval();
423             #endif /* !defined(NO_IMPORT_EXPORT) */
424             }
425              
426             /**
427             * Find rhash_vector_item by the given hash_id in rhash context.
428             * The context must include the hash algorithm corresponding to the hash_id.
429             *
430             * @param ectx rhash context
431             * @param hash_id id of hash algorithm, or zero for the first hash algorithm in the rhash context
432             * @return item of the rhash context if the hash algorithm has been found, NULL otherwise
433             */
434 61           static rhash_vector_item* rhash_get_info(rhash_context_ext* ectx, unsigned hash_id)
435             {
436 61 50         assert(ectx);
437 61 50         assert(ectx->hash_vector_size > 0 && ectx->hash_vector_size <= RHASH_HASH_COUNT);
    50          
438              
439 61 100         if (hash_id == 0) {
440 5           return &ectx->vector[0]; /* get the first hash */
441             } else {
442             unsigned i;
443             rhash_vector_item* item;
444 577 50         for (i = 0; i < ectx->hash_vector_size; i++) {
445 577           item = &ectx->vector[i];
446 577 50         assert(item->hash_info != NULL);
447 577 50         assert(item->hash_info->info != NULL);
448 577 100         if (item->hash_info->info->hash_id == hash_id)
449 56           return item;
450             }
451             }
452 0           return NULL; /* hash_id not found */
453             }
454              
455             /**
456             * Store message digest from the given rhash context item.
457             *
458             * @param item rhash context item, containing a message digest
459             * @param result buffer to put the message digest into
460             */
461 52           static void rhash_put_digest(rhash_vector_item* item, unsigned char* result)
462             {
463 52           struct rhash_hash_info* info = item->hash_info;
464 52           unsigned char* digest = ((unsigned char*)item->context + info->digest_diff);
465              
466 52 100         if (info->info->flags & F_SWAP32) {
467 10 50         assert((info->info->digest_size & 3) == 0);
468             /* NB: the next call is correct only for multiple of 4 byte size */
469 10           rhash_swap_copy_str_to_u32(result, 0, digest, info->info->digest_size);
470 42 100         } else if (info->info->flags & F_SWAP64) {
471 3           rhash_swap_copy_u64_to_str(result, digest, info->info->digest_size);
472             } else {
473 39           memcpy(result, digest, info->info->digest_size);
474             }
475 52           }
476              
477 0           RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data)
478             {
479 0           ((rhash_context_ext*)ctx)->callback = callback;
480 0           ((rhash_context_ext*)ctx)->callback_data = callback_data;
481 0           }
482              
483             /* HIGH-LEVEL LIBRHASH INTERFACE */
484              
485 1           RHASH_API int rhash_msg(unsigned hash_id, const void* message, size_t length, unsigned char* result)
486             {
487             rhash ctx;
488 1           ctx = rhash_init(hash_id);
489 1 50         if (ctx == NULL) return -1;
490 1           rhash_update(ctx, message, length);
491 1           rhash_final(ctx, result);
492 1           rhash_free(ctx);
493 1           return 0;
494             }
495              
496 0           RHASH_API int rhash_file_update(rhash ctx, FILE* fd)
497             {
498 0           rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
499 0           const size_t block_size = 8192;
500             unsigned char* buffer;
501 0           size_t length = 0;
502 0           int res = 0;
503 0 0         if (ectx->state != STATE_ACTIVE)
504 0           return 0; /* do nothing if canceled */
505 0 0         if (ctx == NULL) {
506 0           errno = EINVAL;
507 0           return -1;
508             }
509 0           buffer = (unsigned char*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, block_size);
510 0 0         if (!buffer)
511 0           return -1; /* errno is set to ENOMEM according to UNIX 98 */
512              
513 0 0         while (!feof(fd)) {
514 0 0         if (ectx->state != STATE_ACTIVE)
515 0           break; /* stop if canceled */
516 0           length = fread(buffer, 1, block_size, fd);
517              
518 0 0         if (ferror(fd)) {
519 0           res = -1; /* note: errno contains error code */
520 0           break;
521 0 0         } else if (length) {
522 0           rhash_update(ctx, buffer, length);
523              
524 0 0         if (ectx->callback) {
525 0           ((rhash_callback_t)ectx->callback)(ectx->callback_data, ectx->rc.msg_size);
526             }
527             }
528             }
529 0           rhash_aligned_free(buffer);
530 0           return res;
531             }
532              
533             #ifdef _WIN32
534             # define FOPEN_MODE "rbS"
535             #else
536             # define FOPEN_MODE "rb"
537             #endif
538              
539 0           RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result)
540             {
541             FILE* fd;
542             rhash ctx;
543             int res;
544              
545 0           ctx = rhash_init(hash_id);
546 0 0         if (!ctx) {
547 0           return -1;
548             }
549 0           fd = fopen(filepath, FOPEN_MODE);
550 0 0         if (!fd) {
551 0           rhash_free(ctx);
552 0           return -1;
553             }
554 0           res = rhash_file_update(ctx, fd); /* hash the file */
555 0           fclose(fd);
556 0 0         if (res >= 0)
557 0           rhash_final(ctx, result);
558 0           rhash_free(ctx);
559 0           return res;
560             }
561              
562             #ifdef _WIN32 /* windows only function */
563             #include
564              
565             RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned char* result)
566             {
567             FILE* fd;
568             rhash ctx;
569             int res;
570              
571             ctx = rhash_init(hash_id);
572             if (!ctx) {
573             return -1;
574             }
575             fd = _wfsopen(filepath, L"rbS", _SH_DENYWR);
576             if (!fd) {
577             rhash_free(ctx);
578             return -1;
579             }
580             res = rhash_file_update(ctx, fd); /* hash the file */
581             fclose(fd);
582             if (res >= 0)
583             rhash_final(ctx, result);
584             rhash_free(ctx);
585             return res;
586             }
587             #endif
588              
589             /* RHash information functions */
590              
591 3           RHASH_API int rhash_is_base32(unsigned hash_id)
592             {
593             /* fast method is just to test a bit-mask */
594 3           return ((hash_id & (RHASH_TTH | RHASH_AICH)) != 0);
595             }
596              
597 2           RHASH_API int rhash_get_digest_size(unsigned hash_id)
598             {
599 2           hash_id &= RHASH_ALL_HASHES;
600 2 50         if (hash_id == 0 || (hash_id & (hash_id - 1)) != 0) return -1;
    50          
601 2           return (int)rhash_info_table[rhash_ctz(hash_id)].info->digest_size;
602             }
603              
604 1           RHASH_API int rhash_get_hash_length(unsigned hash_id)
605             {
606 1           const rhash_info* info = rhash_info_by_id(hash_id);
607 2 50         return (int)(info ? (info->flags & F_BS32 ?
    50          
608 1           BASE32_LENGTH(info->digest_size) : info->digest_size * 2) : 0);
609             }
610              
611 1           RHASH_API const char* rhash_get_name(unsigned hash_id)
612             {
613 1           const rhash_info* info = rhash_info_by_id(hash_id);
614 1 50         return (info ? info->name : 0);
615             }
616              
617 18           RHASH_API const char* rhash_get_magnet_name(unsigned hash_id)
618             {
619 18           const rhash_info* info = rhash_info_by_id(hash_id);
620 18 50         return (info ? info->magnet_name : 0);
621             }
622              
623 4           static size_t rhash_get_magnet_url_size(const char* filepath,
624             rhash_context_ext* ectx, unsigned hash_mask, int flags)
625             {
626 4           size_t size = 0; /* count terminating '\0' */
627             unsigned bit;
628              
629             /* RHPR_NO_MAGNET, RHPR_FILESIZE */
630 4 50         if ((flags & RHPR_NO_MAGNET) == 0) {
631 4           size += 8;
632             }
633              
634 4 50         if ((flags & RHPR_FILESIZE) != 0) {
635 4           uint64_t num = ectx->rc.msg_size;
636              
637 4           size += 4;
638 4 50         if (num == 0) size++;
639             else {
640 8 100         for (; num; num /= 10, size++);
641             }
642             }
643              
644 4 100         if (filepath) {
645 2           size += 4 + rhash_urlencode(NULL, filepath, strlen(filepath), 0);
646             }
647 4 50         if (!hash_mask) {
648 0           return size;
649             }
650              
651             /* loop through hash values */
652 14 100         for (bit = hash_mask & -(int)hash_mask; bit <= hash_mask; bit <<= 1) {
653             const char* name;
654 10 100         if ((bit & hash_mask) == 0) continue;
655 9 50         if ((name = rhash_get_magnet_name(bit)) == 0) continue;
656              
657 9           size += (7 + 2) + strlen(name);
658 9           size += rhash_print(NULL, &ectx->rc, bit,
659 9 100         (bit & RHASH_SHA1 ? RHPR_BASE32 : 0));
660             }
661              
662 4           return size;
663             }
664              
665 8           RHASH_API size_t rhash_print_magnet(char* output, const char* filepath,
666             rhash context, unsigned hash_mask, int flags)
667             {
668 8           rhash_context_ext* const ectx = (rhash_context_ext*)context;
669             int i;
670 8           const char* begin = output;
671              
672 8 50         if (!IS_EXTENDED_RHASH_ID(ectx->rc.hash_id | hash_mask)) {
673 8           hash_mask &= context->hash_id;
674             }
675              
676 8 100         if (output == NULL)
677 4           return rhash_get_magnet_url_size(filepath, ectx, hash_mask, flags);
678              
679             /* RHPR_NO_MAGNET, RHPR_FILESIZE */
680 4 50         if ((flags & RHPR_NO_MAGNET) == 0) {
681 4           strcpy(output, "magnet:?");
682 4           output += 8;
683             }
684              
685 4 50         if ((flags & RHPR_FILESIZE) != 0) {
686 4           strcpy(output, "xl=");
687 4           output += 3;
688 4           output += rhash_sprintI64(output, context->msg_size);
689 4           *(output++) = '&';
690             }
691              
692 4           flags &= RHPR_UPPERCASE;
693 4 100         if (filepath) {
694 2           strcpy(output, "dn=");
695 2           output += 3;
696 2           output += rhash_urlencode(output, filepath, strlen(filepath), flags);
697 2           *(output++) = '&';
698             }
699              
700 12 100         for (i = 0; i <= 1; i++) {
701             unsigned bit;
702             static const unsigned print_first = (RHASH_ED2K | RHASH_AICH);
703 8 100         unsigned hash = (!i ? hash_mask & print_first : hash_mask & ~print_first);
704 8 100         if (!hash)
705 3           continue;
706              
707             /* loop through hash values */
708 14 100         for (bit = hash & -(int)hash; bit <= hash; bit <<= 1) {
709             const char* name;
710 9 50         if ((bit & hash) == 0) continue;
711 9 50         if (!(name = rhash_get_magnet_name(bit))) continue;
712              
713 9           strcpy(output, "xt=urn:");
714 9           output += 7;
715 9           strcpy(output, name);
716 9           output += strlen(name);
717 9           *(output++) = ':';
718 9           output += rhash_print(output, context, bit,
719 9 100         (bit & RHASH_SHA1 ? flags | RHPR_BASE32 : flags));
720 9           *(output++) = '&';
721             }
722             }
723 4           output[-1] = '\0'; /* terminate the line */
724              
725 4           return (output - begin);
726             }
727              
728              
729             /* HASH SUM OUTPUT INTERFACE */
730              
731 56           size_t rhash_print_bytes(char* output, const unsigned char* bytes, size_t size, int flags)
732             {
733             size_t result_length;
734 56           int upper_case = (flags & RHPR_UPPERCASE);
735 56           int format = (flags & ~RHPR_MODIFIER);
736              
737 56           switch (format) {
738 45           case RHPR_HEX:
739 45           result_length = size * 2;
740 45           rhash_byte_to_hex(output, bytes, size, upper_case);
741 45           break;
742 8           case RHPR_BASE32:
743 8           result_length = BASE32_LENGTH(size);
744 8           rhash_byte_to_base32(output, bytes, size, upper_case);
745 8           break;
746 2           case RHPR_BASE64:
747 2           result_length = rhash_base64_url_encoded_helper(output, bytes, size, (flags & RHPR_URLENCODE), upper_case);
748 2           break;
749 1           default:
750 1 50         if (flags & RHPR_URLENCODE) {
751 0           result_length = rhash_urlencode(output, (char*)bytes, size, upper_case);
752             } else {
753 1           memcpy(output, bytes, size);
754 1           result_length = size;
755             }
756 1           break;
757             }
758 56           return result_length;
759             }
760              
761 61           RHASH_API size_t rhash_print(char* output, rhash context, unsigned hash_id, int flags)
762             {
763 61           rhash_context_ext* ectx = (rhash_context_ext*)context;
764 61           rhash_vector_item* item = rhash_get_info(ectx, hash_id);
765             const rhash_info* info;
766             unsigned char digest[80];
767             size_t digest_size;
768              
769 61 50         if (!item || !item->hash_info || !item->hash_info->info) return 0;
    50          
    50          
770              
771 61           info = item->hash_info->info;
772 61           digest_size = info->digest_size;
773 61 50         assert(digest_size <= 64);
774              
775 61           flags &= (RHPR_FORMAT | RHPR_MODIFIER);
776 61 100         if ((flags & RHPR_FORMAT) == 0) {
777             /* use default format if not specified by flags */
778 53 100         flags |= (info->flags & RHASH_INFO_BASE32 ? RHPR_BASE32 : RHPR_HEX);
779             }
780              
781 61 100         if (output == NULL) {
782 9 50         size_t multiplier = (flags & RHPR_URLENCODE ? 3 : 1);
783 9           switch (flags & RHPR_FORMAT) {
784 6           case RHPR_HEX:
785 6           return (digest_size * 2);
786 3           case RHPR_BASE32:
787 3           return BASE32_LENGTH(digest_size);
788 0           case RHPR_BASE64:
789 0           return BASE64_LENGTH(digest_size) * multiplier;
790 0           default:
791 0           return digest_size * multiplier;
792             }
793             }
794              
795             /* finalize context if not yet finalized and auto-final is on */
796 52 100         if ((ectx->flags & RCTX_FINALIZED_MASK) == RCTX_AUTO_FINAL) {
797 5           rhash_final(context, NULL);
798             }
799              
800 52           rhash_put_digest(item, digest);
801              
802 52 100         if ((flags & ~RHPR_UPPERCASE) == (RHPR_REVERSE | RHPR_HEX)) {
803             /* reverse the digest */
804 1           unsigned char* p = digest;
805 1           unsigned char* r = digest + digest_size - 1;
806             char tmp;
807 9 100         for (; p < r; p++, r--) {
808 8           tmp = *p;
809 8           *p = *r;
810 8           *r = tmp;
811             }
812             }
813              
814 52           return rhash_print_bytes(output, digest, digest_size, flags);
815             }
816              
817             #if (defined(_WIN32) || defined(__CYGWIN__)) && defined(RHASH_EXPORTS)
818             #include
819             BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved);
820             BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved)
821             {
822             (void)hModule;
823             (void)reserved;
824             switch (reason) {
825             case DLL_PROCESS_ATTACH:
826             rhash_library_init();
827             break;
828             case DLL_PROCESS_DETACH:
829             /*rhash_library_free();*/
830             case DLL_THREAD_ATTACH:
831             case DLL_THREAD_DETACH:
832             break;
833             }
834             return TRUE;
835             }
836             #endif
837              
838             /* Helper macro */
839             #define ENSURE_THAT(condition) while(!(condition)) { return RHASH_ERROR; }
840              
841 0           static rhash_uptr_t rhash_get_algorithms_impl(const rhash_context_ext* ctx, size_t count, unsigned* data)
842             {
843             size_t i;
844 0 0         if (count != 0 && data != 0) {
    0          
845 0 0         ENSURE_THAT(ctx->hash_vector_size <= count);
846 0 0         for (i = 0; i < ctx->hash_vector_size; i++)
847 0           data[i] = ctx->vector[i].hash_info->info->hash_id;
848             }
849 0           return ctx->hash_vector_size;
850             }
851              
852 0           static size_t hash_bitmask_to_array(unsigned bitmask, size_t count, unsigned* data)
853             {
854             unsigned bits_count;
855 0           bitmask &= ((unsigned)RHASH_EXTENDED_BIT - 1);
856 0           bits_count = rhash_popcount(bitmask);
857 0 0         if (count > 0 && data != 0) {
    0          
858 0           size_t index = 0;
859 0 0         ENSURE_THAT(bits_count <= count);
860 0 0         for (unsigned bit = 1; bit <= bitmask; bit = bit << 1) {
861 0 0         if ((bitmask & bit) != 0)
862 0           data[index++] = bit;
863             }
864 0 0         ENSURE_THAT(bits_count == index);
865             }
866 0           return bits_count;
867             }
868              
869             #if defined(USE_OPENSSL) || defined(OPENSSL_RUNTIME)
870             static unsigned ids_array_to_hash_bitmask(size_t count, unsigned* data)
871             {
872             unsigned bitmask = 0;
873             size_t i;
874             for (i = 0; i < count; i++)
875             bitmask |= data[i];
876             return bitmask;
877             }
878             #endif
879              
880 1           RHASH_API size_t rhash_ctrl(rhash context, int cmd, size_t size, void* data)
881             {
882             /* cast to extented rhash context */
883 1           rhash_context_ext* const ctx = (rhash_context_ext*)context;
884 1           switch (cmd) {
885 0           case RMSG_GET_CONTEXT:
886             {
887             unsigned i;
888 0 0         ENSURE_THAT(data);
889 0 0         for (i = 0; i < ctx->hash_vector_size; i++) {
890 0           struct rhash_hash_info* info = ctx->vector[i].hash_info;
891 0 0         if (info->info->hash_id == (unsigned)size) {
892 0           *(void**)data = ctx->vector[i].context;
893 0           return 0;
894             }
895             }
896 0           return RHASH_ERROR;
897             }
898 0           case RMSG_CANCEL:
899             /* mark rhash context as canceled, in a multithreaded program */
900 0           atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPPED);
901 0           break;
902 0           case RMSG_IS_CANCELED:
903 0           return (ctx->state == STATE_STOPPED);
904 0           case RMSG_GET_FINALIZED:
905 0           return ((ctx->flags & RCTX_FINALIZED) != 0);
906 0           case RMSG_SET_AUTOFINAL:
907 0           ctx->flags &= ~RCTX_AUTO_FINAL;
908 0 0         if (size)
909 0           ctx->flags |= RCTX_AUTO_FINAL;
910 0           break;
911              
912 0           case RMSG_GET_ALL_ALGORITHMS:
913 0 0         if (data && size) {
    0          
914             const unsigned* hash_ids;
915 0 0         ENSURE_THAT(size >= RHASH_HASH_COUNT);
916 0           hash_ids = rhash_get_all_hash_ids(&size);
917 0           memcpy(data, hash_ids, size * sizeof(*hash_ids));
918             }
919 0           return RHASH_HASH_COUNT;
920 0           case RMSG_GET_CTX_ALGORITHMS:
921 0 0         ENSURE_THAT(ctx);
922 0           return rhash_get_algorithms_impl(ctx, size, (unsigned*)data);
923              
924 0           case RMSG_GET_OPENSSL_SUPPORTED:
925 0           return hash_bitmask_to_array(
926             rhash_get_openssl_supported_hash_mask(), size, (unsigned*)data);
927 0           case RMSG_GET_OPENSSL_AVAILABLE:
928 0           return hash_bitmask_to_array(
929             rhash_get_openssl_available_hash_mask(), size, (unsigned*)data);
930 0           case RMSG_GET_OPENSSL_ENABLED:
931 0           return hash_bitmask_to_array(
932             rhash_get_openssl_enabled_hash_mask(), size, (unsigned*)data);
933 0           case RMSG_SET_OPENSSL_ENABLED:
934 0 0         ENSURE_THAT(data || !size);
    0          
935             rhash_set_openssl_enabled_hash_mask(ids_array_to_hash_bitmask(size, (unsigned*)data));
936 0           break;
937              
938 1           case RMSG_GET_LIBRHASH_VERSION:
939 1           return RHASH_XVERSION;
940 0           default:
941 0           return RHASH_ERROR; /* unknown message */
942             }
943 0           return 0;
944             }
945              
946             /* Convert a rhash_uptr_t to a void* pointer. */
947             #define UPTR2PVOID(u) ((void*)((u) + 0))
948              
949             /* Deprecated function to control rhash, use rhash_ctrl() instead */
950 0           RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t ldata, rhash_uptr_t rdata)
951             {
952 0           rhash ctx = (rhash)dst;
953             (void)rdata;
954              
955 0           switch (msg_id) {
956 0           case RMSG_CANCEL:
957             case RMSG_IS_CANCELED:
958             case RMSG_GET_FINALIZED:
959             case RMSG_SET_AUTOFINAL:
960             case RMSG_GET_LIBRHASH_VERSION:
961 0           return rhash_ctrl(ctx, msg_id, (unsigned)ldata, UPTR2PVOID(rdata));
962              
963             /* Legacy messages, which operate openssl hash masks directly */
964             #ifdef USE_OPENSSL
965             case RMSG_SET_OPENSSL_MASK:
966             rhash_set_openssl_enabled_hash_mask((unsigned)ldata);
967             break;
968             case RMSG_GET_OPENSSL_MASK:
969             return rhash_get_openssl_enabled_hash_mask();
970             #endif
971 0           case RMSG_GET_OPENSSL_SUPPORTED_MASK:
972 0           return rhash_get_openssl_supported_hash_mask();
973 0           case RMSG_GET_OPENSSL_AVAILABLE_MASK:
974 0           return rhash_get_openssl_available_hash_mask();
975              
976 0           default:
977 0           return RHASH_ERROR; /* unknown message */
978             }
979             return 0;
980             }