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