X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/sslViewer/sslEcdsa.cpp?ds=sidebyside diff --git a/sslViewer/sslEcdsa.cpp b/sslViewer/sslEcdsa.cpp new file mode 100644 index 00000000..1ac49397 --- /dev/null +++ b/sslViewer/sslEcdsa.cpp @@ -0,0 +1,646 @@ +/* + * sslEcdsa.cpp - test SSL connections to a number of known servers. + * + * Note this uses the keychain ecdsa.keychain in cwd; it contains an + * SSL client auth identity. To avoid ACL hassles and to allow this + * program to run hands-off, the identity is imported into this keychain + * with no ACL on the private key. This is done with the kcImport tool + * like so: + * + * % kcImport ecc-secp256r1-client.pfx -k ___path_to_cwd___/ecdsa.keychain -f pkcs12 -z password -n + */ +#include +#include +#include +#include "sslAppUtils.h" +#include "ioSock.h" +//#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#if NO_SERVER +#include +#endif + + +static void usage(char **argv) +{ + printf("Usage: %s [options]\n", argv[0]); + printf("options:\n"); + printf(" -t testNum -- only do test testNum; default is all\n"); + printf(" -q -- quiet\n"); + printf(" -b -- non blocking I/O\n"); + printf(" -p -- pause for malloc debug\n"); + exit(1); +} + +#define IGNORE_SIGPIPE 1 +#if IGNORE_SIGPIPE +#include + +static void sigpipe(int sig) +{ +} +#endif /* IGNORE_SIGPIPE */ + +/* Test params */ +typedef struct { + const char *hostName; + int port; + + /* We enable exacly one CipherSuite and require that to work */ + SSLCipherSuite cipherSuite; + + /* Curve to specify; SSL_Curve_None means use default */ + SSL_ECDSA_NamedCurve specCurve; + + /* Curve to verify; SSL_Curve_None means don't check */ + SSL_ECDSA_NamedCurve expCurve; + + /* + * keychain containing client-side cert, located in LOCAL_BUILD_DIR. + * NULL means no keychain. + */ + const char *keychain; + + /* password for above keychain */ + const char *kcPassword; +} EcdsaTestParams; + +static const EcdsaTestParams ecdsaTestParams[] = +{ + /* client auth */ + { + "tls.secg.org", 8443, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_None, + "ecdsa.keychain", "password" + }, + /* tla.secg.org -- port 40023 - secp256r1 */ + { + "tls.secg.org", 40023, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "tls.secg.org", 40023, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + + /* tla.secg.org -- port 40024 - secp384r1 */ + /* This one doesn't let you specify a curve */ + { + "tls.secg.org", 40024, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + { + "tls.secg.org", 40024, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + { + "tls.secg.org", 40024, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + { + "tls.secg.org", 40024, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + { + "tls.secg.org", 40024, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + { + "tls.secg.org", 40024, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + { + "tls.secg.org", 40024, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + { + "tls.secg.org", 40024, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + + /* tla.secg.org -- port 40025 - secp521r1 */ + { + "tls.secg.org", 40025, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + { + "tls.secg.org", 40025, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + { + "tls.secg.org", 40025, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + { + "tls.secg.org", 40025, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + { + "tls.secg.org", 40025, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + { + "tls.secg.org", 40025, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + { + "tls.secg.org", 40025, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + { + "tls.secg.org", 40025, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + + + /* ecc.fedora.redhat.com - port 8443 - secp256r1 */ + { + "ecc.fedora.redhat.com", 8443, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 8443, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 8443, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 8443, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 8443, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 8443, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 8443, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 8443, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + + /* ecc.fedora.redhat.com - port 8444 - SSL_Curve_secp384r1 */ + /* This doesn't work, the server requires a redirect ... + { + "ecc.fedora.redhat.com", 8444, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + */ + { + "ecc.fedora.redhat.com", 8445, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + { + "ecc.fedora.redhat.com", 8444, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_secp384r1, SSL_Curve_secp384r1 + }, + { + "ecc.fedora.redhat.com", 8444, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_secp384r1, SSL_Curve_secp384r1 + }, + { + "ecc.fedora.redhat.com", 8444, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + { + "ecc.fedora.redhat.com", 8444, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp384r1 + }, + { + "ecc.fedora.redhat.com", 8444, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + SSL_Curve_secp384r1, SSL_Curve_secp384r1 + }, + { + "ecc.fedora.redhat.com", 8444, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_secp384r1, SSL_Curve_secp384r1 + }, + + /* ecc.fedora.redhat.com - port 8445 - SSL_Curve_secp521r1 */ + /* This one can't do RC4_128 without some HTTP redirection */ + { + "ecc.fedora.redhat.com", 8445, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp521r1 + }, + { + "ecc.fedora.redhat.com", 8445, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + SSL_Curve_secp521r1, SSL_Curve_secp521r1 + }, + { + "ecc.fedora.redhat.com", 8445, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SSL_Curve_secp521r1, SSL_Curve_secp521r1 + }, + + /* ecc.fedora.redhat.com - port 443 - secp256r1 with RSA authentication */ + { + "ecc.fedora.redhat.com", 443, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 443, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 443, TLS_ECDHE_RSA_WITH_RC4_128_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 443, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 443, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 443, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + SSL_Curve_secp256r1, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 443, TLS_ECDH_RSA_WITH_RC4_128_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + { + "ecc.fedora.redhat.com", 443, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + SSL_Curve_None, SSL_Curve_secp256r1 + }, + + /* etc. */ +}; +#define NUM_TEST_PARAMS (sizeof(ecdsaTestParams) / sizeof(ecdsaTestParams[0])) + +static void dumpParams( + const EcdsaTestParams *testParams) +{ + printf("%s:%d %-33s ", + testParams->hostName, testParams->port, + /* skip leading "TLS_" */ + sslGetCipherSuiteString(testParams->cipherSuite)+4); + if(testParams->expCurve != SSL_Curve_None) { + printf("expCurve = %s ", sslCurveString(testParams->expCurve)); + } + if(testParams->specCurve != SSL_Curve_None) { + printf("specCurve = %s ", sslCurveString(testParams->specCurve)); + } + if(testParams->keychain) { + printf("Client Auth Enabled"); + } + putchar('\n'); +} + +static void dumpErrInfo( + const char *op, + const EcdsaTestParams *testParams, + OSStatus ortn) +{ + printf("***%s failed for ", op); + dumpParams(testParams); + printf(" error: %s\n", sslGetSSLErrString(ortn)); +} + +/* + * Custom ping for this test. + */ +#define RCV_BUF_SIZE 256 + +static int doSslPing( + const EcdsaTestParams *testParams, + bool quiet, + int nonBlocking) +{ + PeerSpec peerId; + otSocket sock = 0; + OSStatus ortn; + SSLContextRef ctx = NULL; + SSLCipherSuite negCipher; + + /* first make sure requested server is there */ + ortn = MakeServerConnection(testParams->hostName, testParams->port, + nonBlocking, &sock, &peerId); + if(ortn) { + printf("MakeServerConnection(%s) returned %d\n", + testParams->hostName, (int)ortn); + return -1; + } + + /* + * Set up a SecureTransport session. + * First the standard calls. + */ + ortn = SSLNewContext(false, &ctx); + if(ortn) { + printSslErrStr("SSLNewContext", ortn); + goto cleanup; + } + ortn = SSLSetIOFuncs(ctx, SocketRead, SocketWrite); + if(ortn) { + printSslErrStr("SSLSetIOFuncs", ortn); + goto cleanup; + } + + /* Restrict to only TLSv1 - we have to do this because of Radar 6133465 */ + ortn = SSLSetProtocolVersionEnabled(ctx, kSSLProtocolAll, false); + if(ortn) { + printSslErrStr("SSLSetProtocolVersionEnabled", ortn); + goto cleanup; + } + ortn = SSLSetProtocolVersionEnabled(ctx, kTLSProtocol1, true); + if(ortn) { + printSslErrStr("SSLSetProtocolVersionEnabled", ortn); + goto cleanup; + } + + /* Restrict to only one CipherSuite */ + ortn = SSLSetEnabledCiphers(ctx, &testParams->cipherSuite, 1); + if(ortn) { + printSslErrStr("SSLSetEnabledCiphers", ortn); + goto cleanup; + } + + ortn = SSLSetConnection(ctx, (SSLConnectionRef)(intptr_t)sock); + if(ortn) { + printSslErrStr("SSLSetConnection", ortn); + goto cleanup; + } + + /* These test servers have custom roots, just allow any roots for this test */ + ortn = SSLSetAllowsExpiredCerts(ctx, true); + if(ortn) { + printSslErrStr("SSLSetAllowExpiredCerts", ortn); + goto cleanup; + } + ortn = SSLSetAllowsAnyRoot(ctx, true); + if(ortn) { + printSslErrStr("SSLSetAllowAnyRoot", ortn); + goto cleanup; + } + + if(testParams->specCurve != SSL_Curve_None) { + ortn = SSLSetECDSACurves(ctx, &testParams->specCurve, 1); + if(ortn) { + printSslErrStr("SSLSetAllowAnyRoot", ortn); + goto cleanup; + } + } + +#if 0 + if(testParams->keychain) { + char kcPath[2000]; + const char *lbd = getenv("LOCAL_BUILD_DIR"); + if(lbd == NULL) { + printf("WARNING: no LOCAL_BUILD_DIR env var faound\n"); + lbd = ""; + } + snprintf(kcPath, 2000, "%s/%s", lbd, testParams->keychain); + SecKeychainRef kcRef = NULL; + CFArrayRef certArray = getSslCerts(kcPath, + false, // encryptOnly + false, // completeCertChain + NULL, // anchorFile + &kcRef); + if(kcRef) { + /* Unlock it */ + ortn = SecKeychainUnlock(kcRef, + strlen(testParams->kcPassword), testParams->kcPassword, + true); + if(ortn) { + cssmPerror("SecKeychainUnlock", ortn); + /* oh well */ + } + CFRelease(kcRef); + } + if(certArray == NULL) { + printf("***WARNING no keychain found at %s\n", kcPath); + } + ortn = SSLSetCertificate(ctx, certArray); + if(ortn) { + printSslErrStr("SSLSetAllowAnyRoot", ortn); + goto cleanup; + } + CFRelease(certArray); + } +#endif + do { + ortn = SSLHandshake(ctx); + } while (ortn == errSSLWouldBlock); + + /* convert normal "shutdown" into zero err rtn */ + switch(ortn) { + case errSecSuccess: + break; + case errSSLClosedGraceful: + case errSSLClosedNoNotify: + ortn = errSecSuccess; + goto cleanup; + default: + dumpErrInfo("SSLHandshake", testParams, ortn); + goto cleanup; + } + + + /* + * Unlike other ping tests we don't bother with a GET - just validate + * the handshake + */ + ortn = SSLGetNegotiatedCipher(ctx, &negCipher); + if(ortn) { + dumpErrInfo("SSLHandshake", testParams, ortn); + goto cleanup; + } + + /* here is really what we're testing */ + if(negCipher != testParams->cipherSuite) { + printf("***Cipher mismatch for "); + dumpParams(testParams); + printf("Negotiated cipher: %s\n", sslGetCipherSuiteString(negCipher)); + ortn = errSecIO; + goto cleanup; + } + if(testParams->expCurve != SSL_Curve_None) { + SSL_ECDSA_NamedCurve actNegCurve; + ortn = SSLGetNegotiatedCurve(ctx, &actNegCurve); + if(ortn) { + printSslErrStr("SSLGetNegotiatedCurve", ortn); + goto cleanup; + } + if(actNegCurve != testParams->expCurve) { + printf("***Negotiated curve error\n"); + printf("Specified curve: %s\n", sslCurveString(testParams->specCurve)); + printf("Expected curve: %s\n", sslCurveString(testParams->expCurve)); + printf("Obtained curve: %s\n", sslCurveString(actNegCurve)); + ortn = errSecIO; + goto cleanup; + } + } + if(testParams->keychain) { + /* Verify client auth */ + SSLClientCertificateState authState; + ortn = SSLGetClientCertificateState(ctx, &authState); + if(ortn) { + printSslErrStr("SSLGetClientCertificateState", ortn); + goto cleanup; + } + if(authState != kSSLClientCertSent) { + printf("***Unexpected ClientCertificateState\n"); + printf(" Expected: ClientCertSent\n"); + printf(" Received: %s\n", sslGetClientCertStateString(authState)); + ortn = errSecIO; + goto cleanup; + } + } + + ortn = SSLClose(ctx); + +cleanup: + if(sock) { + endpointShutdown(sock); + } + if(ctx) { + SSLDisposeContext(ctx); + } + return (int)ortn; +} + + +int main(int argc, char **argv) +{ + int ourRtn = 0; + bool quiet = false; + int nonBlocking = false; + unsigned minDex = 0; + unsigned maxDex = NUM_TEST_PARAMS-1; + bool doPause = false; + + extern char *optarg; + int arg; + while ((arg = getopt(argc, argv, "t:bpqh")) != -1) { + switch (arg) { + case 't': + minDex = maxDex = atoi(optarg); + if(minDex > (NUM_TEST_PARAMS - 1)) { + printf("***max test number is %u.\n", (unsigned)NUM_TEST_PARAMS); + exit(1); + } + break; + case 'q': + quiet = true; + break; + case 'b': + nonBlocking = true; + break; + case 'p': + doPause = true; + break; + default: + usage(argv); + } + } + if(optind != argc) { + usage(argv); + } + +#if NO_SERVER +# if DEBUG + securityd_init(NULL); +# endif +#endif + + #if IGNORE_SIGPIPE + signal(SIGPIPE, sigpipe); + #endif + + //testStartBanner("sslEcdsa", argc, argv); + + if(doPause) { + fpurge(stdin); + printf("Pausing at top of loop; CR to continue: "); + fflush(stdout); + getchar(); + } + + for(unsigned dex=minDex; dex<=maxDex; dex++) { + const EcdsaTestParams *testParams = &ecdsaTestParams[dex]; + if(!quiet) { + printf("[%u]: ", dex); + dumpParams(testParams); + } + ourRtn = doSslPing(testParams, quiet, nonBlocking); + if(ourRtn) { + //printf("** Test %u failed **\n", dex); + //if(testError(quiet)) { + // break; + //} + } + } + + if(doPause) { + fpurge(stdin); + printf("Pausing at end of loop; CR to continue: "); + fflush(stdout); + getchar(); + } + + if(!quiet) { + if(ourRtn == 0) { + printf("===== sslEcdsa test PASSED =====\n"); + } + else { + printf("****sslEcdsa test FAILED\n"); + } + } + + return ourRtn; +}