sslCrypto.c   [plain text]


/*
 * Copyright (c) 2006-2013 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@
 */

/*
 * sslCrypto.c - internally implemented handshake layer crypto.
 */

#include <tls_handshake_priv.h>

#include "sslCrypto.h"
#include "CipherSuite.h"
#include "sslMemory.h"
#include "sslUtils.h"
#include "sslDebug.h"
#include <AssertMacros.h>

#include <string.h>
#include <assert.h>

#include <TargetConditionals.h>

#include <corecrypto/ccec.h>
#include <corecrypto/ccdh.h>
#include <corecrypto/ccrsa.h>

#include <stdlib.h>

/* extern struct ccrng_state *ccDRBGGetRngState(); */
#include <CommonCrypto/CommonRandomSPI.h>
#define CCRNGSTATE() ccDRBGGetRngState()
//#define CCRNGSTATE() ccDevRandomGetRngState()

#if APPLE_DH

int sslDecodeDhParams(
    ccdh_const_gp_t params,			/* Input - corecrypto object */
	tls_buffer		*prime,			/* Output - wire format */
	tls_buffer		*generator)     /* Output - wire format */
{
    int ortn = errSSLSuccess;
    cc_size n = ccdh_gp_n(params);

    require_noerr(ortn=SSLAllocBuffer(prime, ccn_write_uint_size(n, ccdh_gp_prime(params))), errOut);
    require_noerr(ortn=SSLAllocBuffer(generator, ccn_write_uint_size(n, ccdh_gp_g(params))), errOut);

    ccn_write_uint(n, ccdh_gp_prime(params), prime->length, prime->data);
    ccn_write_uint(n, ccdh_gp_g(params), generator->length, generator->data);

    // TODO: might leaks on allocation failure.
errOut:
    return ortn;
}

int sslEncodeDhParams(ccdh_gp_t         *params,		/* data mallocd and RETURNED - corecrypto object */
                      const tls_buffer	*prime,			/* Wire format */
                      const tls_buffer	*generator)     /* Wire format */
{
    int ortn = errSSLAllocate;

    cc_size n = ccn_nof_size(prime->length);
    ccdh_gp_t gp;

    gp.gp = sslMalloc(ccdh_gp_size(ccn_sizeof_n(n)));
    if(!gp.gp)
        return errSSLAllocate;
    bzero(gp.gp,ccdh_gp_size(ccn_sizeof_n(n)));

    CCDH_GP_N(gp) = n;
    require_noerr(ortn=ccn_read_uint(n, CCDH_GP_PRIME(gp), prime->length, prime->data), errOut);
    cczp_init(gp.zp);
    CCDH_GP_L(gp) = 0; 
    require_noerr(ortn=ccn_read_uint(n, CCDH_GP_G(gp),generator->length, generator->data), errOut);

    *params = gp;
    return errSSLSuccess;
    
errOut:
    sslFree(gp.gp);
    return ortn;

}

int sslDhCreateKey(ccdh_const_gp_t gp, ccdh_full_ctx_in_t *dhKey)
{
    ccdh_full_ctx *dhContext = NULL;
    int ortn;

    dhContext = sslMalloc(ccdh_full_ctx_size(ccdh_ccn_size(gp)));
    if(!dhContext)
        return errSSLAllocate;

    struct ccrng_state *rng = CCRNGSTATE();
    if (!rng) {
        abort();
    }

    require_noerr((ortn=ccdh_generate_key(gp, rng, dhContext)), errOut);

    dhKey->_full = dhContext;
    return errSSLSuccess;

errOut:
    sslFree(dhContext);
    return ortn;
}

int sslDhExportPub(ccdh_full_ctx_t dhKey, tls_buffer *pubKey)
{
    int ortn = errSSLSuccess;

    size_t pub_size = ccdh_export_pub_size(dhKey);

    require_noerr(ortn = SSLAllocBuffer(pubKey, pub_size), errOut);

    ccdh_export_pub(dhKey, pubKey->data);

errOut:
    return ortn;
}

int sslDhKeyExchange(ccdh_full_ctx_in_t dhKey, const tls_buffer *dhPeerPublic, tls_buffer *preMasterSecret)
{
    int result = errSSLProtocol, rtn;
    cc_unit *r = NULL;
    size_t len;
    cc_size n;

    ccdh_const_gp_t gp = ccdh_ctx_gp(dhKey._full);
    ccdh_pub_ctx_decl_gp(gp, pubKey);
    require_noerr(ccdh_import_pub(gp, dhPeerPublic->length, dhPeerPublic->data, pubKey), errOut);

    n = ccdh_gp_n(gp);
    len = ccn_sizeof_n(n);
    r = sslMalloc(len);

    require_noerr_action(rtn = SSLAllocBuffer(preMasterSecret, len), errOut, result = rtn);
    require_noerr(ccdh_compute_key(dhKey._full, pubKey, r), errOut);

    ccn_write_uint(n, r, &preMasterSecret->length, preMasterSecret->data);
    size_t out_size = ccn_write_uint_size(n, r);
    if(out_size < preMasterSecret->length)
        preMasterSecret->length=out_size;

	sslDebugLog("sslEcdhKeyExchange: exchanged key length=%ld, data=%p\n",
                preMasterSecret->length, preMasterSecret->data);

    result = errSSLSuccess;

errOut:
    if (r) {
        memset(r, 0, len);
        sslFree(r);
    }
    if (result)
        sslErrorLog("sslDhKeyExchange: failed to compute key (error %d)\n", (int)result);
	return result;
}

#endif /* APPLE_DH */


/*
 * Generate ECDH key pair with the given tls_named_curve.
 */
int sslEcdhCreateKey(ccec_const_cp_t cp, ccec_full_ctx_in_t *ecdhKey)
{
    ccec_full_ctx *ecdhContext = NULL;
    int ortn;

    ecdhContext = sslMalloc(ccec_full_ctx_size(ccec_ccn_size(cp)));
    if(!ecdhContext)
        return errSSLAllocate;

    struct ccrng_state *rng = CCRNGSTATE();
    if (!rng) {
        abort();
    }

    require_noerr((ortn=ccec_generate_key(cp, rng, ecdhContext)), errOut);

    ecdhKey->_full = ecdhContext;
    return errSSLSuccess;

errOut:
	return ortn;
}


int sslEcdhExportPub(ccec_full_ctx_t ecdhKey, tls_buffer *pubKey)
{
    int ortn = errSSLSuccess;

    size_t pub_size = ccec_export_pub_size(ecdhKey);

    require_noerr(ortn = SSLAllocBuffer(pubKey, pub_size), errOut);
    ccec_export_pub(ecdhKey, pubKey->data);

errOut:
    return ortn;
}

/*
 * Perform ECDH key exchange. Obtained key material is the same
 * size as our private key.
 *
 * On entry, ecdhPrivate is our private key. The peer's public key
 * is either ctx->ecdhPeerPublic for ECDHE exchange, or
 * ctx->peerPubKey for ECDH exchange.
 */

int sslEcdhKeyExchange(const ccec_full_ctx_in_t ecdhKey, const ccec_pub_ctx_in_t ecdhPeerPublic, tls_buffer *preMasterSecret)
{
    int ortn = errSSLSuccess;

    size_t len = 1 + 2 * ccec_ccn_size(ccec_ctx_cp(ecdhKey._full));
    require_noerr(ortn = SSLAllocBuffer(preMasterSecret, len), errOut);
    require_noerr(ccec_compute_key(ecdhKey._full, ecdhPeerPublic._pub,  &preMasterSecret->length, preMasterSecret->data), errOut);

	sslDebugLog("sslEcdhKeyExchange: exchanged key length=%ld, data=%p\n",
                preMasterSecret->length, preMasterSecret->data);

errOut:
	return ortn;
}


int sslRand(tls_buffer *buf)
{
	if(buf->length == 0) {
		sslErrorLog("sslRand: zero buf->length\n");
		return 0;
	}

    struct ccrng_state *rng = CCRNGSTATE();
    if (!rng) {
        abort();
    }

    ccrng_generate(rng, buf->length, buf->data);

    return 0;
}

/*
 * Free a pubKey object.
 */
int sslFreePubKey(SSLPubKey *pubKey)
{
	if (pubKey) {
        sslFree(pubKey->rsa.pub);
        pubKey->rsa.pub=NULL;
        sslFree(pubKey->ecc.pub);
        pubKey->ecc.pub = NULL;
	}
	return errSSLSuccess;
}


#if 0
/*
 * Get algorithm id for a SSLPubKey object.
 */
int sslPubKeyGetAlgorithmID(SSLPubKey *pubKey)
{
    pubKey->type;
}
#endif

/*
 * Raw RSA/DSA sign/verify.
 */
int sslRawSign(
	tls_private_key_t   privKey,
	const uint8_t       *plainText,
	size_t              plainTextLen,
	uint8_t				*sig,			// mallocd by caller; RETURNED
	size_t              sigLen,         // available
	size_t              *actualBytes)   // RETURNED
{
	size_t inOutSigLen = sigLen;

	assert(actualBytes != NULL);
    int status = errSSLParam;
    if (privKey->type == kSSLPrivKeyType_RSA) {
        status = privKey->rsa.sign(privKey->ctx, tls_hash_algorithm_None, plainText, plainTextLen, sig, &inOutSigLen);
    } else if (privKey->type == kSSLPrivKeyType_ECDSA) {
        status = privKey->ecdsa.sign(privKey->ctx, plainText, plainTextLen, sig, &inOutSigLen);
    } else
        status = errSSLParam;
	if (status) {
		sslErrorLog("privKey->rsa.sign: failed (error %d)\n", (int)status);
	}

    /* Since the KeyExchange already allocated modulus size bytes we'll
        use all of them.  SecureTransport has always sent that many bytes,
        so we're not going to deviate, to avoid interoperability issues. */
    if (!status && (privKey->type == kSSLPrivKeyType_RSA) && (inOutSigLen < sigLen)) {
        size_t offset = sigLen - inOutSigLen;
        memmove(sig + offset, sig, inOutSigLen);
        memset(sig, 0, offset);
        inOutSigLen = sigLen;
    }


	*actualBytes = inOutSigLen;
	return status;
}


/* TLS 1.2 ECDSA signature */
int sslEcdsaSign(
   tls_private_key_t   privKey,
   const uint8_t       *plainText,
   size_t              plainTextLen,
   uint8_t				*sig,			// mallocd by caller; RETURNED
   size_t              sigLen,         // available
   size_t              *actualBytes)   // RETURNED
{
    size_t inOutSigLen = sigLen;

    assert(actualBytes != NULL);
    assert(privKey->type == kSSLPrivKeyType_ECDSA);

    int status = privKey->ecdsa.sign(privKey->ctx, plainText, plainTextLen, sig,
                                     &inOutSigLen);

    if (status) {
        sslErrorLog("privKey->ecdsa.sign: failed (error %d)\n", (int)status);
    }

    *actualBytes = inOutSigLen;
    return status;
}


/* TLS 1.2 RSA signature */
int sslRsaSign(
    tls_private_key_t   privKey,
    tls_hash_algorithm   hash,
    const uint8_t       *plainText,
    size_t              plainTextLen,
    uint8_t				*sig,			// mallocd by caller; RETURNED
    size_t              sigLen,         // available
    size_t              *actualBytes)   // RETURNED
{
	size_t inOutSigLen = sigLen;

	assert(actualBytes != NULL);
    assert(privKey->type == kSSLPrivKeyType_RSA);

    int status = privKey->rsa.sign(privKey->ctx, hash, plainText, plainTextLen, sig, &inOutSigLen);

	if (status) {
		sslErrorLog("privKey->rsa.sign: failed (error %d)\n", (int)status);
	}

    /* Since the KeyExchange already allocated modulus size bytes we'll
     use all of them.  SecureTransport has always sent that many bytes,
     so we're not going to deviate, to avoid interoperability issues. */
    if (!status && (inOutSigLen < sigLen)) {
        size_t offset = sigLen - inOutSigLen;
        memmove(sig + offset, sig, inOutSigLen);
        memset(sig, 0, offset);
        inOutSigLen = sigLen;
    }

	*actualBytes = inOutSigLen;
	return status;
}

/* FIXME: This is a "raw" verify, without OID, this should really be implemented in corecrypto. */
#define RSA_PKCS1_PAD_SIGN		0x01

static
int sslRawRsaVerify(
	SSLPubKey           *pubKey,
	const uint8_t       *plainText,
	size_t              plainTextLen,
	const uint8_t       *sig,
	size_t              sigLen)         // available
{
    int status = errSSLCrypto;

    if(!pubKey->isRSA || pubKey->rsa.pub==NULL) {
        sslErrorLog("Internal Error: Invalid RSA public key\n");
        return errSSLInternal;
    }

    ccrsa_pub_ctx_t pubkey = pubKey->rsa;

    cc_unit s[ccrsa_ctx_n(pubkey)];

    require_noerr(ccn_read_uint(ccrsa_ctx_n(pubkey), s, sigLen, sig), errOut);
    require_noerr(ccrsa_pub_crypt(pubkey, s, s), errOut);
    ccn_swap(ccrsa_ctx_n(pubkey), s);

    const uint8_t* sBytes = (uint8_t*) s;
    const uint8_t* sEnd = (uint8_t*) (s + ccrsa_ctx_n(pubkey));

    // Verify and skip PKCS1 padding:
    //
    // 0x00, 0x01 (RSA_PKCS1_PAD_SIGN), 0xFF .. 0x00, signedData
    //
    size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey));
    size_t prefix_zeros = ccn_sizeof_n(ccrsa_ctx_n(pubkey)) - m_size;

    while (prefix_zeros--)
        require_quiet(*sBytes++ == 0x00, errOut);

    require_quiet(*sBytes++ == 0x00, errOut);
    require_quiet(*sBytes++ == RSA_PKCS1_PAD_SIGN, errOut);

    while (*sBytes == 0xFF) {
        require_quiet(++sBytes < sEnd, errOut);
    }
    // Required to have at least 8 0xFFs
    require_quiet((sBytes - (uint8_t*)s) - 2 >= 8, errOut);

    require_quiet(*sBytes == 0x00, errOut);
    require_quiet(++sBytes < sEnd, errOut);

    // Compare the rest.
    require_quiet((sEnd - sBytes) == (ptrdiff_t)plainTextLen, errOut);
    require_quiet(memcmp(sBytes, plainText, plainTextLen) == 0, errOut);

    status = errSSLSuccess;

errOut:
	if (status) {
		sslErrorLog("sslRawVerify: failed (error %d)\n", (int) status);
	}

	return status;
}

static
int sslRawEccVerify(
                    SSLPubKey           *pubKey,
                    const uint8_t       *plainText,
                    size_t              plainTextLen,
                    const uint8_t       *sig,
                    size_t              sigLen)         // available
{
    int status = errSSLCrypto;
    bool valid;

    if(pubKey->isRSA || pubKey->ecc.pub==NULL) {
        sslErrorLog("Internal Error: Invalid EC public key\n");
        return errSSLInternal;
    }

    status = ccec_verify(pubKey->ecc, plainTextLen, plainText, sigLen, sig, &valid);
	if (status) {
		sslErrorLog("sslRawEccVerify: ccec_verify failed (error %d)\n", (int) status);
	} else {
        if(!valid) {
            sslErrorLog("sslRawEccVerify: ccec_verify signature verify error\n", (int) status);
            status = errSSLCrypto;
        }
    }
	return status;
}

int sslRawVerify(
                 SSLPubKey           *pubKey,
                 const uint8_t       *plainText,
                 size_t              plainTextLen,
                 const uint8_t       *sig,
                 size_t              sigLen)         // available
{
    if(pubKey->isRSA) {
        return sslRawRsaVerify(pubKey, plainText, plainTextLen, sig, sigLen);
    } else {
        return sslRawEccVerify(pubKey, plainText, plainTextLen, sig, sigLen);
    }
}

#include <corecrypto/ccsha1.h>
#include <corecrypto/ccsha2.h>

static
const uint8_t *ccoidForSSLHash(tls_hash_algorithm hash)
{
    switch (hash) {
        case tls_hash_algorithm_SHA1:
            return ccoid_sha1;
        case tls_hash_algorithm_SHA256:
            return ccoid_sha256;
        case tls_hash_algorithm_SHA384:
            return ccoid_sha384;
        default:
            break;
    }
    // Internal error
    assert(0);
    // This guarantee failure down the line
    return NULL;
}


/* TLS 1.2 RSA verify */
int sslRsaVerify(
                      SSLPubKey           *pubKey,
                      tls_hash_algorithm   hash,
                      const uint8_t       *plainText,
                      size_t              plainTextLen,
                      const uint8_t       *sig,
                      size_t              sigLen)         // available
{
    const uint8_t *oid = ccoidForSSLHash(hash);
    bool valid;

    if(!pubKey->isRSA || pubKey->rsa.pub==NULL) {
        sslErrorLog("Internal Error: Invalid RSA public key\n");
        return errSSLInternal;
    }

    int status = ccrsa_verify_pkcs1v15(pubKey->rsa, oid, plainTextLen, plainText, sigLen, sig, &valid);

	if (status) {
		sslErrorLog("sslRsaVerify: ccrsa_verify_pkcs1v15 failed (error %d)\n", (int) status);
	} else {
        if(!valid) {
            sslErrorLog("sslRsaVerify: ccrsa_verify_pkcs1v15 signature verify error\n", (int) status);
            status = errSSLCrypto;
        }
    }
	return status;
}


/* TODO: Parts of this should really be in corecrypto */
#define RSA_PKCS1_PAD_ENCRYPT	0x02

/*
 * Encrypt/Decrypt
 */
int sslRsaEncrypt(
	SSLPubKey           *pubKey,
	const uint8_t       *plainText,
	size_t              plainTextLen,
	uint8_t				*cipherText,		// mallocd by caller; RETURNED
	size_t              cipherTextLen,      // available
	size_t              *actualBytes)       // RETURNED
{
    size_t ctlen = cipherTextLen;

	assert(actualBytes != NULL);

    if(!pubKey->isRSA || pubKey->rsa.pub==NULL) {
        sslErrorLog("Internal Error: Invalid RSA public key\n");
        return errSSLInternal;
    }

    ccrsa_pub_ctx_t pubkey = pubKey->rsa;

    cc_unit s[ccrsa_ctx_n(pubkey)];
    const size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey));

    uint8_t* sBytes = (uint8_t*) s;

    // Create PKCS1 padding:
    //
    // 0x00, 0x01 (RSA_PKCS1_PAD_ENCRYPT), 0xFF .. 0x00, signedData
    //
    const int kMinimumPadding = 1 + 1 + 8 + 1;

    require_quiet(plainTextLen <= m_size - kMinimumPadding, errOut);

    size_t prefix_zeros = ccn_sizeof_n(ccrsa_ctx_n(pubkey)) - m_size;

    while (prefix_zeros--)
        *sBytes++ = 0x00;

    size_t pad_size = m_size - plainTextLen;

    *sBytes++ = 0x00;
    *sBytes++ = RSA_PKCS1_PAD_ENCRYPT;

    struct ccrng_state *rng = CCRNGSTATE();
    if (!rng) {
        abort();
    }

    ccrng_generate(rng, pad_size - 3, sBytes);
    // Remove zeroes from the random pad

    const uint8_t* sEndOfPad = sBytes + (pad_size - 3);
    while (sBytes < sEndOfPad)
    {
        if (*sBytes == 0x00)
            *sBytes = 0xFF; // Michael said 0xFF was good enough.

        ++sBytes;
    }

    *sBytes++ = 0x00;

    memcpy(sBytes, plainText, plainTextLen);

    ccn_swap(ccrsa_ctx_n(pubkey), s);

    ccrsa_pub_crypt(pubkey, s, s);

    ccn_write_uint_padded(ccrsa_ctx_n(pubkey), s, m_size, cipherText);
    ctlen = m_size;



    /* Since the KeyExchange already allocated modulus size bytes we'll
        use all of them.  SecureTransport has always sent that many bytes,
        so we're not going to deviate, to avoid interoperability issues. */
    if (ctlen < cipherTextLen) {
        size_t offset = cipherTextLen - ctlen;
        memmove(cipherText + offset, cipherText, ctlen);
        memset(cipherText, 0, offset);
        ctlen = cipherTextLen;
    }

    if (actualBytes)
        *actualBytes = ctlen;

    return errSSLSuccess;

errOut:
    sslErrorLog("***sslRsaEncrypt error\n");
    return errSSLCrypto;
}


int sslRsaDecrypt(
	tls_private_key_t   privKey,
	const uint8_t       *cipherText,
	size_t              cipherTextLen,
	uint8_t				*plainText,			// mallocd by caller; RETURNED
	size_t              plainTextLen,		// available
	size_t              *actualBytes) 		// RETURNED
{
    assert(privKey->type == kSSLPrivKeyType_RSA);
	size_t ptlen = plainTextLen;

	assert(actualBytes != NULL);

    int status = privKey->rsa.decrypt(privKey->ctx, cipherText, cipherTextLen, plainText, &ptlen);
	*actualBytes = ptlen;

    if (status) {
        sslErrorLog("sslRsaDecrypt: privKey->rsa->decrypt failed (error %d)\n", (int)status);
	}

	return status;
}

/*
 * Obtain size of the modulus of privKey in bytes.
 */
size_t sslPrivKeyLengthInBytes(tls_private_key_t privKey)
{
    return privKey->rsa.size;
}

/*
 * Obtain size of the modulus of pubKey in bytes.
 */
size_t sslPubKeyLengthInBytes(SSLPubKey *pubKey)
{
    assert(pubKey->isRSA);
    return ccn_write_uint_size(ccrsa_ctx_n(pubKey->rsa), ccrsa_ctx_m(pubKey->rsa));
}


/*
 * Obtain maximum size of signature in bytes.
 */
int sslGetMaxSigSize(
	tls_private_key_t privKey,
	size_t            *maxSigSize)
{
	assert(maxSigSize != NULL);

    if (privKey->type == kSSLPrivKeyType_ECDSA) {
        *maxSigSize = privKey->ecdsa.size;
    } else if (privKey->type == kSSLPrivKeyType_RSA) {
        *maxSigSize = privKey->rsa.size;
    } else
        return errSSLParam;

	return errSSLSuccess;
}


/*
 * Given raw RSA key bits, cook up a SSLPubKey. Used in
 * Server-initiated key exchange.
 */
int sslGetPubKeyFromBits(
	const tls_buffer		*modulus,
	const tls_buffer		*exponent,
	SSLPubKey           *pubKey)
{
	if (!pubKey)
		return errSSLParam;

    cc_size n = ccn_nof_size(modulus->length);

    cc_unit mod[n];
    cc_unit exp[n];
    ccrsa_pub_ctx_t pub;

    require_noerr(ccn_read_uint(n, mod, modulus->length, modulus->data), errOut);
    require_noerr(ccn_read_uint(n, exp, exponent->length, exponent->data), errOut);
    require((ccn_bitlen(n, exp) > 1), errOut);

    require((pub.pub = sslMalloc(ccrsa_pub_ctx_size(ccn_sizeof_n(n)))), errOut);

    ccrsa_ctx_n(pub) = n;
    ccrsa_init_pub(pub, mod, exp);

    pubKey->isRSA = true;
    pubKey->rsa = pub;
    return errSSLSuccess;

errOut:
    sslErrorLog("sslGetPubKeyFromBits: SecKeyCreateRSAPublicKey failed\n");
    return errSSLCrypto;
}

/* Given a curve name and bits, create a public EC key */
int sslGetEcPubKeyFromBits(
                        tls_named_curve namedCurve,
                        const tls_buffer     *pubKeyBits,
                        SSLPubKey           *pubKey)
{
    ccec_pub_ctx_t ecpub = pubKey->ecc;

    ccec_const_cp_t cp;
	switch (namedCurve) {
        case tls_curve_secp256r1:
            cp = ccec_cp_256();
            break;
        case tls_curve_secp384r1:
            cp = ccec_cp_384();
            break;
        case tls_curve_secp521r1:
            cp = ccec_cp_521();
            break;
        default:
            /* should not have gotten this far */
            sslErrorLog("sslEcdhGenerateKeyPair: bad namedCurve (%u)\n",
                        (unsigned)namedCurve);
            return errSSLParam;
	}

    ecpub.pub = sslMalloc(ccec_pub_ctx_size(ccec_ccn_size(cp)));
    if(!ecpub.pub)
        return errSSLAllocate;

    require_noerr(ccec_import_pub(cp, pubKeyBits->length, pubKeyBits->data, ecpub), errOut);

    pubKey->isRSA = false;
    pubKey->ecc = ecpub;

    return errSSLSuccess;
errOut:
    sslFree(ecpub.pub);
    return errSSLCrypto;
}