/ utils / benchmark.c
benchmark.c
  1  /* benchmark.c -- Benchmarks how quickly the computer does SHA512 hashing
  2   *
  3   *  GPLv2 only - Copyright (C) 2009 - 2012
  4   *               David Sommerseth <dazo@users.sourceforge.net>
  5   *
  6   *  This program is free software; you can redistribute it and/or
  7   *  modify it under the terms of the GNU General Public License
  8   *  as published by the Free Software Foundation; version 2
  9   *  of the License.
 10   *
 11   *  This program is distributed in the hope that it will be useful,
 12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14   *  GNU General Public License for more details.
 15   *
 16   *  You should have received a copy of the GNU General Public License
 17   *  along with this program; if not, write to the Free Software
 18   *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 19   *
 20   */
 21  
 22  /**
 23   * @file   benchmark.c
 24   * @author David Sommerseth <dazo@users.sourceforge.net>
 25   * @date   2009-03-22
 26   *
 27   * @brief  Benchmarking utility to test how quickly a computer can calculate SHA512 hashes
 28   *
 29   */
 30  
 31  
 32  #include <stdio.h>
 33  #include <stdlib.h>
 34  #include <string.h>
 35  #include <assert.h>
 36  #include <sys/time.h>
 37  
 38  #include <eurephia_nullsafe.h>
 39  #include <eurephia_context.h>
 40  
 41  #define ROUNDS_MIN 1000         /**< Define minimum acceptable rounds for the hashing */
 42  #define ROUNDS_MAX 999999999    /**< Define maximum allowed rounds for the hashing */
 43  
 44  int pack_saltinfo(char *buf, int buflen, int rounds, int saltlen, const char *pwd);
 45  
 46  inline char *sha512_crypt_r(const char *key, const char *salt, size_t maxrounds_cfg, char *buffer, int buflen);
 47  
 48  int gen_randsaltstr(eurephiaCTX *ctx, char *saltstr, int len);
 49  
 50  /**
 51   * Internal function which calculates a hash based on a standard password.  This function is used
 52   * for measuring the time used for hash calculation
 53   *
 54   * @param rounds  Number of rounds the hashing should use
 55   */
 56  void benchmark_hashing(int rounds) {
 57          char *buffer = NULL;
 58          static char randstr[34];
 59          static int rand_set = 0;
 60          char pwdsalt[64], saltstr[32];
 61  
 62          // Get random data for our salt
 63          if( rand_set == 0 ) {
 64                  memset(&randstr, 0, 34);
 65                  if( gen_randsaltstr(NULL, randstr, 32) == 0 ) {
 66                          fprintf(stderr, "Could not retrieve enough random data for hashing");
 67                          exit(19);
 68                  };
 69                  rand_set = 1;
 70          }
 71  
 72          // Prepare a salt package
 73          memset(&pwdsalt, 0, 64);
 74          memset(&saltstr, 0, 32);
 75          pack_saltinfo(saltstr, 30, rounds, 32, "benchmarkpassword");
 76          strncpy(pwdsalt, saltstr, strlen(saltstr));
 77          strncat(pwdsalt, randstr, 62 - strlen(saltstr));
 78          memset(&randstr, 0, 32);
 79  
 80          buffer = (char *) malloc_nullsafe(NULL, 1024);
 81          assert( buffer != NULL );
 82  
 83          sha512_crypt_r("benchmarkpassword", pwdsalt, ROUNDS_MAX, buffer, 1024);
 84  
 85          free_nullsafe(NULL, buffer);
 86  }
 87  
 88  
 89  /**
 90   * Internal function, calculating a time delta between two struct timeval.
 91   *
 92   * @param result Result pointer to where the result will be put
 93   * @param x      Value 1 with time information
 94   * @param y      Value 2 with time information
 95   *
 96   * @return Returns 1 if the X value is lower than the Y value
 97   */
 98  int timeval_subtract (result, x, y)
 99       struct timeval *result, *x, *y;
100  {
101          /* Perform the carry for the later subtraction by updating y. */
102          if (x->tv_usec < y->tv_usec) {
103                  int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
104                  y->tv_usec -= 1000000 * nsec;
105                  y->tv_sec += nsec;
106          }
107          if (x->tv_usec - y->tv_usec > 1000000) {
108                  int nsec = (y->tv_usec - x->tv_usec) / 1000000;
109                  y->tv_usec += 1000000 * nsec;
110                  y->tv_sec -= nsec;
111          }
112  
113          /* Compute the time remaining to wait.
114             tv_usec is certainly positive. */
115          result->tv_sec = x->tv_sec - y->tv_sec;
116          result->tv_usec = x->tv_usec - y->tv_usec;
117  
118          /* Return 1 if result is negative. */
119          return x->tv_sec < y->tv_sec;
120  }
121  
122  
123  /**
124   * Internal function.  The real benchmark function.
125   *
126   * @param rounds Number of rounds to do when hashing a password
127   *
128   * @return Returns a struct timeval containing the time spent calculating the hash
129   */
130  struct timeval *do_benchmark(int rounds) {
131          struct timeval start, end, *timediff = NULL;
132  
133          timediff = (struct timeval *) malloc_nullsafe(NULL, sizeof(struct timeval)+2);
134          assert( timediff != NULL );
135  
136          gettimeofday(&start, NULL);
137          benchmark_hashing(rounds);
138          gettimeofday(&end, NULL);
139          timeval_subtract(timediff, &end, &start);
140  
141          return timediff;
142  }
143  
144  /**
145   * This function will try to find the best minium and maximum rounds for
146   * the SHA512 hashing.  The values are returned in the min and max variables
147   * and the thr_min and thr_max defines the minimum and maximum thresholds in
148   * milliseconds the hashing algorithm should use.
149   *
150   * @param min      Return pointer for the minimum hashing rounds value
151   * @param max      Return pointer for the maximum hashing rounds value
152   * @param thr_min  Allow the calculation of hashes to last for thr_min ms.
153   * @param thr_max  Do not allow the hash calculation to exceed thr_max ms.
154   *
155   * @return Returns 1 if a good suggestion for min and max hashing rounds was found.
156   *         Otherwise 0 is returned.
157   */
158  int benchmark(int *min, int *max, int thr_min, int thr_max) {
159          int i = 0, success = 0;
160          struct timeval *time = NULL, min_time, max_time ;
161  
162          printf("Benchmarking ");
163          *min = 1000;
164          *max = 5000;
165          memset(&min_time, 0, sizeof(struct timeval));
166          memset(&max_time, 0, sizeof(struct timeval));
167  
168          for( i = 1000; i < 100000000; i += 1000 ) {
169                  printf(".");
170                  fflush(stdout);
171                  time = do_benchmark(i);
172                  // printf("%i rounds: %ld sec %ld ms\n", i, time->tv_sec, time->tv_usec);
173                  if( time->tv_usec > (thr_max * 1000) ) {
174                          success = 1;
175                          free(time);
176                          break;
177                  }
178                  if( (*min == 1000) && (time->tv_usec > (thr_min * 1000)) ) {
179                          *min = i;
180                          min_time.tv_usec = time->tv_usec;
181                  }
182                  if( (time->tv_usec < (thr_max * 1000)) ) {
183                          *max = i;
184                          max_time.tv_usec = time->tv_usec;
185                  }
186                  free(time);
187          }
188          printf(" Done\n");
189          if( success == 1 ) {
190                  printf("Suggested minimum rounds: %i (takes %ldms)\n", *min, min_time.tv_usec/1000);
191                  printf("Suggested maximum rounds: %i (takes %ldms)\n", *max, max_time.tv_usec/1000);
192          } else {
193                  *min = 0;
194                  *max = 0;
195                  printf("Could not find any good times, as your computer is too fast for this test.\n");
196          }
197          return success;
198  }
199  
200  #if 0
201  int main() {
202          int min, max;
203  
204          benchmark(&min, &max, 90, 220);
205          printf("---> %i - %i\n", min, max);
206          return 0;
207  }
208  #endif