Hasher::hashStatus_t Hasher::hash(const string& filename, string& hash) { unsigned rhashType; switch( p_hashType ) { case tth: rhashType = RHASH_TTH; break; case md5: rhashType = RHASH_MD5; break; case sha1: rhashType = RHASH_SHA1; break; default: LOG(logError) << "Hasher called with no hash algorithm selected"; return noHashSelected; } unsigned char digest[64]; int res = rhash_file(rhashType, filename.c_str(), digest); if( res < 0 ) { LOG(logError) << "LibRHash error: " << strerror(errno); return hashError; } char output[130]; size_t length = 0; if( p_hashType == tth ) length = rhash_print_bytes(output, digest, rhash_get_digest_size(rhashType), RHPR_BASE32); else length = rhash_print_bytes(output, digest, rhash_get_digest_size(rhashType), RHPR_HEX); hash = string(output, length); LOG(logDetailed) << "Calculated " << rhash_get_name(rhashType) << " hash of file " << filename << ": " << hash; return hashSuccess; }
/** * Verify for all algorithms, that rhash_final() returns the same result as * rhash_print(). */ static void test_results_consistency(void) { const char * msg = "a"; size_t msg_size = strlen(msg); size_t digest_size; struct rhash_context *ctx; unsigned char res1[70]; char res2[70]; unsigned i, hash_id; for(i = 0, hash_id = 1; (hash_id & RHASH_ALL_HASHES); hash_id <<= 1, i++) { digest_size = rhash_get_digest_size(hash_id); assert(digest_size < 70); ctx = rhash_init(hash_id); #ifdef USE_BTIH_WITH_TEST_FILENAME if((hash_id & RHASH_BTIH) != 0) { unsigned long long total_size = msg_size; rhash_transmit(RMSG_BT_ADD_FILE, ctx, RHASH_STR2UPTR("test.txt"), (rhash_uptr_t)&total_size); } #endif rhash_update(ctx, msg, msg_size); rhash_final(ctx, res1); rhash_print(res2, ctx, hash_id, RHPR_RAW); rhash_free(ctx); if(memcmp(res1, res2, digest_size) != 0) { log_message("failed: inconsistent %s(\"%s\") hash results\n", rhash_get_name(hash_id), msg); } } }
/** * Initialize information about hashes, stored in the * hash_info_table global variable. */ void init_hash_info_table(void) { unsigned index, bit; unsigned short_opt_mask = RHASH_CRC32 | RHASH_MD5 | RHASH_SHA1 | RHASH_TTH | RHASH_ED2K | RHASH_AICH | RHASH_WHIRLPOOL | RHASH_RIPEMD160 | RHASH_GOST | OPT_ED2K_LINK; char* short_opt = "cmhteawrgl"; print_hash_info *info = hash_info_table; unsigned fullmask = RHASH_ALL_HASHES | OPT_ED2K_LINK; memset(hash_info_table, 0, sizeof(hash_info_table)); for(index = 0, bit = 1; bit <= fullmask; index++, bit = bit << 1, info++) { const char *p; char *e, *d; info->short_char = ((bit & short_opt_mask) != 0 && *short_opt ? *(short_opt++) : 0); info->name = (bit & RHASH_ALL_HASHES ? rhash_get_name(bit) : "ED2K_LINK"); d = info->short_name; e = info->short_name + 15; /* buffer overflow protection */ assert(strlen(info->name) < (size_t)(e-d)); for(p = info->name; *p && d < e; p++) if(*p != '-' || p[1] >= '9') *(d++) = (*p | 0x20); *d = 0; } }
/** * Verify that calculated hash doesn't depend on message alignment. */ static void test_alignment(void) { int i, start, hash_id, alignment_size; /* loop by sums */ for(i = 0, hash_id = 1; (hash_id & RHASH_ALL_HASHES); hash_id <<= 1, i++) { char expected_hash[130]; assert(rhash_get_digest_size(hash_id) < (int)sizeof(expected_hash)); alignment_size = (hash_id & (RHASH_TTH | RHASH_TIGER | RHASH_WHIRLPOOL | RHASH_SHA512) ? 8 : 4); /* start message with different alignment */ for(start = 0; start < alignment_size; start++) { char message[30]; char* obtained; int j, msg_length = 11 + alignment_size; /* fill the buffer fifth shifted letter sequence */ for(j = 0; j < msg_length; j++) message[start + j] = 'a' + j; message[start + j] = 0; obtained = calc_sum(message + start, hash_id); if(start == 0) { /* save original sum */ strcpy(expected_hash, obtained); } else { /* verify sum result */ assert_equals(obtained, expected_hash, rhash_get_name(hash_id), message); fflush(stdout); } } } }
/** * Test a hash algorithm against a long messages of zeroes. * Report error if calculated hash doesn't coincide with expected value. * * @param hash_id id of the algorithm to test * @param size the length of message of zeroes in bytes * @param expected_hash the expected hash balue */ static void test_long_zero_msg(unsigned hash_id, size_t size, const char* expected_hash) { char buffer[8192]; char msg[80], *obtained; memset(buffer, 0, 8192); sprintf(msg, "\"\\0\"x%u", (unsigned)size); obtained = calc_sums_c(buffer, 8192, size, hash_id); assert_equals(obtained, expected_hash, rhash_get_name(hash_id), msg); }
/** * Print the names of all supported hash algorithms to the console. */ static void list_hashes(void) { int id; for(id = 1; id < RHASH_ALL_HASHES; id <<= 1) { const char* hash_name = rhash_get_name(id); if(hash_name) fprintf(rhash_data.out, "%s\n", hash_name); } rsh_exit(0); }
/** * Test a hash algorithm against a message of given length, consisting * of repeated chunks. * Report error if calculated hash differs from the expected value. * * @param hash_id id of the algorithm to test * @param msg_chunk the message chunk as a null-terminated string * @param chunk_size the size of the chunk in bytes * @param count the number of chunks in the message * @param hash the expected hash value * @param set_filename need to set a filename for BTIH hash */ static void assert_hash_long_msg(unsigned hash_id, const char* msg_chunk, size_t chunk_size, size_t msg_size, const char* hash, const char* msg_name, int set_filename) { char* result; result = repeat_hash(hash_id, msg_chunk, chunk_size, msg_size, set_filename); if(strcmp(result, hash) != 0) { const char* hash_name = rhash_get_name(hash_id); /* the hash function name */ if(msg_name) log_message("failed: %s(%s) = %s, expected %s\n", hash_name, msg_name, result, hash); else log_message("failed: %s(\"%s\") = %s, expected %s\n", hash_name, msg_chunk, result, hash); g_errors++; } }
/** * Print verbose error on hash sums mismatch. * * @param info file information with path and its hash sums. */ static void print_verbose_error(struct file_info *info) { char actual[130], expected[130]; assert(HC_FAILED(info->hc.flags)); rsh_fprintf(rhash_data.out, _("ERROR")); if (HC_WRONG_FILESIZE & info->hc.flags) { sprintI64(actual, info->rctx->msg_size, 0); sprintI64(expected, info->hc.file_size, 0); rsh_fprintf(rhash_data.out, _(", size is %s should be %s"), actual, expected); } if (HC_WRONG_EMBCRC32 & info->hc.flags) { rhash_print(expected, info->rctx, RHASH_CRC32, RHPR_UPPERCASE); rsh_fprintf(rhash_data.out, _(", embedded CRC32 should be %s"), expected); } if (HC_WRONG_HASHES & info->hc.flags) { int i; unsigned reported = 0; for (i = 0; i < info->hc.hashes_num; i++) { hash_value *hv = &info->hc.hashes[i]; char *expected_hash = info->hc.data + hv->offset; unsigned hid = hv->hash_id; int pflags; if ((info->hc.wrong_hashes & (1 << i)) == 0) continue; assert(hid != 0); /* if can't detect precise hash */ if ((hid & (hid - 1)) != 0) { /* guess the hash id */ if (hid & opt.sum_flags) hid &= opt.sum_flags; if (hid & ~info->hc.found_hash_ids) hid &= ~info->hc.found_hash_ids; if (hid & ~reported) hid &= ~reported; /* avoiding repeating */ if (hid & REPORT_FIRST_MASK) hid &= REPORT_FIRST_MASK; hid &= -(int)hid; /* take the lowest bit */ } assert(hid != 0 && (hid & (hid - 1)) == 0); /* single bit only */ reported |= hid; pflags = (hv->length == (rhash_get_digest_size(hid) * 2) ? (RHPR_HEX | RHPR_UPPERCASE) : (RHPR_BASE32 | RHPR_UPPERCASE)); rhash_print(actual, info->rctx, hid, pflags); rsh_fprintf(rhash_data.out, _(", %s is %s should be %s"), rhash_get_name(hid), actual, expected_hash); } } rsh_fprintf(rhash_data.out, "\n"); }
/** * Find hash id by its name. * * @param name hash algorithm name * @return algorithm id */ static unsigned find_hash(const char* name) { char buf[30]; unsigned hash_id; int i; if(strlen(name) > (sizeof(buf) - 1)) return 0; for(i = 0; name[i]; i++) buf[i] = toupper(name[i]); buf[i] = 0; for(hash_id = 1; (hash_id & RHASH_ALL_HASHES); hash_id <<= 1) { if(strcmp(buf, rhash_get_name(hash_id)) == 0) return hash_id; } return 0; }
/** * Test a hash algorithm against a message of given length and consisting * of repeated chunks. * Report error if calculated hash doesn't coincide with expected value. * * @param hash_id id of the algorithm to test * @param chunk the message chunk as a null-terminated string * @param size the length of message of zeroes in bytes * @param expected_hash the expected hash balue */ static void test_long_msg(unsigned hash_id, const char* chunk, size_t size, const char* expected_hash) { size_t chunk_length = strlen(chunk); char* obtained = calc_sums_c(chunk, chunk_length, size, hash_id); assert_equals(obtained, expected_hash, rhash_get_name(hash_id), chunk); }
/** * Test a hash algorithm on given message by comparing calculated result with * expected one. Report error on fail. * * @param message the message to hash * @param expected_hash the expected hash balue * @param hash_id id of the algorithm to test */ static void test_str(const char* message, const char* expected_hash, unsigned hash_id) { char* obtained = calc_sum(message, hash_id); assert_equals(obtained, expected_hash, rhash_get_name(hash_id), message); }
/** * Benchmark a hash algorithm. * * @deprecated This function shall be removed soon, since * it is not related to the hashing library main functionality. * * @param hash_id hash algorithm identifier * @param flags benchmark flags, can be RHASH_BENCHMARK_QUIET and RHASH_BENCHMARK_CPB * @param output the stream to print results */ void rhash_run_benchmark(unsigned hash_id, unsigned flags, FILE* output) { unsigned char ALIGN_ATTR(16) message[8192]; /* 8 KiB */ timedelta_t timer; int i, j; size_t sz_mb, msg_size; double time, total_time = 0; const int rounds = 4; const char* hash_name; unsigned char out[130]; #ifdef HAVE_TSC double cpb = 0; #endif /* HAVE_TSC */ #ifdef _WIN32 benchmark_cpu_init(); /* set cpu affinity to improve test results */ #endif /* set message size for fast and slow hash functions */ msg_size = 1073741824 / 2; if (hash_id & (RHASH_WHIRLPOOL | RHASH_SNEFRU128 | RHASH_SNEFRU256 | RHASH_SHA3_224 | RHASH_SHA3_256 | RHASH_SHA3_384 | RHASH_SHA3_512)) { msg_size /= 8; } else if (hash_id & (RHASH_GOST | RHASH_GOST_CRYPTOPRO | RHASH_SHA384 | RHASH_SHA512)) { msg_size /= 2; } sz_mb = msg_size / (1 << 20); /* size in MiB */ hash_name = rhash_get_name(hash_id); if (!hash_name) hash_name = ""; /* benchmarking several hashes*/ for (i = 0; i < (int)sizeof(message); i++) message[i] = i & 0xff; for (j = 0; j < rounds; j++) { rhash_timer_start(&timer); hash_in_loop(hash_id, message, sizeof(message), (int)(msg_size / sizeof(message)), out); time = rhash_timer_stop(&timer); total_time += time; if ((flags & (RHASH_BENCHMARK_QUIET | RHASH_BENCHMARK_RAW)) == 0) { fprintf(output, "%s %u MiB calculated in %.3f sec, %.3f MBps\n", hash_name, (unsigned)sz_mb, time, (double)sz_mb / time); fflush(output); } } #if defined(HAVE_TSC) /* measure the CPU "clocks per byte" speed */ if (flags & RHASH_BENCHMARK_CPB) { unsigned int c1 = -1, c2 = -1; unsigned volatile long long cy0, cy1, cy2; int msg_size = 128 * 1024; /* make 200 tries */ for (i = 0; i < 200; i++) { cy0 = read_tsc(); hash_in_loop(hash_id, message, sizeof(message), msg_size / sizeof(message), out); cy1 = read_tsc(); hash_in_loop(hash_id, message, sizeof(message), msg_size / sizeof(message), out); hash_in_loop(hash_id, message, sizeof(message), msg_size / sizeof(message), out); cy2 = read_tsc(); cy2 -= cy1; cy1 -= cy0; c1 = (unsigned int)(c1 > cy1 ? cy1 : c1); c2 = (unsigned int)(c2 > cy2 ? cy2 : c2); } cpb = ((c2 - c1) + 1) / (double)msg_size; } #endif /* HAVE_TSC */ if (flags & RHASH_BENCHMARK_RAW) { /* output result in a "raw" machine-readable format */ fprintf(output, "%s\t%u\t%.3f\t%.3f", hash_name, ((unsigned)sz_mb * rounds), total_time, (double)(sz_mb * rounds) / total_time); #if defined(HAVE_TSC) if (flags & RHASH_BENCHMARK_CPB) fprintf(output, "\t%.2f", cpb); #endif /* HAVE_TSC */ fprintf(output, "\n"); } else { fprintf(output, "%s %u MiB total in %.3f sec, %.3f MBps", hash_name, ((unsigned)sz_mb * rounds), total_time, (double)(sz_mb * rounds) / total_time); #if defined(HAVE_TSC) if (flags & RHASH_BENCHMARK_CPB) fprintf(output, ", CPB=%.2f", cpb); #endif /* HAVE_TSC */ fprintf(output, "\n"); } }