/* benchmark.c -- Benchmarks how quickly the computer does SHA512 hashing * * GPLv2 only - Copyright (C) 2009 - 2012 * David Sommerseth * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /** * @file benchmark.c * @author David Sommerseth * @date 2009-03-22 * * @brief Benchmarking utility to test how quickly a computer can calculate SHA512 hashes * */ #include #include #include #include #include #include #include #define ROUNDS_MIN 1000 /**< Define minimum acceptable rounds for the hashing */ #define ROUNDS_MAX 999999999 /**< Define maximum allowed rounds for the hashing */ int pack_saltinfo(char *buf, int buflen, int rounds, int saltlen, const char *pwd); inline char *sha512_crypt_r(const char *key, const char *salt, size_t maxrounds_cfg, char *buffer, int buflen); int gen_randsaltstr(eurephiaCTX *ctx, char *saltstr, int len); /** * Internal function which calculates a hash based on a standard password. This function is used * for measuring the time used for hash calculation * * @param rounds Number of rounds the hashing should use */ void benchmark_hashing(int rounds) { char *buffer = NULL; static char randstr[34]; static int rand_set = 0; char pwdsalt[64], saltstr[32]; // Get random data for our salt if( rand_set == 0 ) { memset(&randstr, 0, 34); if( gen_randsaltstr(NULL, randstr, 32) == 0 ) { fprintf(stderr, "Could not retrieve enough random data for hashing"); exit(19); }; rand_set = 1; } // Prepare a salt package memset(&pwdsalt, 0, 64); memset(&saltstr, 0, 32); pack_saltinfo(saltstr, 30, rounds, 32, "benchmarkpassword"); strncpy(pwdsalt, saltstr, strlen(saltstr)); strncat(pwdsalt, randstr, 62 - strlen(saltstr)); memset(&randstr, 0, 32); buffer = (char *) malloc_nullsafe(NULL, 1024); assert( buffer != NULL ); sha512_crypt_r("benchmarkpassword", pwdsalt, ROUNDS_MAX, buffer, 1024); free_nullsafe(NULL, buffer); } /** * Internal function, calculating a time delta between two struct timeval. * * @param result Result pointer to where the result will be put * @param x Value 1 with time information * @param y Value 2 with time information * * @return Returns 1 if the X value is lower than the Y value */ int timeval_subtract (result, x, y) struct timeval *result, *x, *y; { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (y->tv_usec - x->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. tv_usec is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ return x->tv_sec < y->tv_sec; } /** * Internal function. The real benchmark function. * * @param rounds Number of rounds to do when hashing a password * * @return Returns a struct timeval containing the time spent calculating the hash */ struct timeval *do_benchmark(int rounds) { struct timeval start, end, *timediff = NULL; timediff = (struct timeval *) malloc_nullsafe(NULL, sizeof(struct timeval)+2); assert( timediff != NULL ); gettimeofday(&start, NULL); benchmark_hashing(rounds); gettimeofday(&end, NULL); timeval_subtract(timediff, &end, &start); return timediff; } /** * This function will try to find the best minium and maximum rounds for * the SHA512 hashing. The values are returned in the min and max variables * and the thr_min and thr_max defines the minimum and maximum thresholds in * milliseconds the hashing algorithm should use. * * @param min Return pointer for the minimum hashing rounds value * @param max Return pointer for the maximum hashing rounds value * @param thr_min Allow the calculation of hashes to last for thr_min ms. * @param thr_max Do not allow the hash calculation to exceed thr_max ms. * * @return Returns 1 if a good suggestion for min and max hashing rounds was found. * Otherwise 0 is returned. */ int benchmark(int *min, int *max, int thr_min, int thr_max) { int i = 0, success = 0; struct timeval *time = NULL, min_time, max_time ; printf("Benchmarking "); *min = 1000; *max = 5000; memset(&min_time, 0, sizeof(struct timeval)); memset(&max_time, 0, sizeof(struct timeval)); for( i = 1000; i < 100000000; i += 1000 ) { printf("."); fflush(stdout); time = do_benchmark(i); // printf("%i rounds: %ld sec %ld ms\n", i, time->tv_sec, time->tv_usec); if( time->tv_usec > (thr_max * 1000) ) { success = 1; free(time); break; } if( (*min == 1000) && (time->tv_usec > (thr_min * 1000)) ) { *min = i; min_time.tv_usec = time->tv_usec; } if( (time->tv_usec < (thr_max * 1000)) ) { *max = i; max_time.tv_usec = time->tv_usec; } free(time); } printf(" Done\n"); if( success == 1 ) { printf("Suggested minimum rounds: %i (takes %ldms)\n", *min, min_time.tv_usec/1000); printf("Suggested maximum rounds: %i (takes %ldms)\n", *max, max_time.tv_usec/1000); } else { *min = 0; *max = 0; printf("Could not find any good times, as your computer is too fast for this test.\n"); } return success; } #if 0 int main() { int min, max; benchmark(&min, &max, 90, 220); printf("---> %i - %i\n", min, max); return 0; } #endif