sslCert.c   [plain text]


/*
 * 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;
}