+/*
+ * Measure performance of SecureTransport handshake
+ *
+ * Written by Doug Mitchell.
+ */
+#include <Security/SecureTransport.h>
+#include <clAppUtils/sslAppUtils.h>
+#include <clAppUtils/ioSock.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <utilLib/common.h>
+
+#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+/* default - run both server and client on this machine, port 1200 */
+#define HOST_DEF "localhost"
+#define PORT_DEF 1200
+
+/* default keychain */
+#define DEFAULT_KC "localcert"
+
+#define DH_PARAM_FILE "dhParams_1024.der"
+
+#define GET_MSG "GET / HTTP/1.0\r\n\r\n"
+
+static void usage(char **argv)
+{
+ printf("Usage: %s s[erver]|c[lient] loops [option ...]\n", argv[0]);
+ printf("Options:\n");
+ printf(" -h hostname (default = %s)\n", HOST_DEF);
+ printf(" -p port (default = %d)\n", PORT_DEF);
+ printf(" -k keychain (default = %s)\n", DEFAULT_KC);
+ printf(" -c cipher (default = RSA/RC4; server side only)\n");
+ printf(" ciphers: r=RSA/RC4; d=RSA/DES; D=RSA/3DES; h=DHA/RC4; "
+ "H=DH/DSS/DES\n");
+ printf(" -v version (t|2|3 default = t(TLS1); server side only)\n");
+ printf(" -w password (unlock server keychain with password)\n");
+ printf(" -a (enable client authentication)\n");
+ printf(" -r (resumable session enabled; default is disabled)\n");
+ printf(" -n (No client side anchor specification; root is in system KC)\n");
+ printf(" -d (disable cert verify)\n");
+ printf(" -o (Allow hostname spoofing)\n");
+ printf(" -g Send GET msg (needs for talking to real servers)\n");
+ printf(" -V (verbose)\n");
+ exit(1);
+}
+
+#include <signal.h>
+
+void sigpipe(int sig)
+{
+ fflush(stdin);
+ printf("***SIGPIPE***\n");
+}
+
+int main(int argc, char **argv)
+{
+ /* user-spec'd variables */
+ unsigned loops;
+ char *kcName = DEFAULT_KC;
+ int port = PORT_DEF;
+ char *hostName = HOST_DEF;
+ SSLCipherSuite cipherSuite = SSL_RSA_WITH_RC4_128_SHA;
+ SSLProtocol prot = kTLSProtocol1Only;
+ char password[200];
+ bool clientAuthEnable = false;
+ bool isServer = false;
+ bool diffieHellman = false;
+ bool verbose = false;
+ bool resumeEnable = false;
+ bool setClientAnchor = true;
+ bool certVerifyEnable = true;
+ bool checkHostName = true;
+ bool sendGet = false;
+
+ otSocket listenSock = 0; // for server only
+ CFArrayRef myCerts = NULL;
+
+ signal(SIGPIPE, sigpipe);
+ if(argc < 3) {
+ usage(argv);
+ }
+ password[0] = 0;
+ switch(argv[1][0]) {
+ case 's':
+ isServer = true;
+ break;
+ case 'c':
+ isServer = false;
+ break;
+ default:
+ usage(argv);
+ }
+ loops = atoi(argv[2]);
+ if(loops == 0) {
+ usage(argv);
+ }
+
+ extern int optind;
+ extern char *optarg;
+ int arg;
+ optind = 3;
+ while ((arg = getopt(argc, argv, "h:p:k:x:c:v:w:b:aVrndog")) != -1) {
+ switch (arg) {
+ case 'h':
+ hostName = optarg;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'k':
+ kcName = 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;
+ 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 'a':
+ clientAuthEnable = true;
+ break;
+ case 'V':
+ verbose = true;
+ break;
+ case 'r':
+ resumeEnable = true;
+ break;
+ case 'n':
+ setClientAnchor = false;
+ break;
+ case 'd':
+ certVerifyEnable = false;
+ break;
+ case 'o':
+ checkHostName = false;
+ break;
+ case 'g':
+ sendGet = true;
+ break;
+ default:
+ usage(argv);
+ }
+ }
+
+ /* gather Diffie-Hellman params from cwd */
+ if(JAGUAR_BUILD && diffieHellman) {
+ printf("***SOrry, DIffie Hellman not available in this config.\n");
+ exit(1);
+ }
+ 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;
+ if(isServer || clientAuthEnable || setClientAnchor) {
+ 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 %lu\n", ortn);
+ /* oh well */
+ }
+ }
+ }
+
+ /* just do this once */
+ if(clientAuthEnable || isServer || setClientAnchor) {
+ myCerts = sslKcRefToCertArray(certKc, CSSM_FALSE, CSSM_TRUE, NULL);
+ if(myCerts == NULL) {
+ exit(1);
+ }
+ }
+
+ /* server sets up listen port just once */
+ if(isServer) {
+ printf("...listening for client connection on port %d\n", port);
+ ortn = ListenForClients(port, 0, &listenSock);
+ if(ortn) {
+ printf("...error establishing a listen socket. Aborting.\n");
+ exit(1);
+ }
+ }
+
+ CFAbsoluteTime setupTotal = 0;
+ CFAbsoluteTime handShakeTotal = 0;
+
+ for(unsigned loop=0; loop<loops; loop++) {
+ otSocket peerSock = 0;
+ PeerSpec peerId;
+
+ if(isServer) {
+ ortn = AcceptClientConnection(listenSock, &peerSock, &peerId);
+ if(ortn) {
+ printf("...error listening for connection. Aborting.\n");
+ exit(1);
+ }
+ }
+ else {
+ /* client side */
+ if(verbose) {
+ printf("...connecting to host %s at port %d\n", hostName, port);
+ }
+ ortn = MakeServerConnection(hostName, port, 0, &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, peerSock);
+ if(ortn) {
+ printSslErrStr("SSLSetConnection", ortn);
+ exit(1);
+ }
+ if(checkHostName) {
+ ortn = SSLSetPeerDomainName(ctx, hostName, strlen(hostName) + 1);
+ if(ortn) {
+ printSslErrStr("SSLSetPeerDomainName", ortn);
+ exit(1);
+ }
+ }
+ if(resumeEnable) {
+ ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec));
+ if(ortn) {
+ printSslErrStr("SSLSetPeerID", ortn);
+ exit(1);
+ }
+ }
+ if(!certVerifyEnable) {
+ /*
+ * Do this before setting up certs to allow for optimization
+ * on server side (setting valid cert without verifying them)
+ */
+ ortn = SSLSetEnableCertVerify(ctx, false);
+ if(ortn) {
+ printSslErrStr("SSLSetPeerID", 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().
+ */
+ if(clientAuthEnable || isServer) {
+ if(!certVerifyEnable) {
+ /* don't bother...this is heavyweight */
+ 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);
+ }
+ #if 0
+ /* FIXME why does this fail on Jaguar? */
+ ortn = SSLSetProtocolVersion(ctx, prot);
+ if(ortn) {
+ printSslErrStr("SSLSetProtocolVersion", ortn);
+ exit(1);
+ }
+ #endif
+ #if !JAGUAR_BUILD
+ if(dhParams != NULL) {
+ ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen);
+ if(ortn) {
+ printSslErrStr("SSLSetDiffieHellmanParams", ortn);
+ exit(1);
+ }
+ }
+ #endif
+ }
+ else {
+ /* client setup */
+ if(!clientAuthEnable && setClientAnchor) {
+ /* We're not presenting a cert; trust the server certs */
+ bool foundOne;
+ if(certKc == NULL) {
+ printf("sslAddTrustedRoots screwup\n");
+ exit(1);
+ }
+ 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);
+
+ /*
+ * Shut down. Server does the SSLClose while client tries to read. Client gets
+ * a errSSLClosedGraceful then does its SSLCLose.
+ */
+ if(!isServer) {
+ char data[2];
+ size_t actRead;
+ bool done = false;
+ if(sendGet) {
+ ortn = SSLWrite(ctx, GET_MSG, strlen(GET_MSG), &actRead);
+ if(ortn) {
+ printSslErrStr("SSLWrite", ortn);
+ }
+ }
+ do {
+ ortn = SSLRead(ctx, data, 2, &actRead);
+ switch(ortn) {
+ case errSSLClosedGraceful:
+ done = true;
+ break;
+ case noErr:
+ /* try again */
+ break;
+ default:
+ printf("Unexpected rtn on client SSLRead(); bytesRead %u\n",
+ (unsigned)actRead);
+ printSslErrStr("SSLRead", ortn);
+ done = true;
+ /* ah, keep going */
+ break;
+ }
+ } while(!done);
+ }
+ /* shut down channel */
+ ortn = SSLClose(ctx);
+ if(ortn) {
+ printSslErrStr("SSLCLose", ortn);
+ exit(1);
+ }
+
+ /* how'd we do? */
+ if(verbose) {
+ printf("SSL version : %s\n",
+ sslGetProtocolVersionString(negVersion));
+ printf("CipherSuite : %s\n",
+ sslGetCipherSuiteString(negCipher));
+ printf("Client Cert State : %s\n",
+ sslGetClientCertStateString(certState));
+ printf("SSLContext setup : %f s\n", hshakeStart - setupStart);
+ printf("SSL Handshake : %f s\n", hshakeEnd - hshakeStart);
+ }
+ setupTotal += (hshakeStart - setupStart);
+ handShakeTotal += (hshakeEnd - hshakeStart);
+ }
+
+ printf("\n");
+ printf("SSL setup avg %f s\n", setupTotal / loops);
+ printf("SSL handshake avg %f s\n", handShakeTotal / loops);
+ return 0;
+}
+
+
+