/*
 * Obtain size of the modulus of pubKey in bytes.
 */
size_t sslPubKeyLengthInBytes(SSLPubKey *pubKey)
{
#if 0
	/* Get the length of the modulus in bytes. */
	return giantNumBytes(&pubKey->rsaKey.n.g);
#else
    return SecKeyGetBlockSize(SECKEYREF(pubKey));
#endif
}
/*
 * Obtain size of the modulus of privKey in bytes.
 */
size_t sslPrivKeyLengthInBytes(SSLPrivKey *privKey)
{
#if 0
	/* Get the length of p + q (which is the size of the modulus) in bits. */
	gi_uint16 bitLen = bitlen(&privKey->rsaKey.p.g) +
		bitlen(&privKey->rsaKey.q.g);
	/* Convert it to bytes. */
	return (bitLen + 7) / 8;
#else
    return SecKeyGetBlockSize(SECKEYREF(privKey));
#endif
}
/*
 * Given raw RSA key bits, cook up a SSLPubKey. Used in
 * Server-initiated key exchange.
 */
OSStatus sslGetPubKeyFromBits(
	SSLContext			*ctx,
	const SSLBuffer		*modulus,
	const SSLBuffer		*exponent,
	SSLPubKey           **pubKey)        // mallocd and RETURNED
{
	if (!pubKey)
		return paramErr;
#if 0
	SSLPubKey *key;
	RSAStatus rsaStatus;
	RSAPubKey apiKey = {
		modulus->data, modulus->length,
		NULL, 0,
		exponent->data, exponent->length
	};

	key = sslMalloc(sizeof(*key));
	rsaStatus = rsaInitPubGKey(&apiKey, &key->rsaKey);
	if (rsaStatus) {
		sslFree(key);
		return rsaStatusToSSL(rsaStatus);
	}

	*pubKey = key;
	return noErr;
#else
	check(pubKey);
	SecRSAPublicKeyParams params = {
		modulus->data, modulus->length,
		exponent->data, exponent->length
	};
#if SSL_DEBUG
	sslDebugLog("Creating RSA pub key from modulus=%p len=%lu exponent=%p len=%lu\n",
			(uintptr_t)modulus->data, modulus->length,
			(uintptr_t)exponent->data, exponent->length);
#endif
	SecKeyRef key = SecKeyCreateRSAPublicKey(NULL, (const uint8_t *)&params,
			sizeof(params), kSecKeyEncodingRSAPublicParams);
	if (!key) {
		sslErrorLog("sslGetPubKeyFromBits: SecKeyCreateRSAPublicKey failed\n");
		return errSSLCrypto;
	}
#if SSL_DEBUG
	size_t blocksize = SecKeyGetBlockSize(key);
	sslDebugLog("sslGetPubKeyFromBits: RSA pub key block size=%lu\n", blocksize);
#endif
	*pubKey = (SSLPubKey*)key;
	return noErr;
#endif
}
/*
 * Obtain maximum size of signature in bytes. A bit of a kludge; we could
 * ask the CSP to do this but that would be kind of expensive.
 */
OSStatus sslGetMaxSigSize(
	SSLPrivKey *privKey,
	size_t           *maxSigSize)
{
	assert(maxSigSize != NULL);

#if 0
#if RSA_SIG_SHARE_GIANT
	*maxSigSize = sizeof(RSASignBuffer);
#else
	*maxSigSize = MAX_PRIME_SIZE_BYTES;
#endif
#else
    *maxSigSize = SecKeyGetBlockSize(SECKEYREF(privKey));
#endif

	return noErr;
}
static CFStringRef SecECPrivateKeyCopyKeyDescription(SecKeyRef key) {
    
    //curve
    long curveType = (long)SecECKeyGetNamedCurve(key);
    char* curve= NULL;
    
    switch (curveType)
 	{
        case 23:
            curve = "kSecECCurveSecp256r1";
            break;
        case 24:
            curve = "kSecECCurveSecp384r1";
            break;
        case 25:
            curve = "kSecECCurveSecp521r1";
            break;
        case -1:
            curve = "kSecECCurveNone";
            break;
        default:
            curve = "kSecECCurveNone";
            break;
    }
    
	return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR( "<SecKeyRef curve type: %s, algorithm id: %lu, key type: %s, version: %d, block size: %zu bits, addr: %p>"), curve, (long)SecKeyGetAlgorithmID(key), key->key_class->name, key->key_class->version, (8*SecKeyGetBlockSize(key)), key);

}
static CFStringRef SecECPublicKeyCopyKeyDescription(SecKeyRef key)
{
    ccec_pub_ctx_t ecPubkey;
    CFStringRef keyDescription = NULL;
    size_t xlen, ylen, ix;
    CFMutableStringRef xString = NULL;
    CFMutableStringRef yString = NULL;

    ecPubkey.pub = key->key;
    
    //curve
    long curveType = (long)SecECKeyGetNamedCurve(key);
    char* curve= NULL;
    
    switch (curveType)
 	{
        case 23:
            curve = "kSecECCurveSecp256r1";
            break;
        case 24:
            curve = "kSecECCurveSecp384r1";
            break;
        case 25:
            curve = "kSecECCurveSecp521r1";
            break;
        case -1:
            curve = "kSecECCurveNone";
            break;
        default:
            curve = "kSecECCurveNone";
            break;
    }

    uint8_t *xunit = (uint8_t*)ccec_ctx_x(ecPubkey);
    require_quiet( NULL != xunit, fail);
    xlen = (size_t)strlen((char*)xunit);

    	
    xString = CFStringCreateMutable(kCFAllocatorDefault, xlen * 2);
    require_quiet( NULL != xString, fail);
    
    for (ix = 0; ix < xlen; ++ix)
    {
		CFStringAppendFormat(xString, NULL, CFSTR("%02X"), xunit[ix]);
    }

    uint8_t *yunit = (uint8_t*)ccec_ctx_y(ecPubkey);
    require_quiet( NULL != yunit, fail);
    ylen = (size_t)strlen((char*)yunit);
    
    yString = CFStringCreateMutable(kCFAllocatorDefault, ylen*2);
    require_quiet( NULL != yString, fail);

    for(ix = 0; ix < ylen; ++ix)
    {
        CFStringAppendFormat(yString, NULL, CFSTR("%02X"), yunit[ix]);
    }
    
    keyDescription = CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR( "<SecKeyRef curve type: %s, algorithm id: %lu, key type: %s, version: %d, block size: %zu bits, y: %@, x: %@, addr: %p>"), curve, (long)SecKeyGetAlgorithmID(key), key->key_class->name, key->key_class->version, (8*SecKeyGetBlockSize(key)), yString, xString, key);
    
fail:
	CFReleaseSafe(xString);
	CFReleaseSafe(yString);
	if(!keyDescription)
		keyDescription = CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecKeyRef curve type: %s, algorithm id: %lu, key type: %s, version: %d, block size: %zu bits, addr: %p>"), curve,(long)SecKeyGetAlgorithmID(key), key->key_class->name, key->key_class->version, (8*SecKeyGetBlockSize(key)), key);
    
	return keyDescription;
}
int init_server_keys(bool ecdsa,
                     unsigned char *cert_der, size_t cert_der_len,
                     unsigned char *key_der, size_t key_der_len,
                     SSLCertificate *cert, tls_private_key_t *key)
{
    int err = 0;

    cert->next = NULL;
    cert->derCert.data = cert_der;
    cert->derCert.length = cert_der_len;

    SecKeyRef privKey = NULL;

#if TARGET_OS_IPHONE
    if(ecdsa)
    {
        privKey = SecKeyCreateECPrivateKey(kCFAllocatorDefault, key_der, key_der_len,
                                           kSecKeyEncodingPkcs1);
    } else {
        privKey = SecKeyCreateRSAPrivateKey(kCFAllocatorDefault, key_der, key_der_len,
                                            kSecKeyEncodingPkcs1);
    }

    require_action(privKey, fail, err=-1);
#else
    // Create the SecKeyRef
    CFErrorRef error = NULL;
    CFDataRef keyData = CFDataCreate(kCFAllocatorDefault, key_der, key_der_len);
    CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
    CFDictionarySetValue(parameters, kSecAttrKeyType, ecdsa?kSecAttrKeyTypeECDSA:kSecAttrKeyTypeRSA);
    CFDictionarySetValue(parameters, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
    privKey = SecKeyCreateFromData(parameters, keyData, &error);

    require_action(privKey, fail, err=(int)CFErrorGetCode(error));
#endif

    size_t keySize = SecKeyGetBlockSize(privKey);

    if(ecdsa) {
#if TARGET_OS_IPHONE
        /* Compute signature size from key size */
        size_t sigSize = 8+2*keySize;
#else
        size_t sigSize = keySize;
#endif
        require((*key = tls_private_key_ecdsa_create(privKey, sigSize,
                                                     SecECKeyGetNamedCurve(privKey), mySSLPrivKeyECDSA_sign)), fail);
    } else {
        require((*key = tls_private_key_rsa_create(privKey, keySize,
                                                   mySSLPrivKeyRSA_sign, mySSLPrivKeyRSA_decrypt)), fail);
    }

    err = 0;

fail:
#if !TARGET_OS_IPHONE
    CFReleaseSafe(parameters);
    CFReleaseSafe(keyData);
    CFReleaseSafe(error);
#endif

    return err;
}
static OSStatus
parseIncomingCerts(CFArrayRef			certs,
                   SSLCertificate       **destCertChain, /* &ctx->{localCertChain,encryptCertChain} */
                   tls_private_key_t    *sslPrivKey)	 /* &ctx->signingPrivKeyRef, etc. */
{
    OSStatus			ortn;
    CFIndex				ix, numCerts;
    SecIdentityRef 		identity;
    SSLCertificate      *certChain = NULL;	/* Retained */
    SecCertificateRef	leafCert = NULL;	/* Retained */
    SecKeyRef           privKey = NULL;	/* Retained */

    assert(destCertChain != NULL);		/* though its referent may be NULL */
    assert(sslPrivKey != NULL);

    if (certs == NULL) {
        sslErrorLog("parseIncomingCerts: NULL incoming cert array\n");
        ortn = errSSLBadCert;
        goto errOut;
    }
    numCerts = CFArrayGetCount(certs);
    if (numCerts == 0) {
        sslErrorLog("parseIncomingCerts: empty incoming cert array\n");
        ortn = errSSLBadCert;
        goto errOut;
    }

    certChain=sslMalloc(numCerts*sizeof(SSLCertificate));
    if (!certChain) {
        ortn = errSecAllocate;
        goto errOut;
    }

    /*
     * Certs[0] is an SecIdentityRef from which we extract subject cert,
     * privKey, pubKey.
     *
     * 1. ensure the first element is a SecIdentityRef.
     */
    identity = (SecIdentityRef)CFArrayGetValueAtIndex(certs, 0);
    if (identity == NULL) {
        sslErrorLog("parseIncomingCerts: bad cert array (1)\n");
        ortn = errSecParam;
        goto errOut;
    }
    if (CFGetTypeID(identity) != SecIdentityGetTypeID()) {
        sslErrorLog("parseIncomingCerts: bad cert array (2)\n");
        ortn = errSecParam;
        goto errOut;
    }

    /*
     * 2. Extract cert, keys and convert to local format.
     */
    ortn = SecIdentityCopyCertificate(identity, &leafCert);
    if (ortn) {
        sslErrorLog("parseIncomingCerts: bad cert array (3)\n");
        goto errOut;
    }

    /* Fetch private key from identity */
    ortn = SecIdentityCopyPrivateKey(identity, &privKey);
    if (ortn) {
        sslErrorLog("parseIncomingCerts: SecIdentityCopyPrivateKey err %d\n",
                    (int)ortn);
        goto errOut;
    }

    /* Convert the input array of SecIdentityRef at the start to an array of
     all certificates. */
    SSLCopyBufferFromData(SecCertificateGetBytePtr(leafCert), SecCertificateGetLength(leafCert), &certChain[0].derCert);
    certChain[0].next = NULL;

    for (ix = 1; ix < numCerts; ++ix) {
        SecCertificateRef intermediate =
        (SecCertificateRef)CFArrayGetValueAtIndex(certs, ix);
        if (intermediate == NULL) {
            sslErrorLog("parseIncomingCerts: bad cert array (5)\n");
            ortn = errSecParam;
            goto errOut;
        }
        if (CFGetTypeID(intermediate) != SecCertificateGetTypeID()) {
            sslErrorLog("parseIncomingCerts: bad cert array (6)\n");
            ortn = errSecParam;
            goto errOut;
        }

        SSLCopyBufferFromData(SecCertificateGetBytePtr(intermediate), SecCertificateGetLength(intermediate), &certChain[ix].derCert);
        certChain[ix].next = NULL;
        certChain[ix-1].next = &certChain[ix];

    }

    size_t size = SecKeyGetBlockSize(privKey);
    tls_private_key_desc_t desc;

    if(SecKeyGetAlgorithmId(privKey) == kSecRSAAlgorithmID) {
        desc.type = tls_private_key_type_rsa;
        desc.rsa.sign = mySSLPrivKeyRSA_sign;
        desc.rsa.decrypt = mySSLPrivKeyRSA_decrypt;
        desc.rsa.size = SecKeyGetBlockSize(privKey);
    } else if (SecKeyGetAlgorithmId(privKey) == kSecECDSAAlgorithmID) {
        desc.type = tls_private_key_type_ecdsa;
        desc.ecdsa.sign = mySSLPrivKeyECDSA_sign;
        desc.ecdsa.curve = SecECKeyGetNamedCurve(privKey);
#if TARGET_OS_IPHONE
        /* Compute signature size from key size */
        desc.ecdsa.size  = 8+2*size;
#else
        desc.ecdsa.size  = size;
#endif
    } else {
        ortn = errSecParam;
        goto errOut;
    }
    *sslPrivKey = tls_private_key_create(&desc, privKey, (tls_private_key_ctx_release)&CFRelease);
    if(*sslPrivKey)
        ortn = errSecSuccess;
    else
        ortn = errSecAllocate;
    
    /* SUCCESS */
errOut:
    CFReleaseSafe(leafCert);

    if (ortn) {
        free(certChain);
        CFReleaseSafe(privKey);
        *destCertChain = NULL;
    } else {
        *destCertChain = certChain;
    }
    
    return ortn;
}