1/*
2 * Measure performance of SecureTransport - setup and sustained data
3 * throughput. Single process version, no sockets - all data transfer
4 * between client and server is via local memory shared between two
5 * threads.
6 *
7 * Written by Doug Mitchell.
8 */
9#include <Security/SecureTransport.h>
10#include <clAppUtils/sslAppUtils.h>
11#include <utilLib/fileIo.h>
12#include <utilLib/common.h>
13#include <clAppUtils/ringBufferIo.h>
14#include <clAppUtils/sslRingBufferThreads.h>
15
16#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <time.h>
21#include <ctype.h>
22#include <sys/param.h>
23#include <CoreFoundation/CoreFoundation.h>
24#include <security_utilities/devrandom.h>
25
26#define DEFAULT_KC		"localcert"			/* default keychain */
27#define DEFAULT_XFER	(1024 * 1024 * 20)	/* total xfer size in bytes */
28
29/* we might make these user-tweakable */
30#define DEFAULT_NUM_BUFS	16
31#define DEFAULT_BUF_SIZE	2048		/* in the ring buffers */
32#define DEFAULT_CHUNK		1024		/* bytes to write per SSLWrite() */
33
34static void usage(char **argv)
35{
36    printf("Usage: %s [option ...]\n", argv[0]);
37    printf("Options:\n");
38	printf("   -k keychain (default = %s)\n", DEFAULT_KC);
39	printf("   -x transferSize (default=%d; 0=forever)\n", DEFAULT_XFER);
40	printf("   -c cipher (default = RSA/AES128\n");
41	printf("      ciphers: a=RSA/AES128; r=RSA/RC4; d=RSA/DES; D=RSA/3DES;\n");
42	printf("               h=DHA/RC4; H=DH/DSS/DES; A=AES256\n");
43	printf("   -v version (t|2|3; default = t(TLS1)\n");
44	printf("   -w password  (unlock server keychain with password)\n");
45	printf("   -a (enable client authentication)\n");
46	printf("   -p (pause on error)\n");
47	printf("   -m (pause for malloc debug)\n");
48    exit(1);
49}
50
51int main(int argc, char **argv)
52{
53	RingBuffer		serverToClientRing;
54	RingBuffer		clientToServerRing;
55	unsigned		numBufs = DEFAULT_NUM_BUFS;
56	unsigned		bufSize = DEFAULT_BUF_SIZE;
57	unsigned		chunkSize = DEFAULT_CHUNK;
58	unsigned char	clientBuf[DEFAULT_CHUNK];
59	unsigned char	serverBuf[DEFAULT_CHUNK];
60	CFArrayRef		idArray;					/* for SSLSetCertificate */
61	CFArrayRef		anchorArray;				/* trusted roots */
62	SslRingBufferArgs clientArgs;
63	SslRingBufferArgs serverArgs;
64	SecKeychainRef	kcRef = NULL;
65	SecCertificateRef anchorCert = NULL;
66	SecIdentityRef	idRef = NULL;
67	bool			abortFlag = false;
68	pthread_t		client_thread = NULL;
69	int				result;
70	bool			diffieHellman = true;		/* FIXME needs work */
71	OSStatus		ortn;
72
73	/* user-spec'd variables */
74	char 			*kcName = DEFAULT_KC;
75	unsigned 		xferSize = DEFAULT_XFER;
76	SSLCipherSuite 	cipherSuite = TLS_RSA_WITH_AES_128_CBC_SHA;
77	SSLProtocol 	prot = kTLSProtocol1;
78	char 			password[200];
79	bool 			clientAuthEnable = false;
80	bool			pauseOnError = false;
81	bool			runForever = false;
82	bool			mallocPause = false;
83
84	password[0] = 0;
85
86	extern int optind;
87	extern char *optarg;
88	int arg;
89	optind = 1;
90	while ((arg = getopt(argc, argv, "k:x:c:v:w:aBpm")) != -1) {
91		switch (arg) {
92			case 'k':
93				kcName = optarg;
94				break;
95			case 'x':
96			{
97				unsigned xsize = atoi(optarg);
98				if(xsize == 0) {
99					runForever = true;
100					/* and leave xferSize alone */
101				}
102				else {
103					xferSize = xsize;
104				}
105				break;
106			}
107			case 'c':
108				switch(optarg[0]) {
109					case 'a':
110						cipherSuite = TLS_RSA_WITH_AES_128_CBC_SHA;
111						break;
112					case 'r':
113						cipherSuite = SSL_RSA_WITH_RC4_128_SHA;
114						break;
115					case 'd':
116						cipherSuite = SSL_RSA_WITH_DES_CBC_SHA;
117						break;
118					case 'D':
119						cipherSuite = SSL_RSA_WITH_3DES_EDE_CBC_SHA;
120						break;
121					case 'h':
122						cipherSuite = SSL_DH_anon_WITH_RC4_128_MD5;
123						diffieHellman = true;
124						break;
125					case 'H':
126						cipherSuite = SSL_DHE_DSS_WITH_DES_CBC_SHA;
127						diffieHellman = true;
128						break;
129					case 'A':
130						cipherSuite = TLS_RSA_WITH_AES_256_CBC_SHA;
131						break;
132					default:
133						usage(argv);
134				}
135				break;
136			case 'v':
137				switch(optarg[0]) {
138					case 't':
139						prot = kTLSProtocol1;
140						break;
141					case '2':
142						prot = kSSLProtocol2;
143						break;
144					case '3':
145						prot = kSSLProtocol3;
146						break;
147					default:
148						usage(argv);
149				}
150				break;
151			case 'w':
152				strcpy(password, optarg);
153				break;
154			case 'a':
155				clientAuthEnable = true;
156				break;
157			case 'p':
158				pauseOnError = true;
159				break;
160			case 'm':
161				mallocPause = true;
162				break;
163			default:
164				usage(argv);
165		}
166	}
167	if(optind != argc) {
168		usage(argv);
169	}
170
171	/* set up ring buffers */
172	ringBufSetup(&serverToClientRing, "serveToClient", numBufs, bufSize);
173	ringBufSetup(&clientToServerRing, "clientToServe", numBufs, bufSize);
174
175	/* get server SecIdentity */
176	idArray = getSslCerts(kcName,
177		CSSM_FALSE,		/* encryptOnly */
178		CSSM_FALSE,		/* completeCertChain */
179		NULL,			/* anchorFile */
180		&kcRef);
181	if(idArray == NULL) {
182		printf("***Can't get signing cert from %s\n", kcName);
183		exit(1);
184	}
185	idRef = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, 0);
186	ortn = SecIdentityCopyCertificate(idRef, &anchorCert);
187	if(ortn) {
188		cssmPerror("SecIdentityCopyCertificate", ortn);
189		exit(1);
190	}
191	anchorArray = CFArrayCreate(NULL, (const void **)&anchorCert,
192			1, &kCFTypeArrayCallBacks);
193
194	/* unlock keychain? */
195	if(password[0]) {
196		ortn = SecKeychainUnlock(kcRef, strlen(password), password, true);
197		if(ortn) {
198			cssmPerror("SecKeychainUnlock", ortn);
199			/* oh well */
200		}
201	}
202	CFRelease(kcRef);
203
204	if(mallocPause) {
205		fpurge(stdin);
206		printf("Pausing for MallocDebug setup. CR to proceed: ");
207		getchar();
208	}
209
210	/* set up server side */
211	memset(&serverArgs, 0, sizeof(serverArgs));
212	serverArgs.idArray = idArray;
213	serverArgs.trustedRoots = anchorArray;
214	serverArgs.xferSize = xferSize;
215	serverArgs.xferBuf = serverBuf;
216	serverArgs.chunkSize = chunkSize;
217	serverArgs.runForever = runForever;
218	serverArgs.cipherSuite = cipherSuite;
219	serverArgs.prot = prot;
220	serverArgs.ringWrite = &serverToClientRing;
221	serverArgs.ringRead = &clientToServerRing;
222	serverArgs.goFlag = &clientArgs.iAmReady;
223	serverArgs.abortFlag = &abortFlag;
224	serverArgs.pauseOnError = pauseOnError;
225
226	/* set up client side */
227	memset(&clientArgs, 0, sizeof(clientArgs));
228	clientArgs.idArray = NULL;		/* until we do client auth */
229	clientArgs.trustedRoots = anchorArray;
230	clientArgs.xferSize = xferSize;
231	clientArgs.xferBuf = clientBuf;
232	clientArgs.chunkSize = chunkSize;
233	clientArgs.runForever = runForever;
234	clientArgs.cipherSuite = cipherSuite;
235	clientArgs.prot = prot;
236	clientArgs.ringWrite = &clientToServerRing;
237	clientArgs.ringRead = &serverToClientRing;
238	clientArgs.goFlag = &serverArgs.iAmReady;
239	clientArgs.abortFlag = &abortFlag;
240	clientArgs.pauseOnError = pauseOnError;
241
242	/* fire up client thread */
243	result = pthread_create(&client_thread, NULL,
244			sslRbClientThread, &clientArgs);
245	if(result) {
246		printf("***pthread_create returned %d, aborting\n", result);
247		exit(1);
248	}
249
250	/*
251	 * And the server pseudo thread. This returns when all data has been transferred.
252	 */
253	ortn = sslRbServerThread(&serverArgs);
254
255	if(abortFlag) {
256		printf("***Test aborted.\n");
257		exit(1);
258	}
259
260	printf("\n");
261
262	if(mallocPause) {
263		fpurge(stdin);
264		printf("End of test. Pausing for MallocDebug analysis. CR to proceed: ");
265		getchar();
266	}
267
268	printf("SSL Protocol Version : %s\n",
269		sslGetProtocolVersionString(serverArgs.negotiatedProt));
270	printf("SSL Cipher           : %s\n",
271		sslGetCipherSuiteString(serverArgs.negotiatedCipher));
272
273	printf("SSL Handshake        : %f s\n",
274		serverArgs.startData - serverArgs.startHandshake);
275	printf("Data Transfer        : %u bytes in %f s\n", (unsigned)xferSize,
276		serverArgs.endData - serverArgs.startHandshake);
277	printf("                     : %.1f Kbytes/s\n",
278			xferSize / (serverArgs.endData - serverArgs.startHandshake) / 1024.0);
279	return 0;
280}
281
282
283
284