]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_ssl/lib/sslCert.c
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_ssl / lib / sslCert.c
diff --git a/libsecurity_ssl/lib/sslCert.c b/libsecurity_ssl/lib/sslCert.c
new file mode 100644 (file)
index 0000000..85c58b6
--- /dev/null
@@ -0,0 +1,788 @@
+/*
+ * Copyright (c) 1999-2001,2005-2012 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@
+ */
+
+/*
+ * sslCert.c - certificate request/verify messages
+ */
+
+#include "ssl.h"
+#include "sslContext.h"
+#include "sslHandshake.h"
+#include "sslMemory.h"
+#include "sslAlertMessage.h"
+#include "sslDebug.h"
+#include "sslUtils.h"
+#include "sslDigests.h"
+#include "sslCrypto.h"
+
+#include <string.h>
+#include <assert.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecCertificatePriv.h>
+#include <Security/SecInternal.h>
+#include <Security/oidsalg.h>
+
+
+OSStatus
+SSLEncodeCertificate(SSLRecord *certificate, SSLContext *ctx)
+{   OSStatus            err;
+    size_t              totalLength;
+    UInt8               *charPtr;
+    CFIndex             i, certCount;
+#ifdef USE_SSLCERTIFICATE
+    int                 j;
+    SSLCertificate      *cert;
+#else
+    CFArrayRef                 certChain;
+#endif
+    int                 head;
+
+    /*
+        * TBD: for client side, match Match DER-encoded acceptable DN list
+        * (ctx->acceptableDNList) to one of our certs. For now we just send
+        * what we have since we don't support multiple certs.
+        *
+        * Note this can be called with localCert==0 for client side in TLS1+ and DTLS;
+        * in that case we send an empty cert msg.
+        */
+       assert(ctx->negProtocolVersion >= SSL_Version_3_0);
+       assert((ctx->localCert != NULL) || (ctx->negProtocolVersion >= TLS_Version_1_0));
+    totalLength = 0;
+
+#ifdef USE_SSLCERTIFICATE
+    certCount = 0;
+    cert = ctx->localCert;
+    while (cert)
+    {   totalLength += 3 + cert->derCert.length;    /* 3 for encoded length field */
+        ++certCount;
+        cert = cert->next;
+    }
+#else
+    certChain = ctx->localCert;
+       certCount = certChain ? CFArrayGetCount(certChain) : 0;
+       for (i = 0; i < certCount; ++i) {
+               SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, i);
+               totalLength += 3 + SecCertificateGetLength(cert);    /* 3 for encoded length field */
+       }
+#endif
+    certificate->contentType = SSL_RecordTypeHandshake;
+    certificate->protocolVersion = ctx->negProtocolVersion;
+    head = SSLHandshakeHeaderSize(certificate);
+    if ((err = SSLAllocBuffer(&certificate->contents, totalLength + head + 3, ctx)) != 0)
+        return err;
+
+    charPtr = SSLEncodeHandshakeHeader(ctx, certificate, SSL_HdskCert, totalLength+3);
+
+    charPtr = SSLEncodeSize(charPtr, totalLength, 3);      /* Vector length */
+
+#ifdef USE_SSLCERTIFICATE
+    /* Root cert is first in the linked list, but has to go last,
+        * so walk list backwards */
+    for (i = 0; i < certCount; ++i)
+    {   cert = ctx->localCert;
+        for (j = i+1; j < certCount; ++j)
+            cert = cert->next;
+        charPtr = SSLEncodeSize(charPtr, cert->derCert.length, 3);
+        memcpy(charPtr, cert->derCert.data, cert->derCert.length);
+        charPtr += cert->derCert.length;
+    }
+#else
+    /* Root cert is last in the array, and has to go last,
+        * so walk list forwards */
+       for (i = 0; i < certCount; ++i) {
+               SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, i);
+               CFIndex certLength = SecCertificateGetLength(cert);
+        charPtr = SSLEncodeSize(charPtr, certLength, 3);
+        memcpy(charPtr, SecCertificateGetBytePtr(cert), certLength);
+        charPtr += certLength;
+    }
+#endif
+
+    assert(charPtr == certificate->contents.data + certificate->contents.length);
+
+    if ((ctx->protocolSide == kSSLClientSide) && (ctx->localCert)) {
+               /* this tells us to send a CertificateVerify msg after the
+                * client key exchange. We skip the cert vfy if we just
+                * sent an empty cert msg (i.e., we were asked for a cert
+                * but we don't have one). */
+        ctx->certSent = 1;
+               assert(ctx->clientCertState == kSSLClientCertRequested);
+               assert(ctx->certRequested);
+               ctx->clientCertState = kSSLClientCertSent;
+       }
+       if(certCount == 0) {
+               sslCertDebug("...sending empty cert msg");
+       }
+    return noErr;
+}
+
+OSStatus
+SSLProcessCertificate(SSLBuffer message, SSLContext *ctx)
+{
+    size_t          listLen, certLen;
+    UInt8           *p;
+    OSStatus        err;
+#ifdef USE_SSLCERTIFICATE
+    SSLCertificate      *cert;
+#else
+    CFMutableArrayRef   certChain = NULL;
+    SecCertificateRef   cert;
+#endif
+
+    p = message.data;
+    listLen = SSLDecodeInt(p,3);
+    p += 3;
+    if (listLen + 3 != message.length) {
+       sslErrorLog("SSLProcessCertificate: length decode error 1\n");
+        return errSSLProtocol;
+    }
+
+    while (listLen > 0)
+    {   certLen = SSLDecodeInt(p,3);
+        p += 3;
+        if (listLen < certLen + 3) {
+               sslErrorLog("SSLProcessCertificate: length decode error 2\n");
+            return errSSLProtocol;
+        }
+#ifdef USE_SSLCERTIFICATE
+               cert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate));
+               if(cert == NULL) {
+                       return memFullErr;
+               }
+        if ((err = SSLAllocBuffer(&cert->derCert, certLen, ctx)) != 0)
+        {   sslFree(cert);
+            return err;
+        }
+        memcpy(cert->derCert.data, p, certLen);
+        p += certLen;
+        cert->next = ctx->peerCert;     /* Insert backwards; root cert
+                                                                                * will be first in linked list */
+        ctx->peerCert = cert;
+#else
+               if (!certChain) {
+                       certChain = CFArrayCreateMutable(kCFAllocatorDefault, 0,
+                               &kCFTypeArrayCallBacks);
+                       if (certChain == NULL) {
+                               return memFullErr;
+                       }
+                       if (ctx->peerCert) {
+                               sslDebugLog("SSLProcessCertificate: releasing existing cert chain\n");
+                               CFRelease(ctx->peerCert);
+                       }
+                       ctx->peerCert = certChain;
+               }
+               cert = SecCertificateCreateWithBytes(NULL, p, certLen);
+               #if SSL_DEBUG
+               {
+                       /* print cert name when debugging; leave disabled otherwise */
+                       CFStringRef certName = NULL;
+                       OSStatus status = SecCertificateInferLabel(cert, &certName);
+                       char buf[1024];
+                       if (!certName || !CFStringGetCString(certName, buf, 1024-1, kCFStringEncodingUTF8)) { buf[0]=0; }
+                       sslDebugLog("SSLProcessCertificate: received \"%s\" (%ld bytes)\n", buf, certLen);
+                       CFReleaseSafe(certName);
+               }
+               #endif
+               if (cert == NULL) {
+                       sslErrorLog("SSLProcessCertificate: unable to create cert ref from data\n");
+                       return memFullErr;
+               }
+        p += certLen;
+               /* Insert forwards; root cert will be last in linked list */
+               CFArrayAppendValue(certChain, cert);
+               CFRelease(cert);
+#endif
+        listLen -= 3+certLen;
+    }
+    assert(p == message.data + message.length && listLen == 0);
+
+    if (!ctx->peerCert) {
+               /* this *might* be OK... */
+               if((ctx->protocolSide == kSSLServerSide) &&
+                  (ctx->clientAuth != kAlwaysAuthenticate)) {
+                       /*
+                        * we tried to authenticate, client doesn't have a cert, and
+                        * app doesn't require it. OK.
+                        */
+                       return noErr;
+               }
+               else {
+                       AlertDescription desc;
+                       if(ctx->negProtocolVersion == SSL_Version_3_0) {
+                               /* this one's for SSL3 only */
+                               desc = SSL_AlertBadCert;
+                       }
+                       else {
+                               desc = SSL_AlertCertUnknown;
+                       }
+                       SSLFatalSessionAlert(desc, ctx);
+                       return errSSLXCertChainInvalid;
+               }
+    }
+
+    if((err = sslVerifyCertChain(ctx, ctx->peerCert, true)) != 0) {
+        AlertDescription desc;
+        switch(err) {
+        case errSSLUnknownRootCert:
+        case errSSLNoRootCert:
+            desc = SSL_AlertUnknownCA;
+            break;
+        case errSSLCertExpired:
+        case errSSLCertNotYetValid:
+            desc = SSL_AlertCertExpired;
+            break;
+        case errSSLXCertChainInvalid:
+        default:
+            desc = SSL_AlertCertUnknown;
+            break;
+        }
+        SSLFatalSessionAlert(desc, ctx);
+    }
+
+    if (err == noErr) {
+        if(ctx->peerPubKey != NULL) {
+            /* renegotiating - free old key first */
+            sslFreePubKey(&ctx->peerPubKey);
+        }
+        err = sslCopyPeerPubKey(ctx, &ctx->peerPubKey);
+    }
+
+    /* Now that cert verification is done, update context state */
+    /* (this code was formerly in SSLProcessHandshakeMessage, */
+    /* directly after the return from SSLProcessCertificate) */
+    if(ctx->protocolSide == kSSLServerSide) {
+        if(err) {
+            /*
+             * Error could be from no cert (when we require one)
+             * or invalid cert
+             */
+            if(ctx->peerCert != NULL) {
+                ctx->clientCertState = kSSLClientCertRejected;
+            }
+        } else if(ctx->peerCert != NULL) {
+            /*
+             * This still might change if cert verify msg
+             * fails. Note we avoid going to state
+             * if we get en empty cert message which is
+             * otherwise valid.
+             */
+            ctx->clientCertState = kSSLClientCertSent;
+        }
+
+        /*
+         * Schedule return to the caller to verify the client's identity.
+         * Note that an error during processing will cause early
+         * termination of the handshake.
+         */
+        if (ctx->breakOnClientAuth) {
+            ctx->signalClientAuth = true;
+        }
+    } else {
+        /*
+         * Schedule return to the caller to verify the server's identity.
+         * Note that an error during processing will cause early
+         * termination of the handshake.
+         */
+        if (ctx->breakOnServerAuth) {
+            ctx->signalServerAuth = true;
+        }
+    }
+
+    return err;
+}
+
+OSStatus
+SSLEncodeCertificateRequest(SSLRecord *request, SSLContext *ctx)
+{
+       OSStatus    err;
+    size_t      shListLen = 0, dnListLen, msgLen;
+    UInt8       *charPtr;
+    DNListElem  *dn;
+    int         head;
+
+       assert(ctx->protocolSide == kSSLServerSide);
+    if (sslVersionIsLikeTls12(ctx)) {
+        shListLen = 2 + 2 * (ctx->ecdsaEnable ? 5 : 3);  //FIXME: 5:3 should not be hardcoded here.
+    }
+
+       dnListLen = 0;
+    dn = ctx->acceptableDNList;
+    while (dn)
+    {   dnListLen += 2 + dn->derDN.length;
+        dn = dn->next;
+    }
+    msgLen = 1 +       // number of cert types
+                        2 +    // cert types
+             shListLen +  // SignatureAlgorithms
+                        2 +    // length of DN list
+                        dnListLen;
+
+    request->contentType = SSL_RecordTypeHandshake;
+    assert(ctx->negProtocolVersion >= SSL_Version_3_0);
+
+    request->protocolVersion = ctx->negProtocolVersion;
+    head = SSLHandshakeHeaderSize(request);
+    if ((err = SSLAllocBuffer(&request->contents, msgLen + head, ctx)) != 0)
+        return err;
+
+    charPtr = SSLEncodeHandshakeHeader(ctx, request, SSL_HdskCertRequest, msgLen);
+
+    *charPtr++ = 2;        /* two cert types */
+    *charPtr++ = SSLClientAuth_RSASign;
+    *charPtr++ = SSLClientAuth_ECDSASign;
+
+    if (shListLen) {
+        /* Encode the supported_signature_algorithms added in TLS1.2 */
+        /* We dont support SHA512 or SHA224 because we didnot implement the digest abstraction for those
+           and we dont keep a running hash for those.
+           We dont support SHA384/ECDSA because corecrypto ec does not support it with 256 bits curves */
+        charPtr = SSLEncodeSize(charPtr, shListLen - 2, 2);
+        // *charPtr++ = SSL_HashAlgorithmSHA512;
+        // *charPtr++ = SSL_SignatureAlgorithmRSA;
+        *charPtr++ = SSL_HashAlgorithmSHA384;
+        *charPtr++ = SSL_SignatureAlgorithmRSA;
+        *charPtr++ = SSL_HashAlgorithmSHA256;
+        *charPtr++ = SSL_SignatureAlgorithmRSA;
+        // *charPtr++ = SSL_HashAlgorithmSHA224;
+        // *charPtr++ = SSL_SignatureAlgorithmRSA;
+        *charPtr++ = SSL_HashAlgorithmSHA1;
+        *charPtr++ = SSL_SignatureAlgorithmRSA;
+        if (ctx->ecdsaEnable) {
+            // *charPtr++ = SSL_HashAlgorithmSHA512;
+            // *charPtr++ = SSL_SignatureAlgorithmECDSA;
+            // *charPtr++ = SSL_HashAlgorithmSHA384;
+            // *charPtr++ = SSL_SignatureAlgorithmECDSA;
+            *charPtr++ = SSL_HashAlgorithmSHA256;
+            *charPtr++ = SSL_SignatureAlgorithmECDSA;
+            // *charPtr++ = SSL_HashAlgorithmSHA224;
+            // *charPtr++ = SSL_SignatureAlgorithmECDSA;
+            *charPtr++ = SSL_HashAlgorithmSHA1;
+            *charPtr++ = SSL_SignatureAlgorithmECDSA;
+        }
+    }
+
+    charPtr = SSLEncodeSize(charPtr, dnListLen, 2);
+    dn = ctx->acceptableDNList;
+    while (dn)
+    {   charPtr = SSLEncodeSize(charPtr, dn->derDN.length, 2);
+        memcpy(charPtr, dn->derDN.data, dn->derDN.length);
+        charPtr += dn->derDN.length;
+        dn = dn->next;
+    }
+
+    assert(charPtr == request->contents.data + request->contents.length);
+    return noErr;
+}
+
+#define SSL_ENABLE_ECDSA_SIGN_AUTH                     0
+#define SSL_ENABLE_RSA_FIXED_ECDH_AUTH         0
+#define SSL_ENABLE_ECDSA_FIXED_ECDH_AUTH       0
+
+OSStatus
+SSLProcessCertificateRequest(SSLBuffer message, SSLContext *ctx)
+{
+    unsigned        i;
+    unsigned       typeCount;
+    unsigned           shListLen = 0;
+    UInt8           *charPtr;
+    unsigned           dnListLen;
+       unsigned                dnLen;
+    SSLBuffer          dnBuf;
+    DNListElem         *dn;
+       OSStatus                err;
+
+       /*
+        * Cert request only happens in during client authentication.
+        * We'll send a client cert if we have an appropriate one, but
+        * we don't do any DNList compare.
+        */
+    unsigned minLen = (sslVersionIsLikeTls12(ctx)) ? 5 : 3;
+    if (message.length < minLen) {
+       sslErrorLog("SSLProcessCertificateRequest: length decode error 1\n");
+        return errSSLProtocol;
+    }
+    charPtr = message.data;
+    typeCount = *charPtr++;
+    if (typeCount < 1 || message.length < minLen + typeCount) {
+       sslErrorLog("SSLProcessCertificateRequest: length decode error 2\n");
+        return errSSLProtocol;
+    }
+       if(typeCount != 0) {
+               /* Store server-specified auth types */
+               if(ctx->clientAuthTypes != NULL) {
+                       sslFree(ctx->clientAuthTypes);
+               }
+               ctx->clientAuthTypes = (SSLClientAuthenticationType *)
+                       sslMalloc(typeCount * sizeof(SSLClientAuthenticationType));
+               for(i=0; i<typeCount; i++) {
+                       sslLogNegotiateDebug("===Server specifies authType %d", (int)(*charPtr));
+                       ctx->clientAuthTypes[i] = (SSLClientAuthenticationType)(*charPtr++);
+               }
+               ctx->numAuthTypes = typeCount;
+    }
+
+    if (sslVersionIsLikeTls12(ctx)) {
+        /* Parse the supported_signature_algorithms field added in TLS1.2 */
+        shListLen = SSLDecodeInt(charPtr, 2);
+        charPtr += 2;
+        if (message.length < minLen + typeCount + shListLen) {
+            sslErrorLog("SSLProcessCertificateRequest: length decode error 3\n");
+            return errSSLProtocol;
+        }
+
+        if (shListLen & 1) {
+            sslErrorLog("SSLProcessCertificateRequest: signAlg len odd\n");
+            return errSSLProtocol;
+        }
+               ctx->numServerSigAlgs = shListLen / 2;
+               if(ctx->serverSigAlgs != NULL) {
+                       sslFree(ctx->serverSigAlgs);
+               }
+               ctx->serverSigAlgs = (SSLSignatureAndHashAlgorithm *)
+        sslMalloc((ctx->numServerSigAlgs) * sizeof(SSLSignatureAndHashAlgorithm));
+               for(i=0; i<ctx->numServerSigAlgs; i++) {
+            /* TODO: Validate hash and signature fields. */
+                       ctx->serverSigAlgs[i].hash = *charPtr++;
+                       ctx->serverSigAlgs[i].signature = *charPtr++;
+                       sslLogNegotiateDebug("===Server specifies sigAlg %d %d",
+                                 ctx->serverSigAlgs[i].hash,
+                                 ctx->serverSigAlgs[i].signature);
+               }
+    }
+
+       /* if a client cert is set, it must match a server-specified auth type */
+       err = SSLUpdateNegotiatedClientAuthType(ctx);
+
+       /* obtain server's DNList */
+    dnListLen = SSLDecodeInt(charPtr, 2);
+    charPtr += 2;
+    if (message.length != minLen + typeCount + shListLen + dnListLen) {
+       sslErrorLog("SSLProcessCertificateRequest: length decode error 3\n");
+        return errSSLProtocol;
+       }
+    while (dnListLen > 0)
+    {   if (dnListLen < 2) {
+               sslErrorLog("SSLProcessCertificateRequest: dnListLen error 1\n");
+            return errSSLProtocol;
+        }
+        dnLen = SSLDecodeInt(charPtr, 2);
+        charPtr += 2;
+        if (dnListLen < 2 + dnLen) {
+               sslErrorLog("SSLProcessCertificateRequest: dnListLen error 2\n");
+               return errSSLProtocol;
+       }
+        if ((err = SSLAllocBuffer(&dnBuf, sizeof(DNListElem), ctx)) != 0)
+            return err;
+        dn = (DNListElem*)dnBuf.data;
+        if ((err = SSLAllocBuffer(&dn->derDN, dnLen, ctx)) != 0)
+        {   SSLFreeBuffer(&dnBuf, ctx);
+            return err;
+        }
+        memcpy(dn->derDN.data, charPtr, dnLen);
+        charPtr += dnLen;
+        dn->next = ctx->acceptableDNList;
+        ctx->acceptableDNList = dn;
+        dnListLen -= 2 + dnLen;
+    }
+
+    assert(charPtr == message.data + message.length);
+
+    return noErr;
+}
+
+
+/* TODO: this should be refactored with FindSigAlg in sslKeyExchange.c */
+static
+OSStatus FindCertSigAlg(SSLContext *ctx,
+                        SSLSignatureAndHashAlgorithm *alg)
+{
+    unsigned i;
+
+    assert(ctx->protocolSide == kSSLClientSide);
+    assert(ctx->negProtocolVersion >= TLS_Version_1_2);
+    assert(!ctx->isDTLS);
+
+    if((ctx->numServerSigAlgs==0) ||(ctx->serverSigAlgs==NULL))
+        return errSSLInternal;
+
+    for(i=0; i<ctx->numServerSigAlgs; i++) {
+        alg->hash = ctx->serverSigAlgs[i].hash;
+        alg->signature = ctx->serverSigAlgs[i].signature;
+        // We only support RSA cert for our own certs.
+        if(ctx->serverSigAlgs[i].signature != SSL_SignatureAlgorithmRSA)
+            continue;
+
+        //Let's only support SHA1 and SHA256. SHA384 does not work with 512 bits RSA keys
+        // We should actually test against what the client cert can do.
+        if((alg->hash==SSL_HashAlgorithmSHA1) || (alg->hash==SSL_HashAlgorithmSHA256)) {
+            return noErr;
+        }
+    }
+    // We could not find a supported signature and hash algorithm
+    return errSSLProtocol;
+}
+
+OSStatus
+SSLEncodeCertificateVerify(SSLRecord *certVerify, SSLContext *ctx)
+{   OSStatus        err;
+    UInt8           hashData[SSL_MAX_DIGEST_LEN];
+    SSLBuffer       hashDataBuf;
+    size_t          len;
+    size_t                 outputLen;
+    UInt8           *charPtr;
+    int             head;
+    size_t          maxSigLen;
+    bool            isRSA = false;
+
+    certVerify->contents.data = 0;
+    hashDataBuf.data = hashData;
+    hashDataBuf.length = SSL_MAX_DIGEST_LEN;
+
+
+       assert(ctx->signingPrivKeyRef != NULL);
+    err = sslGetMaxSigSize(ctx->signingPrivKeyRef, &maxSigLen);
+    if(err) {
+        goto fail;
+    }
+
+       switch(ctx->negAuthType) {
+               case SSLClientAuth_RSASign:
+            isRSA = true;
+                       break;
+#if SSL_ENABLE_ECDSA_SIGN_AUTH
+               case SSLClientAuth_ECDSASign:
+                       break;
+#endif
+               default:
+                       /* shouldn't be here */
+                       assert(0);
+                       return errSSLInternal;
+       }
+
+    certVerify->contentType = SSL_RecordTypeHandshake;
+       assert(ctx->negProtocolVersion >= SSL_Version_3_0);
+    certVerify->protocolVersion = ctx->negProtocolVersion;
+    head = SSLHandshakeHeaderSize(certVerify);
+
+    outputLen = maxSigLen + head + 2;
+
+    SSLSignatureAndHashAlgorithm sigAlg;
+
+    if (sslVersionIsLikeTls12(ctx)) {
+        err=FindCertSigAlg(ctx, &sigAlg);
+        if(err)
+            goto fail;
+        outputLen += 2;
+    }
+
+    assert(ctx->sslTslCalls != NULL);
+    if ((err = ctx->sslTslCalls->computeCertVfyMac(ctx, &hashDataBuf, sigAlg.hash)) != 0)
+        goto fail;
+
+    if ((err = SSLAllocBuffer(&certVerify->contents, outputLen, ctx)) != 0)
+        goto fail;
+
+    /* Sign now to get the actual length */
+    charPtr = certVerify->contents.data+head;
+
+    if (sslVersionIsLikeTls12(ctx))
+    {
+        *charPtr++ = sigAlg.hash;
+        *charPtr++ = sigAlg.signature;
+
+        /* We don't support anything but RSA for client side auth yet */
+        assert(isRSA);
+        SecAsn1AlgId algId;
+        switch (sigAlg.hash) {
+            case SSL_HashAlgorithmSHA384:
+                algId.algorithm = CSSMOID_SHA384WithRSA;
+                break;
+            case SSL_HashAlgorithmSHA256:
+                algId.algorithm = CSSMOID_SHA256WithRSA;
+                break;
+            case SSL_HashAlgorithmSHA1:
+                algId.algorithm = CSSMOID_SHA1WithRSA;
+                break;
+            default:
+                               sslErrorLog("SSLEncodeCertificateVerify: unsupported signature hash algorithm (%d)\n",
+                                       sigAlg.hash);
+                assert(0);          // if you get here, something is wrong in FindCertSigAlg
+                err=errSSLInternal;
+                goto fail;
+        }
+
+        err = sslRsaSign(ctx,
+                         ctx->signingPrivKeyRef,
+                         &algId,
+                         hashData,
+                         hashDataBuf.length,
+                         charPtr+2,
+                         maxSigLen,
+                         &outputLen);
+        len=outputLen+2+2;
+    } else {
+        err = sslRawSign(ctx,
+            ctx->signingPrivKeyRef,
+            hashData,                                          // data to sign
+            hashDataBuf.length,                                // Data to sign size
+            charPtr+2, // signature destination
+            maxSigLen,                                                 // we mallocd len+head+2
+            &outputLen);
+        len = outputLen+2;
+    }
+       if(err) {
+               sslErrorLog("SSLEncodeCertificateVerify: unable to sign data (error %d)\n", err);
+               goto fail;
+       }
+    // At this point:
+    //  len = message length
+    //  outputlen = sig length
+       certVerify->contents.length = len + head;
+
+    /* charPtr point at the len field here */
+    charPtr = SSLEncodeSize(charPtr, outputLen, 2);
+    charPtr = SSLEncodeHandshakeHeader(ctx, certVerify, SSL_HdskCertVerify, len);
+
+    assert(charPtr==(certVerify->contents.data+head));
+
+    err = noErr;
+
+fail:
+
+    return err;
+}
+
+OSStatus
+SSLProcessCertificateVerify(SSLBuffer message, SSLContext *ctx)
+{   OSStatus        err;
+    UInt8           hashData[SSL_MAX_DIGEST_LEN];
+    size_t          signatureLen;
+    SSLBuffer       hashDataBuf;
+    size_t          publicModulusLen;
+    uint8_t         *charPtr = message.data;
+       uint8_t         *endCp = charPtr + message.length;
+
+    SSLSignatureAndHashAlgorithm    sigAlg;
+    SecAsn1AlgId                    algId;
+
+    if (ctx->isDTLS
+        ? ctx->negProtocolVersion < DTLS_Version_1_0
+        : ctx->negProtocolVersion >= TLS_Version_1_2) {
+        /* Parse the algorithm field added in TLS1.2 */
+        if((charPtr+2) > endCp) {
+            sslErrorLog("SSLProcessCertificateVerify: msg len error 1\n");
+            return errSSLProtocol;
+        }
+        sigAlg.hash = *charPtr++;
+        sigAlg.signature = *charPtr++;
+
+        switch (sigAlg.hash) {
+            case SSL_HashAlgorithmSHA256:
+                algId.algorithm = CSSMOID_SHA256WithRSA;
+                if(ctx->selectedCipherSpec.macAlgorithm->hmac->alg == HA_SHA384) {
+                    sslErrorLog("SSLProcessCertificateVerify: inconsistent hash, HA_SHA384\n");
+                    return errSSLInternal;
+                }
+                break;
+            case SSL_HashAlgorithmSHA384:
+                algId.algorithm = CSSMOID_SHA384WithRSA;
+                if(ctx->selectedCipherSpec.macAlgorithm->hmac->alg != HA_SHA384) {
+                    sslErrorLog("SSLProcessCertificateVerify: inconsistent hash, %d not HA_SHA384\n", ctx->selectedCipherSpec.macAlgorithm->hmac->alg);
+                    return errSSLInternal;
+                }
+                break;
+            default:
+                sslErrorLog("SSLProcessCertificateVerify: unsupported hash %d\n", sigAlg.hash);
+                return errSSLProtocol;
+        }
+    }
+
+    if ((charPtr + 2) > endCp) {
+       sslErrorLog("SSLProcessCertificateVerify: msg len error\n");
+        return errSSLProtocol;
+    }
+
+    signatureLen = SSLDecodeSize(charPtr, 2);
+    charPtr += 2;
+    if ((charPtr + signatureLen) > endCp) {
+       sslErrorLog("SSLProcessCertificateVerify: sig len error 1\n");
+        return errSSLProtocol;
+    }
+
+       publicModulusLen = sslPubKeyLengthInBytes(ctx->peerPubKey);
+
+       #if 0
+    if (signatureLen != publicModulusLen) {
+       sslErrorLog("SSLProcessCertificateVerify: sig len error 2\n");
+        return errSSLProtocol;
+    }
+       #endif
+       if (publicModulusLen == 0) {
+               sslErrorLog("SSLProcessCertificateVerify: pub key modulus is 0\n");
+       }
+
+    hashDataBuf.data = hashData;
+    hashDataBuf.length = SSL_MAX_DIGEST_LEN;
+
+       assert(ctx->sslTslCalls != NULL);
+    if ((err = ctx->sslTslCalls->computeCertVfyMac(ctx, &hashDataBuf, sigAlg.hash)) != 0)
+        goto fail;
+
+    if (sslVersionIsLikeTls12(ctx))
+    {
+        if(sigAlg.signature==SSL_SignatureAlgorithmRSA) {
+            err = sslRsaVerify(ctx,
+                               ctx->peerPubKey,
+                               &algId,
+                               hashData,
+                               hashDataBuf.length,
+                               charPtr,
+                               signatureLen);
+        } else {
+            err = sslRawVerify(ctx,
+                               ctx->peerPubKey,
+                               hashData,
+                               hashDataBuf.length,
+                               charPtr,
+                               signatureLen);
+        }
+    } else {
+        /* sslRawVerify does the decrypt & compare for us in one shot. */
+        err = sslRawVerify(ctx,
+            ctx->peerPubKey,
+            hashData,                          // data to verify
+            hashDataBuf.length,
+            charPtr,           // signature
+            signatureLen);
+    }
+
+    if(err) {
+               SSLFatalSessionAlert(SSL_AlertDecryptError, ctx);
+               goto fail;
+       }
+    err = noErr;
+
+fail:
+    return err;
+}