#include #include #include #include #include #include #include #include /* SSLSetOption */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if TARGET_OS_IPHONE #include #endif #include "ssl_regressions.h" #include "ssl-utils.h" /* SSL CipherSuite tests Below are all the ciphers that are individually tested. The first element is the SecureTransport/RFC name; the second is what openssl calls it, which can be looked up in ciphers(1). All SSL_DH_* and TLS_DH_* are disabled because neither openssl nor securetransport support them: SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, SSL_DH_DSS_WITH_DES_CBC_SHA, SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA, SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DH_RSA_WITH_DES_CBC_SHA, SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA, TLS_DH_DSS_WITH_AES_128_CBC_SHA, TLS_DH_RSA_WITH_AES_128_CBC_SHA, TLS_DH_DSS_WITH_AES_256_CBC_SHA, TLS_DH_RSA_WITH_AES_256_CBC_SHA, DSS is unimplemented by securetransport on the phone: SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, SSLv2 ciphersuites disabled by securetransport on phone: SSL_RSA_WITH_RC2_CBC_MD5, SSL_RSA_WITH_IDEA_CBC_MD5, SSL_RSA_WITH_DES_CBC_MD5, SSL_RSA_WITH_3DES_EDE_CBC_MD5, SSLv3 ciphersuites disabled by securetransport on phone: SSL_RSA_WITH_IDEA_CBC_SHA, SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 Export ciphersuites disabled on iOS 5.0: SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_RSA_WITH_DES_CBC_SHA, SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA, SSL_DH_anon_WITH_DES_CBC_SHA */ typedef struct _CipherSuiteName { SSLCipherSuite cipher; const char *name; bool dh_anonymous; bool dtls; } CipherSuiteName; #define CIPHER(cipher, dh_anonymous, dtls) { cipher, #cipher, dh_anonymous, dtls}, static const CipherSuiteName ciphers[] = { //SSL_NULL_WITH_NULL_NULL, unsupported CIPHER(SSL_RSA_WITH_NULL_SHA, false, true) CIPHER(SSL_RSA_WITH_NULL_MD5, false, true) CIPHER(TLS_RSA_WITH_NULL_SHA256, false, true) CIPHER(SSL_RSA_WITH_3DES_EDE_CBC_SHA, false, true) CIPHER(SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, false, true) CIPHER(SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, true, true) CIPHER(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, false, true) CIPHER(TLS_DH_anon_WITH_AES_128_CBC_SHA, true, true) CIPHER(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, false, true) CIPHER(TLS_DH_anon_WITH_AES_256_CBC_SHA, true, true) CIPHER(TLS_RSA_WITH_AES_128_CBC_SHA, false, true) CIPHER(TLS_RSA_WITH_AES_256_CBC_SHA, false, true) #if 1 CIPHER(TLS_PSK_WITH_AES_128_CBC_SHA, true, true) CIPHER(TLS_PSK_WITH_AES_256_CBC_SHA384, true, true) CIPHER(TLS_PSK_WITH_AES_128_CBC_SHA256, true, true) CIPHER(TLS_PSK_WITH_AES_256_CBC_SHA, true, true) CIPHER(TLS_PSK_WITH_AES_128_CBC_SHA, true, true) CIPHER(TLS_PSK_WITH_3DES_EDE_CBC_SHA, true, true) CIPHER(TLS_PSK_WITH_NULL_SHA384, true, true) CIPHER(TLS_PSK_WITH_NULL_SHA256, true, true) CIPHER(TLS_PSK_WITH_NULL_SHA, true, true) #endif // Put all the RC4 ciphers at the end: no DTLS for them. CIPHER(SSL_RSA_WITH_RC4_128_MD5, false, false) CIPHER(SSL_RSA_WITH_RC4_128_SHA, false, false) CIPHER(SSL_DH_anon_WITH_RC4_128_MD5, true, false) CIPHER(TLS_PSK_WITH_RC4_128_SHA, true, false) #if 0 // ECDH and ECDHE not supported on server yet CIPHER(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, false) CIPHER(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, false) CIPHER(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, false) CIPHER(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, false) CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, false) CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, false) CIPHER(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, true) CIPHER(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, true) CIPHER(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, false) CIPHER(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, false) CIPHER(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, false) CIPHER(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, false) #endif #if 0 // GCM not supported yet CIPHER(TLS_RSA_WITH_AES_256_GCM_SHA384, false) CIPHER(TLS_RSA_WITH_AES_128_GCM_SHA256, false) #endif { -1 } }; static int ciphers_len = array_size(ciphers); static int protos[]={kTLSProtocol1, kTLSProtocol11, kTLSProtocol12 }; static int nprotos = sizeof(protos)/sizeof(protos[0]); #if 0 // currently unused static SSLCipherSuite sslcipher_atoi(const char *name) { const CipherSuiteName *a = ciphers; while(a->name) { if (0 == strcmp(a->name, name)) break; a++; } return a->cipher; } static const char * sslcipher_itoa(SSLCipherSuite num) { const CipherSuiteName *a = ciphers; while(a->cipher >= 0) { if (num == a->cipher) break; a++; } return a->name; } #endif // currently unused static unsigned char dh_param_512_bytes[] = { 0x30, 0x46, 0x02, 0x41, 0x00, 0xdb, 0x3c, 0xfa, 0x13, 0xa6, 0xd2, 0x64, 0xdf, 0xcc, 0x40, 0xb1, 0x21, 0xd4, 0xf2, 0xad, 0x22, 0x7f, 0xce, 0xa0, 0xb9, 0x5b, 0x95, 0x1c, 0x2e, 0x99, 0xb0, 0x27, 0xd0, 0xed, 0xf4, 0xbd, 0xbb, 0x36, 0x93, 0xd0, 0x9d, 0x2b, 0x32, 0xa3, 0x56, 0x53, 0xe3, 0x7b, 0xed, 0xa1, 0x71, 0x82, 0x2e, 0x83, 0x14, 0xf9, 0xc0, 0x2f, 0x15, 0xcb, 0xcf, 0x97, 0xab, 0x88, 0x49, 0x20, 0x28, 0x2e, 0x63, 0x02, 0x01, 0x02 }; static unsigned char *dh_param_512_der = dh_param_512_bytes; static unsigned int dh_param_512_der_len = 72; typedef struct { uint32_t session_id; bool is_session_resume; SSLContextRef st; bool is_server; bool is_dtls; bool client_side_auth; bool dh_anonymous; int comm; CFArrayRef certs; SSLProtocol proto; } ssl_test_handle; #if 0 // currently unused static CFArrayRef SecIdentityCopySSLClientAuthenticationChain(SecIdentityRef identity) { CFMutableArrayRef chain = NULL; SecPolicyRef policy = NULL; SecTrustRef trust = NULL; SecTrustResultType trust_result; do { policy = SecPolicyCreateSSL(false, NULL); if (!policy) break; SecCertificateRef cert = NULL; if (SecIdentityCopyCertificate(identity, &cert)) break; CFArrayRef certs = CFArrayCreate(NULL, (const void **)&cert, 1, &kCFTypeArrayCallBacks); CFRelease(cert); if (!certs) break; if (SecTrustCreateWithCertificates(certs, policy, &trust)) break; CFRelease(certs); CFRelease(policy); if (SecTrustEvaluate(trust, &trust_result)) break; int i, count = SecTrustGetCertificateCount(trust); chain = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks); CFArrayAppendValue(chain, identity); for (i = 1; i < count; i++) { if ((i+1 == count) && (trust_result == kSecTrustResultUnspecified)) continue; /* skip anchor if chain is complete */ SecCertificateRef s = SecTrustGetCertificateAtIndex(trust, i); CFArrayAppendValue(chain, s); } } while (0); if (trust) CFRelease(trust); if (policy) CFRelease(policy); return chain; } #endif // currently unused // MARK: - // MARK: SecureTransport support #if 0 static void hexdump(const uint8_t *bytes, size_t len) { size_t ix; printf("socket write(%p, %lu)\n", bytes, len); for (ix = 0; ix < len; ++ix) { if (!(ix % 16)) printf("\n"); printf("%02X ", bytes[ix]); } printf("\n"); } #else #define hexdump(bytes, len) #endif static OSStatus SocketWrite(SSLConnectionRef conn, const void *data, size_t *length) { size_t len = *length; uint8_t *ptr = (uint8_t *)data; do { ssize_t ret; do { hexdump(ptr, len); ret = write((int)conn, ptr, len); } while ((ret < 0) && (errno == EAGAIN || errno == EINTR)); if (ret > 0) { len -= ret; ptr += ret; } else return -36; } while (len > 0); *length = *length - len; return errSecSuccess; } static OSStatus SocketRead(SSLConnectionRef conn, void *data, size_t *length) { size_t len = *length; uint8_t *ptr = (uint8_t *)data; do { ssize_t ret; do { ret = read((int)conn, ptr, len); } while ((ret < 0) && (errno == EINPROGRESS || errno == EAGAIN || errno == EINTR)); if (ret > 0) { len -= ret; ptr += ret; } else { printf("read error(%d): ret=%zd, errno=%d\n", (int)conn, ret, errno); return -errno; } } while (len > 0); *length = *length - len; return errSecSuccess; } static unsigned char dn[] = { 0x30, 0x5e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74 }; static unsigned int dn_len = 96; static SSLContextRef make_ssl_ref(bool server, bool client_side_auth, bool dh_anonymous, bool dtls, int sock, CFArrayRef certs, SSLProtocol proto) { SSLContextRef ctx = SSLCreateContext(kCFAllocatorDefault, server?kSSLServerSide:kSSLClientSide, dtls?kSSLDatagramType:kSSLStreamType); require(ctx, out); if(dtls) { size_t mtu; require_noerr(SSLSetMaxDatagramRecordSize(ctx, 400), out); require_noerr(SSLGetMaxDatagramRecordSize(ctx, &mtu), out); } else { require_noerr(SSLSetProtocolVersionMax(ctx, proto), out); } require_noerr(SSLSetIOFuncs(ctx, (SSLReadFunc)SocketRead, (SSLWriteFunc)SocketWrite), out); require_noerr(SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)sock), out); static const char *peer_domain_name = "localhost"; require_noerr(SSLSetPeerDomainName(ctx, peer_domain_name, strlen(peer_domain_name)), out); if (!dh_anonymous) { if (server) require_noerr(SSLSetCertificate(ctx, certs), out); if (client_side_auth && server) { SSLAuthenticate auth; require_noerr(SSLSetClientSideAuthenticate(ctx, kAlwaysAuthenticate), out); require_noerr(SSLGetClientSideAuthenticate(ctx, &auth), out); require(auth==kAlwaysAuthenticate, out); require_noerr(SSLAddDistinguishedName(ctx, dn, dn_len), out); } #if 0 /* Setting client certificate in advance */ if (client_side_auth && !server) require_noerr(SSLSetCertificate(ctx, certs), out); #endif if (client_side_auth && !server) /* enable break from SSLHandshake */ require_noerr(SSLSetSessionOption(ctx, kSSLSessionOptionBreakOnCertRequested, true), out); require_noerr(SSLSetSessionOption(ctx, kSSLSessionOptionBreakOnServerAuth, true), out); } /* Tell SecureTransport to not check certs itself: it will break out of the handshake to let us take care of it instead. */ require_noerr(SSLSetEnableCertVerify(ctx, false), out); if (server) { require_noerr(SSLSetDiffieHellmanParams(ctx, dh_param_512_der, dh_param_512_der_len), out); } else /* if client */ { } return ctx; out: if (ctx) CFRelease(ctx); return NULL; } static bool check_peer_cert(SSLContextRef ctx, const ssl_test_handle *ssl, SecTrustRef *trust) { /* verify peer cert chain */ require_noerr(SSLCopyPeerTrust(ctx, trust), out); SecTrustResultType trust_result = 0; /* this won't verify without setting up a trusted anchor */ require_noerr(SecTrustEvaluate(*trust, &trust_result), out); CFIndex n_certs = SecTrustGetCertificateCount(*trust); /* fprintf(stderr, "%ld certs; trust_eval: %d\n", n_certs, trust_result); */ CFMutableArrayRef peer_cert_array = CFArrayCreateMutable(NULL, n_certs, &kCFTypeArrayCallBacks); CFMutableArrayRef orig_peer_cert_array = CFArrayCreateMutableCopy(NULL, n_certs, ssl->certs); while (n_certs--) CFArrayInsertValueAtIndex(peer_cert_array, 0, SecTrustGetCertificateAtIndex(*trust, n_certs)); SecIdentityRef ident = (SecIdentityRef)CFArrayGetValueAtIndex(orig_peer_cert_array, 0); SecCertificateRef peer_cert = NULL; require_noerr(SecIdentityCopyCertificate(ident, &peer_cert), out); CFArraySetValueAtIndex(orig_peer_cert_array, 0, peer_cert); CFRelease(peer_cert); require(CFEqual(orig_peer_cert_array, peer_cert_array), out); CFRelease(orig_peer_cert_array); CFRelease(peer_cert_array); /* CFStringRef cert_name = SecCertificateCopySubjectSummary(cert); char cert_name_buffer[1024]; require(CFStringGetFileSystemRepresentation(cert_name, cert_name_buffer, sizeof(cert_name_buffer)), out); fprintf(stderr, "cert name: %s\n", cert_name_buffer); CFRelease(trust); */ return true; out: return false; } static void *securetransport_ssl_thread(void *arg) { OSStatus ortn; ssl_test_handle * ssl = (ssl_test_handle *)arg; SSLContextRef ctx = ssl->st; SecTrustRef trust = NULL; bool got_server_auth = false, got_client_cert_req = false; SSLSessionState ssl_state; pthread_setname_np(ssl->is_server?"server thread":"client thread"); require_noerr(ortn=SSLGetSessionState(ctx,&ssl_state), out); require_action(ssl_state==kSSLIdle, out, ortn = -1); //uint64_t start = mach_absolute_time(); do { ortn = SSLHandshake(ctx); require_noerr(SSLGetSessionState(ctx,&ssl_state), out); if (ortn == errSSLPeerAuthCompleted) { require_action(ssl_state==kSSLHandshake, out, ortn = -1); require_string(!got_server_auth, out, "second server auth"); require_string(!got_client_cert_req, out, "got client cert req before server auth"); got_server_auth = true; require_string(!trust, out, "Got errSSLServerAuthCompleted twice?"); require_string(check_peer_cert(ctx, ssl, &trust), out, "Certificate check failed"); } else if (ortn == errSSLClientCertRequested) { require_action(ssl_state==kSSLHandshake, out, ortn = -1); require_string(!got_client_cert_req, out, "second client cert req"); require_string(got_server_auth, out, "didn't get server auth first"); got_client_cert_req = true; /* set client cert */ require_string(!ssl->is_server, out, "errSSLClientCertRequested while running server"); require_string(!ssl->dh_anonymous, out, "errSSLClientCertRequested while running anon DH"); CFArrayRef DNs = NULL; require_noerr(SSLCopyDistinguishedNames (ctx, &DNs), out); require(DNs, out); CFRelease(DNs); require_string(ssl->client_side_auth, out, "errSSLClientCertRequested in run not testing that"); require_noerr(SSLSetCertificate(ctx, ssl->certs), out); } else if (ortn == errSSLWouldBlock) { require_action(ssl_state==kSSLHandshake, out, ortn = -1); } } while (ortn == errSSLWouldBlock || ortn == errSSLServerAuthCompleted || ortn == errSSLClientCertRequested); require_noerr_action_quiet(ortn, out, fprintf(stderr, "Fell out of SSLHandshake with error: %d\n", (int)ortn)); require_action(ssl_state==kSSLConnected, out, ortn = -1); if (!ssl->is_server && !ssl->dh_anonymous && !ssl->is_session_resume) { require_string(got_server_auth, out, "never got server auth"); if (ssl->client_side_auth) require_string(got_client_cert_req, out, "never got client cert req"); } if (!ssl->is_server && !ssl->dh_anonymous && ssl->is_session_resume) { require_string(!got_server_auth, out, "got server auth during resumption??"); require_string(check_peer_cert(ctx, ssl, &trust), out, "Certificate check failed (resumption case)"); } //uint64_t elapsed = mach_absolute_time() - start; //fprintf(stderr, "setr elapsed: %lld\n", elapsed); /* SSLProtocol proto = kSSLProtocolUnknown; require_noerr_quiet(SSLGetNegotiatedProtocolVersion(ctx, &proto), out); */ SSLCipherSuite cipherSuite; require_noerr_quiet(ortn = SSLGetNegotiatedCipher(ctx, &cipherSuite), out); //fprintf(stderr, "st negotiated %s\n", sslcipher_itoa(cipherSuite)); if(ssl->is_dtls) { size_t sz; SSLGetDatagramWriteSize(ctx, &sz); //fprintf(stderr, "Max Write Size = %ld\n", sz); } Boolean sessionWasResumed = false; uint8_t session_id_data[MAX_SESSION_ID_LENGTH]; size_t session_id_length = sizeof(session_id_data); require_noerr_quiet(ortn = SSLGetResumableSessionInfo(ctx, &sessionWasResumed, session_id_data, &session_id_length), out); require_action(ssl->dh_anonymous || (ssl->is_session_resume == sessionWasResumed), out, ortn = -1); // if (sessionWasResumed) fprintf(stderr, "st resumed session\n"); //hexdump(session_id_data, session_id_length); #define BUFSIZE (8*1024) unsigned char ibuf[BUFSIZE], obuf[BUFSIZE]; for(int i=0; i<10; i++) { size_t len; if (ssl->is_server) { memset(obuf, i, BUFSIZE); // SecRandomCopyBytes(kSecRandomDefault, sizeof(obuf), obuf); require_noerr(ortn = SSLWrite(ctx, obuf, BUFSIZE, &len), out); require_action(len == BUFSIZE, out, ortn = -1); require_noerr(ortn = SSLWrite(ctx, obuf, 0, &len), out); require_action(len == 0, out, ortn = -1); } len=0; while(lenis_server) { require_noerr(memcmp(ibuf, obuf, BUFSIZE), out); } else { require_noerr(ortn = SSLWrite(ctx, ibuf, BUFSIZE, &len), out); require_action(len == BUFSIZE, out, ortn = -1); } } out: SSLClose(ctx); CFRelease(ctx); if (trust) CFRelease(trust); close(ssl->comm); pthread_exit((void *)(intptr_t)ortn); return NULL; } static ssl_test_handle * ssl_test_handle_create(uint32_t session_id, bool resume, bool server, bool client_side_auth, bool dh_anonymous, bool dtls, int comm, CFArrayRef certs, SSLProtocol proto) { ssl_test_handle *handle = calloc(1, sizeof(ssl_test_handle)); if (handle) { handle->session_id = session_id; handle->is_session_resume = resume; handle->is_server = server; handle->is_dtls = dtls; handle->client_side_auth = client_side_auth; handle->dh_anonymous = dh_anonymous; handle->comm = comm; handle->certs = certs; handle->proto = proto; handle->st = make_ssl_ref(server, client_side_auth, dh_anonymous, dtls, comm, certs, proto); } return handle; } static void tests(void) { pthread_t client_thread, server_thread; CFArrayRef server_certs = server_chain(); ok(server_certs, "got server certs"); /* Enable this if you want to test a specific d/i/k/l combination */ #if 0 int d=0, i=0, l=0, k=0; { { #else int d,i,k,l,p; for (p=0; pst, &session_id, sizeof(session_id)), out); require_noerr(SSLSetPeerID(client->st, &session_id, sizeof(session_id)), out); /* set fixed cipher on client and server */ require_noerr(SSLSetEnabledCiphers(client->st, &ciphers[i].cipher, 1), out); require_noerr(SSLSetEnabledCiphers(server->st, &ciphers[i].cipher, 1), out); require_noerr(SSLSetPSKSharedSecret(client->st, "123456789", 9), out); require_noerr(SSLSetPSKSharedSecret(server->st, "123456789", 9), out); pthread_create(&client_thread, NULL, securetransport_ssl_thread, client); pthread_create(&server_thread, NULL, securetransport_ssl_thread, server); int server_err, client_err; pthread_join(client_thread, (void*)&client_err); pthread_join(server_thread, (void*)&server_err); ok(!server_err && !client_err, "%40s ADH:%d CSA:%d DTLS:%d RESUME:%d PROTO:%d", ciphers[i].name, server->dh_anonymous, server->client_side_auth, d, l, p); out: free(client); free(server); } } /* all ciphers */ } /* all configs */ CFRelease(server_certs); } int ssl_42_ciphers(int argc, char *const *argv) { plan_tests(2 * 2 * 2 * nprotos * (ciphers_len-1)/* client auth on/off * #configs * #ciphers */ + 1 /*cert*/); tests(); return 0; } /* TODO: count errSSLWouldBlock TODO: skip tests that don't matter: client_auth and anonymous dh TODO: we seem to only be negotiating tls - force a round of sslv3 TODO: allow secure transport to also defer client side auth to client TODO: make sure anonymous dh is never selected if not expicitly enabled TODO: make sure DHE is not available if not explicitly enabled and no parameters are set TODO: resumable sessions */