2 * hashTime.cpp - measure performance of digest ops
5 #include <Security/Security.h>
12 #include <openssl/md5.h>
13 #include <openssl/sha.h>
14 #include <CommonCrypto/CommonDigest.h>
15 #include "MD5.h" /* CryptKit version used Panther and prior */
16 #include "SHA1.h" /* ditto */
18 /* enumerate digest algorithms our way */
29 #define FIRST_ALG HA_MD5
30 #define LAST_ALG HA_SHA512
32 static void usage(char **argv
)
34 printf("Usage: %s [option ...]\n", argv
[0]);
36 printf(" t=testspec; default=all\n");
37 printf(" test specs: c : digest context setup/teardown\n");
38 printf(" b : basic single block digest\n");
39 printf(" d : digest lots of data\n");
40 printf(" a=alg; default=all\n");
41 printf(" algs: m : MD5\n");
42 printf(" s : SHA1\n");
43 printf(" 4 : SHA224\n");
44 printf(" 2 : SHA256\n");
45 printf(" 3 : SHA384\n");
46 printf(" 5 : SHA512\n");
47 printf(" l=loops (only valid if testspec is given)\n");
48 printf(" o (use openssl implementations, MD5 and SHA1 only)\n");
49 printf(" c (use CommonCrypto implementation)\n");
50 printf(" k (use CryptKit implementations, MD5 and SHA1 only\n");
51 printf(" v verify digest by printing it\n");
55 static void dumpDigest(
56 const unsigned char *digest
,
59 for(unsigned dex
=0; dex
<len
; dex
++) {
60 printf("%02X", *digest
++);
68 /* sort-of random, but repeatable */
69 static void initPtext(
74 for(unsigned dex
=0; dex
<len
; dex
++) {
79 /* passed to each test */
82 CSSM_CSP_HANDLE cspHand
;
83 CSSM_ALGORITHMS algId
; // MD5, SHA1
87 /* just CDSA context setup/teardown - no CSP activity */
88 static CSSM_RETURN
hashContext(
91 CSSM_CC_HANDLE ccHand
;
97 startTime
= CPUTimeRead();
98 for(loop
=0; loop
<params
->loops
; loop
++) {
99 crtn
= CSSM_CSP_CreateDigestContext(params
->cspHand
,
100 params
->algId
, &ccHand
);
104 crtn
= CSSM_DeleteContext(ccHand
);
109 timeSpentMs
= CPUTimeDeltaMs(startTime
, CPUTimeRead());
110 printf(" context setup/delete : %u ops in %.2f ms; %f ms/op\n",
111 params
->loops
, timeSpentMs
, timeSpentMs
/ (double)params
->loops
);
115 /* Minimal CSP init/digest/final */
116 #define BASIC_BLOCK_SIZE 64 // to digest in bytes
117 #define MAX_DIGEST_SIZE 64 // we provide, no malloc below CSSM
119 static CSSM_RETURN
hashBasic(
122 CSSM_CC_HANDLE ccHand
;
127 uint8 ptext
[BASIC_BLOCK_SIZE
];
128 uint8 digest
[MAX_DIGEST_SIZE
];
129 CSSM_DATA ptextData
= {BASIC_BLOCK_SIZE
, ptext
};
130 CSSM_DATA digestData
= {MAX_DIGEST_SIZE
, digest
};
132 /* we reuse this one inside the loop */
133 crtn
= CSSM_CSP_CreateDigestContext(params
->cspHand
,
134 params
->algId
, &ccHand
);
139 /* random data, const thru the loops */
140 appGetRandomBytes(ptext
, BASIC_BLOCK_SIZE
);
142 /* start critical timing loop */
143 startTime
= CPUTimeRead();
144 for(loop
=0; loop
<params
->loops
; loop
++) {
145 crtn
= CSSM_DigestDataInit(ccHand
);
149 crtn
= CSSM_DigestDataUpdate(ccHand
, &ptextData
, 1);
153 crtn
= CSSM_DigestDataFinal(ccHand
, &digestData
);
158 CSSM_DeleteContext(ccHand
);
159 timeSpentMs
= CPUTimeDeltaMs(startTime
, CPUTimeRead());
160 printf(" Digest one %u byte block : %u ops in %.2f ms; %f ms/op\n",
161 BASIC_BLOCK_SIZE
, params
->loops
,
162 timeSpentMs
, timeSpentMs
/ (double)params
->loops
);
167 #define PTEXT_SIZE 1000 // to digest in bytes
168 #define INNER_LOOPS 1000
170 static CSSM_RETURN
hashDataRate(
173 CSSM_CC_HANDLE ccHand
;
178 double timeSpent
, timeSpentMs
;
179 uint8 ptext
[PTEXT_SIZE
];
180 uint8 digest
[MAX_DIGEST_SIZE
];
181 CSSM_DATA ptextData
= {PTEXT_SIZE
, ptext
};
182 CSSM_DATA digestData
= {MAX_DIGEST_SIZE
, digest
};
184 /* we reuse this one inside the loop */
185 crtn
= CSSM_CSP_CreateDigestContext(params
->cspHand
,
186 params
->algId
, &ccHand
);
191 /* random data, const thru the loops */
192 initPtext(ptext
, PTEXT_SIZE
);
194 /* start critical timing loop */
195 startTime
= CPUTimeRead();
196 for(loop
=0; loop
<params
->loops
; loop
++) {
197 crtn
= CSSM_DigestDataInit(ccHand
);
201 for(iloop
=0; iloop
<INNER_LOOPS
; iloop
++) {
202 crtn
= CSSM_DigestDataUpdate(ccHand
, &ptextData
, 1);
207 crtn
= CSSM_DigestDataFinal(ccHand
, &digestData
);
212 timeSpentMs
= CPUTimeDeltaMs(startTime
, CPUTimeRead());
213 timeSpent
= timeSpentMs
/ 1000.0;
215 CSSM_DeleteContext(ccHand
);
216 float bytesPerLoop
= INNER_LOOPS
* PTEXT_SIZE
;
217 float totalBytes
= params
->loops
* bytesPerLoop
;
219 /* careful, KByte = 1024, ms = 1/1000 */
220 printf(" Digest %.0f bytes : %u ops in %.2f ms; %f ms/op, %.0f KBytes/s\n",
221 bytesPerLoop
, params
->loops
,
222 timeSpentMs
, timeSpentMs
/ (double)params
->loops
,
223 ((float)totalBytes
/ 1024.0) / timeSpent
);
224 if(params
->dumpDigest
) {
225 dumpDigest(digest
, digestData
.Length
);
230 /* Lots of data, openssl version */
237 typedef void (*initFcn
)(void *digestCtx
);
238 typedef void (*updateFcn
)(void *digestCtx
, const void *data
, unsigned long len
);
239 typedef void (*finalFcn
)(unsigned char *digest
, void *digestCtx
);
241 static CSSM_RETURN
hashDataRateOpenssl(
245 initFcn initPtr
= NULL
;
246 updateFcn updatePtr
= NULL
;
247 finalFcn finalPtr
= NULL
;
251 double timeSpent
, timeSpentMs
;
252 uint8 ptext
[PTEXT_SIZE
];
253 uint8 digest
[MAX_DIGEST_SIZE
];
254 unsigned digestLen
= 16;
256 /* we reuse this one inside the loop */
257 switch(params
->algId
) {
258 case CSSM_ALGID_SHA1
:
259 initPtr
= (initFcn
)SHA1_Init
;
260 updatePtr
= (updateFcn
)SHA1_Update
;
261 finalPtr
= (finalFcn
)SHA1_Final
;
265 initPtr
= (initFcn
)MD5_Init
;
266 updatePtr
= (updateFcn
)MD5_Update
;
267 finalPtr
= (finalFcn
)MD5_Final
;
270 printf("***Sorry, Openssl can only do SHA1 and MD5.\n");
274 /* random data, const thru the loops */
275 initPtext(ptext
, PTEXT_SIZE
);
277 /* start critical timing loop */
278 startTime
= CPUTimeRead();
279 for(loop
=0; loop
<params
->loops
; loop
++) {
281 for(iloop
=0; iloop
<INNER_LOOPS
; iloop
++) {
282 updatePtr(&ctx
, ptext
, PTEXT_SIZE
);
284 finalPtr(digest
, &ctx
);
286 timeSpentMs
= CPUTimeDeltaMs(startTime
, CPUTimeRead());
287 timeSpent
= timeSpentMs
/ 1000.0;
289 float bytesPerLoop
= INNER_LOOPS
* PTEXT_SIZE
;
290 float totalBytes
= params
->loops
* bytesPerLoop
;
292 /* careful, KByte = 1024, ms = 1/1000 */
293 printf(" Digest %.0f bytes : %u ops in %.2f ms; %f ms/op, %.0f KBytes/s\n",
294 bytesPerLoop
, params
->loops
,
295 timeSpentMs
, timeSpentMs
/ (double)params
->loops
,
296 ((float)totalBytes
/ 1024.0) / timeSpent
);
297 if(params
->dumpDigest
) {
298 dumpDigest(digest
, digestLen
);
303 /* Lots of data, CommonCrypto version (not thru CSP) */
308 CC_SHA256_CTX sha256
;
309 CC_SHA512_CTX sha512
;
312 typedef void (*ccUpdateFcn
)(void *digestCtx
, const void *data
, CC_LONG len
);
313 typedef void (*ccFinalFcn
)(unsigned char *digest
, void *digestCtx
);
315 static CSSM_RETURN
hashDataRateCommonCrypto(
319 ccUpdateFcn updatePtr
= NULL
;
320 ccFinalFcn finalPtr
= NULL
;
321 initFcn initPtr
= NULL
;
325 double timeSpent
, timeSpentMs
;
326 uint8 ptext
[PTEXT_SIZE
];
327 uint8 digest
[MAX_DIGEST_SIZE
];
328 unsigned digestLen
= 16;
330 /* we reuse this one inside the loop */
331 switch(params
->algId
) {
332 case CSSM_ALGID_SHA1
:
333 initPtr
= (initFcn
)CC_SHA1_Init
;
334 updatePtr
= (ccUpdateFcn
)CC_SHA1_Update
;
335 finalPtr
= (ccFinalFcn
)CC_SHA1_Final
;
336 digestLen
= CC_SHA1_DIGEST_LENGTH
;
338 case CSSM_ALGID_SHA224
:
339 initPtr
= (initFcn
)CC_SHA224_Init
;
340 updatePtr
= (ccUpdateFcn
)CC_SHA224_Update
;
341 finalPtr
= (ccFinalFcn
)CC_SHA224_Final
;
342 digestLen
= CC_SHA224_DIGEST_LENGTH
;
344 case CSSM_ALGID_SHA256
:
345 initPtr
= (initFcn
)CC_SHA256_Init
;
346 updatePtr
= (ccUpdateFcn
)CC_SHA256_Update
;
347 finalPtr
= (ccFinalFcn
)CC_SHA256_Final
;
348 digestLen
= CC_SHA256_DIGEST_LENGTH
;
350 case CSSM_ALGID_SHA384
:
351 initPtr
= (initFcn
)CC_SHA384_Init
;
352 updatePtr
= (ccUpdateFcn
)CC_SHA384_Update
;
353 finalPtr
= (ccFinalFcn
)CC_SHA384_Final
;
354 digestLen
= CC_SHA384_DIGEST_LENGTH
;
356 case CSSM_ALGID_SHA512
:
357 initPtr
= (initFcn
)CC_SHA512_Init
;
358 updatePtr
= (ccUpdateFcn
)CC_SHA512_Update
;
359 finalPtr
= (ccFinalFcn
)CC_SHA512_Final
;
360 digestLen
= CC_SHA512_DIGEST_LENGTH
;
363 initPtr
= (initFcn
)CC_MD5_Init
;
364 updatePtr
= (ccUpdateFcn
)CC_MD5_Update
;
365 finalPtr
= (ccFinalFcn
)CC_MD5_Final
;
366 digestLen
= CC_MD5_DIGEST_LENGTH
;
369 printf("***BRRRZAP!\n");
373 /* random data, const thru the loops */
374 initPtext(ptext
, PTEXT_SIZE
);
376 /* start critical timing loop */
377 startTime
= CPUTimeRead();
378 for(loop
=0; loop
<params
->loops
; loop
++) {
380 for(iloop
=0; iloop
<INNER_LOOPS
; iloop
++) {
381 updatePtr(&ctx
, ptext
, PTEXT_SIZE
);
383 finalPtr(digest
, &ctx
);
385 timeSpentMs
= CPUTimeDeltaMs(startTime
, CPUTimeRead());
386 timeSpent
= timeSpentMs
/ 1000.0;
388 float bytesPerLoop
= INNER_LOOPS
* PTEXT_SIZE
;
389 float totalBytes
= params
->loops
* bytesPerLoop
;
391 /* careful, KByte = 1024, ms = 1/1000 */
392 printf(" Digest %.0f bytes : %u ops in %.2f ms; %f ms/op, %.0f KBytes/s\n",
393 bytesPerLoop
, params
->loops
,
394 timeSpentMs
, timeSpentMs
/ (double)params
->loops
,
395 ((float)totalBytes
/ 1024.0) / timeSpent
);
396 if(params
->dumpDigest
) {
397 dumpDigest(digest
, digestLen
);
402 /* Lots of data, CryptKit version */
404 /* cryptkit final routines are not orthoganal, fix up here */
405 static void ckSha1Final(
406 unsigned char *digest
,
409 sha1GetDigest((sha1Obj
)ctx
, digest
);
412 static void ckMD5Final(
413 unsigned char *digest
,
416 MD5Final((struct MD5Context
*)ctx
, digest
);
419 typedef void (*ckUpdateFcn
)(void *digestCtx
, const void *data
, unsigned len
);
420 typedef void (*ckFinalFcn
)(unsigned char *digest
, void *digestCtx
);
422 static CSSM_RETURN
hashDataRateCryptKit(
425 ckUpdateFcn updatePtr
= NULL
;
426 ckFinalFcn finalPtr
= NULL
;
427 initFcn initPtr
= NULL
;
428 struct MD5Context md5
;
435 double timeSpent
, timeSpentMs
;
436 uint8 ptext
[PTEXT_SIZE
];
437 uint8 digest
[MAX_DIGEST_SIZE
];
438 unsigned digestLen
= 16;
440 /* we reuse this one inside the loop */
441 switch(params
->algId
) {
442 case CSSM_ALGID_SHA1
:
445 initPtr
= (initFcn
)sha1Reinit
;
446 updatePtr
= (ckUpdateFcn
)sha1AddData
;
447 finalPtr
= (ckFinalFcn
)ckSha1Final
;
452 initPtr
= (initFcn
)MD5Init
;
453 updatePtr
= (ckUpdateFcn
)MD5Update
;
454 finalPtr
= (ckFinalFcn
)ckMD5Final
;
457 printf("***Sorry, CryptKit can only do SHA1 and MD5.\n");
461 /* random data, const thru the loops */
462 initPtext(ptext
, PTEXT_SIZE
);
464 /* start critical timing loop */
465 startTime
= CPUTimeRead();
466 for(loop
=0; loop
<params
->loops
; loop
++) {
468 for(iloop
=0; iloop
<INNER_LOOPS
; iloop
++) {
469 updatePtr(ctx
, ptext
, PTEXT_SIZE
);
471 finalPtr(digest
, ctx
);
473 timeSpentMs
= CPUTimeDeltaMs(startTime
, CPUTimeRead());
474 timeSpent
= timeSpentMs
/ 1000.0;
476 float bytesPerLoop
= INNER_LOOPS
* PTEXT_SIZE
;
477 float totalBytes
= params
->loops
* bytesPerLoop
;
479 /* careful, KByte = 1024, ms = 1/1000 */
480 printf(" Digest %.0f bytes : %u ops in %.2f ms; %f ms/op, %.0f KBytes/s\n",
481 bytesPerLoop
, params
->loops
,
482 timeSpentMs
, timeSpentMs
/ (double)params
->loops
,
483 ((float)totalBytes
/ 1024.0) / timeSpent
);
484 if(params
->dumpDigest
) {
485 dumpDigest(digest
, digestLen
);
490 typedef CSSM_RETURN (*testRunFcn
)(TestParams
*testParams
);
493 * Static declaration of a test
496 const char *testName
;
499 char testSpec
; // for t=xxx cmd line opt
502 static TestDefs testDefs
[] =
504 { "Digest context setup/teardown",
509 { "Basic single block digest",
514 { "Large data digest",
521 static TestDefs testDefsOpenSSL
[] =
523 { "Digest context setup/teardown",
525 NULL
, // not implemented
528 { "Basic single block digest",
530 NULL
, // not implemented
533 { "Large data digest, OpenSSL",
540 static TestDefs testDefsCommonCrypto
[] =
542 { "Digest context setup/teardown",
544 NULL
, // not implemented
547 { "Basic single block digest",
549 NULL
, // not implemented
552 { "Large data digest, CommonCrypto",
554 hashDataRateCommonCrypto
,
559 static TestDefs testDefsCryptKit
[] =
561 { "Digest context setup/teardown",
563 NULL
, // not implemented
566 { "Basic single block digest",
568 NULL
, // not implemented
571 { "Large data digest, CryptKit",
573 hashDataRateCryptKit
,
579 static void algToAlgId(
581 CSSM_ALGORITHMS
*algId
,
586 *algId
= CSSM_ALGID_MD5
;
590 *algId
= CSSM_ALGID_SHA1
;
594 *algId
= CSSM_ALGID_SHA224
;
598 *algId
= CSSM_ALGID_SHA256
;
602 *algId
= CSSM_ALGID_SHA384
;
606 *algId
= CSSM_ALGID_SHA512
;
610 printf("***algToAlgId screwup\n");
615 #define NUM_TESTS (sizeof(testDefs) / sizeof(testDefs[0]))
617 int main(int argc
, char **argv
)
619 TestParams testParams
;
621 TestDefs
*ourTestDefs
= testDefs
;
625 unsigned cmdLoops
= 0; // can be specified in cmd line
626 // if not, use TestDefs.loops
627 char testSpec
= '\0'; // allows specification of one test
631 int firstAlg
= FIRST_ALG
;
632 int lastAlg
= LAST_ALG
;
634 memset(&testParams
, 0, sizeof(testParams
));
636 for(arg
=1; arg
<argc
; arg
++) {
643 cmdLoops
= atoi(&argp
[2]);
646 if(argp
[1] == '\0') {
651 firstAlg
= lastAlg
= HA_MD5
;
654 firstAlg
= lastAlg
= HA_SHA1
;
657 firstAlg
= lastAlg
= HA_SHA224
;
660 firstAlg
= lastAlg
= HA_SHA256
;
663 firstAlg
= lastAlg
= HA_SHA384
;
666 firstAlg
= lastAlg
= HA_SHA512
;
673 ourTestDefs
= testDefsOpenSSL
;
676 ourTestDefs
= testDefsCommonCrypto
;
679 ourTestDefs
= testDefsCryptKit
;
682 testParams
.dumpDigest
= true;
689 testParams
.cspHand
= cspStartup();
690 if(testParams
.cspHand
== 0) {
691 printf("***Error attaching to CSP. Aborting.\n");
695 for(unsigned testNum
=0; testNum
<NUM_TESTS
; testNum
++) {
696 testDef
= &ourTestDefs
[testNum
];
698 if(testSpec
&& (testDef
->testSpec
!= testSpec
)) {
701 if(testDef
->run
== NULL
) {
704 printf("%s:\n", testDef
->testName
);
707 testParams
.loops
= cmdLoops
;
711 testParams
.loops
= testDef
->loops
;
713 for(alg
=firstAlg
; alg
<=lastAlg
; alg
++) {
714 algToAlgId(alg
, &testParams
.algId
, &algStr
);
715 printf(" === %s ===\n", algStr
);
716 crtn
= testDef
->run(&testParams
);