1/*
2 * hashTimeLibCrypt.cpp - measure performance of libcrypt digest ops.
3 *
4 * Thjis is obsolete; hashTime does this a lot better,a dn it also measures raw
5 * CommonCrypto and CryptKit versions.
6 */
7
8#include <openssl/sha.h>
9#include <openssl/md5.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include "cputime.h"
14#include "cspwrap.h"
15#include "common.h"
16#include "pbkdDigest.h"
17
18/* enumerate digest algorithms our way */
19typedef int HT_Alg;
20enum {
21	HA_MD5 = 0,
22	HA_SHA1
23};
24
25#define FIRST_ALG	HA_MD5
26#define LAST_ALG	HA_SHA1
27
28static void usage(char **argv)
29{
30    printf("Usage: %s [option ...]\n", argv[0]);
31    printf("Options:\n");
32	printf("   t=testspec; default=all\n");
33	printf("     test specs: c digest context setup/teardown\n");
34	printf("                 b basic single block digest\n");
35	printf("                 d digest lots of data\n");
36	printf("   l=loops (only valid if testspec is given)\n");
37	exit(1);
38}
39
40/* passed to each test */
41typedef struct {
42	unsigned			loops;
43	bool				isSha;
44} TestParams;
45
46/* just digest context setup/teardown */
47/* returns nonzero on error */
48static int hashContext(
49	TestParams	*params)
50{
51	unsigned 		loop;
52	CPUTime 		startTime;
53	double			timeSpentMs;
54	DigestCtx		ctx;
55	int				rtn;
56
57	startTime = CPUTimeRead();
58	for(loop=0; loop<params->loops; loop++) {
59		rtn = DigestCtxInit(&ctx, params->isSha);
60		if(!rtn) {
61			return -1;
62		}
63	}
64	timeSpentMs = CPUTimeDeltaMs(startTime, CPUTimeRead());
65
66	printf("   context setup/delete : %u ops in %.2f ms; %f ms/op\n",
67		params->loops, timeSpentMs, timeSpentMs / (double)params->loops);
68	return 0;
69}
70
71/* Minimal init/digest/final */
72#define BASIC_BLOCK_SIZE	64		// to digest in bytes
73#define MAX_DIGEST_SIZE		20		// we provide, no malloc below CSSM
74
75static int hashBasic(
76	TestParams	*params)
77{
78	unsigned 		loop;
79	CPUTime 		startTime;
80	double			timeSpentMs;
81	uint8			ptext[BASIC_BLOCK_SIZE];
82	uint8			digest[MAX_DIGEST_SIZE];
83	DigestCtx		ctx;
84	int 			rtn;
85
86	/* random data, const thru the loops */
87	appGetRandomBytes(ptext, BASIC_BLOCK_SIZE);
88
89	/* start critical timing loop */
90	startTime = CPUTimeRead();
91	for(loop=0; loop<params->loops; loop++) {
92		rtn = DigestCtxInit(&ctx, params->isSha);
93		if(!rtn) {
94			return -1;
95		}
96		rtn = DigestCtxUpdate(&ctx, ptext, BASIC_BLOCK_SIZE);
97		if(!rtn) {
98			return -1;
99		}
100		rtn = DigestCtxFinal(&ctx, digest);
101		if(!rtn) {
102			return -1;
103		}
104	}
105	DigestCtxFree(&ctx);
106	timeSpentMs = CPUTimeDeltaMs(startTime, CPUTimeRead());
107	printf("   Digest one %u byte block : %u ops in %.2f ms; %f ms/op\n",
108		BASIC_BLOCK_SIZE, params->loops,
109		timeSpentMs, timeSpentMs / (double)params->loops);
110	return 0;
111}
112
113/* Lots of data */
114#define PTEXT_SIZE			1000		// to digest in bytes
115#define INNER_LOOPS			1000
116
117static int hashDataRate(
118	TestParams	*params)
119{
120	unsigned 		loop;
121	unsigned		iloop;
122	CPUTime 		startTime;
123	double			timeSpent, timeSpentMs;
124	uint8			ptext[PTEXT_SIZE];
125	uint8			digest[MAX_DIGEST_SIZE];
126	DigestCtx		ctx;
127	int				rtn;
128
129	/* random data, const thru the loops */
130	appGetRandomBytes(ptext, PTEXT_SIZE);
131
132	/* start critical timing loop */
133	startTime = CPUTimeRead();
134	for(loop=0; loop<params->loops; loop++) {
135		rtn = DigestCtxInit(&ctx, params->isSha);
136		if(!rtn) {
137			return -1;
138		}
139		for(iloop=0; iloop<INNER_LOOPS; iloop++) {
140			rtn = DigestCtxUpdate(&ctx, ptext, PTEXT_SIZE);
141			if(!rtn) {
142				return -1;
143			}
144		}
145		rtn = DigestCtxFinal(&ctx, digest);
146		if(!rtn) {
147			return -1;
148		}
149	}
150	timeSpentMs = CPUTimeDeltaMs(startTime, CPUTimeRead());
151	timeSpent = timeSpentMs / 1000.0;
152
153	float bytesPerLoop = INNER_LOOPS * PTEXT_SIZE;
154	float totalBytes   = params->loops * bytesPerLoop;
155
156	/* careful, KByte = 1024, ms = 1/1000 */
157	printf("   Digest %.0f bytes : %u ops in %.2f ms; %f ms/op, %.0f KBytes/s\n",
158		bytesPerLoop, params->loops,
159		timeSpentMs, timeSpentMs / (double)params->loops,
160		((float)totalBytes / 1024.0) / timeSpent);
161	return 0;
162}
163
164
165typedef int (*testRunFcn)(TestParams *testParams);
166
167/*
168 * Static declaration of a test
169 */
170typedef struct {
171	const char 			*testName;
172	unsigned			loops;
173	testRunFcn			run;
174	char				testSpec;		// for t=xxx cmd line opt
175} TestDefs;
176
177static TestDefs testDefs[] =
178{
179	{ 	"Digest context setup/teardown",
180		100000,
181		hashContext,
182		'c',
183	},
184	{ 	"Basic single block digest",
185		100000,
186		hashBasic,
187		'b',
188	},
189	{ 	"Large data digest",
190		1000,
191		hashDataRate,
192		'd',
193	},
194};
195
196static void algToAlgId(
197	HT_Alg			alg,
198	bool			*isSha,
199	const char		**algStr)
200{
201	switch(alg) {
202		case HA_MD5:
203			*isSha = false;
204			*algStr = "MD5";
205			break;
206		case HA_SHA1:
207			*isSha = true;
208			*algStr = "SHA1";
209			break;
210		default:
211			printf("***algToAlgId screwup\n");
212			exit(1);
213	}
214}
215
216#define NUM_TESTS	(sizeof(testDefs) / sizeof(testDefs[0]))
217
218int main(int argc, char **argv)
219{
220	TestParams 		testParams;
221	TestDefs		*testDef;
222	int				rtn;
223	int 			arg;
224	char			*argp;
225	unsigned		cmdLoops = 0;		// can be specified in cmd line
226										// if not, use TestDefs.loops
227	char			testSpec = '\0';	// allows specification of one test
228										// otherwise run all
229	HT_Alg			alg;
230	const char		*algStr;
231
232	for(arg=1; arg<argc; arg++) {
233		argp = argv[arg];
234		switch(argp[0]) {
235			case 't':
236				testSpec = argp[2];
237				break;
238			case 'l':
239				cmdLoops = atoi(&argp[2]);
240				break;
241			default:
242				usage(argv);
243		}
244	}
245
246	for(unsigned testNum=0; testNum<NUM_TESTS; testNum++) {
247		testDef = &testDefs[testNum];
248
249		if(testSpec && (testDef->testSpec != testSpec)) {
250			continue;
251		}
252		printf("%s:\n", testDef->testName);
253		if(cmdLoops) {
254			/* user specified */
255			testParams.loops = cmdLoops;
256		}
257		else {
258			/* default */
259			testParams.loops = testDef->loops;
260		}
261		for(alg=FIRST_ALG; alg<=LAST_ALG; alg++) {
262			algToAlgId(alg, &testParams.isSha, &algStr);
263			printf("   === %s ===\n", algStr);
264			rtn = testDef->run(&testParams);
265			if(rtn) {
266				printf("Test returned error\n");
267				exit(1);
268			}
269		}
270	}
271	return 0;
272}
273