summaryrefslogtreecommitdiffstats
path: root/Utilities/cmlibrhash/librhash/rhash.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmlibrhash/librhash/rhash.c')
-rw-r--r--Utilities/cmlibrhash/librhash/rhash.c395
1 files changed, 291 insertions, 104 deletions
diff --git a/Utilities/cmlibrhash/librhash/rhash.c b/Utilities/cmlibrhash/librhash/rhash.c
index 2530112..4e60c21 100644
--- a/Utilities/cmlibrhash/librhash/rhash.c
+++ b/Utilities/cmlibrhash/librhash/rhash.c
@@ -38,15 +38,24 @@
#include <string.h>
#define STATE_ACTIVE 0xb01dbabe
-#define STATE_STOPED 0xdeadbeef
+#define STATE_STOPPED 0xdeadbeef
#define STATE_DELETED 0xdecea5ed
+#define IS_BAD_STATE(s) ((s) != STATE_ACTIVE && (s) != STATE_STOPPED)
#define RCTX_AUTO_FINAL 0x1
#define RCTX_FINALIZED 0x2
#define RCTX_FINALIZED_MASK (RCTX_AUTO_FINAL | RCTX_FINALIZED)
#define RHPR_FORMAT (RHPR_RAW | RHPR_HEX | RHPR_BASE32 | RHPR_BASE64)
#define RHPR_MODIFIER (RHPR_UPPERCASE | RHPR_URLENCODE | RHPR_REVERSE)
-void rhash_library_init(void)
+#define HAS_ZERO_OR_ONE_BIT(id) (((id) & ((id) - 1)) == 0)
+#define IS_VALID_HASH_MASK(bitmask) ((bitmask) != 0 && ((bitmask) & ~RHASH_ALL_HASHES) == 0)
+#define IS_VALID_HASH_ID(id) (IS_VALID_HASH_MASK(id) && HAS_ZERO_OR_ONE_BIT(id))
+
+/* each hash function context must be aligned to DEFAULT_ALIGNMENT bytes */
+#define GET_CTX_ALIGNED(size) ALIGN_SIZE_BY((size), DEFAULT_ALIGNMENT)
+#define GET_EXPORT_ALIGNED(size) ALIGN_SIZE_BY((size), 8)
+
+RHASH_API void rhash_library_init(void)
{
rhash_init_algorithms(RHASH_ALL_HASHES);
#ifdef USE_OPENSSL
@@ -54,103 +63,120 @@ void rhash_library_init(void)
#endif
}
-int RHASH_API rhash_count(void)
+RHASH_API int rhash_count(void)
{
return rhash_info_size;
}
/* LOW-LEVEL LIBRHASH INTERFACE */
-RHASH_API rhash rhash_init(unsigned hash_id)
+/**
+ * Allocate and initialize RHash context for calculating a single or multiple hash functions.
+ * The context after usage must be freed by calling rhash_free().
+ *
+ * @param count the size of the hash_ids array, the count must be greater than zero
+ * @param hash_ids array of identifiers of hash functions. Each element must
+ * be an identifier of one hash function
+ * @param need_init initialize context for each hash function
+ * @return initialized rhash context, NULL on fail with error code stored in errno
+ */
+static rhash_context_ext* rhash_alloc_multi(size_t count, const unsigned hash_ids[], int need_init)
{
- unsigned tail_bit_index; /* index of hash_id trailing bit */
- unsigned num = 0; /* number of hashes to compute */
+ struct rhash_hash_info* info; /* hash algorithm information */
rhash_context_ext* rctx = NULL; /* allocated rhash context */
- size_t hash_size_sum = 0; /* size of hash contexts to store in rctx */
-
- unsigned i, bit_index, id;
- struct rhash_hash_info* info;
- size_t aligned_size;
+ const size_t header_size = GET_CTX_ALIGNED(sizeof(rhash_context_ext) + sizeof(rhash_vector_item) * count);
+ size_t ctx_size_sum = 0; /* size of hash contexts to store in rctx */
+ size_t i;
char* phash_ctx;
+ unsigned hash_bitmask = 0;
- hash_id &= RHASH_ALL_HASHES;
- if (hash_id == 0) {
+ if (count < 1) {
errno = EINVAL;
return NULL;
}
-
- tail_bit_index = rhash_ctz(hash_id); /* get trailing bit index */
- assert(tail_bit_index < RHASH_HASH_COUNT);
-
- id = 1 << tail_bit_index;
-
- if (hash_id == id) {
- /* handle the most common case of only one hash */
- num = 1;
- info = &rhash_info_table[tail_bit_index];
- hash_size_sum = info->context_size;
- } else {
- /* another case: hash_id contains several hashes */
- for (bit_index = tail_bit_index; id <= hash_id; bit_index++, id = id << 1) {
- assert(id != 0);
- assert(bit_index < RHASH_HASH_COUNT);
- info = &rhash_info_table[bit_index];
- if (hash_id & id) {
- /* align sizes by 8 bytes */
- aligned_size = (info->context_size + 7) & ~7;
- hash_size_sum += aligned_size;
- num++;
- }
+ for (i = 0; i < count; i++) {
+ unsigned hash_index;
+ if (!IS_VALID_HASH_ID(hash_ids[i])) {
+ errno = EINVAL;
+ return NULL;
}
- assert(num > 1);
- }
+ hash_bitmask |= hash_ids[i];
+ hash_index = rhash_ctz(hash_ids[i]);
+ assert(hash_index < RHASH_HASH_COUNT); /* correct until extended hash_ids are supported */
+ info = &rhash_info_table[hash_index];
- /* align the size of the rhash context common part */
- aligned_size = ((offsetof(rhash_context_ext, vector) + sizeof(rhash_vector_item) * num) + 7) & ~7;
- assert(aligned_size >= sizeof(rhash_context_ext));
+ /* align context sizes and sum up */
+ ctx_size_sum += GET_CTX_ALIGNED(info->context_size);
+ }
- /* allocate rhash context with enough memory to store contexts of all used hashes */
- rctx = (rhash_context_ext*)malloc(aligned_size + hash_size_sum);
- if (rctx == NULL) return NULL;
+ /* allocate rhash context with enough memory to store contexts of all selected hash functions */
+ rctx = (rhash_context_ext*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, header_size + ctx_size_sum);
+ if (rctx == NULL)
+ return NULL;
/* initialize common fields of the rhash context */
- memset(rctx, 0, sizeof(rhash_context_ext));
- rctx->rc.hash_id = hash_id;
+ memset(rctx, 0, header_size);
+ rctx->rc.hash_id = hash_bitmask;
rctx->flags = RCTX_AUTO_FINAL; /* turn on auto-final by default */
rctx->state = STATE_ACTIVE;
- rctx->hash_vector_size = num;
-
- /* aligned hash contexts follows rctx->vector[num] in the same memory block */
- phash_ctx = (char*)rctx + aligned_size;
- assert(phash_ctx >= (char*)&rctx->vector[num]);
-
- /* initialize context for every hash in a loop */
- for (bit_index = tail_bit_index, id = 1 << tail_bit_index, i = 0;
- id <= hash_id; bit_index++, id = id << 1)
- {
- /* check if a hash function with given id shall be included into rctx */
- if ((hash_id & id) != 0) {
- info = &rhash_info_table[bit_index];
- assert(info->context_size > 0);
- assert(((phash_ctx - (char*)0) & 7) == 0); /* hash context is aligned */
- assert(info->init != NULL);
-
- rctx->vector[i].hash_info = info;
- rctx->vector[i].context = phash_ctx;
+ rctx->hash_vector_size = count;
+
+ /* calculate aligned pointer >= (&rctx->vector[count]) */
+ phash_ctx = (char*)rctx + header_size;
+ assert(phash_ctx >= (char*)&rctx->vector[count]);
+ assert(phash_ctx < ((char*)&rctx->vector[count] + DEFAULT_ALIGNMENT));
+
+ for (i = 0; i < count; i++) {
+ unsigned hash_index = rhash_ctz(hash_ids[i]);
+ info = &rhash_info_table[hash_index];
+ assert(info->context_size > 0);
+ assert(info->init != NULL);
+ assert(IS_PTR_ALIGNED_BY(phash_ctx, DEFAULT_ALIGNMENT)); /* hash context is aligned */
+
+ rctx->vector[i].hash_info = info;
+ rctx->vector[i].context = phash_ctx;
#if 0
- /* BTIH initialization is complex, save pointer for later */
- if ((id & RHASH_BTIH) != 0) rctx->bt_ctx = phash_ctx;
+ /* BTIH initialization is a bit complicated, so store the context pointer for later usage */
+ if ((hash_ids[i] & RHASH_BTIH) != 0)
+ rctx->bt_ctx = phash_ctx;
#endif
- phash_ctx += (info->context_size + 7) & ~7;
+ phash_ctx += GET_CTX_ALIGNED(info->context_size);
- /* initialize the i-th hash context */
+ /* initialize the i-th hash context */
+ if (need_init)
info->init(rctx->vector[i].context);
- i++;
- }
}
+ return rctx;
+}
- return &rctx->rc; /* return allocated and initialized rhash context */
+RHASH_API rhash rhash_init_multi(size_t count, const unsigned hash_ids[])
+{
+ rhash_context_ext* ectx = rhash_alloc_multi(count, hash_ids, 1);
+ return &ectx->rc; /* return initialized rhash context */
+}
+
+RHASH_API rhash rhash_init(unsigned hash_id)
+{
+ if (!IS_VALID_HASH_MASK(hash_id)) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (HAS_ZERO_OR_ONE_BIT(hash_id)) {
+ return rhash_init_multi(1, &hash_id);
+ } else {
+ /* handle the depricated case, when hash_id is a bitwise union of several hash function identifiers */
+ size_t count;
+ unsigned hash_ids[32];
+ unsigned id = hash_id & -hash_id; /* get the trailing bit */
+ for (count = 0; id <= hash_id; id = id << 1) {
+ assert(id != 0);
+ if (hash_id & id)
+ hash_ids[count++] = id;
+ }
+ assert(count > 1);
+ return rhash_init_multi(count, hash_ids);
+ }
}
void rhash_free(rhash ctx)
@@ -159,7 +185,6 @@ void rhash_free(rhash ctx)
unsigned i;
if (ctx == 0) return;
- assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
ectx->state = STATE_DELETED; /* mark memory block as being removed */
/* clean the hash functions, which require additional clean up */
@@ -169,8 +194,7 @@ void rhash_free(rhash ctx)
info->cleanup(ectx->vector[i].context);
}
}
-
- free(ectx);
+ rhash_aligned_free(ectx);
}
RHASH_API void rhash_reset(rhash ctx)
@@ -239,6 +263,161 @@ RHASH_API int rhash_final(rhash ctx, unsigned char* first_result)
}
/**
+ * Header block for rhash context import/export.
+ */
+typedef struct export_header
+{
+ uint32_t state;
+ uint16_t hash_vector_size;
+ uint16_t flags;
+ uint64_t msg_size;
+} export_header;
+
+/**
+ * Process export error. Returns 0 and set errno to EINVAL.
+ *
+ * @return NULL
+ */
+static size_t export_error_einval(void)
+{
+ errno = EINVAL;
+ return 0;
+}
+
+/**
+ * Process import error. Returns NULL and set errno to EINVAL.
+ *
+ * @return NULL
+ */
+static rhash import_error_einval(void)
+{
+ errno = EINVAL;
+ return NULL;
+}
+
+RHASH_API size_t rhash_export(rhash ctx, void* out, size_t size)
+{
+#if !defined(NO_IMPORT_EXPORT)
+ size_t export_size;
+ size_t i;
+ rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
+ export_header* header = (export_header*)out;
+ unsigned* hash_ids = NULL;
+ if (!ctx || (out && size < sizeof(export_header)) || IS_BAD_STATE(ectx->state))
+ return export_error_einval();
+ export_size = sizeof(export_header) + sizeof(unsigned) * ectx->hash_vector_size;
+ if (out != NULL) {
+ memset(out, 0, size);
+ header->state = ectx->state;
+ header->hash_vector_size = (uint16_t)(ectx->hash_vector_size);
+ header->flags = (uint16_t)(ectx->flags);
+ header->msg_size = ctx->msg_size;
+ hash_ids = (unsigned*)(void*)(header + 1);
+ }
+ for (i = 0; i < ectx->hash_vector_size; i++) {
+ void* src_context = ectx->vector[i].context;
+ struct rhash_hash_info* hash_info = ectx->vector[i].hash_info;
+ unsigned is_special = (hash_info->info->flags & F_SPCEXP);
+ size_t item_size;
+ if (out != NULL) {
+ if (size <= export_size)
+ return export_error_einval();
+ hash_ids[i] = hash_info->info->hash_id;
+ if (is_special) {
+ char* dst_item;
+ size_t left_size;
+ export_size = GET_EXPORT_ALIGNED(export_size);
+ dst_item = (char*)out + export_size;
+ left_size = size - export_size;
+ item_size = rhash_export_alg(hash_info->info->hash_id,
+ src_context, dst_item, left_size);
+ if (!item_size)
+ return export_error_einval();
+ } else {
+ char* dst_item = (char*)out + export_size;
+ item_size = hash_info->context_size;
+ if (size < (export_size + item_size))
+ return export_error_einval();
+ memcpy(dst_item, src_context, item_size);
+ }
+ } else {
+ if (is_special) {
+ export_size = GET_EXPORT_ALIGNED(export_size);
+ item_size = rhash_export_alg(
+ hash_info->info->hash_id, src_context, NULL, 0);
+ } else
+ item_size = hash_info->context_size;
+ }
+ export_size += item_size;
+ }
+ if (export_size < size)
+ return export_error_einval();
+ return export_size;
+#else
+ return export_error_einval();
+#endif /* !defined(NO_IMPORT_EXPORT) */
+}
+
+RHASH_API rhash rhash_import(const void* in, size_t size)
+{
+#if !defined(NO_IMPORT_EXPORT)
+ const export_header* header = (const export_header*)in;
+ size_t i;
+ size_t imported_size;
+ const unsigned* hash_ids;
+ const char* src_item;
+ rhash_context_ext* ectx;
+ if (!header || IS_BAD_STATE(header->state) || size < sizeof(export_header))
+ return import_error_einval();
+ imported_size = sizeof(export_header) + sizeof(unsigned) * header->hash_vector_size;
+ if (!header->hash_vector_size || size < imported_size)
+ return import_error_einval();
+ hash_ids = (const unsigned*)(const void*)(header + 1);
+ ectx = (rhash_context_ext*)rhash_alloc_multi(header->hash_vector_size, hash_ids, 0);
+ if (!ectx)
+ return NULL; /* errno must be set by the previous function */
+ ectx->state = header->state;
+ ectx->hash_vector_size = header->hash_vector_size;
+ ectx->flags = header->flags;
+ ectx->rc.msg_size = header->msg_size;
+ for (i = 0; i < ectx->hash_vector_size; i++) {
+ void* dst_context = ectx->vector[i].context;
+ struct rhash_hash_info* hash_info = ectx->vector[i].hash_info;
+ unsigned is_special = (hash_info->info->flags & F_SPCEXP);
+ size_t item_size;
+
+ if (is_special) {
+ size_t left_size;
+ imported_size = GET_EXPORT_ALIGNED(imported_size);
+ src_item = (const char*)in + imported_size;
+ left_size = size - imported_size;
+ assert(size >= imported_size);
+ item_size = rhash_import_alg(hash_ids[i], dst_context, src_item, left_size);
+ imported_size += item_size;
+ if (!item_size || size < imported_size) {
+ ectx->hash_vector_size = i + 1; /* clean only initialized contextes */
+ rhash_free(&ectx->rc);
+ return import_error_einval();
+ }
+ } else {
+ src_item = (const char*)in + imported_size;
+ item_size = hash_info->context_size;
+ imported_size += item_size;
+ if (size < imported_size) {
+ ectx->hash_vector_size = i + 1;
+ rhash_free(&ectx->rc);
+ return import_error_einval();
+ }
+ memcpy(dst_context, src_item, item_size);
+ }
+ }
+ return &ectx->rc;
+#else
+ return import_error_einval();
+#endif /* !defined(NO_IMPORT_EXPORT) */
+}
+
+/**
* Store digest for given hash_id.
* If hash_id is zero, function stores digest for a hash with the lowest id found in the context.
* For nonzero hash_id the context must contain it, otherwise function silently does nothing.
@@ -290,7 +469,7 @@ static void rhash_put_digest(rhash ctx, unsigned hash_id, unsigned char* result)
RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data)
{
- ((rhash_context_ext*)ctx)->callback = (void*)callback;
+ ((rhash_context_ext*)ctx)->callback = callback;
((rhash_context_ext*)ctx)->callback_data = callback_data;
}
@@ -313,26 +492,21 @@ RHASH_API int rhash_file_update(rhash ctx, FILE* fd)
rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
const size_t block_size = 8192;
unsigned char* buffer;
- unsigned char* pmem;
- size_t length = 0, align8;
+ size_t length = 0;
int res = 0;
- if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */
-
+ if (ectx->state != STATE_ACTIVE)
+ return 0; /* do nothing if canceled */
if (ctx == NULL) {
errno = EINVAL;
return -1;
}
-
- pmem = (unsigned char*)malloc(block_size + 8);
- if (!pmem) return -1; /* errno is set to ENOMEM according to UNIX 98 */
-
- align8 = ((unsigned char*)0 - pmem) & 7;
- buffer = pmem + align8;
+ buffer = (unsigned char*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, block_size);
+ if (!buffer)
+ return -1; /* errno is set to ENOMEM according to UNIX 98 */
while (!feof(fd)) {
- /* stop if canceled */
- if (ectx->state != STATE_ACTIVE) break;
-
+ if (ectx->state != STATE_ACTIVE)
+ break; /* stop if canceled */
length = fread(buffer, 1, block_size, fd);
if (ferror(fd)) {
@@ -346,11 +520,16 @@ RHASH_API int rhash_file_update(rhash ctx, FILE* fd)
}
}
}
-
- free(buffer);
+ rhash_aligned_free(buffer);
return res;
}
+#ifdef _WIN32
+# define FOPEN_MODE "rbS"
+#else
+# define FOPEN_MODE "rb"
+#endif
+
RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result)
{
FILE* fd;
@@ -363,17 +542,19 @@ RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char*
return -1;
}
- if ((fd = fopen(filepath, "rb")) == NULL) return -1;
+ fd = fopen(filepath, FOPEN_MODE);
+ if (!fd)
+ return -1;
- if ((ctx = rhash_init(hash_id)) == NULL) {
+ ctx = rhash_init(hash_id);
+ if (!ctx) {
fclose(fd);
return -1;
}
-
res = rhash_file_update(ctx, fd); /* hash the file */
fclose(fd);
-
- rhash_final(ctx, result);
+ if (res >= 0)
+ rhash_final(ctx, result);
rhash_free(ctx);
return res;
}
@@ -393,17 +574,19 @@ RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned ch
return -1;
}
- if ((fd = _wfsopen(filepath, L"rb", _SH_DENYWR)) == NULL) return -1;
+ fd = _wfsopen(filepath, L"rbS", _SH_DENYWR);
+ if (!fd)
+ return -1;
- if ((ctx = rhash_init(hash_id)) == NULL) {
+ ctx = rhash_init(hash_id);
+ if (!ctx) {
fclose(fd);
return -1;
}
-
res = rhash_file_update(ctx, fd); /* hash the file */
fclose(fd);
-
- rhash_final(ctx, result);
+ if (res >= 0)
+ rhash_final(ctx, result);
rhash_free(ctx);
return res;
}
@@ -576,7 +759,7 @@ size_t rhash_print_bytes(char* output, const unsigned char* bytes, size_t size,
return result_length;
}
-size_t RHASH_API rhash_print(char* output, rhash context, unsigned hash_id, int flags)
+RHASH_API size_t rhash_print(char* output, rhash context, unsigned hash_id, int flags)
{
const rhash_info* info;
unsigned char digest[80];
@@ -654,6 +837,7 @@ RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t l
{
/* for messages working with rhash context */
rhash_context_ext* const ctx = (rhash_context_ext*)dst;
+ (void)rdata;
switch (msg_id) {
case RMSG_GET_CONTEXT:
@@ -669,11 +853,11 @@ RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t l
case RMSG_CANCEL:
/* mark rhash context as canceled, in a multithreaded program */
- atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPED);
+ atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPPED);
return 0;
case RMSG_IS_CANCELED:
- return (ctx->state == STATE_STOPED);
+ return (ctx->state == STATE_STOPPED);
case RMSG_GET_FINALIZED:
return ((ctx->flags & RCTX_FINALIZED) != 0);
@@ -695,6 +879,9 @@ RHASH_API rhash_uptr_t rhash_transmit(unsigned msg_id, void* dst, rhash_uptr_t l
case RMSG_GET_OPENSSL_AVAILABLE_MASK:
return rhash_get_openssl_available_hash_mask();
+ case RMSG_GET_LIBRHASH_VERSION:
+ return RHASH_XVERSION;
+
default:
return RHASH_ERROR; /* unknown message */
}