1/*
2 * secTrustTime.cpp - measure performance of SecTrust and TP cert verify
3 */
4
5#include <stdlib.h>
6#include <strings.h>
7#include <stdio.h>
8#include <unistd.h>
9#include <CoreFoundation/CoreFoundation.h>
10#include <Security/Security.h>
11#include <Security/TrustSettingsSchema.h>
12#include <security_cdsa_utils/cuFileIo.h>
13#include <utilLib/common.h>
14#include <clAppUtils/clutils.h>
15#include <clAppUtils/tpUtils.h>
16
17#define LOOPS_DEF			100
18
19const char *certFiles[] = {
20	"keybank_v3.100.cer", "keybank_v3.101.cer", "keybank_v3.102.cer"
21};
22
23#define NUM_CERTS (sizeof(certFiles) / sizeof(certFiles[0]))
24
25static void usage(char **argv)
26{
27	printf("usage: %s [options]\n", argv[0]);
28	printf("Options:\n");
29	printf("  -l loops        -- loops; default %d; 0=forever\n", LOOPS_DEF);
30	printf("  -k              -- open and hold keychains\n");
31	printf("  -t              -- TP, not SecTrust\n");
32	printf("  -T              -- TP, no Trust Settings\n");
33	printf("  -n              -- don't include root in cert chain\n");
34	printf("  -K              -- set empty KC list\n");
35	/* etc. */
36	exit(1);
37}
38
39static SecCertificateRef readCertFile(
40	const char *fileName)
41{
42	unsigned char *cp = NULL;
43	unsigned len = 0;
44	CSSM_DATA certData;
45	OSStatus ortn;
46
47	if(readFile(fileName, &cp, &len)) {
48		printf("***Error reading file %s\n", fileName);
49		return NULL;
50	}
51	certData.Length = len;
52	certData.Data = cp;
53	SecCertificateRef certRef;
54
55	ortn = SecCertificateCreateFromData(&certData,
56			CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &certRef);
57	if(ortn) {
58		cssmPerror("SecCertificateCreateFromData", ortn);
59		return NULL;
60	}
61	free(cp);
62	return certRef;
63}
64
65/* perfrom one cert chain evaluation using SecTrust */
66static OSStatus doEval(
67	CFArrayRef certArray,
68	SecPolicyRef policyRef,
69	CFArrayRef kcList)
70{
71	OSStatus ortn;
72	SecTrustRef trustRef;
73
74	ortn = SecTrustCreateWithCertificates(certArray, policyRef, &trustRef);
75	if(ortn) {
76		cssmPerror("SecTrustCreateWithCertificates", ortn);
77		return ortn;
78	}
79	if(kcList) {
80		ortn = SecTrustSetKeychains(trustRef, kcList);
81		if(ortn) {
82			cssmPerror("SecTrustCreateWithCertificates", ortn);
83			return ortn;
84		}
85	}
86	SecTrustResultType secTrustResult;
87	ortn = SecTrustEvaluate(trustRef, &secTrustResult);
88	if(ortn) {
89		cssmPerror("SecTrustEvaluate", ortn);
90		return ortn;
91	}
92	switch(secTrustResult) {
93		case kSecTrustResultProceed:
94		case kSecTrustResultUnspecified:
95			break;
96		default:
97			printf("***Unexpected SecTrustResultType (%d)\n", (int)secTrustResult);
98			ortn = -1;
99	}
100	CFRelease(trustRef);
101	return ortn;
102}
103
104/* cached CSSM anchors - simulate old SecTrustGetCSSMAnchorCertificates() */
105static CFArrayRef cachedRootArray = NULL;
106static CSSM_DATA *cachedAnchors = NULL;
107static unsigned cachedNumAnchors = 0;
108
109static OSStatus getAnchors(
110	CSSM_DATA **anchors,	/* RETURNED */
111	unsigned *numAnchors)	/* RETURNED */
112{
113	if(cachedRootArray == NULL) {
114		/* fetch, once */
115		OSStatus ortn = getSystemAnchors(&cachedRootArray, &cachedAnchors,
116			&cachedNumAnchors);
117		if(ortn) {
118			return ortn;
119		}
120	}
121	*anchors = cachedAnchors;
122	*numAnchors = cachedNumAnchors;
123	return noErr;
124}
125
126/* perfrom one cert chain evaluation using CSSM_TP_CertGroupVerify */
127static CSSM_RETURN doTpEval(
128	CSSM_TP_HANDLE tpHand,
129	CSSM_CL_HANDLE clHand,
130	CSSM_CSP_HANDLE cspHand,
131	CSSM_DATA_PTR certs,
132	uint32 numCerts,
133	bool useTrustSettings)
134{
135	CSSM_FIELD policyId;
136
137	policyId.FieldOid = CSSMOID_APPLE_X509_BASIC;
138	policyId.FieldValue.Data = NULL;
139	policyId.FieldValue.Length = 0;
140
141	CSSM_TP_CALLERAUTH_CONTEXT		authCtx;
142	memset(&authCtx, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT));
143	authCtx.Policy.NumberOfPolicyIds = 1;
144	authCtx.Policy.PolicyIds = &policyId;
145	authCtx.VerificationAbortOn = CSSM_TP_STOP_ON_POLICY;
146	if(!useTrustSettings) {
147		OSStatus ortn = getAnchors(&authCtx.AnchorCerts,
148			&authCtx.NumberOfAnchorCerts);
149		if(ortn) {
150			return ortn;
151		}
152	}
153	CSSM_APPLE_TP_ACTION_DATA tpAction;
154	tpAction.Version = CSSM_APPLE_TP_ACTION_VERSION;
155	if(useTrustSettings) {
156		tpAction.ActionFlags = CSSM_TP_ACTION_TRUST_SETTINGS;
157	}
158	else {
159		tpAction.ActionFlags = 0;
160	}
161
162	CSSM_TP_VERIFY_CONTEXT vfyCtx;
163	memset(&vfyCtx, 0, sizeof(CSSM_TP_VERIFY_CONTEXT));
164	vfyCtx.ActionData.Data   = (uint8 *)&tpAction;
165	vfyCtx.ActionData.Length = sizeof(tpAction);
166	vfyCtx.Action = CSSM_TP_ACTION_DEFAULT;
167	vfyCtx.Cred = &authCtx;
168
169	CSSM_CERTGROUP cssmCerts;
170	cssmCerts.CertType = CSSM_CERT_X_509v3;
171	cssmCerts.CertEncoding = CSSM_CERT_ENCODING_DER;
172	cssmCerts.NumCerts = numCerts;
173	cssmCerts.GroupList.CertList = certs;
174	cssmCerts.CertGroupType = CSSM_CERTGROUP_DATA;
175
176	CSSM_RETURN crtn = CSSM_TP_CertGroupVerify(tpHand, clHand, cspHand,
177		&cssmCerts,
178		&vfyCtx,
179		NULL);		/* no results */
180	if(crtn) {
181		cssmPerror("CSSM_TP_CertGroupVerify", crtn);
182	}
183	return crtn;
184}
185
186int main(int argc, char **argv)
187{
188	unsigned dex;
189	CSSM_RETURN crtn;
190
191	/* common SecTrust args */
192	CFMutableArrayRef 	kcList = NULL;
193	CFMutableArrayRef 	certArray = NULL;
194	SecPolicyRef      	policyRef = NULL;
195	unsigned			numCerts = NUM_CERTS;
196	CFArrayRef			emptyKCList = NULL;
197
198	/* common TP args */
199	CSSM_TP_HANDLE tpHand;
200	CSSM_CL_HANDLE clHand;
201	CSSM_CSP_HANDLE cspHand;
202	CSSM_DATA cssmCerts[NUM_CERTS];
203
204	/* user-spec'd variables */
205	unsigned loops = LOOPS_DEF;
206	bool holdKeychains = false;		/* hold references to KC list during operation */
207	bool useTp = false;				/* TP, not SecTrust */
208	bool useTrustSettings = true;	/* TP w/TrustSettings; false = old school TP way */
209	bool noRoot = false;			/* don't include root in chain to be verified */
210	bool emptyList = false;			/* SecTrust only: specify empty KC list */
211
212	extern char *optarg;
213	int arg;
214	while ((arg = getopt(argc, argv, "l:ktTnKh")) != -1) {
215		switch (arg) {
216			case 'l':
217				loops = atoi(optarg);
218				break;
219			case 'k':
220				holdKeychains = true;
221				break;
222			case 't':
223				useTp = true;
224				break;
225			case 'T':
226				useTp = true;
227				useTrustSettings = false;
228				break;
229			case 'n':
230				numCerts--;
231				noRoot = true;
232				break;
233			case 'K':
234				emptyList = true;
235				emptyKCList = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
236				break;
237			case 'h':
238				usage(argv);
239		}
240	}
241	if(optind != argc) {
242		usage(argv);
243	}
244
245	/* gather certs to verify */
246	certArray = CFArrayCreateMutable(NULL, 3, &kCFTypeArrayCallBacks);
247	for(dex=0; dex<numCerts; dex++) {
248		SecCertificateRef certRef = readCertFile(certFiles[dex]);
249		if(certRef == NULL) {
250			exit(1);
251		}
252		CFArrayInsertValueAtIndex(certArray, dex, certRef);
253		CFRelease(certRef);
254	}
255
256	/* prepare for one method or another */
257	if(useTp) {
258		for(dex=0; dex<numCerts; dex++) {
259			crtn = SecCertificateGetData(
260				(SecCertificateRef)CFArrayGetValueAtIndex(certArray, dex),
261					&cssmCerts[dex]);
262			if(crtn) {
263				cssmPerror("SecCertificateGetData", crtn);
264				exit(1);
265			}
266		}
267		tpHand  = tpStartup();
268		clHand  = clStartup();
269		cspHand = cspStartup();
270	}
271	else {
272		/* cook up reusable policy object */
273		SecPolicySearchRef	policySearch = NULL;
274		OSStatus ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
275			&CSSMOID_APPLE_X509_BASIC,
276			NULL,				// policy opts
277			&policySearch);
278		if(ortn) {
279			cssmPerror("SecPolicySearchCreate", ortn);
280			exit(1);
281		}
282		ortn = SecPolicySearchCopyNext(policySearch, &policyRef);
283		if(ortn) {
284			cssmPerror("SecPolicySearchCopyNext", ortn);
285			exit(1);
286		}
287		CFRelease(policySearch);
288
289		if(holdKeychains) {
290			/* the standard keychains */
291			ortn = SecKeychainCopySearchList((CFArrayRef *)&kcList);
292			if(ortn) {
293				cssmPerror("SecKeychainCopySearchList", ortn);
294				exit(1);
295			}
296
297			/* plus the ones TrustSettings needs */
298			SecKeychainRef rootKc;
299			ortn = SecKeychainOpen(SYSTEM_ROOT_STORE_PATH, &rootKc);
300			if(ortn) {
301				cssmPerror("SecKeychainOpen", ortn);
302				exit(1);
303			}
304			CFArrayAppendValue(kcList, rootKc);
305			CFRelease(rootKc);
306		}
307	}
308
309	CFAbsoluteTime startTimeFirst;
310	CFAbsoluteTime endTimeFirst;
311	CFAbsoluteTime startTimeMulti;
312	CFAbsoluteTime endTimeMulti;
313
314	/* print a banner describing current test parameters */
315	printf("Starting test: mode = ");
316	if(useTp) {
317		if(useTrustSettings) {
318			printf("TP w/TrustSettings");
319		}
320		else {
321			printf("TP w/o TrustSettings");
322		}
323	}
324	else {
325		printf("SecTrust");
326		if(holdKeychains) {
327			printf("; hold KC refs");
328		}
329		if(emptyList) {
330			printf("; empty KC list");
331		}
332	}
333	if(noRoot) {
334		printf("; no root in input certs\n");
335	}
336	else {
337		printf("\n");
338	}
339
340	/* GO */
341	startTimeFirst = CFAbsoluteTimeGetCurrent();
342	if(useTp) {
343		if(doTpEval(tpHand, clHand, cspHand, cssmCerts, numCerts,
344				useTrustSettings)) {
345			exit(1);
346		}
347		endTimeFirst = CFAbsoluteTimeGetCurrent();
348
349		startTimeMulti = CFAbsoluteTimeGetCurrent();
350		for(dex=0; dex<loops; dex++) {
351			if(doTpEval(tpHand, clHand, cspHand, cssmCerts, numCerts,
352					useTrustSettings)) {
353				exit(1);
354			}
355		}
356	}
357	else {
358		if(doEval(certArray, policyRef, emptyKCList)) {
359			exit(1);
360		}
361		endTimeFirst = CFAbsoluteTimeGetCurrent();
362
363		startTimeMulti = CFAbsoluteTimeGetCurrent();
364		for(dex=0; dex<loops; dex++) {
365			if(doEval(certArray, policyRef, emptyKCList)) {
366				exit(1);
367			}
368		}
369	}
370	endTimeMulti = CFAbsoluteTimeGetCurrent();
371	CFTimeInterval elapsed = endTimeMulti - startTimeMulti;
372
373	printf("First eval = %4.1f ms\n", (endTimeFirst - startTimeFirst) * 1000.0);
374	printf("Next evals = %4.2f ms/op (%f s total for %u loops)\n",
375		elapsed * 1000.0 / loops, elapsed, loops);
376
377	return 0;
378}
379