/* Public key static functions. */
static void SecECPublicKeyDestroy(SecKeyRef key) {
    /* Zero out the public key */
    ccec_pub_ctx_t pubkey;
    pubkey.pub = key->key;
    if (ccec_ctx_cp(pubkey).zp)
        cc_zero(ccec_pub_ctx_size(ccn_sizeof_n(ccec_ctx_n(pubkey))), pubkey.pub);
}
/* Private key static functions. */
static void SecECPrivateKeyDestroy(SecKeyRef key) {
    /* Zero out the public key */
    ccec_full_ctx_t fullkey;
    fullkey.hdr = key->key;
    if (ccec_ctx_cp(fullkey).zp)
        cc_zero(ccec_full_ctx_size(ccn_sizeof_n(ccec_ctx_n(fullkey))), fullkey.hdr);
}
static OSStatus SecECPublicKeyInit(SecKeyRef key,
    const uint8_t *keyData, CFIndex keyDataLength, SecKeyEncoding encoding) {
    ccec_pub_ctx_t pubkey;
    pubkey.pub = key->key;
    OSStatus err = errSecParam;

    switch (encoding) {
    case kSecDERKeyEncoding:
    {
        const SecDERKey *derKey = (const SecDERKey *)keyData;
        if (keyDataLength != sizeof(SecDERKey)) {
            err = errSecDecode;
            break;
        }

        ccec_const_cp_t cp = getCPForPublicSize(derKey->keyLength);

        /* TODO: Parse and use real params from passed in derKey->algId.params */
        err = (ccec_import_pub(cp, derKey->keyLength, derKey->key, pubkey)
               ? errSecDecode : errSecSuccess);
        break;
    }
    case kSecKeyEncodingBytes:
    {
        ccec_const_cp_t cp = getCPForPublicSize(keyDataLength);
        err = (ccec_import_pub(cp, keyDataLength, keyData, pubkey)
               ? errSecDecode : errSecSuccess);
        break;
    }
    case kSecExtractPublicFromPrivate:
    {
        ccec_full_ctx_t fullKey;
        fullKey._full = (ccec_full_ctx *) keyData;

        cc_size fullKeyN = ccec_ctx_n(fullKey);
        require(fullKeyN <= ccn_nof(kMaximumECKeySize), errOut);
        memcpy(pubkey._pub, fullKey.pub, ccec_pub_ctx_size(ccn_sizeof_n(fullKeyN)));
        err = errSecSuccess;
        break;
    }
    case kSecKeyEncodingApplePkcs1:
    default:
        err = errSecParam;
        break;
    }

errOut:
    return err;
}
static CFDataRef SecECPPrivateKeyExport(CFAllocatorRef allocator,
                                        ccec_full_ctx_t fullkey) {
    size_t prime_size = ccec_cp_prime_size(ccec_ctx_cp(fullkey));
    size_t key_size = ccec_export_pub_size(fullkey) + prime_size;
	CFMutableDataRef blob = CFDataCreateMutable(allocator, key_size);
    if (blob) {
        CFDataSetLength(blob, key_size);
        ccec_export_pub(fullkey, CFDataGetMutableBytePtr(blob));
        UInt8 *dest = CFDataGetMutableBytePtr(blob) + ccec_export_pub_size(fullkey);
        const cc_unit *k = ccec_ctx_k(fullkey);
        ccn_write_uint_padded(ccec_ctx_n(fullkey), k, prime_size, dest);
    }

	return blob;
}
int
ccec_get_fullkey_components(ccec_full_ctx_t key, size_t *nbits,
                            uint8_t *x, size_t *xsize,
                            uint8_t *y, size_t *ysize,
                            uint8_t *d, size_t *dsize)
{
    cc_size n = ccec_ctx_n(key);
    size_t len;

    if(ccec_get_pubkey_components(key, nbits, x, xsize, y, ysize)) return -1;
    if((len = ccn_write_uint_size(n, ccec_ctx_k(key))) > *dsize) return -1;
    *dsize = len;
    ccn_write_uint(n, ccec_ctx_k(key), *dsize, d);
    return 0;
}
static OSStatus SecECPrivateKeyInit(SecKeyRef key,
    const uint8_t *keyData, CFIndex keyDataLength, SecKeyEncoding encoding) {
    ccec_full_ctx_t fullkey;
    fullkey.hdr = key->key;
    OSStatus err = errSecParam;

    switch (encoding) {
    case kSecKeyEncodingPkcs1:
    {
        /* TODO: DER import size (and thus cp), pub.x, pub.y and k. */
        //err = ecc_import(keyData, keyDataLength, fullkey);

        /* DER != PKCS#1, but we'll go along with it */
        ccoid_t oid;
        size_t n;
        ccec_const_cp_t cp;

        require_noerr(ccec_der_import_priv_keytype(keyDataLength, keyData, &oid, &n), abort);
        cp = ccec_cp_for_oid(oid);
        if (cp.zp == NULL) {
            cp = ccec_curve_for_length_lookup(n * 8 /* bytes -> bits */,
                ccec_cp_192(), ccec_cp_224(), ccec_cp_256(), ccec_cp_384(), ccec_cp_521(), NULL);
        }
        require_action(cp.zp != NULL, abort, err = errSecDecode);
        ccec_ctx_init(cp, fullkey);

        require_noerr(ccec_der_import_priv(cp, keyDataLength, keyData, fullkey), abort);
        err = errSecSuccess;
        break;
    }
    case kSecKeyEncodingBytes:
    {
        ccec_const_cp_t cp = getCPForPrivateSize(keyDataLength);
        require(cp.zp != NULL, abort);

        ccec_ctx_init(cp, fullkey);
        size_t pubSize = ccec_export_pub_size(fullkey);

        require(pubSize < (size_t) keyDataLength, abort);
        require_noerr_action(ccec_import_pub(cp, pubSize, keyData, fullkey),
                             abort,
                             err = errSecDecode);


        keyData += pubSize;
        keyDataLength -= pubSize;

        cc_unit *k = ccec_ctx_k(fullkey);
        require_noerr_action(ccn_read_uint(ccec_ctx_n(fullkey), k, keyDataLength, keyData),
                             abort,
                             err = errSecDecode);

        err = errSecSuccess;
        break;

    }
    case kSecGenerateKey:
    {
        CFDictionaryRef parameters = (CFDictionaryRef) keyData;

        CFTypeRef ksize = CFDictionaryGetValue(parameters, kSecAttrKeySizeInBits);
        CFIndex keyLengthInBits = getIntValue(ksize);

        ccec_const_cp_t cp = ccec_get_cp(keyLengthInBits);

        if (!cp.zp) {
            secwarning("Invalid or missing key size in: %@", parameters);
            return errSecKeySizeNotAllowed;
        }

        if (!ccec_generate_key(cp, ccrng_seckey, fullkey))
            err = errSecSuccess;
        break;
    }

    default:
        break;
    }
abort:
    return err;
}