diff --git a/include/libssh/curve25519.h b/include/libssh/curve25519.h index 9e05e7a8..a84326b9 100644 --- a/include/libssh/curve25519.h +++ b/include/libssh/curve25519.h @@ -51,6 +51,7 @@ typedef unsigned char ssh_curve25519_pubkey[CURVE25519_PUBKEY_SIZE]; typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE]; int ssh_curve25519_init(ssh_session session); +int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k); int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k); int ssh_client_curve25519_init(ssh_session session); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8fa0e5d4..f8eb2985 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -175,6 +175,13 @@ if (WITH_GCRYPT) chachapoly.c ) endif (NOT HAVE_GCRYPT_CHACHA_POLY) + + if (HAVE_GCRYPT_CURVE25519) + set(libssh_SRCS + ${libssh_SRCS} + curve25519_gcrypt.c + ) + endif(HAVE_GCRYPT_CURVE25519) elseif (WITH_MBEDTLS) set(libssh_SRCS ${libssh_SRCS} @@ -201,13 +208,19 @@ elseif (WITH_MBEDTLS) chachapoly.c ) endif() - + if (HAVE_MBEDTLS_CURVE25519) + set(libssh_SRCS + ${libssh_SRCS} + curve25519_mbedcrypto.c + ) + endif(HAVE_MBEDTLS_CURVE25519) else (WITH_GCRYPT) set(libssh_SRCS ${libssh_SRCS} threads/libcrypto.c pki_crypto.c ecdh_crypto.c + curve25519_crypto.c getrandom_crypto.c md_crypto.c libcrypto.c @@ -264,9 +277,10 @@ if (WITH_GSSAPI AND GSSAPI_FOUND) endif (WITH_GSSAPI AND GSSAPI_FOUND) if (NOT WITH_NACL) - if (NOT HAVE_LIBCRYPTO) + if (NOT (HAVE_LIBCRYPTO OR HAVE_MBEDTLS_CURVE25519 OR HAVE_GCRYPT_CURVE25519)) set(libssh_SRCS ${libssh_SRCS} + curve25519_fallback.c external/curve25519_ref.c ) endif() diff --git a/src/curve25519.c b/src/curve25519.c index 562ccd7b..4172e4b3 100644 --- a/src/curve25519.c +++ b/src/curve25519.c @@ -26,269 +26,43 @@ #include "libssh/curve25519.h" #ifdef HAVE_CURVE25519 -#ifdef WITH_NACL -#include "nacl/crypto_scalarmult_curve25519.h" -#endif - -#include "libssh/ssh2.h" +#include "libssh/bignum.h" #include "libssh/buffer.h" -#include "libssh/priv.h" -#include "libssh/session.h" #include "libssh/crypto.h" #include "libssh/dh.h" #include "libssh/pki.h" -#include "libssh/bignum.h" - -#ifdef HAVE_LIBCRYPTO -#include -#elif defined(HAVE_MBEDTLS_CURVE25519) -#include "mbedcrypto-compat.h" -#include -#include -#elif defined(HAVE_GCRYPT_CURVE25519) -#include -#endif +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/ssh2.h" static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply); static ssh_packet_callback dh_client_callbacks[] = { - ssh_packet_client_curve25519_reply + ssh_packet_client_curve25519_reply, }; static struct ssh_packet_callbacks_struct ssh_curve25519_client_callbacks = { .start = SSH2_MSG_KEX_ECDH_REPLY, .n_callbacks = 1, .callbacks = dh_client_callbacks, - .user = NULL + .user = NULL, }; -int ssh_curve25519_init(ssh_session session) +int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k) { - ssh_curve25519_pubkey *pubkey_loc = NULL; - -#ifdef HAVE_LIBCRYPTO int rc; - EVP_PKEY_CTX *pctx = NULL; - EVP_PKEY *pkey = NULL; - size_t pubkey_len = CURVE25519_PUBKEY_SIZE; -#elif defined(HAVE_MBEDTLS_CURVE25519) - int rc; - mbedtls_ecdh_context ecdh_ctx; - mbedtls_ctr_drbg_context *ctr_drbg = NULL; - char error_buf[128]; - int ret = SSH_ERROR; - -#elif defined(HAVE_GCRYPT_CURVE25519) - gcry_error_t gcry_err; - gcry_sexp_t param = NULL, keypair_sexp = NULL; - ssh_string privkey = NULL, pubkey = NULL; - char *pubkey_data = NULL; - int ret = SSH_ERROR; -#else - int rc; +#ifdef DEBUG_CRYPTO + ssh_log_hexdump("Session server cookie", + session->next_crypto->server_kex.cookie, + 16); + ssh_log_hexdump("Session client cookie", + session->next_crypto->client_kex.cookie, + 16); #endif - if (session->server) { - pubkey_loc = &session->next_crypto->curve25519_server_pubkey; - } else { - pubkey_loc = &session->next_crypto->curve25519_client_pubkey; - } - -#ifdef HAVE_LIBCRYPTO - pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL); - if (pctx == NULL) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to initialize X25519 context: %s", - ERR_error_string(ERR_get_error(), NULL)); - return SSH_ERROR; - } - - rc = EVP_PKEY_keygen_init(pctx); - if (rc != 1) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to initialize X25519 keygen: %s", - ERR_error_string(ERR_get_error(), NULL)); - EVP_PKEY_CTX_free(pctx); - return SSH_ERROR; - } - - rc = EVP_PKEY_keygen(pctx, &pkey); - EVP_PKEY_CTX_free(pctx); - if (rc != 1) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to generate X25519 keys: %s", - ERR_error_string(ERR_get_error(), NULL)); - return SSH_ERROR; - } - - rc = EVP_PKEY_get_raw_public_key(pkey, *pubkey_loc, &pubkey_len); - if (rc != 1) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to get X25519 raw public key: %s", - ERR_error_string(ERR_get_error(), NULL)); - EVP_PKEY_free(pkey); - return SSH_ERROR; - } - - session->next_crypto->curve25519_privkey = pkey; - pkey = NULL; - -#elif defined(HAVE_MBEDTLS_CURVE25519) - ctr_drbg = ssh_get_mbedtls_ctr_drbg_context(); - - mbedtls_ecdh_init(&ecdh_ctx); - rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519); - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf); - goto out; - } - -#if MBEDTLS_VERSION_MAJOR > 2 - rc = mbedtls_ecdh_gen_public(&ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(grp), - &ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(d), - &ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(Q), - mbedtls_ctr_drbg_random, - ctr_drbg); -#else - rc = mbedtls_ecdh_gen_public(&ecdh_ctx.grp, - &ecdh_ctx.d, - &ecdh_ctx.Q, - mbedtls_ctr_drbg_random, - ctr_drbg); -#endif - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, - "Failed to generate X25519 keypair: %s", - error_buf); - goto out; - } - -#if MBEDTLS_VERSION_MAJOR > 2 - rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(d), - session->next_crypto->curve25519_privkey, - CURVE25519_PRIVKEY_SIZE); -#else - rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.d, - session->next_crypto->curve25519_privkey, - CURVE25519_PRIVKEY_SIZE); -#endif - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, - "Failed to write X25519 private key: %s", - error_buf); - goto out; - } - -#if MBEDTLS_VERSION_MAJOR > 2 - rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(Q) - .MBEDTLS_PRIVATE(X), - *pubkey_loc, - CURVE25519_PUBKEY_SIZE); -#else - rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.Q.X, - *pubkey_loc, - CURVE25519_PUBKEY_SIZE); -#endif - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, - "Failed to write X25519 public key: %s", - error_buf); - goto out; - } - - ret = SSH_OK; - -out: - mbedtls_ecdh_free(&ecdh_ctx); - return ret; - -#elif defined(HAVE_GCRYPT_CURVE25519) - gcry_err = - gcry_sexp_build(¶m, NULL, "(genkey (ecdh (curve Curve25519)))"); - if (gcry_err != GPG_ERR_NO_ERROR) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to create keypair sexp: %s", - gcry_strerror(gcry_err)); - goto out; - } - - gcry_err = gcry_pk_genkey(&keypair_sexp, param); - if (gcry_err != GPG_ERR_NO_ERROR) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to generate keypair: %s", - gcry_strerror(gcry_err)); - goto out; - } - - /* Extract the public key */ - pubkey = ssh_sexp_extract_mpi(keypair_sexp, - "q", - GCRYMPI_FMT_USG, - GCRYMPI_FMT_STD); - if (pubkey == NULL) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to extract public key: %s", - gcry_strerror(gcry_err)); - goto out; - } - - /* Store the public key in the session */ - /* The first byte should be 0x40 indicating that the point is compressed, so - * we skip storing it */ - pubkey_data = (char *)ssh_string_data(pubkey); - if (ssh_string_len(pubkey) != CURVE25519_PUBKEY_SIZE + 1 || - pubkey_data[0] != 0x40) { - SSH_LOG(SSH_LOG_TRACE, - "Invalid public key with length: %zu", - ssh_string_len(pubkey)); - goto out; - } - - memcpy(*pubkey_loc, pubkey_data + 1, CURVE25519_PUBKEY_SIZE); - - /* Store the private key */ - session->next_crypto->curve25519_privkey = keypair_sexp; - keypair_sexp = NULL; - ret = SSH_OK; - -out: - ssh_string_burn(privkey); - SSH_STRING_FREE(privkey); - ssh_string_burn(pubkey); - SSH_STRING_FREE(pubkey); - gcry_sexp_release(param); - gcry_sexp_release(keypair_sexp); - return ret; - -#else - rc = ssh_get_random(session->next_crypto->curve25519_privkey, - CURVE25519_PRIVKEY_SIZE, 1); - if (rc != 1) { - ssh_set_error(session, SSH_FATAL, "PRNG error"); - return SSH_ERROR; - } - - crypto_scalarmult_base(*pubkey_loc, - session->next_crypto->curve25519_privkey); - -#endif /* HAVE_LIBCRYPTO */ - - return SSH_OK; + rc = curve25519_do_create_k(session, k); + return rc; } /** @internal @@ -327,311 +101,6 @@ void ssh_client_curve25519_remove_callbacks(ssh_session session) ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks); } -int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k) -{ - ssh_curve25519_pubkey *peer_pubkey_loc = NULL; - -#ifdef HAVE_LIBCRYPTO - int rc, ret = SSH_ERROR; - EVP_PKEY_CTX *pctx = NULL; - EVP_PKEY *pkey = NULL, *pubkey = NULL; - size_t shared_key_len = CURVE25519_PUBKEY_SIZE; - -#elif defined(HAVE_MBEDTLS_CURVE25519) - int rc, ret = SSH_ERROR; - mbedtls_ecdh_context ecdh_ctx; - mbedtls_ctr_drbg_context *ctr_drbg = NULL; - char error_buf[128]; - -#elif defined(HAVE_GCRYPT_CURVE25519) - gcry_error_t gcry_err; - gcry_sexp_t pubkey_sexp = NULL, privkey_data_sexp = NULL, - result_sexp = NULL; - ssh_string shared_secret = NULL, privkey = NULL; - char *shared_secret_data = NULL; - int ret = SSH_ERROR; - -#endif - if (session->server) { - peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey; - } else { - peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey; - } - -#ifdef HAVE_LIBCRYPTO - pkey = session->next_crypto->curve25519_privkey; - if (pkey == NULL) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to create X25519 EVP_PKEY: %s", - ERR_error_string(ERR_get_error(), NULL)); - return SSH_ERROR; - } - - pctx = EVP_PKEY_CTX_new(pkey, NULL); - if (pctx == NULL) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to initialize X25519 context: %s", - ERR_error_string(ERR_get_error(), NULL)); - goto out; - } - - rc = EVP_PKEY_derive_init(pctx); - if (rc != 1) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to initialize X25519 key derivation: %s", - ERR_error_string(ERR_get_error(), NULL)); - goto out; - } - - pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, - NULL, - *peer_pubkey_loc, - CURVE25519_PUBKEY_SIZE); - if (pubkey == NULL) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to create X25519 public key EVP_PKEY: %s", - ERR_error_string(ERR_get_error(), NULL)); - goto out; - } - - rc = EVP_PKEY_derive_set_peer(pctx, pubkey); - if (rc != 1) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to set peer X25519 public key: %s", - ERR_error_string(ERR_get_error(), NULL)); - goto out; - } - - rc = EVP_PKEY_derive(pctx, k, &shared_key_len); - if (rc != 1) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to derive X25519 shared secret: %s", - ERR_error_string(ERR_get_error(), NULL)); - goto out; - } - ret = SSH_OK; -out: - EVP_PKEY_free(pubkey); - EVP_PKEY_CTX_free(pctx); - if (ret == SSH_ERROR) { - return ret; - } - -#elif defined(HAVE_MBEDTLS_CURVE25519) - ctr_drbg = ssh_get_mbedtls_ctr_drbg_context(); - - mbedtls_ecdh_init(&ecdh_ctx); - rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519); - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf); - goto out; - } - -#if MBEDTLS_VERSION_MAJOR > 2 - rc = mbedtls_mpi_read_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(d), - session->next_crypto->curve25519_privkey, - CURVE25519_PRIVKEY_SIZE); -#else - rc = mbedtls_mpi_read_binary_le(&ecdh_ctx.d, - session->next_crypto->curve25519_privkey, - CURVE25519_PRIVKEY_SIZE); -#endif - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, "Failed to read private key: %s", error_buf); - goto out; - } - -#if MBEDTLS_VERSION_MAJOR > 2 - rc = mbedtls_mpi_read_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(Qp) - .MBEDTLS_PRIVATE(X), - *peer_pubkey_loc, - CURVE25519_PUBKEY_SIZE); -#else - rc = mbedtls_mpi_read_binary_le(&ecdh_ctx.Qp.X, - *peer_pubkey_loc, - CURVE25519_PUBKEY_SIZE); -#endif - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, "Failed to read peer public key: %s", error_buf); - goto out; - } - -#if MBEDTLS_VERSION_MAJOR > 2 - rc = mbedtls_mpi_lset(&ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(Qp) - .MBEDTLS_PRIVATE(Z), - 1); -#else - rc = mbedtls_mpi_lset(&ecdh_ctx.Qp.Z, 1); -#endif - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, "Failed to set Z coordinate: %s", error_buf); - goto out; - } - -#if MBEDTLS_VERSION_MAJOR > 2 - rc = mbedtls_ecdh_compute_shared(&ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(grp), - &ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(z), - &ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(Qp), - &ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(d), - mbedtls_ctr_drbg_random, - ctr_drbg); -#else - rc = mbedtls_ecdh_compute_shared(&ecdh_ctx.grp, - &ecdh_ctx.z, - &ecdh_ctx.Qp, - &ecdh_ctx.d, - mbedtls_ctr_drbg_random, - ctr_drbg); -#endif - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, - "Failed to compute shared secret: %s", - error_buf); - goto out; - } - -#if MBEDTLS_VERSION_MAJOR > 2 - rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.MBEDTLS_PRIVATE(ctx) - .MBEDTLS_PRIVATE(mbed_ecdh) - .MBEDTLS_PRIVATE(z), - k, - CURVE25519_PUBKEY_SIZE); -#else - rc = mbedtls_mpi_write_binary_le(&ecdh_ctx.z, k, CURVE25519_PUBKEY_SIZE); -#endif - if (rc != 0) { - mbedtls_strerror(rc, error_buf, sizeof(error_buf)); - SSH_LOG(SSH_LOG_TRACE, "Failed to write shared secret: %s", error_buf); - goto out; - } - - ret = SSH_OK; - -out: - mbedtls_ecdh_free(&ecdh_ctx); - if (ret == SSH_ERROR) { - return ret; - } - -#elif defined(HAVE_GCRYPT_CURVE25519) - gcry_err = gcry_sexp_build( - &pubkey_sexp, - NULL, - "(key-data(public-key (ecdh (curve Curve25519) (q %b))))", - CURVE25519_PUBKEY_SIZE, - *peer_pubkey_loc); - if (gcry_err != GPG_ERR_NO_ERROR) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to create peer public key sexp: %s", - gcry_strerror(gcry_err)); - goto out; - } - - privkey = ssh_sexp_extract_mpi(session->next_crypto->curve25519_privkey, - "d", - GCRYMPI_FMT_USG, - GCRYMPI_FMT_STD); - if (privkey == NULL) { - SSH_LOG(SSH_LOG_TRACE, "Failed to extract private key"); - goto out; - } - - gcry_err = gcry_sexp_build(&privkey_data_sexp, - NULL, - "(data(flags raw)(value %b))", - ssh_string_len(privkey), - ssh_string_data(privkey)); - if (gcry_err != GPG_ERR_NO_ERROR) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to create private key sexp: %s", - gcry_strerror(gcry_err)); - goto out; - } - - gcry_err = gcry_pk_encrypt(&result_sexp, privkey_data_sexp, pubkey_sexp); - if (gcry_err != GPG_ERR_NO_ERROR) { - SSH_LOG(SSH_LOG_TRACE, - "Failed to compute shared secret: %s", - gcry_strerror(gcry_err)); - goto out; - } - - shared_secret = ssh_sexp_extract_mpi(result_sexp, - "s", - GCRYMPI_FMT_USG, - GCRYMPI_FMT_USG); - if (shared_secret == NULL) { - SSH_LOG(SSH_LOG_TRACE, "Failed to extract shared secret"); - goto out; - } - - /* Copy the shared secret to the output buffer */ - /* The first byte should be 0x40 indicating that it is a compressed point, - * so we skip it */ - shared_secret_data = (char *)ssh_string_data(shared_secret); - if (ssh_string_len(shared_secret) != CURVE25519_PUBKEY_SIZE + 1 || - shared_secret_data[0] != 0x40) { - SSH_LOG(SSH_LOG_TRACE, - "Invalid shared secret with length: %zu", - ssh_string_len(shared_secret)); - goto out; - } - - memcpy(k, shared_secret_data + 1, CURVE25519_PUBKEY_SIZE); - - ret = SSH_OK; - gcry_sexp_release(session->next_crypto->curve25519_privkey); - session->next_crypto->curve25519_privkey = NULL; - -out: - ssh_string_burn(shared_secret); - SSH_STRING_FREE(shared_secret); - ssh_string_burn(privkey); - SSH_STRING_FREE(privkey); - gcry_sexp_release(privkey_data_sexp); - gcry_sexp_release(pubkey_sexp); - gcry_sexp_release(result_sexp); - if (ret == SSH_ERROR) { - return ret; - } - -#else - crypto_scalarmult(k, - session->next_crypto->curve25519_privkey, - *peer_pubkey_loc); -#endif /* HAVE_LIBCRYPTO */ - -#ifdef DEBUG_CRYPTO - ssh_log_hexdump("Session server cookie", - session->next_crypto->server_kex.cookie, - 16); - ssh_log_hexdump("Session client cookie", - session->next_crypto->client_kex.cookie, - 16); -#endif - - return 0; -} - static int ssh_curve25519_build_k(ssh_session session) { ssh_curve25519_pubkey k; @@ -642,7 +111,9 @@ static int ssh_curve25519_build_k(ssh_session session) return rc; } - bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->shared_secret); + bignum_bin2bn(k, + CURVE25519_PUBKEY_SIZE, + &session->next_crypto->shared_secret); if (session->next_crypto->shared_secret == NULL) { return SSH_ERROR; } @@ -651,98 +122,102 @@ static int ssh_curve25519_build_k(ssh_session session) ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret); #endif - return 0; + return SSH_OK; } /** @internal * @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back * a SSH_MSG_NEWKEYS */ -static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){ - ssh_string q_s_string = NULL; - ssh_string pubkey_blob = NULL; - ssh_string signature = NULL; - int rc; - (void)type; - (void)user; +static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply) +{ + ssh_string q_s_string = NULL; + ssh_string pubkey_blob = NULL; + ssh_string signature = NULL; + int rc; + (void)type; + (void)user; - ssh_client_curve25519_remove_callbacks(session); + ssh_client_curve25519_remove_callbacks(session); - pubkey_blob = ssh_buffer_get_ssh_string(packet); - if (pubkey_blob == NULL) { - ssh_set_error(session,SSH_FATAL, "No public key in packet"); - goto error; - } + pubkey_blob = ssh_buffer_get_ssh_string(packet); + if (pubkey_blob == NULL) { + ssh_set_error(session, SSH_FATAL, "No public key in packet"); + goto error; + } - rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); - SSH_STRING_FREE(pubkey_blob); - if (rc != 0) { - ssh_set_error(session, - SSH_FATAL, - "Failed to import next public key"); - goto error; - } + rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob); + SSH_STRING_FREE(pubkey_blob); + if (rc != 0) { + ssh_set_error(session, SSH_FATAL, "Failed to import next public key"); + goto error; + } - q_s_string = ssh_buffer_get_ssh_string(packet); - if (q_s_string == NULL) { - ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet"); - goto error; - } - if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE){ - ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", - (int)ssh_string_len(q_s_string)); - SSH_STRING_FREE(q_s_string); - goto error; - } - memcpy(session->next_crypto->curve25519_server_pubkey, ssh_string_data(q_s_string), CURVE25519_PUBKEY_SIZE); - SSH_STRING_FREE(q_s_string); + q_s_string = ssh_buffer_get_ssh_string(packet); + if (q_s_string == NULL) { + ssh_set_error(session, SSH_FATAL, "No Q_S ECC point in packet"); + goto error; + } + if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE) { + ssh_set_error(session, + SSH_FATAL, + "Incorrect size for server Curve25519 public key: %zu", + ssh_string_len(q_s_string)); + SSH_STRING_FREE(q_s_string); + goto error; + } + memcpy(session->next_crypto->curve25519_server_pubkey, + ssh_string_data(q_s_string), + CURVE25519_PUBKEY_SIZE); + SSH_STRING_FREE(q_s_string); - signature = ssh_buffer_get_ssh_string(packet); - if (signature == NULL) { - ssh_set_error(session, SSH_FATAL, "No signature in packet"); - goto error; - } - session->next_crypto->dh_server_signature = signature; - signature=NULL; /* ownership changed */ - /* TODO: verify signature now instead of waiting for NEWKEYS */ - if (ssh_curve25519_build_k(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - goto error; - } + signature = ssh_buffer_get_ssh_string(packet); + if (signature == NULL) { + ssh_set_error(session, SSH_FATAL, "No signature in packet"); + goto error; + } + session->next_crypto->dh_server_signature = signature; + signature = NULL; /* ownership changed */ + /* TODO: verify signature now instead of waiting for NEWKEYS */ + if (ssh_curve25519_build_k(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + goto error; + } - /* Send the MSG_NEWKEYS */ - rc = ssh_packet_send_newkeys(session); - if (rc == SSH_ERROR) { - goto error; - } - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + /* Send the MSG_NEWKEYS */ + rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { + goto error; + } + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - return SSH_PACKET_USED; + return SSH_PACKET_USED; error: - session->session_state=SSH_SESSION_STATE_ERROR; - return SSH_PACKET_USED; + session->session_state = SSH_SESSION_STATE_ERROR; + return SSH_PACKET_USED; } #ifdef WITH_SERVER static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init); -static ssh_packet_callback dh_server_callbacks[]= { - ssh_packet_server_curve25519_init +static ssh_packet_callback dh_server_callbacks[] = { + ssh_packet_server_curve25519_init, }; static struct ssh_packet_callbacks_struct ssh_curve25519_server_callbacks = { .start = SSH2_MSG_KEX_ECDH_INIT, .n_callbacks = 1, .callbacks = dh_server_callbacks, - .user = NULL + .user = NULL, }; /** @internal * @brief sets up the curve25519-sha256@libssh.org kex callbacks */ -void ssh_server_curve25519_init(ssh_session session){ +void ssh_server_curve25519_init(ssh_session session) +{ /* register the packet callbacks */ ssh_packet_set_callbacks(session, &ssh_curve25519_server_callbacks); } @@ -750,7 +225,8 @@ void ssh_server_curve25519_init(ssh_session session){ /** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a * SSH_MSG_KEXDH_REPLY */ -static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ +static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init) +{ /* ECDH keys */ ssh_string q_c_string = NULL; ssh_string q_s_string = NULL; @@ -769,10 +245,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ /* Extract the client pubkey from the init packet */ q_c_string = ssh_buffer_get_ssh_string(packet); if (q_c_string == NULL) { - ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet"); + ssh_set_error(session, SSH_FATAL, "No Q_C ECC point in packet"); goto error; } - if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE){ + if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE) { ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %zu", @@ -781,7 +257,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ } memcpy(session->next_crypto->curve25519_client_pubkey, - ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE); + ssh_string_data(q_c_string), + CURVE25519_PUBKEY_SIZE); SSH_STRING_FREE(q_c_string); /* Build server's key pair */ @@ -823,8 +300,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){ } /* add host's public key */ - rc = ssh_buffer_add_ssh_string(session->out_buffer, - server_pubkey_blob); + rc = ssh_buffer_add_ssh_string(session->out_buffer, server_pubkey_blob); SSH_STRING_FREE(server_pubkey_blob); if (rc < 0) { ssh_set_error_oom(session); @@ -885,7 +361,7 @@ error: SSH_STRING_FREE(q_c_string); SSH_STRING_FREE(q_s_string); ssh_buffer_reinit(session->out_buffer); - session->session_state=SSH_SESSION_STATE_ERROR; + session->session_state = SSH_SESSION_STATE_ERROR; return SSH_PACKET_USED; } diff --git a/src/curve25519_crypto.c b/src/curve25519_crypto.c new file mode 100644 index 00000000..b7e18411 --- /dev/null +++ b/src/curve25519_crypto.c @@ -0,0 +1,158 @@ +/* + * curve25519_crypto.c - Curve25519 ECDH functions for key exchange (OpenSSL) + * + * This file is part of the SSH Library + * + * Copyright (c) 2013-2023 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 2.1 of the License. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/curve25519.h" + +#include "libssh/crypto.h" +#include "libssh/priv.h" +#include "libssh/session.h" + +#include +#include + +int ssh_curve25519_init(ssh_session session) +{ + ssh_curve25519_pubkey *pubkey_loc = NULL; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; + size_t pubkey_len = CURVE25519_PUBKEY_SIZE; + int rc; + + if (session->server) { + pubkey_loc = &session->next_crypto->curve25519_server_pubkey; + } else { + pubkey_loc = &session->next_crypto->curve25519_client_pubkey; + } + + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL); + if (pctx == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to initialize X25519 context: %s", + ERR_error_string(ERR_get_error(), NULL)); + return SSH_ERROR; + } + + rc = EVP_PKEY_keygen_init(pctx); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to initialize X25519 keygen: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_PKEY_CTX_free(pctx); + return SSH_ERROR; + } + + rc = EVP_PKEY_keygen(pctx, &pkey); + EVP_PKEY_CTX_free(pctx); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to generate X25519 keys: %s", + ERR_error_string(ERR_get_error(), NULL)); + return SSH_ERROR; + } + + rc = EVP_PKEY_get_raw_public_key(pkey, *pubkey_loc, &pubkey_len); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to get X25519 raw public key: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_PKEY_free(pkey); + return SSH_ERROR; + } + + session->next_crypto->curve25519_privkey = pkey; + pkey = NULL; + + return SSH_OK; +} + +int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k) +{ + ssh_curve25519_pubkey *peer_pubkey_loc = NULL; + int rc, ret = SSH_ERROR; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL, *pubkey = NULL; + size_t shared_key_len = CURVE25519_PUBKEY_SIZE; + + if (session->server) { + peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey; + } else { + peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey; + } + + pkey = session->next_crypto->curve25519_privkey; + if (pkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create X25519 EVP_PKEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + return SSH_ERROR; + } + + pctx = EVP_PKEY_CTX_new(pkey, NULL); + if (pctx == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to initialize X25519 context: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + rc = EVP_PKEY_derive_init(pctx); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to initialize X25519 key derivation: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + pubkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, + NULL, + *peer_pubkey_loc, + CURVE25519_PUBKEY_SIZE); + if (pubkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create X25519 public key EVP_PKEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + rc = EVP_PKEY_derive_set_peer(pctx, pubkey); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to set peer X25519 public key: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + rc = EVP_PKEY_derive(pctx, k, &shared_key_len); + if (rc != 1) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to derive X25519 shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + ret = SSH_OK; + +out: + EVP_PKEY_free(pubkey); + EVP_PKEY_CTX_free(pctx); + return ret; +} diff --git a/src/curve25519_fallback.c b/src/curve25519_fallback.c new file mode 100644 index 00000000..e05331d5 --- /dev/null +++ b/src/curve25519_fallback.c @@ -0,0 +1,73 @@ +/* + * curve25519_fallback.c - Curve25519 ECDH functions for key exchange + * + * This file is part of the SSH Library + * + * Copyright (c) 2013-2023 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 2.1 of the License. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/curve25519.h" + +#include "libssh/crypto.h" +#include "libssh/priv.h" +#include "libssh/session.h" + +#ifdef WITH_NACL +#include "nacl/crypto_scalarmult_curve25519.h" +#endif + +int ssh_curve25519_init(ssh_session session) +{ + ssh_curve25519_pubkey *pubkey_loc = NULL; + int rc; + + if (session->server) { + pubkey_loc = &session->next_crypto->curve25519_server_pubkey; + } else { + pubkey_loc = &session->next_crypto->curve25519_client_pubkey; + } + + rc = ssh_get_random(session->next_crypto->curve25519_privkey, + CURVE25519_PRIVKEY_SIZE, + 1); + if (rc != 1) { + ssh_set_error(session, SSH_FATAL, "PRNG error"); + return SSH_ERROR; + } + + crypto_scalarmult_base(*pubkey_loc, + session->next_crypto->curve25519_privkey); + + return SSH_OK; +} + +int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k) +{ + ssh_curve25519_pubkey *peer_pubkey_loc = NULL; + + if (session->server) { + peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey; + } else { + peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey; + } + + crypto_scalarmult(k, + session->next_crypto->curve25519_privkey, + *peer_pubkey_loc); + return SSH_OK; +} diff --git a/src/curve25519_gcrypt.c b/src/curve25519_gcrypt.c new file mode 100644 index 00000000..5fae8cde --- /dev/null +++ b/src/curve25519_gcrypt.c @@ -0,0 +1,199 @@ +/* + * curve25519_gcrypt.c - Curve25519 ECDH functions for key exchange (Gcrypt) + * + * This file is part of the SSH Library + * + * Copyright (c) 2013-2023 by Aris Adamantiadis + * Copyright (c) 2025 Praneeth Sarode + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 2.1 of the License. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/curve25519.h" + +#include "libssh/buffer.h" +#include "libssh/crypto.h" +#include "libssh/priv.h" +#include "libssh/session.h" + +#include + +int ssh_curve25519_init(ssh_session session) +{ + ssh_curve25519_pubkey *pubkey_loc = NULL; + gcry_error_t gcry_err; + gcry_sexp_t param = NULL, keypair_sexp = NULL; + ssh_string pubkey = NULL; + const char *pubkey_data = NULL; + int ret = SSH_ERROR; + + if (session->server) { + pubkey_loc = &session->next_crypto->curve25519_server_pubkey; + } else { + pubkey_loc = &session->next_crypto->curve25519_client_pubkey; + } + + gcry_err = + gcry_sexp_build(¶m, NULL, "(genkey (ecdh (curve Curve25519)))"); + if (gcry_err != GPG_ERR_NO_ERROR) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create keypair sexp: %s", + gcry_strerror(gcry_err)); + goto out; + } + + gcry_err = gcry_pk_genkey(&keypair_sexp, param); + if (gcry_err != GPG_ERR_NO_ERROR) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to generate keypair: %s", + gcry_strerror(gcry_err)); + goto out; + } + + /* Extract the public key */ + pubkey = ssh_sexp_extract_mpi(keypair_sexp, + "q", + GCRYMPI_FMT_USG, + GCRYMPI_FMT_STD); + if (pubkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to extract public key: %s", + gcry_strerror(gcry_err)); + goto out; + } + + /* Store the public key in the session */ + /* The first byte should be 0x40 indicating that the point is compressed, so + * we skip storing it */ + pubkey_data = (char *)ssh_string_data(pubkey); + if (ssh_string_len(pubkey) != CURVE25519_PUBKEY_SIZE + 1 || + pubkey_data[0] != 0x40) { + SSH_LOG(SSH_LOG_TRACE, + "Invalid public key with length: %zu", + ssh_string_len(pubkey)); + goto out; + } + + memcpy(*pubkey_loc, pubkey_data + 1, CURVE25519_PUBKEY_SIZE); + + /* Store the private key */ + session->next_crypto->curve25519_privkey = keypair_sexp; + keypair_sexp = NULL; + ret = SSH_OK; + +out: + ssh_string_burn(pubkey); + SSH_STRING_FREE(pubkey); + gcry_sexp_release(param); + gcry_sexp_release(keypair_sexp); + return ret; +} + +int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k) +{ + ssh_curve25519_pubkey *peer_pubkey_loc = NULL; + gcry_error_t gcry_err; + gcry_sexp_t pubkey_sexp = NULL, privkey_data_sexp = NULL, + result_sexp = NULL; + ssh_string shared_secret = NULL, privkey = NULL; + char *shared_secret_data = NULL; + int ret = SSH_ERROR; + + if (session->server) { + peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey; + } else { + peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey; + } + + gcry_err = gcry_sexp_build( + &pubkey_sexp, + NULL, + "(key-data(public-key (ecdh (curve Curve25519) (q %b))))", + CURVE25519_PUBKEY_SIZE, + *peer_pubkey_loc); + if (gcry_err != GPG_ERR_NO_ERROR) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create peer public key sexp: %s", + gcry_strerror(gcry_err)); + goto out; + } + + privkey = ssh_sexp_extract_mpi(session->next_crypto->curve25519_privkey, + "d", + GCRYMPI_FMT_USG, + GCRYMPI_FMT_STD); + if (privkey == NULL) { + SSH_LOG(SSH_LOG_TRACE, "Failed to extract private key"); + goto out; + } + + gcry_err = gcry_sexp_build(&privkey_data_sexp, + NULL, + "(data(flags raw)(value %b))", + ssh_string_len(privkey), + ssh_string_data(privkey)); + if (gcry_err != GPG_ERR_NO_ERROR) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to create private key sexp: %s", + gcry_strerror(gcry_err)); + goto out; + } + + gcry_err = gcry_pk_encrypt(&result_sexp, privkey_data_sexp, pubkey_sexp); + if (gcry_err != GPG_ERR_NO_ERROR) { + SSH_LOG(SSH_LOG_TRACE, + "Failed to compute shared secret: %s", + gcry_strerror(gcry_err)); + goto out; + } + + shared_secret = ssh_sexp_extract_mpi(result_sexp, + "s", + GCRYMPI_FMT_USG, + GCRYMPI_FMT_USG); + if (shared_secret == NULL) { + SSH_LOG(SSH_LOG_TRACE, "Failed to extract shared secret"); + goto out; + } + + /* Copy the shared secret to the output buffer */ + /* The first byte should be 0x40 indicating that it is a compressed point, + * so we skip it */ + shared_secret_data = (char *)ssh_string_data(shared_secret); + if (ssh_string_len(shared_secret) != CURVE25519_PUBKEY_SIZE + 1 || + shared_secret_data[0] != 0x40) { + SSH_LOG(SSH_LOG_TRACE, + "Invalid shared secret with length: %zu", + ssh_string_len(shared_secret)); + goto out; + } + + memcpy(k, shared_secret_data + 1, CURVE25519_PUBKEY_SIZE); + + ret = SSH_OK; + gcry_sexp_release(session->next_crypto->curve25519_privkey); + session->next_crypto->curve25519_privkey = NULL; + +out: + ssh_string_burn(shared_secret); + SSH_STRING_FREE(shared_secret); + ssh_string_burn(privkey); + SSH_STRING_FREE(privkey); + gcry_sexp_release(privkey_data_sexp); + gcry_sexp_release(pubkey_sexp); + gcry_sexp_release(result_sexp); + return ret; +} diff --git a/src/curve25519_mbedcrypto.c b/src/curve25519_mbedcrypto.c new file mode 100644 index 00000000..f328f7b7 --- /dev/null +++ b/src/curve25519_mbedcrypto.c @@ -0,0 +1,189 @@ +/* + * curve25519_mbedcrypto.c - Curve25519 ECDH functions for key exchange + * (MbedTLS) + * + * This file is part of the SSH Library + * + * Copyright (c) 2013-2023 by Aris Adamantiadis + * Copyright (c) 2025 Praneeth Sarode + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 2.1 of the License. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/curve25519.h" + +#include "libssh/crypto.h" +#include "libssh/priv.h" +#include "libssh/session.h" +#include "mbedcrypto-compat.h" + +#include +#include + +int ssh_curve25519_init(ssh_session session) +{ + ssh_curve25519_pubkey *pubkey_loc = NULL; + mbedtls_ecdh_context ecdh_ctx; + mbedtls_ecdh_params *ecdh_params = NULL; + mbedtls_ctr_drbg_context *ctr_drbg = NULL; + int rc, ret = SSH_ERROR; + char error_buf[128]; + + if (session->server) { + pubkey_loc = &session->next_crypto->curve25519_server_pubkey; + } else { + pubkey_loc = &session->next_crypto->curve25519_client_pubkey; + } + + ctr_drbg = ssh_get_mbedtls_ctr_drbg_context(); + + mbedtls_ecdh_init(&ecdh_ctx); + rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf); + goto out; + } + + ecdh_params = &MBEDTLS_ECDH_PARAMS(ecdh_ctx); + + rc = mbedtls_ecdh_gen_public(&ecdh_params->MBEDTLS_ECDH_PRIVATE(grp), + &ecdh_params->MBEDTLS_ECDH_PRIVATE(d), + &ecdh_params->MBEDTLS_ECDH_PRIVATE(Q), + mbedtls_ctr_drbg_random, + ctr_drbg); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, + "Failed to generate X25519 keypair: %s", + error_buf); + goto out; + } + + rc = mbedtls_mpi_write_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(d), + session->next_crypto->curve25519_privkey, + CURVE25519_PRIVKEY_SIZE); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, + "Failed to write X25519 private key: %s", + error_buf); + goto out; + } + + rc = mbedtls_mpi_write_binary_le( + &ecdh_params->MBEDTLS_ECDH_PRIVATE(Q).MBEDTLS_ECDH_PRIVATE(X), + *pubkey_loc, + CURVE25519_PUBKEY_SIZE); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, + "Failed to write X25519 public key: %s", + error_buf); + goto out; + } + + ret = SSH_OK; + +out: + mbedtls_ecdh_free(&ecdh_ctx); + return ret; +} + +int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k) +{ + ssh_curve25519_pubkey *peer_pubkey_loc = NULL; + int rc, ret = SSH_ERROR; + mbedtls_ecdh_context ecdh_ctx; + mbedtls_ecdh_params *ecdh_params = NULL; + mbedtls_ctr_drbg_context *ctr_drbg = NULL; + char error_buf[128]; + + if (session->server) { + peer_pubkey_loc = &session->next_crypto->curve25519_client_pubkey; + } else { + peer_pubkey_loc = &session->next_crypto->curve25519_server_pubkey; + } + + ctr_drbg = ssh_get_mbedtls_ctr_drbg_context(); + + mbedtls_ecdh_init(&ecdh_ctx); + rc = mbedtls_ecdh_setup(&ecdh_ctx, MBEDTLS_ECP_DP_CURVE25519); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, "Failed to setup X25519 context: %s", error_buf); + goto out; + } + + ecdh_params = &MBEDTLS_ECDH_PARAMS(ecdh_ctx); + + rc = mbedtls_mpi_read_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(d), + session->next_crypto->curve25519_privkey, + CURVE25519_PRIVKEY_SIZE); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, "Failed to read private key: %s", error_buf); + goto out; + } + + rc = mbedtls_mpi_read_binary_le( + &ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp).MBEDTLS_ECDH_PRIVATE(X), + *peer_pubkey_loc, + CURVE25519_PUBKEY_SIZE); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, "Failed to read peer public key: %s", error_buf); + goto out; + } + + rc = mbedtls_mpi_lset( + &ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp).MBEDTLS_ECDH_PRIVATE(Z), + 1); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, "Failed to set Z coordinate: %s", error_buf); + goto out; + } + + rc = mbedtls_ecdh_compute_shared(&ecdh_params->MBEDTLS_ECDH_PRIVATE(grp), + &ecdh_params->MBEDTLS_ECDH_PRIVATE(z), + &ecdh_params->MBEDTLS_ECDH_PRIVATE(Qp), + &ecdh_params->MBEDTLS_ECDH_PRIVATE(d), + mbedtls_ctr_drbg_random, + ctr_drbg); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, + "Failed to compute shared secret: %s", + error_buf); + goto out; + } + + rc = mbedtls_mpi_write_binary_le(&ecdh_params->MBEDTLS_ECDH_PRIVATE(z), + k, + CURVE25519_PUBKEY_SIZE); + if (rc != 0) { + mbedtls_strerror(rc, error_buf, sizeof(error_buf)); + SSH_LOG(SSH_LOG_TRACE, "Failed to write shared secret: %s", error_buf); + goto out; + } + + ret = SSH_OK; + +out: + mbedtls_ecdh_free(&ecdh_ctx); + return ret; +} diff --git a/src/mbedcrypto-compat.h b/src/mbedcrypto-compat.h index 705294a7..f028854f 100644 --- a/src/mbedcrypto-compat.h +++ b/src/mbedcrypto-compat.h @@ -5,13 +5,17 @@ * v3 defines the version inside build_info.h so if it isn't defined * in version.h we should have v3 */ -#include #include -#ifdef MBEDTLS_VERSION_MAJOR +#include + +#ifndef MBEDTLS_VERSION_MAJOR +#include +#endif /* MBEDTLS_VERSION_MAJOR */ + #if MBEDTLS_VERSION_MAJOR < 3 -static inline size_t mbedtls_cipher_info_get_key_bitlen( - const mbedtls_cipher_info_t *info) +static inline size_t +mbedtls_cipher_info_get_key_bitlen(const mbedtls_cipher_info_t *info) { if (info == NULL) { return 0; @@ -19,8 +23,8 @@ static inline size_t mbedtls_cipher_info_get_key_bitlen( return info->key_bitlen; } -static inline size_t mbedtls_cipher_info_get_iv_size( - const mbedtls_cipher_info_t *info) +static inline size_t +mbedtls_cipher_info_get_iv_size(const mbedtls_cipher_info_t *info) { if (info == NULL) { return 0; @@ -29,11 +33,24 @@ static inline size_t mbedtls_cipher_info_get_iv_size( } #define MBEDTLS_PRIVATE(X) X + +#ifdef HAVE_MBEDTLS_CURVE25519 +#include + +#define MBEDTLS_ECDH_PRIVATE(X) X +#define MBEDTLS_ECDH_PARAMS(X) X +typedef mbedtls_ecdh_context mbedtls_ecdh_params; +#endif /* HAVE_MBEDTLS_CURVE25519 */ + +#else /* MBEDTLS_VERSION_MAJOR < 3 */ + +#ifdef HAVE_MBEDTLS_CURVE25519 +#include + +#define MBEDTLS_ECDH_PRIVATE(X) MBEDTLS_PRIVATE(X) +#define MBEDTLS_ECDH_PARAMS(X) X.MBEDTLS_PRIVATE(ctx).MBEDTLS_PRIVATE(mbed_ecdh) +typedef mbedtls_ecdh_context_mbed mbedtls_ecdh_params; +#endif /* HAVE_MBEDTLS_CURVE25519 */ + #endif /* MBEDTLS_VERSION_MAJOR < 3 */ -#else /* MBEDTLS_VERSION_MAJOR */ -#include -#if MBEDTLS_VERSION_MAJOR < 3 -#define MBEDTLS_PRIVATE(X) X -#endif /* MBEDTLS_VERSION_MAJOR < 3 */ -#endif /* MBEDTLS_VERSION_MAJOR */ #endif /* MBEDCRYPTO_COMPAT_H */