1/* 2 * Measure performance of SecureTransport handshake 3 * 4 * Written by Doug Mitchell. 5 */ 6#include <Security/SecureTransport.h> 7#include <clAppUtils/sslAppUtils.h> 8#include <clAppUtils/ioSock.h> 9#include <security_cdsa_utils/cuFileIo.h> 10#include <utilLib/common.h> 11 12#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 13#include <stdio.h> 14#include <stdlib.h> 15#include <string.h> 16#include <time.h> 17#include <ctype.h> 18#include <sys/param.h> 19#include <CoreFoundation/CoreFoundation.h> 20 21/* default - run both server and client on this machine, port 1200 */ 22#define HOST_DEF "localhost" 23#define PORT_DEF 1200 24 25/* default keychain */ 26#define DEFAULT_KC "localcert" 27 28#define DH_PARAM_FILE "dhParams_1024.der" 29 30#define GET_MSG "GET / HTTP/1.0\r\n\r\n" 31 32static void usage(char **argv) 33{ 34 printf("Usage: %s s[erver]|c[lient] loops [option ...]\n", argv[0]); 35 printf("Options:\n"); 36 printf(" -h hostname (default = %s)\n", HOST_DEF); 37 printf(" -p port (default = %d)\n", PORT_DEF); 38 printf(" -k keychain (default = %s)\n", DEFAULT_KC); 39 printf(" -c cipher (default = RSA/RC4; server side only)\n"); 40 printf(" ciphers: r=RSA/RC4; d=RSA/DES; D=RSA/3DES; h=DHA/RC4; " 41 "H=DH/DSS/DES\n"); 42 printf(" -v version (t|2|3 default = t(TLS1); server side only)\n"); 43 printf(" -w password (unlock server keychain with password)\n"); 44 printf(" -a (enable client authentication)\n"); 45 printf(" -r (resumable session enabled; default is disabled)\n"); 46 printf(" -n (No client side anchor specification; root is in system KC)\n"); 47 printf(" -d (disable cert verify)\n"); 48 printf(" -o (Allow hostname spoofing)\n"); 49 printf(" -g Send GET msg (needs for talking to real servers)\n"); 50 printf(" -V (verbose)\n"); 51 exit(1); 52} 53 54#include <signal.h> 55 56void sigpipe(int sig) 57{ 58 fflush(stdin); 59 printf("***SIGPIPE***\n"); 60} 61 62int main(int argc, char **argv) 63{ 64 /* user-spec'd variables */ 65 unsigned loops; 66 char *kcName = DEFAULT_KC; 67 int port = PORT_DEF; 68 char *hostName = HOST_DEF; 69 SSLCipherSuite cipherSuite = SSL_RSA_WITH_RC4_128_SHA; 70 SSLProtocol prot = kTLSProtocol1Only; 71 char password[200]; 72 bool clientAuthEnable = false; 73 bool isServer = false; 74 bool diffieHellman = false; 75 bool verbose = false; 76 bool resumeEnable = false; 77 bool setClientAnchor = true; 78 bool certVerifyEnable = true; 79 bool checkHostName = true; 80 bool sendGet = false; 81 82 otSocket listenSock = 0; // for server only 83 CFArrayRef myCerts = NULL; 84 85 signal(SIGPIPE, sigpipe); 86 if(argc < 3) { 87 usage(argv); 88 } 89 password[0] = 0; 90 switch(argv[1][0]) { 91 case 's': 92 isServer = true; 93 break; 94 case 'c': 95 isServer = false; 96 break; 97 default: 98 usage(argv); 99 } 100 loops = atoi(argv[2]); 101 if(loops == 0) { 102 usage(argv); 103 } 104 105 extern int optind; 106 extern char *optarg; 107 int arg; 108 optind = 3; 109 while ((arg = getopt(argc, argv, "h:p:k:x:c:v:w:b:aVrndog")) != -1) { 110 switch (arg) { 111 case 'h': 112 hostName = optarg; 113 break; 114 case 'p': 115 port = atoi(optarg); 116 break; 117 case 'k': 118 kcName = optarg; 119 break; 120 case 'c': 121 if(!isServer) { 122 printf("***Specify cipherSuite on server side.\n"); 123 exit(1); 124 } 125 switch(optarg[0]) { 126 case 'r': 127 cipherSuite = SSL_RSA_WITH_RC4_128_SHA; 128 break; 129 case 'd': 130 cipherSuite = SSL_RSA_WITH_DES_CBC_SHA; 131 break; 132 case 'D': 133 cipherSuite = SSL_RSA_WITH_3DES_EDE_CBC_SHA; 134 break; 135 case 'h': 136 cipherSuite = SSL_DH_anon_WITH_RC4_128_MD5; 137 diffieHellman = true; 138 break; 139 case 'H': 140 cipherSuite = SSL_DHE_DSS_WITH_DES_CBC_SHA; 141 diffieHellman = true; 142 break; 143 default: 144 usage(argv); 145 } 146 break; 147 case 'v': 148 if(!isServer) { 149 printf("***Specify protocol on server side.\n"); 150 exit(1); 151 } 152 switch(optarg[0]) { 153 case 't': 154 prot = kTLSProtocol1Only; 155 break; 156 case '2': 157 prot = kSSLProtocol2; 158 break; 159 case '3': 160 prot = kSSLProtocol3Only; 161 break; 162 default: 163 usage(argv); 164 } 165 break; 166 case 'w': 167 strcpy(password, optarg); 168 break; 169 case 'a': 170 clientAuthEnable = true; 171 break; 172 case 'V': 173 verbose = true; 174 break; 175 case 'r': 176 resumeEnable = true; 177 break; 178 case 'n': 179 setClientAnchor = false; 180 break; 181 case 'd': 182 certVerifyEnable = false; 183 break; 184 case 'o': 185 checkHostName = false; 186 break; 187 case 'g': 188 sendGet = true; 189 break; 190 default: 191 usage(argv); 192 } 193 } 194 195 /* gather Diffie-Hellman params from cwd */ 196 if(JAGUAR_BUILD && diffieHellman) { 197 printf("***SOrry, DIffie Hellman not available in this config.\n"); 198 exit(1); 199 } 200 unsigned char *dhParams = NULL; 201 unsigned dhParamsLen = 0; 202 if(diffieHellman && isServer) { 203 if(readFile(DH_PARAM_FILE, &dhParams, &dhParamsLen)) { 204 printf("***Error reading Diffie-Hellman Params. Prepare to " 205 "wait for a minute during SSL handshake.\n"); 206 } 207 } 208 209 /* 210 * Open keychain; both sides use the same one. 211 */ 212 OSStatus ortn; 213 SecKeychainRef certKc = NULL; 214 if(isServer || clientAuthEnable || setClientAnchor) { 215 ortn = SecKeychainOpen(kcName, &certKc); 216 if(ortn) { 217 printf("Error opening keychain %s (%d); aborting.\n", 218 kcName, (int)ortn); 219 exit(1); 220 } 221 if(password[0]) { 222 ortn = SecKeychainUnlock(certKc, strlen(password), password, true); 223 if(ortn) { 224 printf("SecKeychainUnlock returned %lu\n", ortn); 225 /* oh well */ 226 } 227 } 228 } 229 230 /* just do this once */ 231 if(clientAuthEnable || isServer || setClientAnchor) { 232 myCerts = sslKcRefToCertArray(certKc, CSSM_FALSE, CSSM_TRUE, NULL); 233 if(myCerts == NULL) { 234 exit(1); 235 } 236 } 237 238 /* server sets up listen port just once */ 239 if(isServer) { 240 printf("...listening for client connection on port %d\n", port); 241 ortn = ListenForClients(port, 0, &listenSock); 242 if(ortn) { 243 printf("...error establishing a listen socket. Aborting.\n"); 244 exit(1); 245 } 246 } 247 248 CFAbsoluteTime setupTotal = 0; 249 CFAbsoluteTime handShakeTotal = 0; 250 251 for(unsigned loop=0; loop<loops; loop++) { 252 otSocket peerSock = 0; 253 PeerSpec peerId; 254 255 if(isServer) { 256 ortn = AcceptClientConnection(listenSock, &peerSock, &peerId); 257 if(ortn) { 258 printf("...error listening for connection. Aborting.\n"); 259 exit(1); 260 } 261 } 262 else { 263 /* client side */ 264 if(verbose) { 265 printf("...connecting to host %s at port %d\n", hostName, port); 266 } 267 ortn = MakeServerConnection(hostName, port, 0, &peerSock, 268 &peerId); 269 if(ortn) { 270 printf("...error connecting to server %s. Aborting.\n", 271 hostName); 272 exit(1); 273 } 274 } 275 276 /* start timing SSL setup */ 277 CFAbsoluteTime setupStart = CFAbsoluteTimeGetCurrent(); 278 279 SSLContextRef ctx; 280 ortn = SSLNewContext(isServer, &ctx); 281 if(ortn) { 282 printSslErrStr("SSLNewContext", ortn); 283 exit(1); 284 } 285 ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite); 286 if(ortn) { 287 printSslErrStr("SSLSetIOFuncs", ortn); 288 exit(1); 289 } 290 ortn = SSLSetConnection(ctx, peerSock); 291 if(ortn) { 292 printSslErrStr("SSLSetConnection", ortn); 293 exit(1); 294 } 295 if(checkHostName) { 296 ortn = SSLSetPeerDomainName(ctx, hostName, strlen(hostName) + 1); 297 if(ortn) { 298 printSslErrStr("SSLSetPeerDomainName", ortn); 299 exit(1); 300 } 301 } 302 if(resumeEnable) { 303 ortn = SSLSetPeerID(ctx, &peerId, sizeof(PeerSpec)); 304 if(ortn) { 305 printSslErrStr("SSLSetPeerID", ortn); 306 exit(1); 307 } 308 } 309 if(!certVerifyEnable) { 310 /* 311 * Do this before setting up certs to allow for optimization 312 * on server side (setting valid cert without verifying them) 313 */ 314 ortn = SSLSetEnableCertVerify(ctx, false); 315 if(ortn) { 316 printSslErrStr("SSLSetPeerID", ortn); 317 exit(1); 318 } 319 } 320 321 /* 322 * Server/client specific setup. 323 * 324 * Client uses the same keychain as server, but it uses it for 325 * sslAddTrustedRoots() instead of getSslCerts() and 326 * SSLSetCertificate(). 327 */ 328 if(clientAuthEnable || isServer) { 329 if(!certVerifyEnable) { 330 /* don't bother...this is heavyweight */ 331 ortn = addIdentityAsTrustedRoot(ctx, myCerts); 332 if(ortn) { 333 exit(1); 334 } 335 } 336 ortn = SSLSetCertificate(ctx, myCerts); 337 if(ortn) { 338 printSslErrStr("SSLSetCertificate", ortn); 339 exit(1); 340 } 341 } 342 if(isServer) { 343 SSLAuthenticate auth; 344 if(clientAuthEnable) { 345 auth = kAlwaysAuthenticate; 346 } 347 else { 348 auth = kNeverAuthenticate; 349 } 350 ortn = SSLSetClientSideAuthenticate(ctx, auth); 351 if(ortn) { 352 printSslErrStr("SSLSetClientSideAuthenticate", ortn); 353 exit(1); 354 } 355 ortn = SSLSetEnabledCiphers(ctx, &cipherSuite, 1); 356 if(ortn) { 357 printSslErrStr("SSLSetEnabledCiphers", ortn); 358 exit(1); 359 } 360 #if 0 361 /* FIXME why does this fail on Jaguar? */ 362 ortn = SSLSetProtocolVersion(ctx, prot); 363 if(ortn) { 364 printSslErrStr("SSLSetProtocolVersion", ortn); 365 exit(1); 366 } 367 #endif 368 #if !JAGUAR_BUILD 369 if(dhParams != NULL) { 370 ortn = SSLSetDiffieHellmanParams(ctx, dhParams, dhParamsLen); 371 if(ortn) { 372 printSslErrStr("SSLSetDiffieHellmanParams", ortn); 373 exit(1); 374 } 375 } 376 #endif 377 } 378 else { 379 /* client setup */ 380 if(!clientAuthEnable && setClientAnchor) { 381 /* We're not presenting a cert; trust the server certs */ 382 bool foundOne; 383 if(certKc == NULL) { 384 printf("sslAddTrustedRoots screwup\n"); 385 exit(1); 386 } 387 ortn = sslAddTrustedRoots(ctx, certKc, &foundOne); 388 if(ortn) { 389 printSslErrStr("sslAddTrustedRoots", ortn); 390 exit(1); 391 } 392 } 393 } 394 395 /* 396 * Context setup complete. Start timing handshake. 397 */ 398 CFAbsoluteTime hshakeStart = CFAbsoluteTimeGetCurrent(); 399 do { 400 ortn = SSLHandshake(ctx); 401 } while (ortn == errSSLWouldBlock); 402 if(ortn) { 403 printSslErrStr("SSLHandshake", ortn); 404 exit(1); 405 } 406 CFAbsoluteTime hshakeEnd = CFAbsoluteTimeGetCurrent(); 407 408 /* snag these before data xfer possibly shuts down connection */ 409 SSLProtocol negVersion; 410 SSLCipherSuite negCipher; 411 SSLClientCertificateState certState; // RETURNED 412 413 SSLGetNegotiatedCipher(ctx, &negCipher); 414 SSLGetNegotiatedProtocolVersion(ctx, &negVersion); 415 SSLGetClientCertificateState(ctx, &certState); 416 417 /* 418 * Shut down. Server does the SSLClose while client tries to read. Client gets 419 * a errSSLClosedGraceful then does its SSLCLose. 420 */ 421 if(!isServer) { 422 char data[2]; 423 size_t actRead; 424 bool done = false; 425 if(sendGet) { 426 ortn = SSLWrite(ctx, GET_MSG, strlen(GET_MSG), &actRead); 427 if(ortn) { 428 printSslErrStr("SSLWrite", ortn); 429 } 430 } 431 do { 432 ortn = SSLRead(ctx, data, 2, &actRead); 433 switch(ortn) { 434 case errSSLClosedGraceful: 435 done = true; 436 break; 437 case noErr: 438 /* try again */ 439 break; 440 default: 441 printf("Unexpected rtn on client SSLRead(); bytesRead %u\n", 442 (unsigned)actRead); 443 printSslErrStr("SSLRead", ortn); 444 done = true; 445 /* ah, keep going */ 446 break; 447 } 448 } while(!done); 449 } 450 /* shut down channel */ 451 ortn = SSLClose(ctx); 452 if(ortn) { 453 printSslErrStr("SSLCLose", ortn); 454 exit(1); 455 } 456 457 /* how'd we do? */ 458 if(verbose) { 459 printf("SSL version : %s\n", 460 sslGetProtocolVersionString(negVersion)); 461 printf("CipherSuite : %s\n", 462 sslGetCipherSuiteString(negCipher)); 463 printf("Client Cert State : %s\n", 464 sslGetClientCertStateString(certState)); 465 printf("SSLContext setup : %f s\n", hshakeStart - setupStart); 466 printf("SSL Handshake : %f s\n", hshakeEnd - hshakeStart); 467 } 468 setupTotal += (hshakeStart - setupStart); 469 handShakeTotal += (hshakeEnd - hshakeStart); 470 } 471 472 printf("\n"); 473 printf("SSL setup avg %f s\n", setupTotal / loops); 474 printf("SSL handshake avg %f s\n", handShakeTotal / loops); 475 return 0; 476} 477 478 479 480