/src/CMake/Utilities/cmlibrhash/librhash/rhash.c
Line | Count | Source |
1 | | /* rhash.c - implementation of LibRHash library calls |
2 | | * |
3 | | * Copyright (c) 2008, Aleksey Kravchenko <rhash.admin@gmail.com> |
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 "ustd.h" /* Need this first within CMake. */ |
28 | | |
29 | | #include "rhash.h" |
30 | | #include "algorithms.h" |
31 | | #include "byte_order.h" |
32 | | #include "hex.h" |
33 | | #include "util.h" |
34 | | #include <assert.h> |
35 | | #include <errno.h> |
36 | | #include <stdlib.h> |
37 | | #include <stddef.h> |
38 | | #include <string.h> |
39 | | |
40 | 0 | #define STATE_ACTIVE 0xb01dbabe |
41 | | #define STATE_STOPPED 0xdeadbeef |
42 | 0 | #define STATE_DELETED 0xdecea5ed |
43 | | #define IS_BAD_STATE(s) ((s) != STATE_ACTIVE && (s) != STATE_STOPPED) |
44 | 0 | #define RCTX_AUTO_FINAL 0x1 |
45 | 0 | #define RCTX_FINALIZED 0x2 |
46 | 0 | #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 | 0 | #define HAS_ZERO_OR_ONE_BIT(id) (((id) & ((id) - 1)) == 0) |
51 | 0 | #define IS_VALID_HASH_MASK(bitmask) ((bitmask) != 0 && ((bitmask) & ~RHASH_ALL_HASHES) == 0) |
52 | 0 | #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 | 0 | #define GET_CTX_ALIGNED(size) ALIGN_SIZE_BY((size), DEFAULT_ALIGNMENT) |
56 | | #define GET_EXPORT_ALIGNED(size) ALIGN_SIZE_BY((size), 8) |
57 | | |
58 | | RHASH_API void rhash_library_init(void) |
59 | 0 | { |
60 | 0 | rhash_init_algorithms(RHASH_ALL_HASHES); |
61 | | #ifdef USE_OPENSSL |
62 | | rhash_plug_openssl(); |
63 | | #endif |
64 | 0 | } |
65 | | |
66 | | RHASH_API int rhash_count(void) |
67 | 0 | { |
68 | 0 | return rhash_info_size; |
69 | 0 | } |
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 | | static rhash_context_ext* rhash_alloc_multi(size_t count, const unsigned hash_ids[], int need_init) |
84 | 0 | { |
85 | 0 | struct rhash_hash_info* info; /* hash algorithm information */ |
86 | 0 | rhash_context_ext* rctx = NULL; /* allocated rhash context */ |
87 | 0 | const size_t header_size = GET_CTX_ALIGNED(sizeof(rhash_context_ext) + sizeof(rhash_vector_item) * count); |
88 | 0 | size_t ctx_size_sum = 0; /* size of hash contexts to store in rctx */ |
89 | 0 | size_t i; |
90 | 0 | char* phash_ctx; |
91 | 0 | unsigned hash_bitmask = 0; |
92 | |
|
93 | 0 | if (count < 1) { |
94 | 0 | errno = EINVAL; |
95 | 0 | return NULL; |
96 | 0 | } |
97 | 0 | for (i = 0; i < count; i++) { |
98 | 0 | unsigned hash_index; |
99 | 0 | if (!IS_VALID_HASH_ID(hash_ids[i])) { |
100 | 0 | errno = EINVAL; |
101 | 0 | return NULL; |
102 | 0 | } |
103 | 0 | hash_bitmask |= hash_ids[i]; |
104 | 0 | hash_index = rhash_ctz(hash_ids[i]); |
105 | 0 | assert(hash_index < RHASH_HASH_COUNT); /* correct until extended hash_ids are supported */ |
106 | 0 | info = &rhash_info_table[hash_index]; |
107 | | |
108 | | /* align context sizes and sum up */ |
109 | 0 | ctx_size_sum += GET_CTX_ALIGNED(info->context_size); |
110 | 0 | } |
111 | | |
112 | | /* allocate rhash context with enough memory to store contexts of all selected hash functions */ |
113 | 0 | rctx = (rhash_context_ext*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, header_size + ctx_size_sum); |
114 | 0 | if (rctx == NULL) |
115 | 0 | return NULL; |
116 | | |
117 | | /* initialize common fields of the rhash context */ |
118 | 0 | memset(rctx, 0, header_size); |
119 | 0 | rctx->rc.hash_id = hash_bitmask; |
120 | 0 | rctx->flags = RCTX_AUTO_FINAL; /* turn on auto-final by default */ |
121 | 0 | rctx->state = STATE_ACTIVE; |
122 | 0 | rctx->hash_vector_size = count; |
123 | | |
124 | | /* calculate aligned pointer >= (&rctx->vector[count]) */ |
125 | 0 | phash_ctx = (char*)rctx + header_size; |
126 | 0 | assert(phash_ctx >= (char*)&rctx->vector[count]); |
127 | 0 | assert(phash_ctx < ((char*)&rctx->vector[count] + DEFAULT_ALIGNMENT)); |
128 | |
|
129 | 0 | for (i = 0; i < count; i++) { |
130 | 0 | unsigned hash_index = rhash_ctz(hash_ids[i]); |
131 | 0 | info = &rhash_info_table[hash_index]; |
132 | 0 | assert(info->context_size > 0); |
133 | 0 | assert(info->init != NULL); |
134 | 0 | assert(IS_PTR_ALIGNED_BY(phash_ctx, DEFAULT_ALIGNMENT)); /* hash context is aligned */ |
135 | |
|
136 | 0 | rctx->vector[i].hash_info = info; |
137 | 0 | rctx->vector[i].context = phash_ctx; |
138 | |
|
139 | | #if 0 |
140 | | /* BTIH initialization is a bit complicated, so store the context pointer for later usage */ |
141 | | if ((hash_ids[i] & RHASH_BTIH) != 0) |
142 | | rctx->bt_ctx = phash_ctx; |
143 | | #endif |
144 | 0 | phash_ctx += GET_CTX_ALIGNED(info->context_size); |
145 | | |
146 | | /* initialize the i-th hash context */ |
147 | 0 | if (need_init) |
148 | 0 | info->init(rctx->vector[i].context); |
149 | 0 | } |
150 | 0 | return rctx; |
151 | 0 | } |
152 | | |
153 | | RHASH_API rhash rhash_init_multi(size_t count, const unsigned hash_ids[]) |
154 | 0 | { |
155 | 0 | rhash_context_ext* ectx = rhash_alloc_multi(count, hash_ids, 1); |
156 | 0 | return &ectx->rc; /* return initialized rhash context */ |
157 | 0 | } |
158 | | |
159 | | RHASH_API rhash rhash_init(unsigned hash_id) |
160 | 0 | { |
161 | 0 | if (!IS_VALID_HASH_MASK(hash_id)) { |
162 | 0 | errno = EINVAL; |
163 | 0 | return NULL; |
164 | 0 | } |
165 | 0 | if (HAS_ZERO_OR_ONE_BIT(hash_id)) { |
166 | 0 | return rhash_init_multi(1, &hash_id); |
167 | 0 | } else { |
168 | | /* handle the depricated case, when hash_id is a bitwise union of several hash function identifiers */ |
169 | 0 | size_t count; |
170 | 0 | unsigned hash_ids[32]; |
171 | 0 | unsigned id = hash_id & -hash_id; /* get the trailing bit */ |
172 | 0 | for (count = 0; id <= hash_id; id = id << 1) { |
173 | 0 | assert(id != 0); |
174 | 0 | if (hash_id & id) |
175 | 0 | hash_ids[count++] = id; |
176 | 0 | } |
177 | 0 | assert(count > 1); |
178 | 0 | return rhash_init_multi(count, hash_ids); |
179 | 0 | } |
180 | 0 | } |
181 | | |
182 | | void rhash_free(rhash ctx) |
183 | 0 | { |
184 | 0 | rhash_context_ext* const ectx = (rhash_context_ext*)ctx; |
185 | 0 | unsigned i; |
186 | |
|
187 | 0 | if (ctx == 0) return; |
188 | 0 | ectx->state = STATE_DELETED; /* mark memory block as being removed */ |
189 | | |
190 | | /* clean the hash functions, which require additional clean up */ |
191 | 0 | for (i = 0; i < ectx->hash_vector_size; i++) { |
192 | 0 | struct rhash_hash_info* info = ectx->vector[i].hash_info; |
193 | 0 | if (info->cleanup != 0) { |
194 | 0 | info->cleanup(ectx->vector[i].context); |
195 | 0 | } |
196 | 0 | } |
197 | 0 | rhash_aligned_free(ectx); |
198 | 0 | } |
199 | | |
200 | | RHASH_API void rhash_reset(rhash ctx) |
201 | 0 | { |
202 | 0 | rhash_context_ext* const ectx = (rhash_context_ext*)ctx; |
203 | 0 | unsigned i; |
204 | |
|
205 | 0 | assert(ectx->hash_vector_size > 0); |
206 | 0 | assert(ectx->hash_vector_size <= RHASH_HASH_COUNT); |
207 | 0 | ectx->state = STATE_ACTIVE; /* re-activate the structure */ |
208 | | |
209 | | /* re-initialize every hash in a loop */ |
210 | 0 | for (i = 0; i < ectx->hash_vector_size; i++) { |
211 | 0 | struct rhash_hash_info* info = ectx->vector[i].hash_info; |
212 | 0 | if (info->cleanup != 0) { |
213 | 0 | info->cleanup(ectx->vector[i].context); |
214 | 0 | } |
215 | |
|
216 | 0 | assert(info->init != NULL); |
217 | 0 | info->init(ectx->vector[i].context); |
218 | 0 | } |
219 | 0 | ectx->flags &= ~RCTX_FINALIZED; /* clear finalized state */ |
220 | 0 | } |
221 | | |
222 | | RHASH_API int rhash_update(rhash ctx, const void* message, size_t length) |
223 | 0 | { |
224 | 0 | rhash_context_ext* const ectx = (rhash_context_ext*)ctx; |
225 | 0 | unsigned i; |
226 | |
|
227 | 0 | assert(ectx->hash_vector_size <= RHASH_HASH_COUNT); |
228 | 0 | if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */ |
229 | | |
230 | 0 | ctx->msg_size += length; |
231 | | |
232 | | /* call update method for every algorithm */ |
233 | 0 | for (i = 0; i < ectx->hash_vector_size; i++) { |
234 | 0 | struct rhash_hash_info* info = ectx->vector[i].hash_info; |
235 | 0 | assert(info->update != 0); |
236 | 0 | info->update(ectx->vector[i].context, message, length); |
237 | 0 | } |
238 | 0 | return 0; /* no error processing at the moment */ |
239 | 0 | } |
240 | | |
241 | | RHASH_API int rhash_final(rhash ctx, unsigned char* first_result) |
242 | 0 | { |
243 | 0 | unsigned i = 0; |
244 | 0 | unsigned char buffer[130]; |
245 | 0 | unsigned char* out = (first_result ? first_result : buffer); |
246 | 0 | rhash_context_ext* const ectx = (rhash_context_ext*)ctx; |
247 | 0 | assert(ectx->hash_vector_size <= RHASH_HASH_COUNT); |
248 | | |
249 | | /* skip final call if already finalized and auto-final is on */ |
250 | 0 | if ((ectx->flags & RCTX_FINALIZED_MASK) == |
251 | 0 | (RCTX_AUTO_FINAL | RCTX_FINALIZED)) return 0; |
252 | | |
253 | | /* call final method for every algorithm */ |
254 | 0 | for (i = 0; i < ectx->hash_vector_size; i++) { |
255 | 0 | struct rhash_hash_info* info = ectx->vector[i].hash_info; |
256 | 0 | assert(info->final != 0); |
257 | 0 | assert(info->info->digest_size < sizeof(buffer)); |
258 | 0 | info->final(ectx->vector[i].context, out); |
259 | 0 | out = buffer; |
260 | 0 | } |
261 | 0 | ectx->flags |= RCTX_FINALIZED; |
262 | 0 | return 0; /* no error processing at the moment */ |
263 | 0 | } |
264 | | |
265 | | /** |
266 | | * Header block for rhash context import/export. |
267 | | */ |
268 | | typedef struct export_header |
269 | | { |
270 | | uint32_t state; |
271 | | uint16_t hash_vector_size; |
272 | | uint16_t flags; |
273 | | uint64_t msg_size; |
274 | | } export_header; |
275 | | |
276 | | /** |
277 | | * Process export error. Returns 0 and set errno to EINVAL. |
278 | | * |
279 | | * @return NULL |
280 | | */ |
281 | | static size_t export_error_einval(void) |
282 | 0 | { |
283 | 0 | errno = EINVAL; |
284 | 0 | return 0; |
285 | 0 | } |
286 | | |
287 | | /** |
288 | | * Process import error. Returns NULL and set errno to EINVAL. |
289 | | * |
290 | | * @return NULL |
291 | | */ |
292 | | static rhash import_error_einval(void) |
293 | 0 | { |
294 | 0 | errno = EINVAL; |
295 | 0 | return NULL; |
296 | 0 | } |
297 | | |
298 | | RHASH_API size_t rhash_export(rhash ctx, void* out, size_t size) |
299 | 0 | { |
300 | | #if !defined(NO_IMPORT_EXPORT) |
301 | | size_t export_size; |
302 | | size_t i; |
303 | | rhash_context_ext* const ectx = (rhash_context_ext*)ctx; |
304 | | export_header* header = (export_header*)out; |
305 | | unsigned* hash_ids = NULL; |
306 | | if (!ctx || (out && size < sizeof(export_header)) || IS_BAD_STATE(ectx->state)) |
307 | | return export_error_einval(); |
308 | | export_size = sizeof(export_header) + sizeof(unsigned) * ectx->hash_vector_size; |
309 | | if (out != NULL) { |
310 | | memset(out, 0, size); |
311 | | header->state = ectx->state; |
312 | | header->hash_vector_size = (uint16_t)(ectx->hash_vector_size); |
313 | | header->flags = (uint16_t)(ectx->flags); |
314 | | header->msg_size = ctx->msg_size; |
315 | | hash_ids = (unsigned*)(void*)(header + 1); |
316 | | } |
317 | | for (i = 0; i < ectx->hash_vector_size; i++) { |
318 | | void* src_context = ectx->vector[i].context; |
319 | | struct rhash_hash_info* hash_info = ectx->vector[i].hash_info; |
320 | | unsigned is_special = (hash_info->info->flags & F_SPCEXP); |
321 | | size_t item_size; |
322 | | if (out != NULL) { |
323 | | if (size <= export_size) |
324 | | return export_error_einval(); |
325 | | hash_ids[i] = hash_info->info->hash_id; |
326 | | if (is_special) { |
327 | | char* dst_item; |
328 | | size_t left_size; |
329 | | export_size = GET_EXPORT_ALIGNED(export_size); |
330 | | dst_item = (char*)out + export_size; |
331 | | left_size = size - export_size; |
332 | | item_size = rhash_export_alg(hash_info->info->hash_id, |
333 | | src_context, dst_item, left_size); |
334 | | if (!item_size) |
335 | | return export_error_einval(); |
336 | | } else { |
337 | | char* dst_item = (char*)out + export_size; |
338 | | item_size = hash_info->context_size; |
339 | | if (size < (export_size + item_size)) |
340 | | return export_error_einval(); |
341 | | memcpy(dst_item, src_context, item_size); |
342 | | } |
343 | | } else { |
344 | | if (is_special) { |
345 | | export_size = GET_EXPORT_ALIGNED(export_size); |
346 | | item_size = rhash_export_alg( |
347 | | hash_info->info->hash_id, src_context, NULL, 0); |
348 | | } else |
349 | | item_size = hash_info->context_size; |
350 | | } |
351 | | export_size += item_size; |
352 | | } |
353 | | if (export_size < size) |
354 | | return export_error_einval(); |
355 | | return export_size; |
356 | | #else |
357 | 0 | return export_error_einval(); |
358 | 0 | #endif /* !defined(NO_IMPORT_EXPORT) */ |
359 | 0 | } |
360 | | |
361 | | RHASH_API rhash rhash_import(const void* in, size_t size) |
362 | 0 | { |
363 | | #if !defined(NO_IMPORT_EXPORT) |
364 | | const export_header* header = (const export_header*)in; |
365 | | size_t i; |
366 | | size_t imported_size; |
367 | | const unsigned* hash_ids; |
368 | | const char* src_item; |
369 | | rhash_context_ext* ectx; |
370 | | if (!header || IS_BAD_STATE(header->state) || size < sizeof(export_header)) |
371 | | return import_error_einval(); |
372 | | imported_size = sizeof(export_header) + sizeof(unsigned) * header->hash_vector_size; |
373 | | if (!header->hash_vector_size || size < imported_size) |
374 | | return import_error_einval(); |
375 | | hash_ids = (const unsigned*)(const void*)(header + 1); |
376 | | ectx = (rhash_context_ext*)rhash_alloc_multi(header->hash_vector_size, hash_ids, 0); |
377 | | if (!ectx) |
378 | | return NULL; /* errno must be set by the previous function */ |
379 | | ectx->state = header->state; |
380 | | ectx->hash_vector_size = header->hash_vector_size; |
381 | | ectx->flags = header->flags; |
382 | | ectx->rc.msg_size = header->msg_size; |
383 | | for (i = 0; i < ectx->hash_vector_size; i++) { |
384 | | void* dst_context = ectx->vector[i].context; |
385 | | struct rhash_hash_info* hash_info = ectx->vector[i].hash_info; |
386 | | unsigned is_special = (hash_info->info->flags & F_SPCEXP); |
387 | | size_t item_size; |
388 | | |
389 | | if (is_special) { |
390 | | size_t left_size; |
391 | | imported_size = GET_EXPORT_ALIGNED(imported_size); |
392 | | src_item = (const char*)in + imported_size; |
393 | | left_size = size - imported_size; |
394 | | assert(size >= imported_size); |
395 | | item_size = rhash_import_alg(hash_ids[i], dst_context, src_item, left_size); |
396 | | imported_size += item_size; |
397 | | if (!item_size || size < imported_size) { |
398 | | ectx->hash_vector_size = i + 1; /* clean only initialized contextes */ |
399 | | rhash_free(&ectx->rc); |
400 | | return import_error_einval(); |
401 | | } |
402 | | } else { |
403 | | src_item = (const char*)in + imported_size; |
404 | | item_size = hash_info->context_size; |
405 | | imported_size += item_size; |
406 | | if (size < imported_size) { |
407 | | ectx->hash_vector_size = i + 1; |
408 | | rhash_free(&ectx->rc); |
409 | | return import_error_einval(); |
410 | | } |
411 | | memcpy(dst_context, src_item, item_size); |
412 | | } |
413 | | } |
414 | | return &ectx->rc; |
415 | | #else |
416 | 0 | return import_error_einval(); |
417 | 0 | #endif /* !defined(NO_IMPORT_EXPORT) */ |
418 | 0 | } |
419 | | |
420 | | /** |
421 | | * Store digest for given hash_id. |
422 | | * If hash_id is zero, function stores digest for a hash with the lowest id found in the context. |
423 | | * For nonzero hash_id the context must contain it, otherwise function silently does nothing. |
424 | | * |
425 | | * @param ctx rhash context |
426 | | * @param hash_id id of hash to retrieve or zero for hash with the lowest available id |
427 | | * @param result buffer to put the hash into |
428 | | */ |
429 | | static void rhash_put_digest(rhash ctx, unsigned hash_id, unsigned char* result) |
430 | 0 | { |
431 | 0 | rhash_context_ext* const ectx = (rhash_context_ext*)ctx; |
432 | 0 | unsigned i; |
433 | 0 | rhash_vector_item* item; |
434 | 0 | struct rhash_hash_info* info; |
435 | 0 | unsigned char* digest; |
436 | 0 |
|
437 | 0 | assert(ectx); |
438 | 0 | assert(ectx->hash_vector_size > 0 && ectx->hash_vector_size <= RHASH_HASH_COUNT); |
439 | 0 |
|
440 | 0 | /* finalize context if not yet finalized and auto-final is on */ |
441 | 0 | if ((ectx->flags & RCTX_FINALIZED_MASK) == RCTX_AUTO_FINAL) { |
442 | 0 | rhash_final(ctx, NULL); |
443 | 0 | } |
444 | 0 |
|
445 | 0 | if (hash_id == 0) { |
446 | 0 | item = &ectx->vector[0]; /* get the first hash */ |
447 | 0 | info = item->hash_info; |
448 | 0 | } else { |
449 | 0 | for (i = 0;; i++) { |
450 | 0 | if (i >= ectx->hash_vector_size) { |
451 | 0 | return; /* hash_id not found, do nothing */ |
452 | 0 | } |
453 | 0 | item = &ectx->vector[i]; |
454 | 0 | info = item->hash_info; |
455 | 0 | if (info->info->hash_id == hash_id) break; |
456 | 0 | } |
457 | 0 | } |
458 | 0 | digest = ((unsigned char*)item->context + info->digest_diff); |
459 | 0 | if (info->info->flags & F_SWAP32) { |
460 | 0 | assert((info->info->digest_size & 3) == 0); |
461 | 0 | /* NB: the next call is correct only for multiple of 4 byte size */ |
462 | 0 | rhash_swap_copy_str_to_u32(result, 0, digest, info->info->digest_size); |
463 | 0 | } else if (info->info->flags & F_SWAP64) { |
464 | 0 | rhash_swap_copy_u64_to_str(result, digest, info->info->digest_size); |
465 | 0 | } else { |
466 | 0 | memcpy(result, digest, info->info->digest_size); |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | | RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data) |
471 | 0 | { |
472 | 0 | ((rhash_context_ext*)ctx)->callback = callback; |
473 | 0 | ((rhash_context_ext*)ctx)->callback_data = callback_data; |
474 | 0 | } |
475 | | |
476 | | /* HIGH-LEVEL LIBRHASH INTERFACE */ |
477 | | |
478 | | RHASH_API int rhash_msg(unsigned hash_id, const void* message, size_t length, unsigned char* result) |
479 | 0 | { |
480 | 0 | rhash ctx; |
481 | 0 | hash_id &= RHASH_ALL_HASHES; |
482 | 0 | ctx = rhash_init(hash_id); |
483 | 0 | if (ctx == NULL) return -1; |
484 | 0 | rhash_update(ctx, message, length); |
485 | 0 | rhash_final(ctx, result); |
486 | 0 | rhash_free(ctx); |
487 | 0 | return 0; |
488 | 0 | } |
489 | | |
490 | | RHASH_API int rhash_file_update(rhash ctx, FILE* fd) |
491 | 0 | { |
492 | 0 | rhash_context_ext* const ectx = (rhash_context_ext*)ctx; |
493 | 0 | const size_t block_size = 8192; |
494 | 0 | unsigned char* buffer; |
495 | 0 | size_t length = 0; |
496 | 0 | int res = 0; |
497 | 0 | if (ectx->state != STATE_ACTIVE) |
498 | 0 | return 0; /* do nothing if canceled */ |
499 | 0 | if (ctx == NULL) { |
500 | 0 | errno = EINVAL; |
501 | 0 | return -1; |
502 | 0 | } |
503 | 0 | buffer = (unsigned char*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, block_size); |
504 | 0 | if (!buffer) |
505 | 0 | return -1; /* errno is set to ENOMEM according to UNIX 98 */ |
506 | | |
507 | 0 | while (!feof(fd)) { |
508 | 0 | if (ectx->state != STATE_ACTIVE) |
509 | 0 | break; /* stop if canceled */ |
510 | 0 | length = fread(buffer, 1, block_size, fd); |
511 | |
|
512 | 0 | if (ferror(fd)) { |
513 | 0 | res = -1; /* note: errno contains error code */ |
514 | 0 | break; |
515 | 0 | } else if (length) { |
516 | 0 | rhash_update(ctx, buffer, length); |
517 | |
|
518 | 0 | if (ectx->callback) { |
519 | 0 | ((rhash_callback_t)ectx->callback)(ectx->callback_data, ectx->rc.msg_size); |
520 | 0 | } |
521 | 0 | } |
522 | 0 | } |
523 | 0 | rhash_aligned_free(buffer); |
524 | 0 | return res; |
525 | 0 | } |
526 | | |
527 | | #ifdef _WIN32 |
528 | | # define FOPEN_MODE "rbS" |
529 | | #else |
530 | 0 | # define FOPEN_MODE "rb" |
531 | | #endif |
532 | | |
533 | | RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result) |
534 | 0 | { |
535 | 0 | FILE* fd; |
536 | 0 | rhash ctx; |
537 | 0 | int res; |
538 | |
|
539 | 0 | hash_id &= RHASH_ALL_HASHES; |
540 | 0 | if (hash_id == 0) { |
541 | 0 | errno = EINVAL; |
542 | 0 | return -1; |
543 | 0 | } |
544 | | |
545 | 0 | fd = fopen(filepath, FOPEN_MODE); |
546 | 0 | if (!fd) |
547 | 0 | return -1; |
548 | | |
549 | 0 | ctx = rhash_init(hash_id); |
550 | 0 | if (!ctx) { |
551 | 0 | fclose(fd); |
552 | 0 | return -1; |
553 | 0 | } |
554 | 0 | res = rhash_file_update(ctx, fd); /* hash the file */ |
555 | 0 | fclose(fd); |
556 | 0 | if (res >= 0) |
557 | 0 | rhash_final(ctx, result); |
558 | 0 | rhash_free(ctx); |
559 | 0 | return res; |
560 | 0 | } |
561 | | |
562 | | #ifdef _WIN32 /* windows only function */ |
563 | | #include <share.h> |
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 | | hash_id &= RHASH_ALL_HASHES; |
572 | | if (hash_id == 0) { |
573 | | errno = EINVAL; |
574 | | return -1; |
575 | | } |
576 | | |
577 | | fd = _wfsopen(filepath, L"rbS", _SH_DENYWR); |
578 | | if (!fd) |
579 | | return -1; |
580 | | |
581 | | ctx = rhash_init(hash_id); |
582 | | if (!ctx) { |
583 | | fclose(fd); |
584 | | return -1; |
585 | | } |
586 | | res = rhash_file_update(ctx, fd); /* hash the file */ |
587 | | fclose(fd); |
588 | | if (res >= 0) |
589 | | rhash_final(ctx, result); |
590 | | rhash_free(ctx); |
591 | | return res; |
592 | | } |
593 | | #endif |
594 | | |
595 | | /* RHash information functions */ |
596 | | |
597 | | #if 0 |
598 | | RHASH_API int rhash_is_base32(unsigned hash_id) |
599 | | { |
600 | | /* fast method is just to test a bit-mask */ |
601 | | return ((hash_id & (RHASH_TTH | RHASH_AICH)) != 0); |
602 | | } |
603 | | #endif |
604 | | |
605 | | RHASH_API int rhash_get_digest_size(unsigned hash_id) |
606 | 0 | { |
607 | 0 | hash_id &= RHASH_ALL_HASHES; |
608 | 0 | if (hash_id == 0 || (hash_id & (hash_id - 1)) != 0) return -1; |
609 | 0 | return (int)rhash_info_table[rhash_ctz(hash_id)].info->digest_size; |
610 | 0 | } |
611 | | |
612 | | RHASH_API int rhash_get_hash_length(unsigned hash_id) |
613 | 0 | { |
614 | 0 | const rhash_info* info = rhash_info_by_id(hash_id); |
615 | 0 | return (int)(info ? (info->flags & F_BS32 ? |
616 | 0 | BASE32_LENGTH(info->digest_size) : info->digest_size * 2) : 0); |
617 | 0 | } |
618 | | |
619 | | RHASH_API const char* rhash_get_name(unsigned hash_id) |
620 | 0 | { |
621 | 0 | const rhash_info* info = rhash_info_by_id(hash_id); |
622 | 0 | return (info ? info->name : 0); |
623 | 0 | } |
624 | | |
625 | | RHASH_API const char* rhash_get_magnet_name(unsigned hash_id) |
626 | 0 | { |
627 | 0 | const rhash_info* info = rhash_info_by_id(hash_id); |
628 | 0 | return (info ? info->magnet_name : 0); |
629 | 0 | } |
630 | | |
631 | | #if 0 |
632 | | static size_t rhash_get_magnet_url_size(const char* filepath, |
633 | | rhash context, unsigned hash_mask, int flags) |
634 | | { |
635 | | size_t size = 0; /* count terminating '\0' */ |
636 | | unsigned bit, hash = context->hash_id & hash_mask; |
637 | | |
638 | | /* RHPR_NO_MAGNET, RHPR_FILESIZE */ |
639 | | if ((flags & RHPR_NO_MAGNET) == 0) { |
640 | | size += 8; |
641 | | } |
642 | | |
643 | | if ((flags & RHPR_FILESIZE) != 0) { |
644 | | uint64_t num = context->msg_size; |
645 | | |
646 | | size += 4; |
647 | | if (num == 0) size++; |
648 | | else { |
649 | | for (; num; num /= 10, size++); |
650 | | } |
651 | | } |
652 | | |
653 | | if (filepath) { |
654 | | size += 4 + rhash_urlencode(NULL, filepath, strlen(filepath), 0); |
655 | | } |
656 | | |
657 | | /* loop through hash values */ |
658 | | for (bit = hash & -(int)hash; bit <= hash; bit <<= 1) { |
659 | | const char* name; |
660 | | if ((bit & hash) == 0) continue; |
661 | | if ((name = rhash_get_magnet_name(bit)) == 0) continue; |
662 | | |
663 | | size += (7 + 2) + strlen(name); |
664 | | size += rhash_print(NULL, context, bit, |
665 | | (bit & RHASH_SHA1 ? RHPR_BASE32 : 0)); |
666 | | } |
667 | | |
668 | | return size; |
669 | | } |
670 | | |
671 | | RHASH_API size_t rhash_print_magnet(char* output, const char* filepath, |
672 | | rhash context, unsigned hash_mask, int flags) |
673 | | { |
674 | | int i; |
675 | | const char* begin = output; |
676 | | |
677 | | if (output == NULL) |
678 | | return rhash_get_magnet_url_size(filepath, context, hash_mask, flags); |
679 | | |
680 | | /* RHPR_NO_MAGNET, RHPR_FILESIZE */ |
681 | | if ((flags & RHPR_NO_MAGNET) == 0) { |
682 | | strcpy(output, "magnet:?"); |
683 | | output += 8; |
684 | | } |
685 | | |
686 | | if ((flags & RHPR_FILESIZE) != 0) { |
687 | | strcpy(output, "xl="); |
688 | | output += 3; |
689 | | output += rhash_sprintI64(output, context->msg_size); |
690 | | *(output++) = '&'; |
691 | | } |
692 | | |
693 | | flags &= RHPR_UPPERCASE; |
694 | | if (filepath) { |
695 | | strcpy(output, "dn="); |
696 | | output += 3; |
697 | | output += rhash_urlencode(output, filepath, strlen(filepath), flags); |
698 | | *(output++) = '&'; |
699 | | } |
700 | | |
701 | | for (i = 0; i < 2; i++) { |
702 | | unsigned bit; |
703 | | unsigned hash = context->hash_id & hash_mask; |
704 | | hash = (i == 0 ? hash & (RHASH_ED2K | RHASH_AICH) |
705 | | : hash & ~(RHASH_ED2K | RHASH_AICH)); |
706 | | if (!hash) continue; |
707 | | |
708 | | /* loop through hash values */ |
709 | | for (bit = hash & -(int)hash; bit <= hash; bit <<= 1) { |
710 | | const char* name; |
711 | | if ((bit & hash) == 0) continue; |
712 | | if (!(name = rhash_get_magnet_name(bit))) continue; |
713 | | |
714 | | strcpy(output, "xt=urn:"); |
715 | | output += 7; |
716 | | strcpy(output, name); |
717 | | output += strlen(name); |
718 | | *(output++) = ':'; |
719 | | output += rhash_print(output, context, bit, |
720 | | (bit & RHASH_SHA1 ? flags | RHPR_BASE32 : flags)); |
721 | | *(output++) = '&'; |
722 | | } |
723 | | } |
724 | | output[-1] = '\0'; /* terminate the line */ |
725 | | |
726 | | return (output - begin); |
727 | | } |
728 | | |
729 | | |
730 | | /* HASH SUM OUTPUT INTERFACE */ |
731 | | |
732 | | size_t rhash_print_bytes(char* output, const unsigned char* bytes, size_t size, int flags) |
733 | | { |
734 | | size_t result_length; |
735 | | int upper_case = (flags & RHPR_UPPERCASE); |
736 | | int format = (flags & ~RHPR_MODIFIER); |
737 | | |
738 | | switch (format) { |
739 | | case RHPR_HEX: |
740 | | result_length = size * 2; |
741 | | rhash_byte_to_hex(output, bytes, size, upper_case); |
742 | | break; |
743 | | case RHPR_BASE32: |
744 | | result_length = BASE32_LENGTH(size); |
745 | | rhash_byte_to_base32(output, bytes, size, upper_case); |
746 | | break; |
747 | | case RHPR_BASE64: |
748 | | result_length = rhash_base64_url_encoded_helper(output, bytes, size, (flags & RHPR_URLENCODE), upper_case); |
749 | | break; |
750 | | default: |
751 | | if (flags & RHPR_URLENCODE) { |
752 | | result_length = rhash_urlencode(output, (char*)bytes, size, upper_case); |
753 | | } else { |
754 | | memcpy(output, bytes, size); |
755 | | result_length = size; |
756 | | } |
757 | | break; |
758 | | } |
759 | | return result_length; |
760 | | } |
761 | | |
762 | | RHASH_API size_t rhash_print(char* output, rhash context, unsigned hash_id, int flags) |
763 | | { |
764 | | const rhash_info* info; |
765 | | unsigned char digest[80]; |
766 | | size_t digest_size; |
767 | | |
768 | | info = (hash_id != 0 ? rhash_info_by_id(hash_id) : |
769 | | ((rhash_context_ext*)context)->vector[0].hash_info->info); |
770 | | |
771 | | if (info == NULL) return 0; |
772 | | digest_size = info->digest_size; |
773 | | assert(digest_size <= 64); |
774 | | |
775 | | flags &= (RHPR_FORMAT | RHPR_MODIFIER); |
776 | | if ((flags & RHPR_FORMAT) == 0) { |
777 | | /* use default format if not specified by flags */ |
778 | | flags |= (info->flags & RHASH_INFO_BASE32 ? RHPR_BASE32 : RHPR_HEX); |
779 | | } |
780 | | |
781 | | if (output == NULL) { |
782 | | size_t multiplier = (flags & RHPR_URLENCODE ? 3 : 1); |
783 | | switch (flags & RHPR_FORMAT) { |
784 | | case RHPR_HEX: |
785 | | return (digest_size * 2); |
786 | | case RHPR_BASE32: |
787 | | return BASE32_LENGTH(digest_size); |
788 | | case RHPR_BASE64: |
789 | | return BASE64_LENGTH(digest_size) * multiplier; |
790 | | default: |
791 | | return digest_size * multiplier; |
792 | | } |
793 | | } |
794 | | |
795 | | /* note: use info->hash_id, cause hash_id can be 0 */ |
796 | | rhash_put_digest(context, info->hash_id, digest); |
797 | | |
798 | | if ((flags & ~RHPR_UPPERCASE) == (RHPR_REVERSE | RHPR_HEX)) { |
799 | | /* reverse the digest */ |
800 | | unsigned char* p = digest; |
801 | | unsigned char* r = digest + digest_size - 1; |
802 | | char tmp; |
803 | | for (; p < r; p++, r--) { |
804 | | tmp = *p; |
805 | | *p = *r; |
806 | | *r = tmp; |
807 | | } |
808 | | } |
809 | | |
810 | | return rhash_print_bytes(output, digest, digest_size, flags); |
811 | | } |
812 | | |
813 | | #if (defined(_WIN32) || defined(__CYGWIN__)) && defined(RHASH_EXPORTS) |
814 | | #include <windows.h> |
815 | | BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved); |
816 | | BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) |
817 | | { |
818 | | (void)hModule; |
819 | | (void)reserved; |
820 | | switch (reason) { |
821 | | case DLL_PROCESS_ATTACH: |
822 | | rhash_library_init(); |
823 | | break; |
824 | | case DLL_PROCESS_DETACH: |
825 | | /*rhash_library_free();*/ |
826 | | case DLL_THREAD_ATTACH: |
827 | | case DLL_THREAD_DETACH: |
828 | | break; |
829 | | } |
830 | | return TRUE; |
831 | | } |
832 | | #endif |
833 | | |
834 | | #define PVOID2UPTR(p) ((rhash_uptr_t)(((char*)(p)) + 0)) |
835 | | |
836 | | RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t ldata, rhash_uptr_t rdata) |
837 | | { |
838 | | /* for messages working with rhash context */ |
839 | | rhash_context_ext* const ctx = (rhash_context_ext*)dst; |
840 | | (void)rdata; |
841 | | |
842 | | switch (msg_id) { |
843 | | case RMSG_GET_CONTEXT: |
844 | | { |
845 | | unsigned i; |
846 | | for (i = 0; i < ctx->hash_vector_size; i++) { |
847 | | struct rhash_hash_info* info = ctx->vector[i].hash_info; |
848 | | if (info->info->hash_id == (unsigned)ldata) |
849 | | return PVOID2UPTR(ctx->vector[i].context); |
850 | | } |
851 | | return (rhash_uptr_t)0; |
852 | | } |
853 | | |
854 | | case RMSG_CANCEL: |
855 | | /* mark rhash context as canceled, in a multithreaded program */ |
856 | | atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPPED); |
857 | | return 0; |
858 | | |
859 | | case RMSG_IS_CANCELED: |
860 | | return (ctx->state == STATE_STOPPED); |
861 | | |
862 | | case RMSG_GET_FINALIZED: |
863 | | return ((ctx->flags & RCTX_FINALIZED) != 0); |
864 | | case RMSG_SET_AUTOFINAL: |
865 | | ctx->flags &= ~RCTX_AUTO_FINAL; |
866 | | if (ldata) ctx->flags |= RCTX_AUTO_FINAL; |
867 | | break; |
868 | | |
869 | | /* OpenSSL related messages */ |
870 | | #ifdef USE_OPENSSL |
871 | | case RMSG_SET_OPENSSL_MASK: |
872 | | rhash_openssl_hash_mask = (unsigned)ldata; |
873 | | break; |
874 | | case RMSG_GET_OPENSSL_MASK: |
875 | | return rhash_openssl_hash_mask; |
876 | | #endif |
877 | | case RMSG_GET_OPENSSL_SUPPORTED_MASK: |
878 | | return rhash_get_openssl_supported_hash_mask(); |
879 | | case RMSG_GET_OPENSSL_AVAILABLE_MASK: |
880 | | return rhash_get_openssl_available_hash_mask(); |
881 | | |
882 | | case RMSG_GET_LIBRHASH_VERSION: |
883 | | return RHASH_XVERSION; |
884 | | |
885 | | default: |
886 | | return RHASH_ERROR; /* unknown message */ |
887 | | } |
888 | | return 0; |
889 | | } |
890 | | #endif |