1/* 2 * hashTimeSA.cpp - measure performance of digest ops, standalone version (no 3 * dependency on Security.framewortk or on CommonCrypto portion of libSystem). 4 */ 5 6#include <stdio.h> 7#include <stdlib.h> 8#include <string.h> 9#include <CommonCrypto/CommonDigest.h> /* static lib used in Tiger */ 10#include "MD5.h" /* CryptKit version used in Panther and prior */ 11#include "SHA1.h" /* ditto */ 12#include <Security/cssmtype.h> /* for ALGID values */ 13#include <Security/cssmapple.h> /* more ALGID values */ 14#include <CoreFoundation/CFDate.h> 15 16/* enumerate digest algorithms our way */ 17typedef int HT_Alg; 18enum { 19 HA_MD5 = 0, 20 HA_SHA1, 21 HA_SHA224, 22 HA_SHA256, 23 HA_SHA384, 24 HA_SHA512 25}; 26 27#define FIRST_ALG HA_MD5 28#define LAST_ALG HA_SHA512 29 30static void usage(char **argv) 31{ 32 printf("Usage: %s c|k [option ...]\n", argv[0]); 33 printf(" c=CommonCrypto; k=CryptKit\n"); 34 printf("Options:\n"); 35 printf(" a=alg; default=all\n"); 36 printf(" algs: m : MD5\n"); 37 printf(" s : SHA1\n"); 38 printf(" 4 : SHA224\n"); 39 printf(" 2 : SHA256\n"); 40 printf(" 3 : SHA384\n"); 41 printf(" 5 : SHA512\n"); 42 printf(" l=loops (only valid if testspec is given)\n"); 43 printf(" v verify digest by printing it\n"); 44 exit(1); 45} 46 47static void dumpDigest( 48 const unsigned char *digest, 49 unsigned len) 50{ 51 for(unsigned dex=0; dex<len; dex++) { 52 printf("%02X", *digest++); 53 if((dex % 4) == 3) { 54 printf(" "); 55 } 56 } 57 printf("\n"); 58} 59 60/* sort-of random, but repeatable */ 61static void initPtext( 62 unsigned char *ptext, 63 unsigned len) 64{ 65 srandom(1); 66 for(unsigned dex=0; dex<len; dex++) { 67 *ptext++ = random(); 68 } 69} 70 71/* passed to each test */ 72typedef struct { 73 unsigned loops; 74 CSSM_ALGORITHMS algId; // MD5, SHA1 75 bool dumpDigest; 76} TestParams; 77 78#define MAX_DIGEST_SIZE 64 // we provide, no malloc below CSSM 79 80#define PTEXT_SIZE 1000 // to digest in bytes 81#define INNER_LOOPS 500 82 83 84/* SHA1 digest is not orthoganal, fix up here */ 85static void ckSha1Final( 86 void *ctx, 87 unsigned char *digest) 88{ 89 sha1GetDigest((sha1Obj)ctx, digest); 90} 91 92typedef void (*ckInitFcn)(void *digestCtx); 93typedef void (*ckUpdateFcn)(void *digestCtx, const void *data, unsigned len); 94typedef void (*ckFinalFcn)(void *digestCtx, unsigned char *digest); 95 96static CSSM_RETURN hashDataRateCryptKit( 97 TestParams *params) 98{ 99 ckUpdateFcn updatePtr = NULL; 100 ckFinalFcn finalPtr = NULL; 101 ckInitFcn initPtr = NULL; 102 struct MD5Context md5; 103 sha1Obj sha; 104 void *ctx; 105 106 unsigned loop; 107 unsigned iloop; 108 double startTime, endTime; 109 double timeSpent, timeSpentMs; 110 uint8 ptext[PTEXT_SIZE]; 111 uint8 digest[MAX_DIGEST_SIZE]; 112 unsigned digestLen = 16; 113 114 /* we reuse this one inside the loop */ 115 switch(params->algId) { 116 case CSSM_ALGID_SHA1: 117 sha = sha1Alloc(); 118 ctx = sha; 119 initPtr = (ckInitFcn)sha1Reinit; 120 updatePtr = (ckUpdateFcn)sha1AddData; 121 finalPtr = (ckFinalFcn)ckSha1Final; 122 digestLen = 20; 123 break; 124 case CSSM_ALGID_MD5: 125 ctx = &md5; 126 initPtr = (ckInitFcn)MD5Init; 127 updatePtr = (ckUpdateFcn)MD5Update; 128 finalPtr = (ckFinalFcn)MD5Final; 129 break; 130 default: 131 printf("***Sorry, CryptKit can only do SHA1 and MD5.\n"); 132 return 1; 133 } 134 135 /* random data, const thru the loops */ 136 initPtext(ptext, PTEXT_SIZE); 137 138 /* start critical timing loop */ 139 startTime = CFAbsoluteTimeGetCurrent(); 140 for(loop=0; loop<params->loops; loop++) { 141 initPtr(ctx); 142 for(iloop=0; iloop<INNER_LOOPS; iloop++) { 143 updatePtr(ctx, ptext, PTEXT_SIZE); 144 } 145 finalPtr(ctx, digest); 146 } 147 endTime = CFAbsoluteTimeGetCurrent(); 148 timeSpent = endTime - startTime; 149 timeSpentMs = timeSpent * 1000.0; 150 151 float bytesPerLoop = INNER_LOOPS * PTEXT_SIZE; 152 float totalBytes = params->loops * bytesPerLoop; 153 154 /* careful, KByte = 1024, ms = 1/1000 */ 155 printf(" Digest %.0f bytes : %u ops in %.2f ms; %f ms/op, %.0f KBytes/s\n", 156 bytesPerLoop, params->loops, 157 timeSpentMs, timeSpentMs / (double)params->loops, 158 ((float)totalBytes / 1024.0) / timeSpent); 159 if(params->dumpDigest) { 160 dumpDigest(digest, digestLen); 161 } 162 return CSSM_OK; 163} 164 165typedef union { 166 CC_MD5_CTX md5; 167 CC_SHA1_CTX sha; 168 CC_SHA256_CTX sha256; 169 CC_SHA512_CTX sha512; 170} CC_CTX; 171 172typedef void (*ccInitFcn)(void *digestCtx); 173typedef void (*ccUpdateFcn)(void *digestCtx, const void *data, CC_LONG len); 174typedef void (*ccFinalFcn)(unsigned char *digest, void *digestCtx); 175 176static CSSM_RETURN hashDataRateCommonCrypto( 177 TestParams *params) 178{ 179 CC_CTX ctx; 180 ccUpdateFcn updatePtr = NULL; 181 ccFinalFcn finalPtr = NULL; 182 ccInitFcn initPtr = NULL; 183 unsigned loop; 184 unsigned iloop; 185 double startTime, endTime; 186 double timeSpent, timeSpentMs; 187 uint8 ptext[PTEXT_SIZE]; 188 uint8 digest[MAX_DIGEST_SIZE]; 189 unsigned digestLen = 16; 190 191 /* we reuse this one inside the loop */ 192 switch(params->algId) { 193 case CSSM_ALGID_SHA1: 194 initPtr = (ccInitFcn)CC_SHA1_Init; 195 updatePtr = (ccUpdateFcn)CC_SHA1_Update; 196 finalPtr = (ccFinalFcn)CC_SHA1_Final; 197 digestLen = 20; 198 break; 199 case CSSM_ALGID_SHA224: 200 initPtr = (ccInitFcn)CC_SHA224_Init; 201 updatePtr = (ccUpdateFcn)CC_SHA224_Update; 202 finalPtr = (ccFinalFcn)CC_SHA224_Final; 203 digestLen = 28; 204 break; 205 case CSSM_ALGID_SHA256: 206 initPtr = (ccInitFcn)CC_SHA256_Init; 207 updatePtr = (ccUpdateFcn)CC_SHA256_Update; 208 finalPtr = (ccFinalFcn)CC_SHA256_Final; 209 digestLen = 32; 210 break; 211 case CSSM_ALGID_SHA384: 212 initPtr = (ccInitFcn)CC_SHA384_Init; 213 updatePtr = (ccUpdateFcn)CC_SHA384_Update; 214 finalPtr = (ccFinalFcn)CC_SHA384_Final; 215 digestLen = 48; 216 break; 217 case CSSM_ALGID_SHA512: 218 initPtr = (ccInitFcn)CC_SHA512_Init; 219 updatePtr = (ccUpdateFcn)CC_SHA512_Update; 220 finalPtr = (ccFinalFcn)CC_SHA512_Final; 221 digestLen = 64; 222 break; 223 case CSSM_ALGID_MD5: 224 initPtr = (ccInitFcn)CC_MD5_Init; 225 updatePtr = (ccUpdateFcn)CC_MD5_Update; 226 finalPtr = (ccFinalFcn)CC_MD5_Final; 227 digestLen = 16; 228 break; 229 default: 230 printf("***BRRRZAP!\n"); 231 return 1; 232 } 233 234 /* random data, const thru the loops */ 235 initPtext(ptext, PTEXT_SIZE); 236 237 /* start critical timing loop */ 238 startTime = CFAbsoluteTimeGetCurrent(); 239 for(loop=0; loop<params->loops; loop++) { 240 initPtr(&ctx); 241 for(iloop=0; iloop<INNER_LOOPS; iloop++) { 242 updatePtr(&ctx, ptext, PTEXT_SIZE); 243 } 244 finalPtr(digest, &ctx); 245 } 246 endTime = CFAbsoluteTimeGetCurrent(); 247 timeSpent = endTime - startTime; 248 timeSpentMs = timeSpent * 1000.0; 249 250 float bytesPerLoop = INNER_LOOPS * PTEXT_SIZE; 251 float totalBytes = params->loops * bytesPerLoop; 252 253 /* careful, KByte = 1024, ms = 1/1000 */ 254 printf(" Digest %.0f bytes : %u ops in %.2f ms; %f ms/op, %.0f KBytes/s\n", 255 bytesPerLoop, params->loops, 256 timeSpentMs, timeSpentMs / (double)params->loops, 257 ((float)totalBytes / 1024.0) / timeSpent); 258 if(params->dumpDigest) { 259 dumpDigest(digest, digestLen); 260 } 261 return CSSM_OK; 262} 263 264typedef CSSM_RETURN (*testRunFcn)(TestParams *testParams); 265 266/* 267 * Static declaration of a test 268 */ 269typedef struct { 270 const char *testName; 271 unsigned loops; 272 testRunFcn run; 273 char testSpec; // for t=xxx cmd line opt 274} TestDefs; 275 276static TestDefs testDefsCryptKit = 277{ "Large data digest, CryptKit", 278 1000, 279 hashDataRateCryptKit, 280 'd', 281}; 282 283static TestDefs testDefsCommonCrypto = 284{ "Large data digest, CommonCrypto", 285 1000, 286 hashDataRateCommonCrypto, 287 'd', 288}; 289 290static void algToAlgId( 291 HT_Alg alg, 292 CSSM_ALGORITHMS *algId, 293 const char **algStr) 294{ 295 switch(alg) { 296 case HA_MD5: 297 *algId = CSSM_ALGID_MD5; 298 *algStr = "MD5"; 299 break; 300 case HA_SHA1: 301 *algId = CSSM_ALGID_SHA1; 302 *algStr = "SHA1"; 303 break; 304 case HA_SHA224: 305 *algId = CSSM_ALGID_SHA224; 306 *algStr = "SHA224"; 307 break; 308 case HA_SHA256: 309 *algId = CSSM_ALGID_SHA256; 310 *algStr = "SHA256"; 311 break; 312 case HA_SHA384: 313 *algId = CSSM_ALGID_SHA384; 314 *algStr = "SHA384"; 315 break; 316 case HA_SHA512: 317 *algId = CSSM_ALGID_SHA512; 318 *algStr = "SHA512"; 319 break; 320 default: 321 printf("***algToAlgId screwup\n"); 322 exit(1); 323 } 324} 325 326int main(int argc, char **argv) 327{ 328 TestParams testParams; 329 TestDefs *testDefs = NULL; 330 CSSM_RETURN crtn; 331 int arg; 332 char *argp; 333 unsigned cmdLoops = 0; // can be specified in cmd line 334 // if not, use TestDefs.loops 335 HT_Alg alg; 336 const char *algStr; 337 int firstAlg = FIRST_ALG; 338 int lastAlg = LAST_ALG; 339 340 memset(&testParams, 0, sizeof(testParams)); 341 342 if(argc < 2) { 343 usage(argv); 344 } 345 switch(argv[1][0]) { 346 case 'c': 347 testDefs = &testDefsCommonCrypto; 348 break; 349 case 'k': 350 testDefs = &testDefsCryptKit; 351 break; 352 default: 353 usage(argv); 354 } 355 356 for(arg=2; arg<argc; arg++) { 357 argp = argv[arg]; 358 switch(argp[0]) { 359 case 'l': 360 cmdLoops = atoi(&argp[2]); 361 break; 362 case 'a': 363 if(argp[1] == '\0') { 364 usage(argv); 365 } 366 switch(argp[2]) { 367 case 'm': 368 firstAlg = lastAlg = HA_MD5; 369 break; 370 case 's': 371 firstAlg = lastAlg = HA_SHA1; 372 break; 373 case '4': 374 firstAlg = lastAlg = HA_SHA224; 375 break; 376 case '2': 377 firstAlg = lastAlg = HA_SHA256; 378 break; 379 case '3': 380 firstAlg = lastAlg = HA_SHA384; 381 break; 382 case '5': 383 firstAlg = lastAlg = HA_SHA512; 384 break; 385 default: 386 usage(argv); 387 } 388 break; 389 case 'v': 390 testParams.dumpDigest = true; 391 break; 392 default: 393 usage(argv); 394 } 395 } 396 397 printf("%s:\n", testDefs->testName); 398 if(cmdLoops) { 399 /* user specified */ 400 testParams.loops = cmdLoops; 401 } 402 else { 403 /* default */ 404 testParams.loops = testDefs->loops; 405 } 406 if((lastAlg > HA_SHA1) && (testDefs == &testDefsCryptKit)) { 407 /* CryptKit can only do MD5 and SHA1 */ 408 lastAlg = HA_SHA1; 409 } 410 for(alg=firstAlg; alg<=lastAlg; alg++) { 411 algToAlgId(alg, &testParams.algId, &algStr); 412 printf(" === %s ===\n", algStr); 413 crtn = testDefs->run(&testParams); 414 if(crtn) { 415 printf("***Error detected in test, somehow....aborting.\n"); 416 exit(1); 417 } 418 } 419 return 0; 420} 421