]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_ssl/lib/sslCipherSpecs.c
Security-55471.tar.gz
[apple/security.git] / libsecurity_ssl / lib / sslCipherSpecs.c
diff --git a/libsecurity_ssl/lib/sslCipherSpecs.c b/libsecurity_ssl/lib/sslCipherSpecs.c
new file mode 100644 (file)
index 0000000..ca52036
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 1999-2001,2005-2011 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * cipherSpecs.c - SSLCipherSpec declarations
+ */
+
+#include "sslBuildFlags.h"
+#include "CipherSuite.h"
+#include "sslContext.h"
+#include "sslCipherSpecs.h"
+#include "sslDebug.h"
+#include "sslMemory.h"
+#include "sslDebug.h"
+#include "sslUtils.h"
+#include "sslPriv.h"
+#include "sslCrypto.h"
+
+#include <string.h>
+#include <assert.h>
+#include <Security/SecBase.h>
+#include <utilities/array_size.h>
+
+#include <TargetConditionals.h>
+
+
+#define ENABLE_RSA_DES_SHA_NONEXPORT           ENABLE_DES
+#define ENABLE_RSA_DES_MD5_NONEXPORT           ENABLE_DES
+#define ENABLE_RSA_DES_SHA_EXPORT              ENABLE_DES
+#define ENABLE_RSA_RC4_MD5_EXPORT              ENABLE_RC4      /* the most common one */
+#define ENABLE_RSA_RC4_MD5_NONEXPORT           ENABLE_RC4
+#define ENABLE_RSA_RC4_SHA_NONEXPORT           ENABLE_RC4
+#define ENABLE_RSA_RC2_MD5_EXPORT              ENABLE_RC2
+#define ENABLE_RSA_RC2_MD5_NONEXPORT           ENABLE_RC2
+#define ENABLE_RSA_3DES_SHA                    ENABLE_3DES
+#define ENABLE_RSA_3DES_MD5                    ENABLE_3DES
+
+#define ENABLE_ECDH                    1
+#define ENABLE_AES_GCM                  0
+
+#define ENABLE_PSK                      1
+
+#if    APPLE_DH
+#define ENABLE_DH_ANON                 1
+#define ENABLE_DH_EPHEM_RSA            1
+#if USE_CDSA_CRYPTO
+#define ENABLE_DH_EPHEM_DSA            1
+#else
+#define ENABLE_DH_EPHEM_DSA            0
+#endif
+#else
+#define ENABLE_DH_ANON                 0
+#define ENABLE_DH_EPHEM_RSA            0
+#define ENABLE_DH_EPHEM_DSA            0
+#endif /* APPLE_DH */
+
+/*
+ * List of all CipherSpecs we implement. Depending on a context's
+ * exportable flag, not all of these might be available for use.
+ */
+/* Order by preference, domestic first */
+static const SSLCipherSuite KnownCipherSuites[] = {
+#if ENABLE_AES_GCM
+    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+#endif
+    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+    TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+#if ENABLE_AES_GCM
+    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+#endif
+    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+    TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+#if ENABLE_ECDH
+#if ENABLE_AES_GCM
+    TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+    TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+#endif
+    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
+    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
+#if ENABLE_AES_GCM
+    TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
+    TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
+#endif
+    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
+    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+    TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
+    TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
+    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
+    TLS_ECDH_RSA_WITH_RC4_128_SHA,
+    TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
+#endif
+#if ENABLE_AES_GCM
+    TLS_RSA_WITH_AES_256_GCM_SHA384,
+    TLS_RSA_WITH_AES_128_GCM_SHA256,
+#endif
+    TLS_RSA_WITH_AES_256_CBC_SHA256,
+    TLS_RSA_WITH_AES_128_CBC_SHA256,
+    TLS_RSA_WITH_AES_128_CBC_SHA,
+    SSL_RSA_WITH_RC4_128_SHA,
+    SSL_RSA_WITH_RC4_128_MD5,
+    TLS_RSA_WITH_AES_256_CBC_SHA,
+    SSL_RSA_WITH_3DES_EDE_CBC_SHA,
+#if ENABLE_SSLV2
+    SSL_RSA_WITH_3DES_EDE_CBC_MD5,
+#endif
+#if ENABLE_DES
+    SSL_RSA_WITH_DES_CBC_SHA,
+#endif
+#if ENABLE_SSLV2
+    SSL_RSA_WITH_DES_CBC_MD5,
+#endif
+#if ENABLE_RC2
+    SSL_RSA_WITH_RC2_CBC_MD5,
+#endif
+#if ENABLE_AES_GCM
+#  if ENABLE_DH_EPHEM_DSA
+    TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
+#  endif // ENABLE_DH_EPHEM_DSA
+    TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+#  if ENABLE_DH_EPHEM_DSA
+    TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
+#  endif // ENABLE_DH_EPHEM_DSA
+    TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+#endif // ENABLE_AES_GCM
+#if ENABLE_DH_EPHEM_DSA
+    TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+#endif
+    TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+#if ENABLE_DH_EPHEM_DSA
+    TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+#endif
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+#if ENABLE_DH_EPHEM_DSA
+    TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+#endif
+    TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+#if ENABLE_DH_EPHEM_DSA
+    TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+#endif
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+    SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+#if ENABLE_DES
+    SSL_DHE_RSA_WITH_DES_CBC_SHA,
+#endif
+#if ENABLE_DH_EPHEM_DSA
+    SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+#if ENABLE_DES
+    SSL_DHE_DSS_WITH_DES_CBC_SHA,
+#endif
+#endif
+#if ENABLE_AES_GCM
+    TLS_DH_anon_WITH_AES_256_GCM_SHA384,
+    TLS_DH_anon_WITH_AES_128_GCM_SHA256,
+#endif
+    TLS_DH_anon_WITH_AES_128_CBC_SHA256,
+    TLS_DH_anon_WITH_AES_256_CBC_SHA256,
+    TLS_DH_anon_WITH_AES_128_CBC_SHA,
+    TLS_DH_anon_WITH_AES_256_CBC_SHA,
+    SSL_DH_anon_WITH_RC4_128_MD5,
+    SSL_DH_anon_WITH_3DES_EDE_CBC_SHA,
+#if ENABLE_DES
+    SSL_DH_anon_WITH_DES_CBC_SHA,
+#endif
+    TLS_ECDHE_ECDSA_WITH_NULL_SHA,
+    TLS_ECDHE_RSA_WITH_NULL_SHA,
+#if ENABLE_ECDH
+    TLS_ECDH_ECDSA_WITH_NULL_SHA,
+    TLS_ECDH_RSA_WITH_NULL_SHA,
+#endif
+
+#if ENABLE_PSK
+    TLS_PSK_WITH_AES_256_CBC_SHA384,
+    TLS_PSK_WITH_AES_128_CBC_SHA256,
+    TLS_PSK_WITH_AES_256_CBC_SHA,
+    TLS_PSK_WITH_AES_128_CBC_SHA,
+    TLS_PSK_WITH_RC4_128_SHA,
+    TLS_PSK_WITH_3DES_EDE_CBC_SHA,
+    TLS_PSK_WITH_NULL_SHA384,
+    TLS_PSK_WITH_NULL_SHA256,
+    TLS_PSK_WITH_NULL_SHA,
+#endif
+
+    TLS_RSA_WITH_NULL_SHA256,
+    SSL_RSA_WITH_NULL_SHA,
+    SSL_RSA_WITH_NULL_MD5
+
+#if 0
+    /* We don't support these yet. */
+    TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+    TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+    TLS_RSA_WITH_RC4_128_SHA,
+    TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+    TLS_RSA_WITH_RC4_128_MD5,
+    TLS_DH_DSS_WITH_AES_256_GCM_SHA384,
+    TLS_DH_DSS_WITH_AES_128_GCM_SHA256,
+    TLS_DH_RSA_WITH_AES_256_GCM_SHA384,
+    TLS_DH_RSA_WITH_AES_128_GCM_SHA256,
+    TLS_DH_DSS_WITH_AES_256_CBC_SHA256,
+    TLS_DH_RSA_WITH_AES_256_CBC_SHA256,
+    TLS_DH_DSS_WITH_AES_128_CBC_SHA256,
+    TLS_DH_RSA_WITH_AES_128_CBC_SHA256,
+    TLS_DH_DSS_WITH_AES_256_CBC_SHA,
+    TLS_DH_RSA_WITH_AES_256_CBC_SHA,
+       TLS_DH_DSS_WITH_AES_128_CBC_SHA,
+    TLS_DH_RSA_WITH_AES_128_CBC_SHA,
+    TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
+    TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
+    TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
+       TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
+    TLS_ECDH_anon_WITH_RC4_128_SHA,
+    TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
+    TLS_ECDH_anon_WITH_NULL_SHA,
+#endif
+};
+
+static const unsigned CipherSuiteCount = array_size(KnownCipherSuites);
+
+
+/*
+ * Given a valid ctx->validCipherSpecs array, calculate how many of those
+ * cipherSpecs are *not* SSLv2 only, storing result in
+ * ctx->numValidNonSSLv2Specs. ClientHello routines need this to set
+ * up outgoing cipherSpecs arrays correctly.
+ *
+ * Also determines if any ECDSA/ECDH ciphers are enabled; we need to know
+ * that when creating a hello message.
+ */
+static void sslAnalyzeCipherSpecs(SSLContext *ctx)
+{
+       unsigned                dex;
+       const SSLCipherSuite *cipherSuite;
+
+#if ENABLE_SSLV2
+       ctx->numValidNonSSLv2Suites = 0;
+#endif
+       cipherSuite = &ctx->validCipherSuites[0];
+       ctx->ecdsaEnable = false;
+       for(dex=0; dex<ctx->numValidCipherSuites; dex++, cipherSuite++) {
+#if ENABLE_SSLV2
+               if(!CIPHER_SPEC_IS_SSLv2(*cipherSuite)) {
+                       ctx->numValidNonSSLv2Suites++;
+               }
+#endif
+               switch(sslCipherSuiteGetKeyExchangeMethod(*cipherSuite)) {
+                       case SSL_ECDH_ECDSA:
+                       case SSL_ECDHE_ECDSA:
+                       case SSL_ECDH_RSA:
+                       case SSL_ECDHE_RSA:
+                       case SSL_ECDH_anon:
+                               ctx->ecdsaEnable = true;
+                               break;
+                       default:
+                               break;
+               }
+       }
+}
+
+/*
+ * Build ctx->validCipherSpecs as a copy of KnownCipherSpecs, assuming that
+ * validCipherSpecs is currently not valid (i.e., SSLSetEnabledCiphers() has
+ * not been called).
+ */
+OSStatus sslBuildCipherSuiteArray(SSLContext *ctx)
+{
+       size_t          size;
+       unsigned        dex;
+
+       assert(ctx != NULL);
+       assert(ctx->validCipherSuites == NULL);
+
+       ctx->numValidCipherSuites = CipherSuiteCount;
+       size = CipherSuiteCount * sizeof(SSLCipherSuite);
+       ctx->validCipherSuites = (SSLCipherSuite *)sslMalloc(size);
+       if(ctx->validCipherSuites == NULL) {
+               ctx->numValidCipherSuites = 0;
+               return errSecAllocate;
+       }
+
+       /*
+        * Trim out inappropriate ciphers:
+        *  -- trim anonymous ciphers if !ctx->anonCipherEnable
+        *  -- trim ECDSA ciphers for server side if appropriate
+        *  -- trim ECDSA ciphers if TLSv1 disable or SSLv2 enabled (since
+        *     we MUST do the Client Hello extensions to make these ciphers
+        *     work reliably)
+         *  -- trim Stream ciphers if DTLSv1 enable
+        */
+       SSLCipherSuite *dst = ctx->validCipherSuites;
+       const SSLCipherSuite *src = KnownCipherSuites;
+
+       bool trimECDSA = false;
+       if((ctx->protocolSide == kSSLServerSide) && !SSL_ECDSA_SERVER) {
+               trimECDSA = true;
+       }
+       if(ctx->minProtocolVersion == SSL_Version_2_0
+       || ctx->maxProtocolVersion == SSL_Version_3_0) {
+        /* We trim ECDSA cipher suites if SSL2 is enabled or
+           The maximum allowed protocol is SSL3.  Note that this
+           won't trim ECDSA cipherspecs for DTLS which should be
+           the right thing to do here. */
+               trimECDSA = true;
+       }
+
+    /* trim Stream Ciphers for DTLS */
+    bool trimRC4 = ctx->isDTLS;
+
+    bool trimDHE = (ctx->protocolSide == kSSLServerSide) &&
+        !ctx->dhParamsEncoded.length;
+
+       for(dex=0; dex<CipherSuiteCount; dex++) {
+        KeyExchangeMethod kem = sslCipherSuiteGetKeyExchangeMethod(*src);
+        uint8_t keySize = sslCipherSuiteGetSymmetricCipherKeySize(*src);
+        HMAC_Algs mac = sslCipherSuiteGetMacAlgorithm(*src);
+        SSL_CipherAlgorithm cipher = sslCipherSuiteGetSymmetricCipherAlgorithm(*src);
+               /* First skip ECDSA ciphers as appropriate */
+               switch(kem) {
+                       case SSL_ECDH_ECDSA:
+                       case SSL_ECDHE_ECDSA:
+                       case SSL_ECDH_RSA:
+                       case SSL_ECDHE_RSA:
+                       case SSL_ECDH_anon:
+                               if(trimECDSA) {
+                                       /* Skip this one */
+                                       ctx->numValidCipherSuites--;
+                                       src++;
+                                       continue;
+                               }
+                               else {
+                                       break;
+                               }
+                       default:
+                               break;
+               }
+               if(!ctx->anonCipherEnable) {
+                       /* trim out the anonymous (and null-auth-cipher) ciphers */
+                       if(mac == HA_Null) {
+                /* skip this one */
+                               ctx->numValidCipherSuites--;
+                               src++;
+                               continue;
+                       }
+                       switch(kem) {
+                               case SSL_DH_anon:
+                               case SSL_DH_anon_EXPORT:
+                               case SSL_ECDH_anon:
+                                       /* skip this one */
+                                       ctx->numValidCipherSuites--;
+                                       src++;
+                                       continue;
+                               default:
+                                       break;
+                       }
+               }
+        if(ctx->falseStartEnabled) {
+            switch(kem){
+                case SSL_ECDHE_ECDSA:
+                case SSL_ECDHE_RSA:
+                case SSL_DHE_RSA:
+                case SSL_DHE_DSS:
+                    /* Ok for false start */
+                    break;
+                default:
+                                       /* Not ok, skip */
+                                       ctx->numValidCipherSuites--;
+                                       src++;
+                                       continue;
+            }
+            switch(cipher) {
+                case SSL_CipherAlgorithmAES_128_CBC:
+                case SSL_CipherAlgorithmAES_128_GCM:
+                case SSL_CipherAlgorithmAES_256_CBC:
+                case SSL_CipherAlgorithmAES_256_GCM:
+                case SSL_CipherAlgorithmRC4_128:
+                    /* Ok for false start */
+                    break;
+                default:
+                                       /* Not ok, skip*/
+                                       ctx->numValidCipherSuites--;
+                                       src++;
+                                       continue;
+            }
+        }
+
+        /* This will skip the simple DES cipher suites, but not the NULL cipher ones */
+        if (keySize == 8)
+        {
+            /* skip this one */
+            ctx->numValidCipherSuites--;
+            src++;
+            continue;
+        }
+
+        /* Trim PSK ciphersuites, they need to be enabled explicitely */
+        if (kem==TLS_PSK) {
+            ctx->numValidCipherSuites--;
+            src++;
+            continue;
+        }
+
+        if (trimDHE) {
+                       switch(kem) {
+                               case SSL_DHE_DSS:
+                               case SSL_DHE_DSS_EXPORT:
+                               case SSL_DHE_RSA:
+                               case SSL_DHE_RSA_EXPORT:
+                                       /* skip this one */
+                                       ctx->numValidCipherSuites--;
+                                       src++;
+                                       continue;
+                               default:
+                                       break;
+                       }
+               }
+
+        if (trimRC4 && (cipher==SSL_CipherAlgorithmRC4_128)) {
+            ctx->numValidCipherSuites--;
+            src++;
+            continue;
+        }
+
+        if(cipher==SSL_CipherAlgorithmNull) {
+            ctx->numValidCipherSuites--;
+            src++;
+            continue;
+        }
+
+        /* This one is good to go */
+        *dst++ = *src++;
+       }
+       sslAnalyzeCipherSpecs(ctx);
+       return errSecSuccess;
+}
+
+/*
+ * Convert an array of SSLCipherSuites (which is always KnownCipherSpecs)
+ * to an array of SSLCipherSuites.
+ */
+static OSStatus
+cipherSuitesToCipherSuites(
+                          size_t                               numCipherSuites,
+                          const SSLCipherSuite *cipherSuites,
+                          SSLCipherSuite               *ciphers,               /* RETURNED */
+                          size_t                               *numCiphers)    /* IN/OUT */
+{
+       if(*numCiphers < numCipherSuites) {
+               return errSSLBufferOverflow;
+       }
+    memcpy(ciphers, cipherSuites, numCipherSuites * sizeof(SSLCipherSuite));
+       *numCiphers = numCipherSuites;
+       return errSecSuccess;
+}
+
+/***
+ *** Publically exported functions declared in SecureTransport.h
+ ***/
+
+/*
+ * Determine number and values of all of the SSLCipherSuites we support.
+ * Caller allocates output buffer for SSLGetSupportedCiphers() and passes in
+ * its size in *numCiphers. If supplied buffer is too small, errSSLBufferOverflow
+ * will be returned.
+ */
+OSStatus
+SSLGetNumberSupportedCiphers (SSLContextRef    ctx,
+                                                         size_t                *numCiphers)
+{
+       if((ctx == NULL) || (numCiphers == NULL)) {
+               return errSecParam;
+       }
+       *numCiphers = CipherSuiteCount;
+       return errSecSuccess;
+}
+
+OSStatus
+SSLGetSupportedCiphers          (SSLContextRef         ctx,
+                                                         SSLCipherSuite        *ciphers,               /* RETURNED */
+                                                         size_t                        *numCiphers)    /* IN/OUT */
+{
+       if((ctx == NULL) || (ciphers == NULL) || (numCiphers == NULL)) {
+               return errSecParam;
+       }
+       return cipherSuitesToCipherSuites(CipherSuiteCount,
+               KnownCipherSuites,
+               ciphers,
+               numCiphers);
+}
+
+/*
+ * Specify a (typically) restricted set of SSLCipherSuites to be enabled by
+ * the current SSLContext. Can only be called when no session is active. Default
+ * set of enabled SSLCipherSuites is NOT the same as the complete set of supported
+ * SSLCipherSuites as obtained by SSLGetSupportedCiphers().
+ */
+OSStatus
+SSLSetEnabledCiphers           (SSLContextRef                  ctx,
+                                                        const SSLCipherSuite   *ciphers,
+                                                        size_t                                 numCiphers)
+{
+       size_t size;
+    size_t foundCiphers=0;
+       unsigned callerDex;
+       unsigned tableDex;
+
+       if((ctx == NULL) || (ciphers == NULL) || (numCiphers == 0)) {
+               return errSecParam;
+       }
+       if(sslIsSessionActive(ctx)) {
+               /* can't do this with an active session */
+               return errSecBadReq;
+       }
+       size = numCiphers * sizeof(SSLCipherSuite);
+       ctx->validCipherSuites = (SSLCipherSuite *)sslMalloc(size);
+       if(ctx->validCipherSuites == NULL) {
+               ctx->numValidCipherSuites = 0;
+               return errSecAllocate;
+       }
+
+       /*
+        * Run thru caller's specs, keep only the supported ones.
+        */
+    for(callerDex=0; callerDex<numCiphers; callerDex++) {
+        /* find matching CipherSpec in our known table */
+        for(tableDex=0; tableDex<CipherSuiteCount; tableDex++) {
+            if(ciphers[callerDex] == KnownCipherSuites[tableDex]) {
+                ctx->validCipherSuites[foundCiphers] = KnownCipherSuites[tableDex];
+                foundCiphers++;
+                break;
+            }
+        }
+       }
+
+    if(foundCiphers==0) {
+        /* caller specified only unsupported ciphersuites */
+        sslFree(ctx->validCipherSuites);
+        ctx->validCipherSuites = NULL;
+        return errSSLBadCipherSuite;
+    }
+    
+       /* success */
+       ctx->numValidCipherSuites = foundCiphers;
+       sslAnalyzeCipherSpecs(ctx);
+       return errSecSuccess;
+}
+
+/*
+ * Determine number and values of all of the SSLCipherSuites currently enabled.
+ * Caller allocates output buffer for SSLGetEnabledCiphers() and passes in
+ * its size in *numCiphers. If supplied buffer is too small, errSSLBufferOverflow
+ * will be returned.
+ */
+OSStatus
+SSLGetNumberEnabledCiphers     (SSLContextRef                  ctx,
+                                                        size_t                                 *numCiphers)
+{
+       if((ctx == NULL) || (numCiphers == NULL)) {
+               return errSecParam;
+       }
+       if(ctx->validCipherSuites == NULL) {
+               /* hasn't been set; use default */
+               *numCiphers = CipherSuiteCount;
+       }
+       else {
+               /* caller set via SSLSetEnabledCiphers */
+               *numCiphers = ctx->numValidCipherSuites;
+       }
+       return errSecSuccess;
+}
+
+OSStatus
+SSLGetEnabledCiphers           (SSLContextRef                  ctx,
+                                                        SSLCipherSuite                 *ciphers,               /* RETURNED */
+                                                        size_t                                 *numCiphers)    /* IN/OUT */
+{
+       if((ctx == NULL) || (ciphers == NULL) || (numCiphers == NULL)) {
+               return errSecParam;
+       }
+       if(ctx->validCipherSuites == NULL) {
+               /* hasn't been set; use default */
+               return cipherSuitesToCipherSuites(CipherSuiteCount,
+                       KnownCipherSuites,
+                       ciphers,
+                       numCiphers);
+       }
+       else {
+               /* use the ones specified in SSLSetEnabledCiphers() */
+               return cipherSuitesToCipherSuites(ctx->numValidCipherSuites,
+                       ctx->validCipherSuites,
+                       ciphers,
+                       numCiphers);
+       }
+}
+
+/***
+ *** End of publically exported functions declared in SecureTransport.h
+ ***/
+
+void InitCipherSpecParams(SSLContext *ctx)
+{
+    SSLCipherSpecParams *dst = &ctx->selectedCipherSpecParams;
+    dst->cipherSpec = ctx->selectedCipher;
+    dst->macSize = sslCipherSuiteGetMacSize(ctx->selectedCipher);
+    dst->macAlg = sslCipherSuiteGetMacAlgorithm(ctx->selectedCipher);
+    dst->keySize = sslCipherSuiteGetSymmetricCipherKeySize(ctx->selectedCipher);
+    dst->blockSize = sslCipherSuiteGetSymmetricCipherBlockIvSize(ctx->selectedCipher);
+    dst->ivSize = dst->blockSize;
+    dst->keyExchangeMethod = sslCipherSuiteGetKeyExchangeMethod(ctx->selectedCipher);
+};
+
+
+OSStatus
+FindCipherSpec(SSLContext *ctx)
+{
+       unsigned i;
+
+    assert(ctx != NULL);
+    assert(ctx->validCipherSuites != NULL);
+
+    for (i=0; i<ctx->numValidCipherSuites; i++)
+    {
+        if (ctx->validCipherSuites[i] == ctx->selectedCipher) {
+            InitCipherSpecParams(ctx);
+            /* Make sure we're configured to handle this cipherSuite. */
+            return sslVerifySelectedCipher(ctx);
+        }
+    }
+    /* Not found */
+    return errSSLNegotiation;
+}