 */

static int
kex_method_ecdh_key_exchange
(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
{
    int ret = 0;
    int rc = 0;
    unsigned char *s;
    libssh2_curve_type type;

    if(key_state->state == libssh2_NB_state_idle) {

        key_state->public_key_oct = NULL;
        key_state->state = libssh2_NB_state_created;
    }

    if(key_state->state == libssh2_NB_state_created) {
        rc = kex_session_ecdh_curve_type(session->kex->name, &type);

        if(rc != 0) {
            ret = _libssh2_error(session, -1,
                                 "Unknown KEX nistp curve type");
            goto ecdh_clean_exit;
        }

        rc = _libssh2_ecdsa_create_key(session, &key_state->private_key, 
                                       &key_state->public_key_oct, &key_state->public_key_oct_len, type);

        if(rc != 0) {
            ret = _libssh2_error(session, rc,
                                 "Unable to create private key");
            goto ecdh_clean_exit;
        }

        key_state->request[0] = SSH2_MSG_KEX_ECDH_INIT;
        s = key_state->request + 1;
        _libssh2_store_str(&s, (const char *)key_state->public_key_oct, key_state->public_key_oct_len);
        key_state->request_len = key_state->public_key_oct_len + 5;

        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                       "Initiating ECDH SHA2 NISTP256");

        key_state->state = libssh2_NB_state_sent;
    }

    if(key_state->state == libssh2_NB_state_sent) {
        rc = _libssh2_transport_send(session, key_state->request,
                                     key_state->request_len, NULL, 0);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
            return rc;
        }
        else if(rc) {
            ret = _libssh2_error(session, rc,
                                 "Unable to send ECDH_INIT");
            goto ecdh_clean_exit;
        }

        key_state->state = libssh2_NB_state_sent1;
    }

    if(key_state->state == libssh2_NB_state_sent1) {
        rc = _libssh2_packet_require(session, SSH2_MSG_KEX_ECDH_REPLY,
                                     &key_state->data, &key_state->data_len,
                                     0, NULL, 0, &key_state->req_state);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
            return rc;
        }
        else if(rc) {
            ret = _libssh2_error(session, rc,
                                 "Timeout waiting for ECDH_REPLY reply");
            goto ecdh_clean_exit;
        }

        key_state->state = libssh2_NB_state_sent2;
    }

    if(key_state->state == libssh2_NB_state_sent2) {

        (void)kex_session_ecdh_curve_type(session->kex->name, &type);

        ret = ecdh_sha2_nistp(session, type, key_state->data, key_state->data_len, (unsigned char *)key_state->public_key_oct,
                             key_state->public_key_oct_len, key_state->private_key, &key_state->exchange_state);

        if(ret == LIBSSH2_ERROR_EAGAIN) {
            return ret;
        }

        LIBSSH2_FREE(session, key_state->data);
    }

ecdh_clean_exit:

    if(key_state->public_key_oct) {
        LIBSSH2_FREE(session, key_state->public_key_oct);
        key_state->public_key_oct = NULL;
    }

    if(key_state->private_key) {
        _libssh2_ecdsa_free(key_state->private_key);
        key_state->private_key = NULL;
    }

    key_state->state = libssh2_NB_state_idle;
 */

static int
kex_method_ecdh_key_exchange
(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
{
    int ret = 0;
    int rc = 0;
    unsigned char *s;
    libssh2_curve_type type;

    if(key_state->state == libssh2_NB_state_idle) {

        key_state->public_key_oct = NULL;
        key_state->state = libssh2_NB_state_created;
    }

    if(key_state->state == libssh2_NB_state_created) {
        rc = kex_session_ecdh_curve_type(session->kex->name, &type);

        if(rc != 0) {
            ret = _libssh2_error(session, -1,
                                 "Unknown KEX nistp curve type");
            goto ecdh_clean_exit;
        }

        rc = _libssh2_ecdsa_create_key(session, &key_state->private_key, 
                                       &key_state->public_key_oct, &key_state->public_key_oct_len, type);

        if(rc != 0) {
            ret = _libssh2_error(session, rc,
                                 "Unable to create private key");
            goto ecdh_clean_exit;
        }

        key_state->request[0] = SSH2_MSG_KEX_ECDH_INIT;
        s = key_state->request + 1;
        _libssh2_store_str(&s, (const char *)key_state->public_key_oct, key_state->public_key_oct_len);
        key_state->request_len = key_state->public_key_oct_len + 5;

        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
                       "Initiating ECDH SHA2 NISTP256");

        key_state->state = libssh2_NB_state_sent;
    }

    if(key_state->state == libssh2_NB_state_sent) {
        rc = _libssh2_transport_send(session, key_state->request,
                                     key_state->request_len, NULL, 0);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
            return rc;
        }
        else if(rc) {
            ret = _libssh2_error(session, rc,
                                 "Unable to send ECDH_INIT");
            goto ecdh_clean_exit;
        }

        key_state->state = libssh2_NB_state_sent1;
    }

    if(key_state->state == libssh2_NB_state_sent1) {
        rc = _libssh2_packet_require(session, SSH2_MSG_KEX_ECDH_REPLY,
                                     &key_state->data, &key_state->data_len,
                                     0, NULL, 0, &key_state->req_state);
        if(rc == LIBSSH2_ERROR_EAGAIN) {
            return rc;
        }
        else if(rc) {
            ret = _libssh2_error(session, rc,
                                 "Timeout waiting for ECDH_REPLY reply");
            goto ecdh_clean_exit;
        }

        key_state->state = libssh2_NB_state_sent2;
    }

    if(key_state->state == libssh2_NB_state_sent2) {

        (void)kex_session_ecdh_curve_type(session->kex->name, &type);

        ret = ecdh_sha2_nistp(session, type, key_state->data, key_state->data_len, (unsigned char *)key_state->public_key_oct,
                             key_state->public_key_oct_len, key_state->private_key, &key_state->exchange_state);

        if(ret == LIBSSH2_ERROR_EAGAIN) {
            return ret;
        }

        LIBSSH2_FREE(session, key_state->data);
    }

ecdh_clean_exit:

    if(key_state->public_key_oct) {
        LIBSSH2_FREE(session, key_state->public_key_oct);
        key_state->public_key_oct = NULL;
    }

    if(key_state->private_key) {
        _libssh2_ecdsa_free(key_state->private_key);
        key_state->private_key = NULL;
    }

    key_state->state = libssh2_NB_state_idle;