int sslVerifyProtVers(
	char		*whichSide,		// "client" or "server"
	SSLProtocol	expectProt,
	SSLProtocol	gotProt)		
{
	if(expectProt == SSL_PROTOCOL_IGNORE) {
		/* app says "don't bopther checking" */
		return 0;
	}
	if(expectProt == gotProt) {
		return 0;
	}
	printf("***%s: Expected return %s; got %s\n", whichSide,
		sslGetProtocolVersionString(expectProt),
		sslGetProtocolVersionString(gotProt));
	return 1;
}
void sslShowResult(
	char				*whichSide,		// "client" or "server"
	SslAppTestParams	*params)
{
	printf("%s status:\n", whichSide);
	if(params->acceptedProts) {
		printf("   Allowed SSL versions   : %s\n", params->acceptedProts);
	}
	else {
		printf("   Attempted  SSL version : %s\n", 
			sslGetProtocolVersionString(params->tryVersion));
	}
	printf("   Result                 : %s\n", sslGetSSLErrString(params->ortn));
	printf("   Negotiated SSL version : %s\n", 
		sslGetProtocolVersionString(params->negVersion));
	printf("   Negotiated CipherSuite : %s\n",
		sslGetCipherSuiteString(params->negCipher));
	if(params->certState != kSSLClientCertNone) {
		printf("   Client Cert State      : %s\n",
			sslGetClientCertStateString(params->certState));
	}
}
Beispiel #3
0
static void showSSLResult(
	SSLProtocol			tryVersion,
	OSStatus			err,
	SSLProtocol			negVersion,
	SSLCipherSuite		negCipher,
	CFArrayRef			peerCerts,
	CSSM_BOOL			displayPeerCerts,
	char				*fileBase)		// non-NULL: write certs to file
{
	CFIndex numPeerCerts;
	
	printf("\n");
	printf("   Attempted  SSL version : %s\n", 
		sslGetProtocolVersionString(tryVersion));
	printf("   Result                 : %s\n", sslGetSSLErrString(err));
	printf("   Negotiated SSL version : %s\n", 
		sslGetProtocolVersionString(negVersion));
	printf("   Negotiated CipherSuite : %s\n",
		sslGetCipherSuiteString(negCipher));
	if(peerCerts == NULL) {
		numPeerCerts = 0;
	}
	else {
		numPeerCerts = CFArrayGetCount(peerCerts);
	}
	printf("   Number of peer certs : %d\n", numPeerCerts);
	if(numPeerCerts != 0) {
		if(displayPeerCerts) {
			showPeerCerts(peerCerts, CSSM_FALSE);
		}
		if(fileBase != NULL) {
			writePeerCerts(peerCerts, fileBase);
		}
	}
	printf("\n");
}
int main(int argc, char **argv)
{
	RingBuffer		serverToClientRing;
	RingBuffer		clientToServerRing;
	unsigned		numBufs = DEFAULT_NUM_BUFS;
	unsigned		bufSize = DEFAULT_BUF_SIZE;
	unsigned		chunkSize = DEFAULT_CHUNK;
	unsigned char	clientBuf[DEFAULT_CHUNK];
	unsigned char	serverBuf[DEFAULT_CHUNK];
	RingBufferArgs	clientArgs;
	RingBufferArgs	serverArgs;
	bool			abortFlag = false;
	pthread_t		client_thread = NULL;
	int				result;
	OSStatus		ortn;
	unsigned char	sessionTicket[SESSION_TICKET_SIZE];
	int				ourRtn = 0;
	CFArrayRef		idArray = NULL;				/* for SSLSetCertificate */
	CFArrayRef		anchorArray = NULL;			/* trusted roots */
	char			*hostName = NULL;
	
	/* user-spec'd variables */
	char 			*kcName = NULL;
	unsigned 		xferSize = DEFAULT_XFER;
	bool			pauseOnError = false;
	bool			runForever = false;
	bool			skipPAC = false;

	extern int optind;
	extern char *optarg;
	int arg;
	optind = 1;
	while ((arg = getopt(argc, argv, "x:c:k:h:np")) != -1) {
		switch (arg) {
			case 'x':
			{
				unsigned xsize = atoi(optarg);
				if(xsize == 0) {
					runForever = true;
					/* and leave xferSize alone */
				}
				else {
					xferSize = xsize;
				}
				break;
			}
			case 'k':
				kcName = optarg;
				break;
			case 'n':
				skipPAC = true;
				break;
			case 'h':
				/* mainly to test EAP session ticket and ServerName simultaneously */
				hostName = optarg;
				break;
			case 'p':
				pauseOnError = true;
				break;
			default:
				usage(argv);
		}
	}
	if(optind != argc) {
		usage(argv);
	}
	
	/* set up ring buffers */
	ringBufSetup(&serverToClientRing, "serveToClient", numBufs, bufSize);
	ringBufSetup(&clientToServerRing, "clientToServe", numBufs, bufSize);

	/* get optional server SecIdentity */
	if(kcName) {
		SecKeychainRef	kcRef = NULL;
		SecCertificateRef anchorCert = NULL;
		SecIdentityRef	idRef = NULL;
		idArray = getSslCerts(kcName, 
			CSSM_FALSE,		/* encryptOnly */
			CSSM_FALSE,		/* completeCertChain */
			NULL,			/* anchorFile */
			&kcRef);
		if(idArray == NULL) {
			printf("***Can't get signing cert from %s\n", kcName);
			exit(1);
		}
		idRef = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, 0);
		ortn = SecIdentityCopyCertificate(idRef, &anchorCert);
		if(ortn) {
			cssmPerror("SecIdentityCopyCertificate", ortn);
			exit(1);
		}
		anchorArray = CFArrayCreate(NULL, (const void **)&anchorCert,
				1, &kCFTypeArrayCallBacks);
		CFRelease(kcRef);
		CFRelease(anchorCert);
	}

	/* set up server side */
	memset(&serverArgs, 0, sizeof(serverArgs));
	serverArgs.xferSize = xferSize;
	serverArgs.xferBuf = serverBuf;
	serverArgs.chunkSize = chunkSize;
	serverArgs.ringWrite = &serverToClientRing;
	serverArgs.ringRead = &clientToServerRing;
	serverArgs.goFlag = &clientArgs.iAmReady;
	serverArgs.abortFlag = &abortFlag;
	serverArgs.pauseOnError = pauseOnError;
	appGetRandomBytes(serverArgs.sharedSecret, SHARED_SECRET_SIZE);
	if(!skipPAC) {
		serverArgs.setMasterSecret = true;
	}
	serverArgs.idArray = idArray;
	serverArgs.trustedRoots = anchorArray;

	/* set up client side */
	memset(&clientArgs, 0, sizeof(clientArgs));
	clientArgs.xferSize = xferSize;
	clientArgs.xferBuf = clientBuf;
	clientArgs.chunkSize = chunkSize;
	clientArgs.ringWrite = &clientToServerRing;
	clientArgs.ringRead = &serverToClientRing;
	clientArgs.goFlag = &serverArgs.iAmReady;
	clientArgs.abortFlag = &abortFlag;
	clientArgs.pauseOnError = pauseOnError;
	memmove(clientArgs.sharedSecret, serverArgs.sharedSecret, SHARED_SECRET_SIZE);
	clientArgs.hostName = hostName;
	
	/* for now set up an easily recognizable ticket */
	for(unsigned dex=0; dex<SESSION_TICKET_SIZE; dex++) {
		sessionTicket[dex] = dex;
	}
	clientArgs.sessionTicket = sessionTicket;
	clientArgs.sessionTicketLen = SESSION_TICKET_SIZE;
	/* client always tries setting the master secret in this test */
	clientArgs.setMasterSecret = true;
	clientArgs.trustedRoots = anchorArray;

	/* fire up client thread */
	result = pthread_create(&client_thread, NULL, 
			rbClientThread, &clientArgs);
	if(result) {
		printf("***pthread_create returned %d, aborting\n", result);
		exit(1);
	}
	
	/* 
	 * And the server pseudo thread. This returns when all data has been transferred. 
	 */
	ortn = rbServerThread(&serverArgs);
	
	if(abortFlag) {
		printf("***Test aborted.\n");
		exit(1);
	}
	
	printf("\n");
	
	printf("SSL Protocol Version : %s\n",
		sslGetProtocolVersionString(serverArgs.negotiatedProt));
	printf("SSL Cipher           : %s\n",
		sslGetCipherSuiteString(serverArgs.negotiatedCipher));
		
	if(skipPAC) {
		if(clientArgs.sessionWasResumed) {
			printf("***skipPAC true, but client reported sessionWasResumed\n");
			ourRtn = -1;
		}
		if(serverArgs.sessionWasResumed) {
			printf("***skipPAC true, but server reported sessionWasResumed\n");
			ourRtn = -1;
		}
		if(ourRtn == 0) {
			printf("...PAC session attempted by client; refused by server;\n");
			printf("   Normal session proceeded correctly.\n");
		}
	}
	else {
		if(!clientArgs.sessionWasResumed) {
			printf("***client reported !sessionWasResumed\n");
			ourRtn = -1;
		}
		if(!serverArgs.sessionWasResumed) {
			printf("***server reported !sessionWasResumed\n");
			ourRtn = -1;
		}
		if(memcmp(clientBuf, serverBuf, DEFAULT_CHUNK)) {
			printf("***Data miscompare***\n");
			ourRtn = -1;
		}
		if(ourRtn == 0) {
			printf("...PAC session resumed correctly.\n");
		}
	}
	/* FIXME other stuff? */

	return ourRtn;
}
static void showSSLResult(
	SSLProtocol			tryVersion,
	char				*acceptedProts,
	OSStatus			err,
	SSLProtocol			negVersion,
	SSLCipherSuite		negCipher,
	Boolean				sessionWasResumed,	
	unsigned char		*sessionID,			
	size_t				sessionIDLength,	
	CFArrayRef			peerCerts,
	bool			displayPeerCerts,
	SSLClientCertificateState	certState,
	char				*fileBase)		// non-NULL: write certs to file
{
	CFIndex numPeerCerts;
	
	printf("\n");
	if(acceptedProts) {
		printf("   Allowed SSL versions   : %s\n", acceptedProts);
	}
	else {
		printf("   Attempted  SSL version : %s\n", 
			sslGetProtocolVersionString(tryVersion));
	}
	printf("   Result                 : %s\n", sslGetSSLErrString(err));
	printf("   Negotiated SSL version : %s\n", 
		sslGetProtocolVersionString(negVersion));
	printf("   Negotiated CipherSuite : %s\n",
		sslGetCipherSuiteString(negCipher));
	if(certState != kSSLClientCertNone) {
		printf("   Client Cert State      : %s\n",
			sslGetClientCertStateString(certState));
	}
	printf("   Resumed Session        : ");
	if(sessionWasResumed) {
		for(unsigned dex=0; dex<sessionIDLength; dex++) {
			printf("%02X ", sessionID[dex]);
			if(((dex % 8) == 7) && (dex != (sessionIDLength - 1))) {
				printf("\n                            ");
			}
		}
		printf("\n");
	}
	else {
		printf("NOT RESUMED\n");
	}
	if(peerCerts == NULL) {
		numPeerCerts = 0;
	}
	else {
		numPeerCerts = CFArrayGetCount(peerCerts);
	}
	printf("   Number of peer certs : %lu\n", numPeerCerts);
	if(numPeerCerts != 0) {
		if(displayPeerCerts) {
			showPeerCerts(peerCerts, false);
		}
		if(fileBase != NULL) {
			writePeerCerts(peerCerts, fileBase);
		}
	}
	printf("\n");
}
int main(int argc, char **argv)
{
	RingBuffer		serverToClientRing;
	RingBuffer		clientToServerRing;
	unsigned		numBufs = DEFAULT_NUM_BUFS;
	unsigned		bufSize = DEFAULT_BUF_SIZE;
	unsigned		chunkSize = DEFAULT_CHUNK;
	unsigned char	clientBuf[DEFAULT_CHUNK];
	unsigned char	serverBuf[DEFAULT_CHUNK];
	CFArrayRef		idArray;					/* for SSLSetCertificate */
	CFArrayRef		anchorArray;				/* trusted roots */
	SslRingBufferArgs clientArgs;
	SslRingBufferArgs serverArgs;
	SecKeychainRef	kcRef = NULL;
	SecCertificateRef anchorCert = NULL;
	SecIdentityRef	idRef = NULL;
	bool			abortFlag = false;
	pthread_t		client_thread = NULL;
	int				result;
	bool			diffieHellman = true;		/* FIXME needs work */
	OSStatus		ortn;
	
	/* user-spec'd variables */
	char 			*kcName = DEFAULT_KC;
	unsigned 		xferSize = DEFAULT_XFER;
	SSLCipherSuite 	cipherSuite = TLS_RSA_WITH_AES_128_CBC_SHA;
	SSLProtocol 	prot = kTLSProtocol1;
	char 			password[200];
	bool 			clientAuthEnable = false;
	bool			pauseOnError = false;
	bool			runForever = false;
	bool			mallocPause = false;
	
	password[0] = 0;

	extern int optind;
	extern char *optarg;
	int arg;
	optind = 1;
	while ((arg = getopt(argc, argv, "k:x:c:v:w:aBpm")) != -1) {
		switch (arg) {
			case 'k':
				kcName = optarg;
				break;
			case 'x':
			{
				unsigned xsize = atoi(optarg);
				if(xsize == 0) {
					runForever = true;
					/* and leave xferSize alone */
				}
				else {
					xferSize = xsize;
				}
				break;
			}
			case 'c':
				switch(optarg[0]) {
					case 'a':
						cipherSuite = TLS_RSA_WITH_AES_128_CBC_SHA;
						break;
					case 'r':
						cipherSuite = SSL_RSA_WITH_RC4_128_SHA;
						break;
					case 'd':
						cipherSuite = SSL_RSA_WITH_DES_CBC_SHA;
						break;
					case 'D':
						cipherSuite = SSL_RSA_WITH_3DES_EDE_CBC_SHA;
						break;
					case 'h':
						cipherSuite = SSL_DH_anon_WITH_RC4_128_MD5;
						diffieHellman = true;
						break;
					case 'H':
						cipherSuite = SSL_DHE_DSS_WITH_DES_CBC_SHA;
						diffieHellman = true;
						break;
					case 'A':
						cipherSuite = TLS_RSA_WITH_AES_256_CBC_SHA;
						break;
					default:
						usage(argv);
				}
				break;
			case 'v':
				switch(optarg[0]) {
					case 't':
						prot = kTLSProtocol1;
						break;
					case '2':
						prot = kSSLProtocol2;
						break;
					case '3':
						prot = kSSLProtocol3;
						break;
					default:
						usage(argv);
				}
				break;
			case 'w':
				strcpy(password, optarg);
				break;
			case 'a':
				clientAuthEnable = true;
				break;
			case 'p':
				pauseOnError = true;
				break;
			case 'm':
				mallocPause = true;
				break;
			default:
				usage(argv);
		}
	}
	if(optind != argc) {
		usage(argv);
	}
	
	/* set up ring buffers */
	ringBufSetup(&serverToClientRing, "serveToClient", numBufs, bufSize);
	ringBufSetup(&clientToServerRing, "clientToServe", numBufs, bufSize);
	
	/* get server SecIdentity */
	idArray = getSslCerts(kcName, 
		CSSM_FALSE,		/* encryptOnly */
		CSSM_FALSE,		/* completeCertChain */
		NULL,			/* anchorFile */
		&kcRef);
	if(idArray == NULL) {
		printf("***Can't get signing cert from %s\n", kcName);
		exit(1);
	}
	idRef = (SecIdentityRef)CFArrayGetValueAtIndex(idArray, 0);
	ortn = SecIdentityCopyCertificate(idRef, &anchorCert);
	if(ortn) {
		cssmPerror("SecIdentityCopyCertificate", ortn);
		exit(1);
	}
	anchorArray = CFArrayCreate(NULL, (const void **)&anchorCert,
			1, &kCFTypeArrayCallBacks);

	/* unlock keychain? */
	if(password[0]) {
		ortn = SecKeychainUnlock(kcRef, strlen(password), password, true);
		if(ortn) {
			cssmPerror("SecKeychainUnlock", ortn);
			/* oh well */
		}
	}
	CFRelease(kcRef);

	if(mallocPause) {
		fpurge(stdin);
		printf("Pausing for MallocDebug setup. CR to proceed: ");
		getchar();
	}
	
	/* set up server side */
	memset(&serverArgs, 0, sizeof(serverArgs));
	serverArgs.idArray = idArray;
	serverArgs.trustedRoots = anchorArray;
	serverArgs.xferSize = xferSize;
	serverArgs.xferBuf = serverBuf;
	serverArgs.chunkSize = chunkSize;
	serverArgs.runForever = runForever;
	serverArgs.cipherSuite = cipherSuite;
	serverArgs.prot = prot;
	serverArgs.ringWrite = &serverToClientRing;
	serverArgs.ringRead = &clientToServerRing;
	serverArgs.goFlag = &clientArgs.iAmReady;
	serverArgs.abortFlag = &abortFlag;
	serverArgs.pauseOnError = pauseOnError;

	/* set up client side */
	memset(&clientArgs, 0, sizeof(clientArgs));
	clientArgs.idArray = NULL;		/* until we do client auth */
	clientArgs.trustedRoots = anchorArray;
	clientArgs.xferSize = xferSize;
	clientArgs.xferBuf = clientBuf;
	clientArgs.chunkSize = chunkSize;
	clientArgs.runForever = runForever;
	clientArgs.cipherSuite = cipherSuite;
	clientArgs.prot = prot;
	clientArgs.ringWrite = &clientToServerRing;
	clientArgs.ringRead = &serverToClientRing;
	clientArgs.goFlag = &serverArgs.iAmReady;
	clientArgs.abortFlag = &abortFlag;
	clientArgs.pauseOnError = pauseOnError;
	
	/* fire up client thread */
	result = pthread_create(&client_thread, NULL, 
			sslRbClientThread, &clientArgs);
	if(result) {
		printf("***pthread_create returned %d, aborting\n", result);
		exit(1);
	}
	
	/* 
	 * And the server pseudo thread. This returns when all data has been transferred. 
	 */
	ortn = sslRbServerThread(&serverArgs);
	
	if(abortFlag) {
		printf("***Test aborted.\n");
		exit(1);
	}
	
	printf("\n");

	if(mallocPause) {
		fpurge(stdin);
		printf("End of test. Pausing for MallocDebug analysis. CR to proceed: ");
		getchar();
	}
	
	printf("SSL Protocol Version : %s\n",
		sslGetProtocolVersionString(serverArgs.negotiatedProt));
	printf("SSL Cipher           : %s\n",
		sslGetCipherSuiteString(serverArgs.negotiatedCipher));
		
	printf("SSL Handshake        : %f s\n", 
		serverArgs.startData - serverArgs.startHandshake);
	printf("Data Transfer        : %u bytes in %f s\n", (unsigned)xferSize, 
		serverArgs.endData - serverArgs.startHandshake);
	printf("                     : %.1f Kbytes/s\n", 
			xferSize / (serverArgs.endData - serverArgs.startHandshake) / 1024.0);
	return 0;
}
int main(int argc, char **argv)
{
	/* user-spec'd variables */
	const char 		*kcName = DEFAULT_KC;
	unsigned 		xferSize = XFERSIZE_DEF;
	int 			port = PORT_DEF;
	const char 		*hostName = HOST_DEF;
	SSLCipherSuite 	cipherSuite = TLS_RSA_WITH_AES_128_CBC_SHA;
	SSLProtocol 	prot = kTLSProtocol1Only;
	char 			password[200];
	bool 			clientAuthEnable = false;
	bool 			isServer = false;
	unsigned 		bufSize = BUFSIZE;
	bool			diffieHellman = false;
	int				nonBlocking = 0;
	
	if(argc < 2) {
		usage(argv);
	}
	password[0] = 0;
	switch(argv[1][0]) {
		case 's':
			isServer = true;
			break;
		case 'c':
			isServer = false;
			break;
		default:
			usage(argv);
	}
	
	extern int optind;
	extern char *optarg;
	int arg;
	optind = 2;
	while ((arg = getopt(argc, argv, "h:p:k:x:c:v:w:b:aB")) != -1) {
		switch (arg) {
			case 'h':
				hostName = optarg;
				break;
			case 'p':
				port = atoi(optarg);
				break;
			case 'k':
				kcName = optarg;
				break;
			case 'x':
				xferSize = atoi(optarg);
				break;
			case 'c':
				if(!isServer) {
					printf("***Specify cipherSuite on server side.\n");
					exit(1);
				}
				switch(optarg[0]) {
					case 'r':
						cipherSuite = SSL_RSA_WITH_RC4_128_SHA;
						break;
					case 'd':
						cipherSuite = SSL_RSA_WITH_DES_CBC_SHA;
						break;
					case 'D':
						cipherSuite = SSL_RSA_WITH_3DES_EDE_CBC_SHA;
						break;
					case 'h':
						cipherSuite = SSL_DH_anon_WITH_RC4_128_MD5;
						diffieHellman = true;
						break;
					case 'H':
						cipherSuite = SSL_DHE_DSS_WITH_DES_CBC_SHA;
						diffieHellman = true;
						break;
					case 'A':
						cipherSuite = TLS_RSA_WITH_AES_256_CBC_SHA;
						break;
					default:
						usage(argv);
				}
				break;
			case 'v':
				if(!isServer) {
					printf("***Specify protocol on server side.\n");
					exit(1);
				}
				switch(optarg[0]) {
					case 't':
						prot = kTLSProtocol1Only;
						break;
					case '2':
						prot = kSSLProtocol2;
						break;
					case '3':
						prot = kSSLProtocol3Only;
						break;
					default:
						usage(argv);
				}
				break;
			case 'w':
				strcpy(password, optarg);
				break;
			case 'b':
				bufSize = atoi(optarg);
				break;
			case 'a':
				clientAuthEnable = true;
				break;
			case 'B':
				nonBlocking = 1;
				break;
			default:
				usage(argv);
		}
	}
	
	/* per-transfer buffer - make it random for server */
	char *buf = (char *)malloc(bufSize);
	if(isServer) {
		Security::DevRandomGenerator rng;
		rng.random(buf, bufSize);
	}
	
	/* gather Diffie-Hellman params from cwd */
	unsigned char *dhParams = NULL;
	unsigned dhParamsLen = 0;
	if(diffieHellman && isServer) {
		if(readFile(DH_PARAM_FILE, &dhParams, &dhParamsLen)) {
			printf("***Error reading Diffie-Hellman Params. Prepare to "
				"wait for a minute during SSL handshake.\n");
		}
	}
	
	/*
	 * Open keychain; both sides use the same one.
	 */
	OSStatus ortn;
	SecKeychainRef certKc = NULL;
	CFAbsoluteTime kcOpenStart = CFAbsoluteTimeGetCurrent();
	ortn = SecKeychainOpen(kcName, &certKc);
	if(ortn) {
		printf("Error opening keychain %s (%d); aborting.\n",
			kcName, (int)ortn);
		exit(1);
	}
	if(password[0]) {
		ortn = SecKeychainUnlock(certKc, strlen(password), password, true);
		if(ortn) {
			printf("SecKeychainUnlock returned %d\n", (int)ortn);
			/* oh well */
		}
	}
	CFAbsoluteTime kcOpenEnd = CFAbsoluteTimeGetCurrent();
	
	otSocket peerSock = 0;
	otSocket listenSock = 0;			// for server only
    PeerSpec peerId;
	
	if(isServer) {
		printf("...listening for client connection on port %d\n", port);
		ortn = ListenForClients(port, nonBlocking, &listenSock);
		if(ortn) {
			printf("...error establishing a listen socket. Aborting.\n");
			exit(1);
		}
		ortn = AcceptClientConnection(listenSock, &peerSock, &peerId);
		if(ortn) {
			printf("...error listening for connection. Aborting.\n");
			exit(1);
		}
	}
	else {
		printf("...connecting to host %s at port %d\n", hostName, port);
		ortn = MakeServerConnection(hostName, port, nonBlocking, &peerSock,
			&peerId);
		if(ortn) {
			printf("...error connecting to server %s. Aborting.\n",
				hostName);
			exit(1);
		}
	}

	/* start timing SSL setup */
	CFAbsoluteTime setupStart = CFAbsoluteTimeGetCurrent();

	SSLContextRef ctx;
	ortn = SSLNewContext(isServer, &ctx);
	if(ortn) {
		printSslErrStr("SSLNewContext", ortn);
		exit(1);
	} 
	ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite);
	if(ortn) {
		printSslErrStr("SSLSetIOFuncs", ortn);
		exit(1);
	} 
	ortn = SSLSetConnection(ctx, (SSLConnectionRef)peerSock);
	if(ortn) {
		printSslErrStr("SSLSetConnection", ortn);
		exit(1);
	}
	ortn = SSLSetPeerDomainName(ctx, hostName, strlen(hostName) + 1);
	if(ortn) {
		printSslErrStr("SSLSetPeerDomainName", ortn);
		exit(1);
	}	
	
	/*
	 * Server/client specific setup.
	 *
	 * Client uses the same keychain as server, but it uses it for 
	 * sslAddTrustedRoots() instead of getSslCerts() and 
	 * SSLSetCertificate().
	 */
	CFArrayRef myCerts = NULL;
	if(clientAuthEnable || isServer) {
		myCerts = sslKcRefToCertArray(certKc, CSSM_FALSE, CSSM_FALSE, NULL, NULL);
		if(myCerts == NULL) {
			exit(1);
		}
		ortn = addIdentityAsTrustedRoot(ctx, myCerts);
		if(ortn) {
			exit(1);
		}
		ortn = SSLSetCertificate(ctx, myCerts);
		if(ortn) {
			printSslErrStr("SSLSetCertificate", ortn);
			exit(1);
		}
	}
	if(isServer) {
		SSLAuthenticate auth;
		if(clientAuthEnable) {
			auth = kAlwaysAuthenticate;
		}
		else {
			auth = kNeverAuthenticate;
		}
		ortn = SSLSetClientSideAuthenticate(ctx, auth);
		if(ortn) {
			printSslErrStr("SSLSetClientSideAuthenticate", ortn);
			exit(1);
		}
		ortn = SSLSetEnabledCiphers(ctx, &cipherSuite, 1);
		if(ortn) {
			printSslErrStr("SSLSetEnabledCiphers", ortn);
			exit(1);
		}
		ortn = SSLSetProtocolVersion(ctx, prot);
		if(ortn) {
			printSslErrStr("SSLSetProtocolVersion", ortn);
			exit(1);
		}
		if(dhParams != NULL) {
			ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen);
			if(ortn) {
				printSslErrStr("SSLSetDiffieHellmanParams", ortn);
				exit(1);
			}
		}
	}
	else {
		/* client setup */
		if(!clientAuthEnable) {
			/* We're not presenting a cert; trust the server certs */
			bool foundOne;
			ortn = sslAddTrustedRoots(ctx, certKc, &foundOne);
			if(ortn) {
				printSslErrStr("sslAddTrustedRoots", ortn);
				exit(1);
			}
		}
	}
	
	/*
	 * Context setup complete. Start timing handshake.
	 */
	CFAbsoluteTime hshakeStart = CFAbsoluteTimeGetCurrent();
    do {   
		ortn = SSLHandshake(ctx);
    } while (ortn == errSSLWouldBlock);
	if(ortn) {
		printSslErrStr("SSLHandshake", ortn);
		exit(1);
	}
	CFAbsoluteTime hshakeEnd = CFAbsoluteTimeGetCurrent();
	
	/* snag these before data xfer possibly shuts down connection */
	SSLProtocol	negVersion;
	SSLCipherSuite negCipher;
	SSLClientCertificateState certState;		// RETURNED

	SSLGetNegotiatedCipher(ctx, &negCipher);
	SSLGetNegotiatedProtocolVersion(ctx, &negVersion);
	SSLGetClientCertificateState(ctx, &certState);
	
	/* server sends xferSize bytes to client and shuts down */
	size_t bytesMoved;
	
	CFAbsoluteTime dataStart = CFAbsoluteTimeGetCurrent();
	size_t totalMoved = 0;
	if(isServer) {
		size_t bytesToGo = xferSize;
		bool done = false;
		do {
			size_t thisMove = bufSize;
			if(thisMove > bytesToGo) {
				thisMove = bytesToGo;
			}
			ortn = SSLWrite(ctx, buf, thisMove, &bytesMoved);
			switch(ortn) {
				case noErr:
				case errSSLWouldBlock:
					break;
				default:
					done = true;
					break;
			}
			bytesToGo -= bytesMoved;
			totalMoved += bytesMoved;
			if(bytesToGo == 0) {
				done = true;
			}
		} while(!done);
		if(ortn != noErr) {
			printSslErrStr("SSLWrite", ortn);
			exit(1);
		}
	}
	else {
		/* client reads until error or errSSLClosedGraceful */
		bool done = false;
		do {
			ortn = SSLRead(ctx, buf, bufSize, &bytesMoved);
			switch(ortn) {
				case errSSLClosedGraceful:
					done = true;
					break;
				case noErr:
				case errSSLWouldBlock:
					break;
				default:
					done = true;
					break;
			}
			totalMoved += bytesMoved;
		} while(!done);
		if(ortn != errSSLClosedGraceful) {
			printSslErrStr("SSLRead", ortn);
			exit(1);
		}
	}
	
	/* shut down channel */
	ortn = SSLClose(ctx);
	if(ortn) {
		printSslErrStr("SSLCLose", ortn);
		exit(1);
	}
	CFAbsoluteTime dataEnd = CFAbsoluteTimeGetCurrent();

	/* how'd we do? */
	printf("SSL version          : %s\n", 
		sslGetProtocolVersionString(negVersion));
	printf("CipherSuite          : %s\n",
		sslGetCipherSuiteString(negCipher));
	printf("Client Cert State    : %s\n",
			sslGetClientCertStateString(certState));

	if(password[0]) {
		printf("keychain open/unlock : ");
	}
	else {
		printf("keychain open        : ");
	}
	printf("%f s\n", kcOpenEnd - kcOpenStart);
	printf("SSLContext setup     : %f s\n", hshakeStart - setupStart);
	printf("SSL Handshake        : %f s\n", hshakeEnd - hshakeStart);
	printf("Data Transfer        : %u bytes in %f s\n", (unsigned)totalMoved, 
		dataEnd - dataStart);
	printf("                     : %.1f Kbytes/s\n", 
			totalMoved / (dataEnd - dataStart) / 1024.0);
	return 0;
}