1/* 2 * ccHmacClone - test CommonCrypto's clone context for HMAC. 3 * 4 * Written 3/30/2006 by Doug Mitchell. 5 */ 6 7#include <stdlib.h> 8#include <stdio.h> 9#include <time.h> 10#include "common.h" 11#include <string.h> 12#include <CommonCrypto/CommonHMAC.h> 13 14/* 15 * Defaults. 16 */ 17#define LOOPS_DEF 200 18 19#define MIN_DATA_SIZE 8 20#define MAX_DATA_SIZE 10000 /* bytes */ 21#define MIN_KEY_SIZE 1 22#define MAX_KEY_SIZE 256 /* bytes */ 23#define LOOP_NOTIFY 20 24 25/* 26 * Enumerate algs our own way to allow iteration. 27 */ 28typedef enum { 29 ALG_MD5 = 1, 30 ALG_SHA1, 31 ALG_SHA224, 32 ALG_SHA256, 33 ALG_SHA384, 34 ALG_SHA512, 35} HmacAlg; 36#define ALG_FIRST ALG_MD5 37#define ALG_LAST ALG_SHA512 38 39#define LOG_SIZE 0 40#if LOG_SIZE 41#define logSize(s) printf s 42#else 43#define logSize(s) 44#endif 45 46static void usage(char **argv) 47{ 48 printf("usage: %s [options]\n", argv[0]); 49 printf(" Options:\n"); 50 printf(" a=algorithm (5=MD5; s=SHA1; 4=SHA224; 2=SHA256; 3=SHA384; 1=SHA512; default=all)\n"); 51 printf(" l=loops (default=%d; 0=forever)\n", LOOPS_DEF); 52 printf(" k=keySizeInBytes\n"); 53 printf(" m=maxPtextSize (default=%d)\n", MAX_DATA_SIZE); 54 printf(" n=minPtextSize (default=%d)\n", MIN_DATA_SIZE); 55 printf(" p=pauseInterval (default=0, no pause)\n"); 56 printf(" s (all ops single-shot, not staged)\n"); 57 printf(" S (all ops staged)\n"); 58 printf(" z (keys and plaintext all zeroes)\n"); 59 printf(" v(erbose)\n"); 60 printf(" q(uiet)\n"); 61 printf(" h(elp)\n"); 62 exit(1); 63} 64 65/* 66 * Given an initialized CCHmacContext, feed it some data and get the result. 67 */ 68static void hmacRun( 69 CCHmacContext *ctx, 70 bool randomUpdates, 71 const unsigned char *ptext, 72 size_t ptextLen, 73 void *dataOut) 74{ 75 while(ptextLen) { 76 size_t thisMoveIn; /* input to CCryptUpdate() */ 77 78 if(randomUpdates) { 79 thisMoveIn = genRand(1, ptextLen); 80 } 81 else { 82 thisMoveIn = ptextLen; 83 } 84 logSize(("###ptext segment (1) len %lu\n", (unsigned long)thisMoveIn)); 85 CCHmacUpdate(ctx, ptext, thisMoveIn); 86 ptext += thisMoveIn; 87 ptextLen -= thisMoveIn; 88 } 89 CCHmacFinal(ctx, dataOut); 90} 91 92 93#define MAX_HMAC_SIZE CC_SHA512_DIGEST_LENGTH 94 95static int doTest(const uint8_t *ptext, 96 size_t ptextLen, 97 CCHmacAlgorithm hmacAlg, 98 uint32 keySizeInBytes, 99 bool stagedOrig, 100 bool stagedClone, 101 bool quiet, 102 bool verbose) 103{ 104 uint8_t keyBytes[MAX_KEY_SIZE]; 105 uint8_t hmacOrig[MAX_HMAC_SIZE]; 106 uint8_t hmacClone[MAX_HMAC_SIZE]; 107 int rtn = 0; 108 CCHmacContext ctxOrig; 109 CCHmacContext ctxClone; 110 unsigned die; /* 0..3 indicates when to clone */ 111 unsigned loopNum = 0; 112 size_t hmacLen; 113 bool didClone = false; 114 115 switch(hmacAlg) { 116 case kCCHmacAlgSHA1: 117 hmacLen = CC_SHA1_DIGEST_LENGTH; 118 break; 119 case kCCHmacAlgMD5: 120 hmacLen = CC_MD5_DIGEST_LENGTH; 121 break; 122 case kCCHmacAlgSHA224: 123 hmacLen = CC_SHA224_DIGEST_LENGTH; 124 break; 125 case kCCHmacAlgSHA256: 126 hmacLen = CC_SHA256_DIGEST_LENGTH; 127 break; 128 case kCCHmacAlgSHA384: 129 hmacLen = CC_SHA384_DIGEST_LENGTH; 130 break; 131 case kCCHmacAlgSHA512: 132 hmacLen = CC_SHA512_DIGEST_LENGTH; 133 break; 134 default: 135 printf("***BRRRZAP!\n"); 136 exit(1); 137 } 138 139 /* random key */ 140 appGetRandomBytes(keyBytes, keySizeInBytes); 141 142 /* cook up first context */ 143 CCHmacInit(&ctxOrig, hmacAlg, keyBytes, keySizeInBytes); 144 145 /* roll the dice */ 146 die = genRand(0, 3); 147 148 /* 149 * In this loop we do updates to the ctxOrig up until we 150 * clone it, then we use hmacRun to finish both of them. 151 */ 152 while(ptextLen) { 153 if((die == loopNum) || !stagedOrig) { 154 /* make the clone now */ 155 if(verbose) { 156 printf(" ...cloning at loop %u\n", loopNum); 157 } 158 ctxClone = ctxOrig; 159 didClone = true; 160 161 /* do all of the clone's updates and final here */ 162 hmacRun(&ctxClone, stagedClone, ptext, ptextLen, hmacClone); 163 164 /* now do all remaining updates and final for original */ 165 hmacRun(&ctxOrig, stagedOrig, ptext, ptextLen, hmacOrig); 166 167 /* we're all done, time to check the HMAC values */ 168 break; 169 } /* making clone */ 170 171 /* feed some data into cryptorOrig */ 172 size_t thisMove; 173 if(stagedOrig) { 174 thisMove = genRand(1, ptextLen); 175 } 176 else { 177 thisMove = ptextLen; 178 } 179 logSize(("###ptext segment (2) len %lu\n", (unsigned long)thisMove)); 180 CCHmacUpdate(&ctxOrig, ptext, thisMove); 181 ptext += thisMove; 182 ptextLen -= thisMove; 183 loopNum++; 184 } 185 186 /* 187 * It's possible to get here without cloning or doing any finals, 188 * if we ran thru multiple updates and finished ptextLen for cryptorOrig 189 * before we hit the cloning spot. 190 */ 191 if(!didClone) { 192 if(!quiet) { 193 printf("...ctxOrig finished before we cloned; skipping test\n"); 194 } 195 return 0; 196 } 197 if(memcmp(hmacOrig, hmacClone, hmacLen)) { 198 printf("***data miscompare\n"); 199 rtn = testError(quiet); 200 } 201 return rtn; 202} 203 204bool isBitSet(unsigned bit, unsigned word) 205{ 206 if(bit > 31) { 207 printf("We don't have that many bits\n"); 208 exit(1); 209 } 210 unsigned mask = 1 << bit; 211 return (word & mask) ? true : false; 212} 213 214int main(int argc, char **argv) 215{ 216 int arg; 217 char *argp; 218 unsigned loop; 219 uint8 *ptext; 220 size_t ptextLen; 221 bool stagedOrig; 222 bool stagedClone; 223 const char *algStr; 224 CCHmacAlgorithm hmacAlg; 225 int i; 226 int currAlg; // ALG_xxx 227 uint32 keySizeInBytes; 228 int rtn = 0; 229 230 /* 231 * User-spec'd params 232 */ 233 bool keySizeSpec = false; // false: use rand key size 234 HmacAlg minAlg = ALG_FIRST; 235 HmacAlg maxAlg = ALG_LAST; 236 unsigned loops = LOOPS_DEF; 237 bool verbose = false; 238 size_t minPtextSize = MIN_DATA_SIZE; 239 size_t maxPtextSize = MAX_DATA_SIZE; 240 bool quiet = false; 241 unsigned pauseInterval = 0; 242 bool stagedSpec = false; // true means caller fixed stagedOrig and stagedClone 243 244 for(arg=1; arg<argc; arg++) { 245 argp = argv[arg]; 246 switch(argp[0]) { 247 case 'a': 248 if(argp[1] != '=') { 249 usage(argv); 250 } 251 switch(argp[2]) { 252 case '5': 253 minAlg = maxAlg = ALG_MD5; 254 break; 255 case 's': 256 minAlg = maxAlg = ALG_SHA1; 257 break; 258 case '4': 259 minAlg = maxAlg = ALG_SHA224; 260 break; 261 case '2': 262 minAlg = maxAlg = ALG_SHA256; 263 break; 264 case '3': 265 minAlg = maxAlg = ALG_SHA384; 266 break; 267 case '1': 268 minAlg = maxAlg = ALG_SHA512; 269 break; 270 default: 271 usage(argv); 272 } 273 break; 274 case 'l': 275 loops = atoi(&argp[2]); 276 break; 277 case 'n': 278 minPtextSize = atoi(&argp[2]); 279 break; 280 case 'm': 281 maxPtextSize = atoi(&argp[2]); 282 break; 283 case 'k': 284 keySizeInBytes = atoi(&argp[2]); 285 keySizeSpec = true; 286 break; 287 case 'v': 288 verbose = true; 289 break; 290 case 'q': 291 quiet = true; 292 break; 293 case 'p': 294 pauseInterval = atoi(&argp[2]);; 295 break; 296 case 's': 297 stagedOrig = stagedClone = false; 298 stagedSpec = true; 299 break; 300 case 'S': 301 stagedOrig = stagedClone = true; 302 stagedSpec = true; 303 break; 304 case 'h': 305 default: 306 usage(argv); 307 } 308 } 309 ptext = (uint8 *)malloc(maxPtextSize); 310 if(ptext == NULL) { 311 printf("Insufficient heap space\n"); 312 exit(1); 313 } 314 /* ptext length set in test loop */ 315 316 printf("Starting ccHmacClone; args: "); 317 for(i=1; i<argc; i++) { 318 printf("%s ", argv[i]); 319 } 320 printf("\n"); 321 322 if(pauseInterval) { 323 fpurge(stdin); 324 printf("Top of test; hit CR to proceed: "); 325 getchar(); 326 } 327 328 for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) { 329 /* when zero, set size randomly or per user setting */ 330 switch(currAlg) { 331 case ALG_MD5: 332 hmacAlg = kCCHmacAlgMD5; 333 algStr = "HMACMD5"; 334 break; 335 case ALG_SHA1: 336 hmacAlg = kCCHmacAlgSHA1; 337 algStr = "HMACSHA1"; 338 break; 339 case ALG_SHA224: 340 hmacAlg = kCCHmacAlgSHA224; 341 algStr = "HMACSHA224"; 342 break; 343 case ALG_SHA256: 344 hmacAlg = kCCHmacAlgSHA256; 345 algStr = "HMACSHA256"; 346 break; 347 case ALG_SHA384: 348 hmacAlg = kCCHmacAlgSHA384; 349 algStr = "HMACSHA384"; 350 break; 351 case ALG_SHA512: 352 hmacAlg = kCCHmacAlgSHA512; 353 algStr = "HMACSHA512"; 354 break; 355 default: 356 printf("***BRRZAP!\n"); 357 exit(1); 358 } 359 if(!quiet || verbose) { 360 printf("Testing alg %s\n", algStr); 361 } 362 for(loop=1; ; loop++) { 363 ptextLen = genRand(minPtextSize, maxPtextSize); 364 appGetRandomBytes(ptext, ptextLen); 365 if(!keySizeSpec) { 366 keySizeInBytes = genRand(MIN_KEY_SIZE, MAX_KEY_SIZE); 367 } 368 369 /* per-loop settings */ 370 if(!stagedSpec) { 371 stagedOrig = isBitSet(1, loop); 372 stagedClone = isBitSet(2, loop); 373 } 374 375 if(!quiet) { 376 if(verbose || ((loop % LOOP_NOTIFY) == 0)) { 377 printf("..loop %d ptextLen %4lu keySize %3lu stagedOrig=%d " 378 "stagedClone=%d\n", 379 loop, (unsigned long)ptextLen, (unsigned long)keySizeInBytes, 380 (int)stagedOrig, (int)stagedClone); 381 } 382 } 383 384 if(doTest(ptext, ptextLen, 385 hmacAlg, keySizeInBytes, 386 stagedOrig, stagedClone, quiet, verbose)) { 387 rtn = 1; 388 break; 389 } 390 if(pauseInterval && ((loop % pauseInterval) == 0)) { 391 char c; 392 fpurge(stdin); 393 printf("Hit CR to proceed, q to abort: "); 394 c = getchar(); 395 if(c == 'q') { 396 goto testDone; 397 } 398 } 399 if(loops && (loop == loops)) { 400 break; 401 } 402 } /* main loop */ 403 if(rtn) { 404 break; 405 } 406 407 } /* for algs */ 408 409testDone: 410 if(pauseInterval) { 411 fpurge(stdin); 412 printf("ModuleDetach/Unload complete; hit CR to exit: "); 413 getchar(); 414 } 415 if((rtn == 0) && !quiet) { 416 printf("%s test complete\n", argv[0]); 417 } 418 free(ptext); 419 return rtn; 420} 421 422 423